Java Date-Time API中的基本类,用户希望使用 java.time
包引入的类来处理日期和时间,可以使用 Instant
、Duration
、Period
、LocalDate
、LocalTime
、LocalDateTime
、ZonedDateTime
等类定义的工厂方法。
Java Date-Time API中的基本类 问题描述
用户希望使用 java.time
包引入的类来处理日期和时间。
Java Date-Time API中的基本类 解决方案
使用 Instant
、Duration
、Period
、LocalDate
、LocalTime
、LocalDateTime
、ZonedDateTime
等类定义的工厂方法。
Java Date-Time API中的基本类 具体实例
Date-Time API 中的所有类均生成不可变实例,它们是线程安全的。由于这些类不提供公共构造函数(public constructor),需要采用工厂方法加以实例化。
读者应对 now
和 of
这两种静态工厂方法予以特别注意。now
方法根据当前日期或时间创建实例,示例代码如例 8-1 所示。
例 8-1 工厂方法
now
System.out.println("Instant.now(): " + Instant.now());
System.out.println("LocalDate.now(): " + LocalDate.now());
System.out.println("LocalTime.now(): " + LocalTime.now());
System.out.println("LocalDateTime.now(): " + LocalDateTime.now());
System.out.println("ZonedDateTime.now(): " + ZonedDateTime.now());
上述代码的输出如例 8-2 所示。
例 8-2 调用
now
方法的结果
Instant.now(): 2017-06-20T17:27:08.184Z
LocalDate.now(): 2017-06-20
LocalTime.now(): 13:27:08.318
LocalDateTime.now(): 2017-06-20T13:27:08.319
ZonedDateTime.now(): 2017-06-20T13:27:08.319-04:00[America/New_York]
如例 8-2 所示,所有输出值均使用 ISO 8601 标准格式。日期的基本格式为 yyyy-MM-dd
,而时间的基本格式为 hh:mm:ss.sss
。LocalDateTime
类将两种格式合二为一,中间用大写字母 T
隔开。ZonedDateTime
类用于显示包含时区信息的日期和时间,其后添加了一个 UTC 偏移量(UTC offset)以及一个地区名(region name),本例分别为 -04:00
和 America/New_York
。此外,Instant
类定义的 toString
方法将输出以祖鲁时间(Zulu time)1 显示的当前时间(精确到纳秒)。
1即 UTC 时间,因为北约音标字母(NATO phonetic alphabet)采用“Zulu”表示“Z”,而“Z”在 UTC 中表示零时区。例如,“03:06 UTC”可以表示为“03:06Z”(“03:06”和“Z”之间没有空格)。——译者注
类似地,Year
、YearMonth
与 MonthDay
类也定义了 now
方法。
静态工厂方法 of
用于生成新的值。对 LocalDate
类而言,of
方法的参数为年、月(枚举或整型)、日。
所有
of
方法的月份字段经过重载,以接受Month
枚举(如Month.JANUARY
)或起始值为 1 的整数。不过,由于java.util.Calendar
类定义的整型常量从 0 开始(即Calendar.JANUARY
为 0),需注意避免出现差一错误(off-by-one error)。如有可能,应尽量使用Month
枚举。
LocalTime
类定义的 of
方法包括多种重载形式,根据可用的小时、分、秒以及纳秒值获取当前日期。LocalDateTime
类定义的 of
方法同样包括多种重载形式,根据可用的年、月、日、小时、分、秒以及纳秒值获取当前日期和时间。相关应用如例 8-3 所示。
例 8-3
of
方法在日期 / 时间类中的应用
System.out.println("First landing on the Moon:");
LocalDate moonLandingDate = LocalDate.of(1969, Month.JULY, 20);
LocalTime moonLandingTime = LocalTime.of(20, 18);
System.out.println("Date: " + moonLandingDate);
System.out.println("Time: " + moonLandingTime);
System.out.println("Neil Armstrong steps onto the surface: ");
LocalTime walkTime = LocalTime.of(20, 2, 56, 150_000_000);
LocalDateTime walk = LocalDateTime.of(moonLandingDate, walkTime);
System.out.println(walk);
输出如下:
First landing on the Moon:
Date: 1969-07-20
Time: 20:18
Neil Armstrong steps onto the surface:
1969-07-20T20:02:56.150
LocalTime.of
方法的最后一个参数是纳秒。本例在数值中使用下划线以增强可读性,这是 Java 7 引入的一个特性。
Instant
类对时间轴上的单一瞬时点(single instantaneous point)建模,可以用于记录应用程序中的事件时间戳。
ZonedDateTime
类将日期和时间与通过 ZoneId
类获取的时区信息结合在一起,时区以 UTC 偏移量的形式表示。
时区 ID 包括两种类型。
- 相对于 UTC/ 格林尼治标准时间的固定偏移量(fixed offset),如
-05:00
。 - 地理区域(geographical region),如
America/Chicago
。
严格来说,还存在第三种时区 ID,即相对于祖鲁时间的偏移量,它由 Z
和相应的数值构成。
java.time.zone.ZoneRules
类定义了调整偏移量的规则,这些规则通过 java.time.zone.ZoneRulesProvider
类载入。ZoneRules
类包括 isDaylightSavings(Instant)
等方法。
静态方法 systemDefault
用于获取系统默认时区(ZoneId
的当前值),而可用时区 ID 的完整列表由静态方法 getAvailableZoneIds
提供:
Set<String> regionNames = ZoneId.getAvailableZoneIds();
System.out.println("There are " + regionNames.size() + " region names");
以 jdk1.8.0_131 为例,它包括 600 个地区名 2。
2或许不只作者感觉地区名的数量是如此之多。
Date-Time API 使用方法名的标准前缀。如果读者熟悉表 8-1 列出的前缀,不难猜出相应方法的用途。3
3根据 Java 官方教程提供的常用前缀表格编写。
表8-1:Date-Time API中各种方法所用的前缀
方法 | 类型 | 用途 |
---|---|---|
of |
静态工厂 | 创建实例 |
from |
静态工厂 | 将输入参数转换为目标类(target class)的实例 |
parse |
静态工厂 | 解析输入字符串 |
format |
实例 | 生成格式化输出 |
get |
实例 | 返回对象状态的一部分 |
is |
实例 | 查询对象状态 |
with |
实例 | 通过修改现有对象的某个元素来创建新对象 |
plus,minus |
实例 | 分别通过现有对象的增减来创建新对象 |
to |
实例 | 将对象转换为另一种类型 |
at |
实例 | 将对象与另一个对象合并 |
前面已讨论过 of
方法;有关 parse
和 format
方法的讨论请参见范例解析与格式化;有关 with
方法的讨论请参见范例根据现有实例创建日期和时间,它是 set
方法的不可变等效形式;有关 plus
和 minus
方法的应用请参见范例根据现有实例创建日期和时间。
例 8-4 显示了利用 at
方法为本地日期和时间添加时区。
例 8-4 为
LocalDateTime
添加时区信息
LocalDateTime dateTime = LocalDateTime.of(2017, Month.JULY, 4, 13, 20, 10);
ZonedDateTime nyc = dateTime.atZone(ZoneId.of("America/New_York"));
System.out.println(nyc);
ZonedDateTime london = nyc.withZoneSameInstant(ZoneId.of("Europe/London"));
System.out.println(london);
输出结果如下:
2017-07-04T13:20:10-04:00[America/New_York]
2017-07-04T18:20:10+01:00[Europe/London]
在本例中,withZoneSameInstant
方法传入一个 ZonedDateTime
,并查找另一个时区的日期和时间。
java.time
包引入了 Month
和 DayOfWeek
两种枚举。Month
包括标准日历中 12 个月份的常量(从 JANUARY
到 DECEMBER
),也定义了许多便利的方法,如例 8-5 所示。
例 8-5
Month
枚举定义的部分方法
System.out.println("Days in Feb in a leap year: " +
Month.FEBRUARY.length(true)); ➊
System.out.println("Day of year for first day of Aug (leap year): " +
Month.AUGUST.firstDayOfYear(true)); ➊
System.out.println("Month.of(1): " + Month.of(1));
System.out.println("Adding two months: " + Month.JANUARY.plus(2));
System.out.println("Subtracting a month: " + Month.MARCH.minus(1));
➊ 参数为 boolean leapYear
输出如下:
Days in Feb in a leap year: 29
Day of year for first day of Aug (leap year): 214
Month.of(1): JANUARY
Adding two months: MARCH
Subtracting a month: FEBRUARY
在本例中,最后两条语句分别使用 plus
和 minus
方法创建了新的实例。
java.time
包中的类是不可变的,如果实例方法(如plus
、minus
或with
)试图修改某个类,将生成一个新的实例。
DayOfWeek
枚举包括表示 7 个工作日的常量(从 MONDAY
到 SUNDAY
)。所有工作日的 int
值均遵循 ISO 标准,因此 MONDAY
为 1,SUNDAY
为 7。