一 Jode-Time 介绍
任何企业应用程序都需要处理时间问题。应用程序需要知道当前的时间点和下一个时间点,有时它们还必须计算这两个
时间点之间的路径。使用 JDK 完成这项任务将非常痛苦和繁琐。
既然无法摆脱时间,为何不设法简化时间处理?现在来看看 Joda Time,一个面向 Java™ 平台的易于
使用的开源时间/日期库。正如您在本文中了解的那样,JodaTime轻松化解了处理日期和时间的痛苦和繁琐。
Joda-Time 令时间和日期值变得易于管理、操作和理解。事实上,易于使用是 Joda 的主要设计目标。其他目标包括可扩展性、完整的特性集以及对多种日历系统的支持。
并且 Joda 与 JDK 是百分之百可互操作的,因此您无需替换所有 Java 代码,只需要替换执行日期/时间计算的那部分代码。
Joda-Time提供了一组Java类包用于处理包括ISO8601标准在内的date和time。可以利用它把JDK Date和Calendar类完全替换掉,而且仍然能够提供很好的集成。
为什么要使用 Joda?
考虑创建一个用时间表示的某个随意的时刻 — 比如,2000 年 1 月 1 日 0 时 0 分。
我如何创建一个用时间表示这个瞬间的 JDK 对象?使用 java.util.Date?
事实上这是行不通的,因为自 JDK 1.1 之后的每个 Java 版本的 Javadoc 都声明应当使用 java.util.Calendar。
Date 中不赞成使用的构造函数的数量严重限制了您创建此类对象的途径。
那么 Calendar 又如何呢?我将使用下面的方式创建必需的实例:
Calendar calendar = Calendar.getInstance(); calendar.set(2000, Calendar.JANUARY, 1, 0, 0, 0);
使用 Joda,代码应该类似如下所示:
DateTime dateTime = new DateTime(2000, 1, 1, 0, 0, 0, 0);
这一行简单代码没有太大的区别。但是现在我将使问题稍微复杂化。
假设我希望在这个日期上加上 90 天并输出结果。使用 JDK,我需要使用清单 1 中的代码:
// 以 JDK 的方式向某一个瞬间加上 90 天并输出结果
Calendar calendar = Calendar.getInstance();
calendar.set(2000, Calendar.JANUARY, 1, 0, 0, 0);
SimpleDateFormat sdf =
new SimpleDateFormat("E MM/dd/yyyy HH:mm:ss.SSS");
calendar.add(Calendar.DAY_OF_MONTH, 90);
System.out.println(sdf.format(calendar.getTime()));
// 以 Joda 的方式向某一个瞬间加上 90 天并输出结果
DateTime dateTime = new DateTime(2000, 1, 1, 0, 0, 0, 0);
System.out.println(dateTime.plusDays(90).toString("E MM/dd/yyyy HH:mm:ss.SSS");
两者之间的差距拉大了(Joda 用了两行代码,JDK 则是 5 行代码)。
现在假设我希望输出这样一个日期:距离 2000.1.1日 45 天之后的某天在下一个月的当前周的最后一天的日期。
坦白地说,我甚至不想使用 Calendar 处理这个问题。
使用 JDK 实在太痛苦了,即使是简单的日期计算,比如上面这个计算。
正是多年前的这样一个时刻,我第一次领略到 JodaTime的强大。使用 Joda,用于计算的代码所示:
DateTime dateTime = new DateTime(2000, 1, 1, 0, 0, 0, 0);
System.out.println(dateTime.plusDays(45).plusMonths(1).dayOfWeek()
.withMaximumValue().toString("E MM/dd/yyyy HH:mm:ss.SSS");
输出为:
Sun 03/19/2000 00:00:00.000
如果您正在寻找一种易于使用的方式替代 JDK 日期处理,那么您真的应该考虑 Joda。
二 创建Joda-Time对象
现在,我将展示在采用该库时会经常遇到的一些 Joda 类,并展示如何创建这些类的实例。
ReadableInstant
Joda 通过 ReadableInstant 类实现了瞬间性这一概念。表示时间上的不可变瞬间的 Joda 类都属于这个类的子类。
(将这个类命名为ReadOnlyInstant 可能更好,我认为这才是设计者需要传达的意思)。
换句话说,ReadableInstant 表示时间上的某一个不可修改的瞬间。
其中的两个子类分别为 DateTime 和 DateMidnight:
DateTime:这是最常用的一个类。它以毫秒级的精度封装时间上的某个瞬间时刻。
DateTime 始终与 DateTimeZone 相关,如果您不指定它的话,它将被默认设置为运行代码的机器所在的时区。
可以使用多种方式构建 DateTime 对象。这个构造函数使用系统时间:
DateTime dateTime = new DateTime();
如果您创建了一个 DateTime 的实例,并且没有提供 Chronology 或 DateTimeZone,Joda将使用 ISOChronology(默认)和DateTimeZone(来自系统设置)
Joda 可以使您精确地控制创建 DateTime 对象的方式,该对象表示时间上的某个特定的瞬间。
1 DateTime dateTime = new DateTime( 2 2000, //year 3 1, // month 4 1, // day 5 0, // hour (midnight is zero) 6 0, // minute 7 0, // second 8 0 // milliseconds 9 );
下一个构造函数将指定从 epoch(1970年1月1日 子时 格林威治标准时间) 到某个时刻所经过的毫秒数。
它根据 JDK Date 对象的毫秒值创建一个DateTime 对象,其时间精度用毫秒表示,因为 epoch 与 Joda 是相同的:
1 java.util.Date jdkDate = new Date(); 2 long timeInMillis = jdkDate.getTime(); 3 DateTime dateTime = new DateTime(timeInMillis);
或者Date 对象直接传递给构造函数:
dateTime = new DateTime(new Date());
Joda 支持使用许多其他对象作为构造函数的参数,用于创建 DateTime:
1 // Use a Calendar 2 dateTime = new DateTime(calendar); 3 4 // Use another Joda DateTime 5 dateTime = new DateTime(anotherDateTime); 6 7 // Use a String (must be formatted properly) 8 String timeString = "2006-01-26T13:30:00-06:00"; 9 dateTime = new DateTime(timeString); 10 timeString = "2006-01-26"; 11 dateTime = new DateTime(timeString);
注意,如果您准备使用 String(必须经过解析),您必须对其进行精确地格式化。
DateMidnight:这个类封装某个时区(通常为默认时区)在特定年/月/日的午夜时分的时刻。
它基本上类似于 DateTime,不同之处在于时间部分总是为与该对象关联的特定 DateTimeZone 时区的午夜时分。
ReadablePartial
应用程序所需处理的日期问题并不全部都与时间上的某个完整时刻有关,因此您可以处理一个局部时刻。
例如,有时您比较关心年/月/日,或者一天中的时间,甚至是一周中的某天。Joda 设计者使用ReadablePartial 接口捕捉这种表示局部时间的概念,
这是一个不可变的局部时间片段。用于处理这种时间片段的两个有用类分别为 LocalDate 和 LocalTime:
LocalDate:该类封装了一个年/月/日的组合。当地理位置(即时区)变得不重要时,使用它存储日期将非常方便。
例如,某个特定对象的出生日期 可能为 1999 年 4 月 16 日,但是从技术角度来看,
在保存所有业务值的同时不会了解有关此日期的任何其他信息(比如这是一周中的星期几,或者这个人出生地所在的时区)。
在这种情况下,应当使用 LocalDate。
LocalTime:这个类封装一天中的某个时间,当地理位置不重要的情况下,可以使用这个类来只存储一天当中的某个时间。
例如,晚上 11:52 可能是一天当中的一个重要时刻(比如,一个 cron 任务将启动,它将备份文件系统的某个部分),
但是这个时间并没有特定于某一天,因此我不需要了解有关这一时刻的其他信息。
创建对象代码:
1 package com.jt.joda; 2 3 import java.util.Date; 4 5 import org.joda.time.DateTime; 6 import org.joda.time.LocalDate; 7 import org.joda.time.LocalTime; 8 import org.junit.Test; 9 10 public class Demo { 11 12 @Test 13 public void test1(){ 14 15 //方法一:取系统点间 16 DateTime dt1 = new DateTime(); 17 System.out.println(dt1); 18 19 //方法二:通过java.util.Date对象生成 20 DateTime dt2 = new DateTime(new Date()); 21 System.out.println(dt2); 22 23 //方法三:指定年月日点分秒生成(参数依次是:年,月,日,时,分,秒,毫秒) 24 DateTime dt3 = new DateTime(2012, 5, 20, 13, 14, 0, 0); 25 System.out.println(dt3); 26 //方法四:ISO8601形式生成 27 DateTime dt4 = new DateTime("2012-05-20"); 28 System.out.println(dt4); 29 DateTime dt5 = new DateTime("2012-05-20T13:14:00"); 30 System.out.println(dt5); 31 32 //只需要年月日的时候 33 LocalDate localDate = new LocalDate(2009, 9, 6);// September 6, 2009 34 System.out.println(localDate); 35 36 //只需要时分秒毫秒的时候 37 LocalTime localTime = new LocalTime(13, 30, 26, 0);// 1:30:26PM 38 System.out.println(localTime); 39 40 } 41 /* 42 2015-09-25T17:51:12.900+08:00 43 2015-09-25T17:51:12.977+08:00 44 2012-05-20T13:14:00.000+08:00 45 2012-05-20T00:00:00.000+08:00 46 2012-05-20T13:14:00.000+08:00 47 2009-09-06 48 13:30:26.000 49 */ 50 51 }