JAVA的时间日期处理一直是一个比较复杂的问题,大多数程序员都不能很轻松的来处理这些问题。首先Java中关于时间的类,从 JDK 1.1 开始,Date的作用很有限,相应的功能已由Calendar与DateFormat代替。使用Calendar类实现日期和时间字段之间转换,使用 DateFormat 类来格式化和分析日期字符串。
Calendar 类是一个抽象类,它为特定瞬间与一组诸如 YEAR、MONTH、DAY_OF_MONTH、HOUR 等 日历字段之间的转换提供了一些方法,并为操作日历字段(例如获得下星期的日期)提供了一些方法。
TimeZone : 表示时区偏移量。
Locale : 表示了特定的地理、政治和文化地区。需要 Locale 来执行其任务的操作称为语言环境敏感的操作,它使用 Locale 为用户量身定制信息。
SimpleDateFormat : 主要是用来格式化Date,用过之后就会发现,它其实不完善,对Calendar提供的支持很少.
经历几个项目发现apache提供的第三方扩展类库,org.apache.commons.lang.time包比较好用,可以将程序中时间处理变的简单一点,提高你的开发效率,下面介绍下常用的方法和具体使用。
org.apache.commons.lang.time 包括以下几个类:
a. DateFormatUtils 【格式化Calendar与Date并依赖于 FastDateFormat】
b. DateUtils 【围绕Calendar与Date的实用方法】
c. DurationFormatUtils 【毫秒数格式化Calendar与Date】
d. FastDateFormat 【线程安全的SimpleDateFormat】
e. StopWatch 【提供一个方便的定时的API 】
1. DateFormatUtils 和 FastDateFormat
DateFormatUtils相对来说比较简单,它的方法全部都是static的,所以不需要用构造器创建新的实例,但它构造器却是public的,这并不是说我们应该在程序中使用它,官方文档已说明,它是为了与其它工具的集成的准备的。它包括的方法主要就是format。主要用途就是根据传入的pattern格式化Date或Calendar。也可以有一些附加的参数,如Locale,TimeZone。
函数定义:
format(java.util.Calendar calendar, java.lang.String pattern, java.util.TimeZone timeZone, java.util.Locale locale)
原来项目中是这样写的:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String nowDay = sdf.format(new Date());
现在可以这样写:
String nowDay = DateFormatUtils.format(nowDay,"yyyy-MM-dd HH:mm:ss");
DateFormatUtils预定义的日期格式有9种之多,基本能满足项目时间格式化的要求,当然也可以自己自定义格式:
public static final FastDateFormat ISO_DATETIME_FORMAT = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss"); public static final FastDateFormat ISO_DATETIME_TIME_ZONE_FORMAT = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ssZZ"); public static final FastDateFormat ISO_DATE_FORMAT = FastDateFormat.getInstance("yyyy-MM-dd"); public static final FastDateFormat ISO_DATE_TIME_ZONE_FORMAT = FastDateFormat.getInstance("yyyy-MM-ddZZ"); public static final FastDateFormat ISO_TIME_FORMAT = FastDateFormat.getInstance("'T'HH:mm:ss"); public static final FastDateFormat ISO_TIME_TIME_ZONE_FORMAT = FastDateFormat.getInstance("'T'HH:mm:ssZZ"); public static final FastDateFormat ISO_TIME_NO_T_FORMAT = FastDateFormat.getInstance("HH:mm:ss"); public static final FastDateFormat ISO_TIME_NO_T_TIME_ZONE_FORMAT = FastDateFormat.getInstance("HH:mm:ssZZ");
所以上面的也可以这样写:
DateFormatUtils.ISO_DATETIME_FORMAT.format(date);
DateFormatUtils 预定义的日期格式,通过公有的静态final字段来暴露这些常量简化了类的使用,但是却有一些地方是需要注意的,在这方面FastDateFormat做得还是相当不错的。要使final字段真正的不可变,
a. 这些字段是基本类型的值(int,String等),
b. 指向一个不可变对象的引用。
c. 需要注意的特殊情况是,长度为零的数组总是可变的,要么通过不可变的Collection来包装,
要么使数组变成私有的,并添加一个公有的方法,返回一个数组的备份,这在effective java上的Item 15上有详细的介绍。上面的final 字段代表一个不可变的FastDateFormat,然而要让FastDateFormat字段真正的不可变,
FastDateFormat内部必须遵循相应的规则才可以。
a. 不要提供能修改对象状态的方法
b. 确保类不会被继承
c. 让所有字段都成为static final字段
d. 确保所有可变的组件不能被访问
详细介绍可参考Effective java第二版的Item 15。仔细查看,会发现FastDateFormat都遵循这些规则,而Effective java 2rd 的出版也在commons-lang 2.4 开发之后,这说明 commons-lang的代码质量还是相当值得肯定的。
其实DateFormateUtils内部细节实现完全依靠FastDateFormat,DateFormateUtils只是把一些常用的格式化功能单独组织起来,让日期时间的使用变得简单,毕竟大多数时候用户查看API时,如果有太多的方法,会给他们纷繁复杂的感觉。如果需要更加强大灵活的日期格式化功能,可以直接使用FastDateFormat,FastDateFormat这个类编写比较复杂,它有自己的一套解析规则,同时又使用了DateFormat类的一些规则,如Pattern。与SimpleDateFormat不同,
FastDateFormat是线程安全,所以这个类在多线程的服务环境中特别有用。
虽然它们都继承自java.text.Format,其实FastDateFormat相当于DateFormat与SimpleDateFormat的合并,只是功能更强大而已。如果是从DateFormat迁移到FastDateFormat的话,还是有一些地方需要注意的,
比如,Date(String date)被DateFormat.parse(String s) 取代了,仔细查看,FastDateFormat并没有类似的方法,其实准确的说,把日期解析放在DateFormat本身就不太合理,不看文档的话,大多数人都会认为Date应该提供该功能。所以commons-lang把它放在了DateUtils 类中,对应方法则更加的强大:
eg:
public static Date parseDate(String str, String parsePatterns[]); String partterns[] = new String[5]; partterns[0] = "yyyy-MM-dd"; DateUtils.parseDate("2015-09-28", partterns);
因parsePatterns可以包括多种pattern,只要满足其中的一种即可。如果使用SimpleDateFormat,则先要通过SimpleDateFormat(String str)创建实例,然后通过applyPattern(String pattern)来变换不同的pattern。
2. DateUtils
DateUtils提供了很多很方便的功能,减轻了使用Date的复杂性。把原来需用Calendar才能完成的功能统一集中了起来,也就是说没有对应的CalendarUtils类。在JDK中,Date与Calendar概念本身就有些混淆,只是为了保持兼容性才引入的Calendar。相对于Calendar提供的方法,DateUtils提供了更加合理的方法,
对时间的单个字段操作变得更加的容易。如需要修改时间Date的某个字段,必须先获得Date对象实例,再传入Calendar,才能修改,如:
eg:
//方法已经过时了 public static Date add(Date date, int calendarField, int amount) { if (date == null) { throw new IllegalArgumentException("The date must not be null"); } Calendar c = Calendar.getInstance(); c.setTime(date); c.add(calendarField, amount); return c.getTime(); }
在这方面commons-lang的确做得很完善,现在可以这样写:
eg:
方法名也非常的直观,使用也更加方便了。但有一些方法不是很好理解,就像下面这个函数:
eg:
1 public static long getFragmentInSeconds(Date date,int fragment) 2 3 这个方法的作用是:返回一个指定时间的秒数。关键的是参数fragment,它的作用非常重要。它的值必须是Calendar的时间常量字段。如Calendar.MONTH , 4 5 需要注意的是,小时必须用24小时制的,即Calendar.HOUR_OF_DAY ,而不能用Calendar.HOUR字段。 6 /* 7 * 如现在是2014-10-23 13:27:00,那么 8 * DateUtils.getFragmentInDays(new Date(), Calendar.MONTH)返回23,表示从当月起已经过去23天了, 9 * DateUtils.getFragmentInDays(new Date(), Calendar.YEAR)返回296,表示从当年起已经过去296天了, 10 * DateUtils.getFragmentInHours(new Date(), Calendar.DATE)返回13,表示从今天起已经过去13个小时了 11 */
当然在DateUtils也有一些一看就知道是做什么的函数,我在这里列一下,不具体给例子了:
@Description: 判断是否是同一天 public static boolean isSameDay(Calendar cal1, Calendar cal2) @Description: Date 转 Calendar public static Calendar toCalendar(Date date); @Description: 从日期特定字段开始四舍五入 public static Calendar round(Calendar date, int field); * @Description: 格式化截取日期,从给定字段开始 public static Date truncate(Date date, int field); @Description: 从日期特定字段开始向上舍入 public static Date ceiling(Date date, int field); eg : 假设当前时间 2015-09-14 ceiling(new Date(),Calendar.MONTH); 输出 2015-10-01 round(new Date(), Calendar.MONTH); 输出 2015-09-01 假设当前时间 2015-09-16,超过半月,上面输出结果是一致的 @Description: 比较日历对应字段是否相等 public static boolean truncatedEquals(Calendar cal1, Calendar cal2, int field);
3. DurationFormatUtils 和 StopWatch
相对于这些增强已有功能的类,还有一些对常用功能进行补充的类,如DurationFormatUtils ,这个类主要的作用就是处理时间的片断,主要包括两种方法:formatDuration和formatPeriod。如:
eg:
1 formatDuration(long durationMillis, java.lang.String format) 2 4 通过传入一个毫秒数与日期格式(如:yyyy-MM-dd HH:mm:ss),它会返回一个对应日期的字符串形式。 5 6 当然Date类本身有一个与这类似的方法,即Date(String s)方法,用于创建一个Date实例,但它只是创建一个Date实例,如果要转换成相应的String,还要经过一些步骤才行。 7 8 需要注意的是,此日期片断能表示的最大单位为天,用法也很简单: 9 11 String pattern = "yyyy-MM-dd HH:mm:ss"; 12 long durationMillis = (10+20*60+13*3600+4*24*3600) * 1000; 13 String formatDate = DurationFormatUtils.formatDuration(durationMillis, pattern); 14 System.out.println(formatDate);
需要注意的是日期格式的小时必须用HH,而不能用hh 。
formatPeriod方法用于计算两个时间之间的片断,然后转化成相应的日期字符串类型,即能表示的最大单位
1 public static java.lang.String formatPeriod(long startMillis, long endMillis, java.lang.String format) 2 3 4 String[] parsePatterns = {"yyyy-MM-dd HH:mm:ss"}; 5 String str = "2009-09-29 15:30:12"; 6 String str2 = "2010-09-30 15:40:18"; 7 Date date = DateUtils.parseDate(str, parsePatterns); 8 Date date2 = DateUtils.parseDate(str2, parsePatterns); 9 long durationMillis = DateUtils.getFragmentInMilliseconds(date, Calendar.YEAR); 10 long durationMillis2 =DateUtils.getFragmentInMilliseconds(date2,Calendar.YEAR); 11 12 String s = DurationFormatUtils.formatPeriod(durationMillis, durationMillis2,"yyyy-MM-dd HH:mm:ss") 13 14 其中s的值为:0000-00-01 00:10:06
StopWatch 是一个计时器,具体举个简单的例子:
StopWatch stWatch = new StopWatch(); stWatch.start(); /** * 从周日开始,获取一周的 Calendar 对象 */ Iterator<?> itr = iterator(new Date(),RANGE_MONTH_MONDAY); while (itr.hasNext()) { Calendar gCal = (Calendar) itr.next(); System.out.println(formatDateObject(gCal.getTime(),"yyyy-MM-dd HH:mm:ss")); } stWatch.stop(); System.out.println("花费时间 >>" + stWatch.getTime() + ",毫秒");
本文转自Orson博客园博客,原文链接:http://www.cnblogs.com/java-class/p/4845962.html,如需转载请自行联系原作者