commons-lang(time应用)

JAVA的时间日期处理一直是一个比较复杂的问题,可以说大多数程序员都不能得心应手的处理这些问题,就算通过commons.lang.time包简化了,使用起来仍然有些麻烦,个人认为这可能是最复杂的一个子包。不过相对原始的JDK API,还是简化了不少,因此此子包也相当值得关注。

首先讨论一下关于时间的类,从 JDK 1.1 开始,Date的作用很有限,相应的功能已由Calendar与DateFormat代替。使用 Calendar 类实现日期和时间字段之间转换,使用 DateFormat 类来格式化和分析日期字符串。Calendar 类是一个抽象类,它为特定瞬间与一组诸如 YEAR、MONTH、DAY_OF_MONTH、HOUR 等 日历字段之间的转换提供了一些方法,并为操作日历字段(例如获得下星期的日期)提供了一些方法。TimeZone 表示时区偏移量。Locale 对象表示了特定的地理、政治和文化地区。需要 Locale 来执行其任务的操作称为语言环境敏感的操作,它使用 Locale 为用户量身定制信息。SimpleDateFormat的作用主要是用来格式化Date,用过之后就会发现,它其实很不完善,对Calendar提供的支持很少.

关于时间的类放在org.apache.commons.lang.time 包下面,它包括以下几个类:
DateUtils 包括一组围绕Calendar与Date的实用方法.
DateFormatUtils
DurationFormatUtils
FastDateFormat 一个快速线程安全的SimpleDateFormat版本
StopWatch 提供一个方便的定时的API

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) 


当然DateFormatUtils还有一些预定义字段。对应的则是相应的FastDateFormat。
可以这样用:

DateFormatUtils.ISO_DATETIME_FORMAT.format(date)


通过公有的静态final字段来暴露这些常量简化了类的使用,但是却有一些地方是需要注意的,在这方面FastDateFormat做得还是相当不错的。要使final字段真正的不可变,要么这些字段是基本类型的值(int,String等),要么是指向一个不可变对象的引用。同时需要注意的特殊情况是,长度为零的数组总是可变的,要么通过不可变的Collection来包装,要么使数组变成私有的,并添加一个公有的方法,返回一个数组的备份,这在effective java上的Item 15上有详细的介绍。回到原来的话题上,DateFormatUtils内部定义了一些常量,如:


 public static final FastDateFormat ISO_DATE_FORMAT
            = FastDateFormat.getInstance("yyyy-MM-dd");


上面的final 字段代表一个不可变的FastDateFormat,然而要让FastDateFormat字段真正的不可变,FastDateFormat内部必须遵循相应的规则才可以。

不要提供能修改对象状态的方法
确保类不会被继承
让所有字段都成为static final字段
确保所有可变的组件不能被访问

详细介绍可参考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 类中,对应方法则更加的强大:

parseDate(java.lang.String str, java.lang.String[] parsePatterns) 


因parsePatterns可以包括多种pattern,只要满足其中的一种即可。如果使用SimpleDateFormat,则先要通过SimpleDateFormat(String str)创建实例,然后通过applyPattern(String pattern)来变换不同的pattern。DateUtils提供了很多很方便的功能,减轻了使用Date的复杂性。把原来需用Calendar才能完成的功能统一集中了起来,也就是说没有对应的CalendarUtils类。在JDK中,Date与Calendar概念本身就有些混淆,只是为了保持兼容性才引入的Calendar。相对于Calendar提供的方法,DateUtils提供了更加合理的方法,对时间的单个字段操作变得更加的容易。如需要修改时间Date的某个字段,必须先获得Date对象实例,再传入Calendar,才能修改,如:

	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的确做得很完善,如:

	public static Date addMinutes(Date date, int amount) {
        return add(date, Calendar.MINUTE, amount);
    }

方法名也非常的直观,使用也更加方便了。

但有一些方法不是很好理解,如:

public static long getFragmentInSeconds(Date date,int fragment)


这个方法的作用是:返回一个指定时间的秒数。关键的是参数fragment,它的作用非常重要。它的值必须是Calendar的时间常量字段。如Calendar.MONTH ,需要注意的是,小时必须用24小时制的,即Calendar.HOUR_OF_DAY ,而不能用Calendar.HOUR字段。如果使用
Calendar.HOUR_OF_DAY 则时间2009-09-29 17:02:37 会返回157 (2*60+37),即所有大于等于fragment单位的字段将被忽略。

相对于这些增强已有功能的类,还有一些对常用功能进行补充的类,如DurationFormatUtils ,这个类主要的作用就是处理时间的片断,主要包括两种方法:
formatDuration和formatPeriod。如:

formatDuration(long durationMillis, java.lang.String format) 


通过传入一个毫秒数与日期格式(如:yyyy-MM-dd HH:mm:ss),它会返回一个对应日期的字符串形式。当然Date类本身有一个与这类似的方法,即Date(String s)方法,用于创建一个Date实例,但它只是创建一个Date实例,如果要转换成相应的String,还要经过一些步骤才行。需要注意的是,此日期片断能表示的最大单位为天,用法也很简单:

String pattern = "yyyy-MM-dd HH:mm:ss";
long durationMillis = (10+20*60+13*3600+4*24*3600) * 1000;
String formatDate = DurationFormatUtils.formatDuration(durationMillis,
				pattern);
System.out.println(formatDate);



需要注意的是日期格式的小时必须用HH,而不能用hh 。
下面介绍一下formatPeriod方法:

public static java.lang.String formatPeriod(long startMillis,
                                            long endMillis,
                                            java.lang.String format)

用于计算两个时间之间的片断,然后转化成相应的日期字符串类型,即能表示的最大单位,如下例所示:
String[] parsePatterns = {"yyyy-MM-dd HH:mm:ss"};
String str = "2009-09-29 15:30:12";
String str2 = "2010-09-30 15:40:18";
Date date = DateUtils.parseDate(str, parsePatterns);
Date date2 = DateUtils.parseDate(str2, parsePatterns);
long durationMillis = DateUtils.getFragmentInMilliseconds(date, Calendar.YEAR);
long durationMillis2 =DateUtils.getFragmentInMilliseconds(date2,Calendar.YEAR);

String s =	DurationFormatUtils.formatPeriod(durationMillis, durationMillis2, 										"yyyy-MM-dd HH:mm:ss")

其中s的值为:0000-00-01 00:10:06

上述两种方法功能有些相似,本人一直以为底层用了同样的实现方法,因为看上去只要把两个时间点计算成毫秒,相后相减就与第一个方法一样了,实际不然。它们的底层完全不一样,各用各的实现方式,没有任何交差,根本原来就在于它们能表示的最大单位不一样,如下例如示:

String[] parsePatterns = {"yyyy-MM-dd HH:mm:ss"};
		String str = "2009-05-29 15:30:12";
		String str2 = "2011-09-30 14:40:18";
		Date date = DateUtils.parseDate(str, parsePatterns);
		Date date2 = DateUtils.parseDate(str2, parsePatterns);
long durationMillis = DateUtils.getFragmentInMilliseconds(date, Calendar.YEAR);
long durationMillis2 =DateUtils.getFragmentInMilliseconds(date2,Calendar.YEAR);
		System.out.println(DurationFormatUtils.formatPeriod(durationMillis, 
											durationMillis2, "yyyy-MM-dd HH:mm:ss"));
		System.out.println(DurationFormatUtils.formatDuration(durationMillis2-											durationMillis,"yyyy-MM-dd HH:mm:ss"));


结果为:

0000-04-01 23:10:06
0000-00-123 23:10:06


time包还有一个StopWatch的类,此类主要作用是用来记时,有start,split,suspend之类的方法,平时能用到的地方不多。

你可能感兴趣的:(java,jdk,多线程,应用服务器,出版)