本章包括 20 个涉及日期和时间的问题。这些问题通过Date、Calendar、LocalDate、LocalTime、LocalDateTime、ZoneDateTime、OffsetDateTime、OffsetTime、Instant等涵盖了广泛的主题(转换、格式化、加减、定义时段/持续时间、计算等)。到本章结束时,您将在确定日期和时间方面没有问题,同时符合您的应用的需要。本章介绍的基本问题将非常有助于了解日期-时间 API 的整体情况,并将像拼图中需要拼凑起来的部分一样解决涉及日期和时间的复杂挑战。
使用以下问题来测试您的日期和时间编程能力。我强烈建议您在使用解决方案和下载示例程序之前,先尝试一下每个问题:
将字符串转换为日期和时间:编写一个程序,演示字符串和日期/时间之间的转换。
格式化日期和时间:**解释日期和时间的格式模式。
获取当前日期/时间(不含日期/时间):编写程序,提取当前日期(不含时间或日期)。
从LocalDate和LocalTime到LocalDateTime:编写一个程序,从LocalDate对象和LocalTime构建一个LocalDateTime。它将日期和时间组合在一个LocalDateTime对象中。
通过Instant类获取机器时间:解释并举例说明InstantAPI。
定义使用基于日期的值的时间段(Period)和使用基于时间的值的时间段(Duration):解释并举例说明Period和DurationAPI 的用法。
获取日期和时间单位:编写一个程序,从表示日期时间的对象中提取日期和时间单位(例如,从日期中提取年、月、分钟等)。
对日期时间的加减:编写一个程序,对日期时间对象加减一定的时间(如年、日、分等)(如对日期加 1 小时,对LocalDateTime减 2 天等)。
获取 UTC 和 GMT 的所有时区:编写一个程序,显示 UTC 和 GMT 的所有可用时区。
获取所有可用时区的本地日期时间:编写一个程序,显示所有可用时区的本地时间。68. 显示航班日期时间信息:编写程序,显示 15 小时 30 分钟的航班时刻信息。更确切地说,是从澳大利亚珀斯飞往欧洲布加勒斯特的航班。
将 Unix 时间戳转换为日期时间:编写将 Unix 时间戳转换为java.util.Date和java.time.LocalDateTime的程序。
查找月份的第一天/最后一天:编写一个程序,通过 JDK8,TemporalAdjusters查找月份的第一天/最后一天。
定义/提取区域偏移:编写一个程序,展示定义和提取区域偏移的不同技术。
Date与Temporal之间的转换:编写Date与Instant、LocalDate、LocalDateTime等之间的转换程序。
迭代一系列日期:编写一个程序,逐日(以一天的步长)迭代一系列给定日期。
计算年龄:编写一个计算一个人年龄的程序。
一天的开始和结束:编写一个程序,返回一天的开始和结束时间。
两个日期之间的差异:编写一个程序,计算两个日期之间的时间量(以天为单位)。
实现象棋时钟:编写实现象棋时钟的程序。
以下各节介绍上述问题的解决方案。记住,通常没有一个正确的方法来解决一个特定的问题。另外,请记住,这里显示的解释仅包括解决问题所需的最有趣和最重要的细节。下载示例解决方案以查看更多详细信息,并在 这个页面 中试用程序。
将String转换或解析为日期和时间可以通过一组parse()方法来完成。从日期和时间到String的转换可以通过toString()或format()方法完成。
在 JDK8 之前,这个问题的典型解决方案依赖于抽象的DateFormat类的主扩展,名为SimpleDateFormat(这不是线程安全类)。在本书附带的代码中,有几个示例说明了如何使用此类。
此外,除了Date类之外,JDK8 还提供了几个新类,它们专门用于处理日期和时间。其中一些类显示在下面的列表中(这些类也被称为临时类,因为它们实现了Temporal接口):
LocalDate(ISO-8601 日历系统中没有时区的日期)
LocalTime(ISO-8601 日历系统中无时区的时间)
LocalDateTime(ISO-8601 日历系统中无时区的日期时间)
ZonedDateTime(ISO-8601 日历系统中带时区的日期时间),依此类推
OffsetDateTime(在 ISO-8601 日历系统中,有 UTC/GMT 偏移的日期时间)
OffsetTime(在 ISO-8601 日历系统中与 UTC/GMT 有偏移的时间)
类似地,在
LocalTime
的情况下,字符串应该遵循
DateTimeFormatter.ISO_LOCAL_TIME
模式;例如,
10:15:30
,如下面的代码片段所示:
最后,在OffsetTime的情况下,字符串必须遵循DateTimeFormatter.ISO_OFFSET_TIME模式,例如10:15:30+01:00,如下代码片段所示:
如果字符串不符合任何预定义的格式化程序,则是时候通过自定义格式模式使用用户定义的格式化程序了;例如,字符串
01.06.2020
表示需要用户定义格式化程序的日期,如下所示:
但是,像
12|23|44
这样的字符串需要如下用户定义的格式化程序:
像
01.06.2020, 11:20:15
这样的字符串需要一个用户定义的格式化程序,如下所示:
像
01.06.2020, 11:20:15+09:00 [Asia/Tokyo]
这样的字符串需要一个用户定义的格式化程序,如下所示:
像
2007.12.03, 10:15:30, +01:00
这样的字符串需要一个用户定义的格式化程序,如下所示:
最后,像
10 15 30 +01:00
这样的字符串需要一个用户定义的格式化程序,如下所示:
前面示例中的每个ofPattern()方法也支持Locale。
从LocalDate、LocalDateTime或ZonedDateTime到String的转换至少可以通过两种方式完成:
依赖于LocalDate、LocalDateTime或ZonedDateTime.toString()方法(自动或显式)。请注意,依赖于toString()将始终通过相应的预定义格式化程序打印日期:
大多数符号与
SimpleDateFormat
(JDK8 之前)和
DateTimeFormatter
(从 JDK8 开始)通用。下表列出了 JDK 文档中提供的最常见符号的完整列表:
字母 | 含义 | 演示 | 示例 |
y
|
年 | 年 |
1994; 94
|
M
|
月 | 数字/文本 |
7; 07; Jul; July; J
|
W
|
每月的一周 | 数字 | 4 |
E
|
星期几 | 文本 |
Tue; Tuesday; T
|
d
|
日期 | 数字 | 15 |
H
|
小时 | 数字 | 22 |
m
|
分钟 | 数字 | 34 |
s
|
秒 | 数字 | 55 |
S
|
秒的分数 | 数字 | 345 |
z
|
时区名称 | 时区名称 |
Pacific Standard Time; PST
|
Z
|
时区偏移 | 时区偏移 |
-0800
|
V
|
时区 ID(JDK8) | 时区 ID |
America/Los_Angeles; Z; -08:30
|
模式 | 示例 |
yyyy-MM-dd
|
2019-02-24
|
MM-dd-yyyy
|
02-24-2019
|
MMM-dd-yyyy
|
Feb-24-2019
|
dd-MM-yy
|
24-02-19
|
dd.MM.yyyy
|
24.02.2019
|
yyyy-MM-dd HH:mm:ss
|
2019-02-24 11:26:26
|
yyyy-MM-dd HH:mm:ssSSS
|
2019-02-24 11:36:32743
|
yyyy-MM-dd HH:mm:ssZ
|
2019-02-24 11:40:35+0200
|
yyyy-MM-dd HH:mm:ss z
|
2019-02-24 11:45:03 EET
|
E MMM yyyy HH:mm:ss.SSSZ
|
Sun Feb 2019 11:46:32.393+0200
|
yyyy-MM-dd HH:MM:ss VV
(JDK8)
|
2019-02-24 11:45:41 Europe/Athens
|
在 JDK8 之前,可以通过
SimpleDateFormat
应用格式模式:
从 JDK8 开始,可以通过
DateTimeFormatter
应用格式模式:
对于
LocalTime
(ISO-8601 日历系统中没有时区的时间):
对于
LocalDateTime
(ISO-8601 日历系统中没有时区的日期时间):
对于
ZonedDateTime
(ISO-8601 日历系统中带时区的日期时间):
对于
OffsetDateTime
(在 ISO-8601 日历系统中,与 UTC/GMT 有偏移的日期时间):
对于
OffsetTime
(在 ISO-8601 日历系统中与 UTC/GMT 有偏移的时间):
在 JDK8 之前,解决方案必须集中在
java.util.Date
类上。绑定到本书的代码包含此解决方案。
从 JDK8 开始,日期和时间可以通过专用类
LocalDate
和
LocalTime
从
java.time
包中获得: