问题是什么?你期待什么?我没有看到任何不当行为。
您的 java.time 类型 ( ZonedDateTime
) 被分配了一个时区America/Chicago
。
您的 JVM 显然有一个与北美东海岸相关的指定时区,线索是EDT
在字符串输出中看到的值。java.util.Date 上的toString
方法在生成日期时间值的文本表示时应用 JVM 的当前默认时区。设计不佳,此行为试图提供帮助,但实际上令人困惑,因为您实际上无法在 java.util.Date 对象上获取或设置此时区。
无论如何,北美东海岸(如America/New_York
时区)比America/Chicago
. 因此,您看到17:xx:xx
的是芝加哥时间和18:xx:xx
东部夏令时间。这些值是正确的。
您应该java.util.TimeZone.getDefault()
在调查旧日期时间类的行为时调用。
java.time
更大的问题是您甚至在使用这些旧的日期时间类,例如 java.util.Date/.Calendar。它们设计不佳,令人困惑且麻烦。完全避免这些旧课程。它们在 Java 8 中被 java.time 框架取代。
此外,避免使用 3-4 个字母的区域缩写,如EDT
. 这些既不是标准化的也不是唯一的。使用大陆/地区格式的正确时区名称。
Instant
要在 java.time 中捕获当前日期时间,只需使用Instant
. 此类在UTC时间轴上捕获一个时刻。使用 UTC 完成大部分工作。除非用户在用户界面中显示时期望时区,否则不需要时区。
Instant now = Instant.now();
数据库
要发送到您的数据库,首先确保您已将表中的列定义为符合 SQL 标准的内容TIMESTAMP WITH TIME ZONE
。顺便说一句,数据库中对日期时间类型的支持各不相同,其中一些做得比其他的好得多。
希望 JDBC 驱动程序有一天会更新以直接处理 java.time 类型。在那之前,我们在向/从数据库传输数据时必须转换为 java.sql 类型。旧的 java.sql 类具有促进这些转换的新方法。
java.sql.Timestamp
对于像这样的日期时间值Instant
,我们需要java.sql.Timestamp
类及其from( Instant )
方法。
java.sql.Timestamp ts = java.sql.Timestamp.from( now );
避免工作,java.sql.Timestamp
因为它是早期 Java 日期时间类的旧设计糟糕的混乱的一部分。仅将它们用于数据库传输,然后立即转换到 java.time。
Instant instant = ts.toInstant();
如此简单,不涉及时区或与 UTC 的偏移。、Instant
和java.sql.Timestamp
数据库存储均采用 UTC 格式。
ZonedDateTime
当您确实需要切换到某个地区的挂钟时间时,请应用时区。
ZoneId zoneId = ZoneId.of( "America/Chicago" ); // Or "America/New_York" and so on.
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId );