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。
极客教程