Groovy 日期和时间速查表
作者: Paul King
发布: 2022-10-24 07:27AM
Java 从一开始就有一个 Date
类,而 Groovy 支持使用它以及一些相关的类,比如 Calendar
。 在这篇博文中,我们将这些类称为 **遗留日期类**。 Groovy 通过更简单的机制来增强使用遗留日期类的体验,这些机制可以用于格式化、解析和从相关类中提取字段。
从 Java 8 开始,JDK 就包含了 JSR-310 日期时间 API。 我们将这些类称为 **新的日期类**。 新的日期类消除了遗留日期类的许多限制,并带来了一些非常赞赏的额外一致性。 Groovy 也为新的日期类提供了类似的增强功能。
Groovy 对遗留日期类的增强功能位于 groovy-dateutil
模块中(在 Groovy 2.5 之前,此功能内置在核心模块中)。 groovy-datetime
模块包含对新日期类的增强功能。 您可以在构建文件中包含对该模块的依赖项,或引用 groovy-all
pom 依赖项。 这两个模块都是标准 Groovy 安装的一部分。
接下来的几节将说明常见的日期和时间任务,以及使用新的和遗留类(在许多地方使用 Groovy 增强功能)来执行这些任务的代码。
**请注意**:某些格式化命令**依赖于区域设置**,并且如果您自己运行这些示例,输出可能会略有不同。
表示当前日期/时间
遗留日期类有一个抽象,它包括日期和时间。 如果您只对这两个方面中的一个感兴趣,您只需忽略另一个方面。 新的日期类允许您拥有仅日期、仅时间和日期时间表示。
这些示例创建代表当前日期和/或时间的实例。 从实例中提取各种信息,并以各种方式打印出来。 一些示例使用 SV
宏,它打印一个或多个变量的名称和字符串值。
任务 | java.time | 遗留 |
---|---|---|
当前日期和时间 |
println LocalDateTime.now() 2022-10-24T12:40:02.218130200 2022-10-24T02:40:02.223131Z |
println new Date() Mon Oct 24 12:40:02 AEST 2022 Mon Oct 24 12:40:02 AEST 2022 |
当前年份的日期 & 当前月份的日期 |
println LocalDateTime.now().dayOfYear 297 24 |
println Calendar.instance[DAY_OF_YEAR] 297 24 |
提取今天的 年份、月份 & 日 |
var now = LocalDate.now() // or LocalDateTime now.year=2022, now.monthValue=10, now.dayOfMonth=24 Today is 2022 10 24 |
var now = Calendar.instance Today is 2022 10 24 Today is 2022 10 24 |
打印今天的替代方案 | println now.format("'Today is 'YYYY-MM-dd") Today is 2022-10-24 Today is 2022-10-24 |
println now.format("'Today is 'YYYY-MM-dd") Today is 2022-10-24 Today is 2022-10-24 |
提取当前时间的部分 |
now = LocalTime.now() // or LocalDateTime now.hour=12, now.minute=40, now.second=2 The time is 12:40:02 |
(H, M, S) = now[HOUR_OF_DAY, MINUTE, SECOND] H=12, M=40, S=2 The time is 12:40:02 |
打印时间的替代方案 | println now.format("'The time is 'HH:mm:ss") The time is 12:40:02 The time is 12:40:02 |
println now.format("'The time is 'HH:mm:ss") The time is 12:40:02 The time is 12:40:02 |
处理时间
新的日期类有一个专门用于表示仅时间数量的 LocalTime
类。 遗留日期类没有这样的专用抽象;您实际上只是忽略日期的日、月和年部分。 java.sql.Time
类可以用作替代方案,但很少使用。 Java 将新的日期类与其遗留等效项进行比较的文档 讨论了将 GregorianCalendar
与日期设置为 1970-01-01
的纪元值作为 LocalTime
类的近似值。 我们将在此处遵循这种方法来提供比较,但我们**强烈建议**如果您需要表示仅时间的值或在 JDK 版本早于 8 时使用 Joda-Time 库,则升级到新的类。
这些示例查看表示午夜前后一分钟,以及您可能吃几顿饭的时间。 对于这些餐食,除了打印各种值之外,我们可能还对根据现有时间计算新时间感兴趣,例如午餐和晚餐相隔 7 个小时。
任务 | java.time | 遗留 |
---|---|---|
午夜后一分钟 | LocalTime.of(0, 1).with { 00:01 12:01 am 0:01 am |
Calendar.instance.with { 00:01 12:01 am 0:01 am |
午夜前一分钟 | LocalTime.of(23, 59).with { 23:59 11:59 pm 11:59 pm |
Calendar.instance.with { 23:59 11:59 pm 11:59 pm |
用餐时间 | var breakfast = LocalTime.of(7, 30) |
var breakfast = Date.parse('hh:mm', '07:30') |
处理日期
要使用遗留日期类表示仅日期信息,您可以将时间方面设置为零,或者干脆忽略它们。 或者,您可以考虑不太常用的 java.sql.Date
类。 新的日期类为此目的专门提供了 LocalDate
类,我们强烈推荐它。
这些示例为万圣节和墨尔本杯赛日(澳大利亚维多利亚州的公共假期)创建日期。 我们查看了这两个日期的各种属性。
任务 | java.time | 遗留 |
---|---|---|
假期 | var halloween22 = LocalDate.of(2022, 10, 31) |
var halloween21 = Date.parse('dd/MM/yyyy', '31/10/2021') |
处理日期和时间组合
新的日期类使用 LocalDateTime
来表示包含日期和时间方面的量。 早些时候看到的大多数方法在这里也适用。
这些示例展示了如何创建和打印墨尔本杯赛日午餐的表示形式。
任务 | java.time | 遗留 |
---|---|---|
假期 | var melbourneCupLunch = LocalDateTime.of(2022, 11, 1, 12, 30) |
var melbourneCupLunch = new GregorianCalendar(2022, 10, 1, 12, 30).time |
处理带时区的日期和时间
遗留日期类具有 TimeZone
的概念,主要由 Calendar 类使用。 新的日期类具有类似的概念,但使用 ZoneId
、ZoneOffset
和 ZonedDateTime
类(以及其他类)。
这些示例展示了时区的各种属性,并展示了在墨尔本杯早餐期间,洛杉矶仍然是前一天晚上(万圣节)。 它们还显示,这两个时区在一年中的那个时间相差 18 个小时。
任务 | java.time | 遗留 |
---|---|---|
假期 | var aet = ZoneId.of('Australia/Sydney') |
var aet = TimeZone.getTimeZone('Australia/Sydney') |
其他有用的类
新的日期类提供了一些更有用的类。 以下是一些常见的类
-
OffsetDateTime
- 与ZonedDateTime
类似,但只有相对于 UTC 的偏移量,而不是完整的时区 -
Instant
- 与OffsetDateTime
类似,但与 UTC 绑定 -
YearMonth
- 与LocalDate
类似,但没有日部分 -
MonthDay
- 与LocalDate
类似,但没有年部分 -
Period
- 用于表示时间段,例如Period.ofDays(14)
、Period.ofYears(2)
;另见上面的LocalDate
示例。 -
Duration
- 基于时间的时间量,例如Duration.ofSeconds(30)
、Duration.ofHours(7)
;另见上面的LocalTime
示例。
转换
在新的和遗留类之间进行转换非常有用。 下面展示了一些有用的转换方法,其中 Groovy 增强功能以 蓝色 显示。
从 | 转换方法/属性 |
---|---|
GregorianCalendar | toInstant() toZonedDateTime() from(ZonedDateTime) |
Calendar | toInstant()
toZonedDateTime()
toOffsetDateTime()
toLocalDateTime()
toLocalDate()
toLocalTime()
toOffsetTime()
toDayOfWeek()
toYear()
toYearMonth()
toMonth()
toMonthDay()
zoneOffset
zoneId
|
Date | toInstant()
from(Instant)
toZonedDateTime()
toOffsetDateTime()
toLocalDateTime()
toLocalDate()
toLocalTime()
toOffsetTime()
toDayOfWeek()
toYear()
toYearMonth()
toMonth()
toMonthDay()
zoneOffset
zoneId
|
ZonedDateTime OffsetDateTime LocalDateTime LocalDate LocalTime |
toDate()
toCalendar()
|
SimpleDateFormat 模式
我们在上面的示例中看到了几个使用 format
和 parse
方法的示例。 对于遗留日期类,许多 Groovy 增强功能委托给 SimpleDateFormat
。 此类使用模式字符串表示日期/时间格式。 这些是表示某些时间或日期组件的特殊字母,与转义的文字字符串混合在一起。 特殊字母通常重复以表示数字组件的最小大小字段,以及其他组件是否使用完整形式或缩写形式。
例如,对于美国区域设置和美国太平洋时间时区,以下模式
yyyy.MM.dd G 'at' HH:mm:ss z
将应用于以下文本
2001.07.04 AD at 12:08:56 PDT
字母 | 描述 |
---|---|
G | 纪元指示符 AD |
y | 年份 1996; 96 |
Y | 周年份(类似于年份,但按周分配;一年的前几天/后几天可能分配给完成/开始上一个/下一个周) |
M | 一年中的月份(上下文敏感) July; Jul; 07 |
L | 一年中的月份(独立形式) July; Jul; 07 |
w | 一年中的周 27 |
W | 一个月中的周 2 |
D | 一年中的日 189 |
d | 一个月中的日 10 |
F | 一个月中的星期几 2 |
E | 一周中的星期几名称 Tuesday; Tue |
u | 一周中的星期几编号(1 = 星期一,...,7 = 星期日) |
a | 上午/下午标记 PM |
H | 一天中的小时数(0-23) 0 |
k | 一天中的小时数(1-24) 24 |
K | 上午/下午中的小时数(0-11) 0 |
h | 上午/下午中的小时数(1-12) 12 |
m | 一小时中的分钟数 30 |
s | 一分钟中的秒数 55 |
S | 毫秒 978 |
z | 时区 Pacific Standard Time; PST; GMT-08:00 |
Z | 时区(RFC 822) -0800 |
X | 时区(ISO 8601) -08; -0800; -08:00 |
' | 要转义文本,在两侧加上单引号 |
'' | 两个单引号表示文字单引号 ' |
DateTimeFormatter 模式
Groovy 的 format
和 parse
对新日期类的增强功能委托给 DateTimeFormatter
类。 它的行为类似于我们为 SimpleDateFormat
所看到的,但转换字母略有不同
转换后缀 | 描述 |
---|---|
G | 纪元 AD |
u | 年份 2004; 04 |
y | 纪元年份 2004; 04 |
D | 一年中的日 189 |
M/L | 一年中的月份 7; 07; Jul; July; J |
d | 一个月中的日 10 |
Q/q | 一年中的季度 3; 03; Q3; 3rd quarter |
Y | 周年份 1996; 96 |
w | 周年份中的周 27 |
W | 一个月中的周 4 |
E | 一周中的星期几 Tue; Tuesday; T |
e/c | 本地化星期几 2; 02; Tue; Tuesday; T |
F | 一个月中的周 3 |
a | 一天中的上午/下午 PM |
h | 上午/下午中的时钟小时数(1-12) 12 |
K | 上午/下午中的小时数(0-11) 0 |
k | 上午/下午中的时钟小时数(1-24) 0 |
H | 一天中的小时数(0-23) 0 |
m | 一小时中的分钟数 30 |
s | 一分钟中的秒数 55 |
S | 秒的分数 978 |
A | 一天中的毫秒数 1234 |
n | 纳秒 987654321 |
N | 一天中的纳秒数 1234000000 |
V | 时区 ID America/Los_Angeles; Z; -08:30 |
z | 时区名称 Pacific Standard Time; PST |
O | 本地化时区偏移量 GMT+8; GMT+08:00; UTC-08:00; |
X | 时区偏移量 'Z' 表示零 Z; -08; -0830; -08:30; -083015; -08:30:15; |
x | 时区偏移量 +0000; -08; -0830; -08:30; -083015; -08:30:15; |
Z | 时区偏移量 +0000; -0800; -08:00; |
p | 填充下一个 |
' | 要转义文本,在两侧加上单引号 |
'' | 两个单引号表示文字单引号 ' |
本地化模式
JDK19 添加了 ofLocalizedPattern(String requestedTemplate)
方法。 请求的模板是一个或多个正则表达式模式符号,按从大到小的单位排序,并由以下模式组成
"G{0,5}" + // Era "y*" + // Year "Q{0,5}" + // Quarter "M{0,5}" + // Month "w*" + // Week of Week Based Year "E{0,5}" + // Day of Week "d{0,2}" + // Day of Month "B{0,5}" + // Period/AmPm of Day "[hHjC]{0,2}" + // Hour of Day/AmPm (refer to LDML for 'j' and 'C') "m{0,2}" + // Minute of Hour "s{0,2}" + // Second of Minute "[vz]{0,4}" // Zone
请求的模板映射到 Unicode LDML 规范 中定义的可用本地化格式的最接近的格式。 以下是一个使用示例
var now = ZonedDateTime.now()
var columns = '%7s | %10s | %10s | %10s | %14s%n'
printf columns, 'locale', 'GDK', 'custom', 'local', 'both'
[locale('en', 'US'),
locale('ro', 'RO'),
locale('vi', 'VN')].each { locale ->
Locale.default = locale
var gdk = now.format('y-MM-dd')
var custom = now.format(ofPattern('y-MM-dd'))
var local = now.format(ofLocalizedDate(SHORT))
var both = now.format(ofLocalizedPattern('yMM'))
printf columns, locale, gdk, custom, local, both
}
输出如下
locale | GDK | custom | local | both en_US | 2022-12-18 | 2022-12-18 | 12/18/22 | 12/2022 ro_RO | 2022-12-18 | 2022-12-18 | 18.12.2022 | 12.2022 vi_VN | 2022-12-18 | 2022-12-18 | 18/12/2022 | tháng 12, 2022
示例来源:这个示例 来自 Nicolai Parlog.
格式化程序格式
java.util.Formatter
类是 Java 中各种格式化类型的基类。 它可以直接使用,通过 String.format
、parse
、printf
或 Groovy 的 sprintf
使用。 在上面的示例中,我们看到了几个使用 printf
和 parse
格式化的示例。
Formatter
类的方法以格式字符串作为第一个参数,以及零个或多个附加参数。格式字符串通常包含一个或多个格式说明符(以百分号开头),表示应将附加参数之一的格式化版本放置到该位置的字符串中。格式说明符的通用形式为
%[argument_index$][flag][width][.precision]conversion
大多数部分都是可选的。argument_index
部分仅在多次引用附加参数之一(或按不同顺序引用)时使用。precision
部分仅用于浮点数。flag
部分用于指示始终包含符号 (+)、零填充 (0)、特定于区域设置的逗号分隔符 (,) 和左对齐 (-)。width
指示输出的最小字符数。conversion
指示应如何处理参数,例如作为数字字段、日期、特殊字符或其他特殊处理。大多数转换都有大写和小写变体,对于大写变体,将在转换完成后调用 toUpperCase
。
转换 | 描述 |
---|---|
'b', 'B' | 当作布尔值处理,如果为 null,则为 false |
'h', 'H' | 输出参数的哈希码作为十六进制字符串 |
's', 'S' | 当作字符串处理 |
'c', 'C' | 当作 Unicode 字符处理 |
'd' | 当作十进制整数处理 |
'o' | 当作八进制整数处理 |
'x', 'X' | 当作十六进制整数处理 |
'e', 'E' | 当作科学计数法的十进制数处理 |
'f' | 当作浮点数处理 |
'g', 'G' | 当作十进制或科学计数法的浮点数处理 |
'a', 'A' | 当作十六进制浮点数处理 |
't', 'T' | 当作日期/时间转换的前缀处理 |
'%' | 百分号字面量 |
'n' | 换行符 |
当使用日期/时间前缀时,附加的后缀适用。
用于格式化时间
转换后缀 | 描述 |
---|---|
'H' | 24 小时制一天中的小时,用两位数字表示 00 - 23 |
'I' | 12 小时制一天中的小时,用两位数字表示 01 - 12 |
'k' | 24 小时制一天中的小时 0 - 23 |
'l' | 12 小时制一天中的小时 1 - 12 |
'M' | 小时内的分钟,用两位数字表示 00 - 59 |
'S' | 分钟内的秒,用两位数字表示 00 - 60 (“60” 用于闰秒) |
'L' | 秒内的毫秒,用三位数字表示 000 - 999 |
'N' | 秒内的纳秒,用九位数字表示 000000000 - 999999999 |
'p' | 特定于区域设置的上午或下午标记,小写,am 或 pm (转换前缀 'T' 将此输出强制为大写) |
'z' | RFC 822 样式的 GMT 时区偏移量 -0800 (根据需要针对夏令时进行调整) |
'Z' | 缩写时区 |
's' | 自 1970 年 1 月 1 日 00:00:00 UTC 开始的纪元开始以来的秒数 |
'Q' | 自 1970 年 1 月 1 日 00:00:00 UTC 开始的纪元开始以来的毫秒数 |
用于格式化日期
转换后缀 | 描述 |
---|---|
'B' | 特定于区域设置的完整月份名称 January |
'b', 'h' | 特定于区域设置的缩写月份名称 Jan |
'A' | 特定于区域设置的完整星期几名称 Sunday |
'a' | 特定于区域设置的缩写星期几名称 Sun |
'C' | 四位数年份的前两位数字 00 - 99 |
'Y' | 年份,用四位数字表示 0092 |
'y' | 年份的后两位数字 00 - 99 |
'j' | 一年中的天数,用三位数字表示 001 - 366 |
'm' | 月份,用两位数字表示 01 - 13 |
'd' | 月份中的天数,用两位数字表示 01 - 31 |
'e' | 月份中的天数 1 - 31 |
用于格式化日期/时间组合
转换后缀 | 描述 |
---|---|
'R' | 24 小时制时间格式化为 "%tH:%tM" |
'T' | 24 小时制时间格式化为 "%tH:%tM:%tS" |
'r' | 12 小时制时间格式化为 "%tI:%tM:%tS %Tp" 上午或下午标记 ('%Tp') 的位置可能与区域设置有关。 |
'D' | 日期格式化为 "%tm/%td/%ty" |
'F' | ISO 8601 日期格式化为 "%tY-%tm-%td" |
'c' | 日期和时间格式化为 "%ta %tb %td %tT %tZ %tY" Sun Jul 21 15:17:00 EDT 1973 |