引用文章:
快速入门指南(英文)
官方API(2.9.7)
只有在Java开发中,经历过日期换算的困扰,才会寻求更好的日期格式。在Jdk方式里,我们需要运用Date,SimpleDateFormat,Canledar三个类里的相关方法才能实现日期的换算操作。如果是使用Oracle开发,那可能会把时间的处理放在Sql语句中来避免使用Jdk处理。如:增加(减少)一个月的计算。在平时的项目中,难免会涉及日期的处理。今天就来介绍一个面向 Java™ 平台的易于使用的开源时间/日期库,Joda-Time,它将会在日期处理上给我们带来极大的便利,简化Jdk方式的复杂和冗长。
Joda-Time 令时间和日期值变得易于管理、操作和理解。事实上,易于使用是 Joda 的主要设计目标。其他目标包括可扩展性、完整的特性集以及对多种日历系统的支持。并且 Joda 与 JDK 是百分之百可互操作的,因此您无需替换所有 Java 代码,只需要替换执行日期/时间计算的那部分代码。
Joda 简介及特性
简介
Joda 和 JDK 互操作性
JDK Calendar 类缺乏可用性,这一点很快就能体会到,而 Joda 弥补了这一不足。Joda 的设计者还做出了一个决定,我认为这是它取得成功的构建:JDK 互操作性。Joda 的类能够生成(但是,正如您将看到的一样,有时会采用一种比较迂回的方式)java.util.Date 的实例(和 Calendar)。这使您能够保留现有的依赖 JDK 的代码,但是又能够使用 Joda 处理复杂的日期/时间计算。
例如以下代码:
Calendar calendar = Calendar.getInstance();
DateTime dateTime = new DateTime(2017, 1, 17, 0, 0, 0, 0);
System.out.println(dateTime.plusDays(20).plusMonths(1).toString("yyyy-MM-dd HH:mm:ss.SSS");
calendar.setTime(dateTime.toDate());
DateTime和Calendar两者可以实现完美转化。除此之外,在后面,将会看到更多的Joda和JDK的互操作性。
特性
- 本地日期(LocalDate) -包含日期不包含时间
- 本地时间(LocalTime) -包含时间不包含日期
- 瞬间(Instant) -一个时间线上的瞬时点
- 日期时间(DateTime) -包含日期和时间与时区
- 时区(DateTimeZone) -一个更好的时区
- 时间跨度(Duration and Period)-时间量
- 区间(Interval) -两个时刻之间的时间(特定的时间跨度)
- 一个全面并且灵活的格式化解析器
Joda 关键日期时间概念
Joda 使用以下概念,它们可以应用到任何日期/时间库:
- 不可变性(Immutability)
- 瞬间性(Instant)
- 局部性(Partial)
- 年表(Chronology)
- 时区(Time zone)
不可变性
瞬间性
局部性
年表
- ISO
- GJ (GregorianJulian)
- Gregorian
- Julian
- Buddhist
- Coptic
- Ethiopic
- Islamic
时区
创建 Joda-Time 对象
现在,我将展示在采用该库时会经常遇到的一些 Joda 类,并展示如何创建这些类的实例。
本节中介绍的所有实现都具有若干构造函数,允许您初始化封装的日期/时间。它们可以分为 4 个类别:
- 使用系统时间。
- 使用多个字段指定一个瞬间时刻(或局部时间片段),达到这个特定实现所能支持的最细粒度的精确度。
- 指定一个瞬间时刻(或局部时间片段),以毫秒为单位。
- 使用另一个对象(例如,java.util.Date,或者是另一个 Joda 对象,或者一个符合格式的String字符串)。
ReadableInstant
Joda 通过 ReadableInstant 类实现了瞬间性这一概念。表示时间上的不可变瞬间的 Joda 类都属于这个类的子类。(将这个类命名为 ReadOnlyInstant 可能更好,我认为这才是设计者需要传达的意思)。换句话说,ReadableInstant 表示时间上的某一个不可修改的瞬间)。其中的两个子类分别为 DateTime 和 DateMidnight:- DateTime:这是最常用的一个类。它以毫秒级的精度封装时间上的某个瞬间时刻。DateTime 始终与 DateTimeZone 相关,如果您不指定它的话,它将被默认设置为运行代码的机器所在的时区。
DateTime dateTime = new DateTime();
下面的代码使用一些字段值构建了一个 DateTime 对象:
DateTime dateTime = new DateTime( 2017, //year年 1, // month月 17,
// day日 0, // hour 时(午夜是0) 0, // minute分 0, // second秒 0 // milliseconds毫秒);
正如您所见,Joda 可以使您精确地控制创建 DateTime 对象的方式,该对象表示时间上的某个特定的瞬间。每一个 Joda 类都有一个与此类似的构造函数,您在此构造函数中指定 Joda 类可以包含的所有字段。您可以用它快速了解特定类在哪一种粒度级别上操作。下一个构造函数将指定从 epoch 到某个时刻所经过的毫秒数。它根据 JDK Date 对象的毫秒值创建一个 DateTime 对象,其时间精度用毫秒表示,因为 epoch 与 Joda 是相同的:
java.util.Date jdkDate = new Date();long timeInMillis = jdkDate.getTime();DateTime dateTime = new DateTime(timeInMillis);
并且这个例子与前例类似,唯一不同之处是我在这里将 Date 对象直接传递给构造函数:
java.util.Date jdkDate = new Date();dateTime = new DateTime(jdkDate);
Joda 支持使用许多其他对象作为构造函数的参数,用于创建 DateTime,例如所示:直接将不同对象传递给 DateTime 的构造函数
// 使用一个 Calendar
java.util.Calendar calendar = Calendar.getInstance();
dateTime = new DateTime(calendar);
// 使用另一个 Joda DateTime
DateTime anotherDateTime = new DateTime();
dateTime = new DateTime(anotherDateTime);
// 使用一个字符串 (必须是符合正确格式的)
String timeString = "2017-01-17T22:30:00-08:00";
dateTime = new DateTime(timeString);
timeString = "2017-01-17";
dateTime = new DateTime(timeString);
注意,如果您准备使用 String(必须经过解析),您必须对其进行精确地格式化。参考 Javadoc,获得有关 Joda 的 ISODateTimeFormat资料
- DateMidnight:这个类封装某个时区(通常为默认时区)在特定年/月/日的午夜时分的时刻。它基本上类似于 DateTime,不同之处在于时间部分总是为与该对象关联的特定 DateTimeZone 时区的午夜时分。
ReadablePartial
应用程序所需处理的日期问题并不全部都与时间上的某个完整时刻有关,因此您可以处理一个局部时刻。例如,有时您比较关心年/月/日,或者一天中的时间,甚至是一周中的某天。Joda 设计者使用 ReadablePartial 接口捕捉这种表示局部时间的概念,这是一个不可变的局部时间片段。用于处理这种时间片段的两个有用类分别为 LocalDate 和 LocalTime:- LocalDate:该类封装了一个年/月/日的组合。当地理位置(即时区)变得不重要时,使用它存储日期将非常方便。例如,某个特定对象的出生日期 可能为 1999 年 4 月 16 日,但是从技术角度来看,在保存所有业务值的同时不会了解有关此日期的任何其他信息(比如这是一周中的星期几,或者这个人出生地所在的时区)。在这种情况下,应当使用 LocalDate。
LocalDate localDate = new LocalDate();
也可以通过显式地提供所含的每个字段的值来创建 LocalDate:
LocalDate localDate = new LocalDate(2017, 1, 17);// 2017-1-17
LocalDate 替代了在早期 Joda 版本中使用的 YearMonthDay。
- LocalTime:这个类封装一天中的某个时间,当地理位置不重要的情况下,可以使用这个类来只存储一天当中的某个时间。
实例化一个时间:
LocalTime localTime = new LocalTime();
也可以通过显式地提供所含的每个字段的值来创建 LocalTime:
LocalTime localTime = new LocalTime(22, 36, 21, 0);// 22:36:21 0
时间跨度
了解特定的时刻或是某个局部时间片段将非常有用,但是如果能够表达一段时间跨度的话,通常也很有用。Joda 提供了三个类来简化这个过程。您可以选择用于表示不同跨度的类:- Duration:这个类表示一个绝对的精确跨度,使用毫秒为单位。这个类提供的方法可以用于通过标准的数学转换(比如 1分钟 = 60 秒,1 天 = 24 小时),将时间跨度转换为标准单位(比如秒、分和小时)。您只在以下情况使用 Duration 的实例:您希望转换一个时间跨度,但是您并不关心这个时间跨度在何时发生,或者使用毫秒处理时间跨度比较方便。
- Period:这个类表示与 Duration 相同的概念,但是以人们比较熟悉的单位表示,比如年、月、周。您可以在以下情况使用 Period:您并不关心这段时期必须在何时发生,或者您更关心检索单个字段的能力,这些字段描述由 Period 封装的时间跨度。
- Interval:这个类表示一个特定的时间跨度,将使用一个明确的时刻界定这段时间跨度的范围。Interval 为半开 区间,这表示由 Interval 封装的时间跨度包括这段时间的起始时刻,但是不包含结束时刻。可以在以下情况使用 Interval:需要表示在时间连续区间中以特定的点开始和结束的一段时间跨度。