"The
答案 by Yasmani Llanes is basically correct. I'll expound.
LocalDateTime
!= UTC Moment
A LocalDateTime
is not a real date-time, it is not tied to the time line. It has no real meaning until you adjust it into a time zone to determine a point on the time line, a moment. Your code, LocalDateTime utcTime
, with your choice of variable name, shows you have conflated a 'local' date-time with being a UTC moment. It is not. One is a vague idea, the other is real. (well, real in the Newton sense, not so much in the Einstein Relativistic sense ;-) )"
所以,
LocalDateTime::toString
的输出并不是
Instant.parse
方法所期望的完整字符串。具体来说,它没有与
UTC 偏移量 或
时区 相关的数据。上一段解释了
这是一个特性而非 bug的原因。
你需要的是一个
ZonedDateTime
,它基本上是一个
Instant
(UTC 时间轴上的时刻)加上一个
ZoneId
(时区)。
ZonedDateTime
= Instant
+ ZoneId
一个时区是距离UTC的偏移量(小时和分钟)
加上一组规则和异常情况(如
夏令时,DST),用于过去、现在和未来的调整。
ZoneId
= 偏移量 + 调整规则
你正确地使用了java.time框架中的
LocalDateTime
,这里有一点让人感到困惑。从一个输入字符串直接解析到
ZonedDateTime
看似很合理,但是由于调整规则的存在,某些没有时区信息的输入字符串可能对于特定的时区来说无效。例如,在春季转换夏令时时,当美国在凌晨2点钟向前调整一小时时,那天不存在"02:38"或"20:54"。时钟从01:59.59.x跳跃到03:00:00.0。
我的理解是java.time框架希望通过将
LocalDateTime
对象传递给
ZonedDateTime
来处理此调整,而不是直接由
ZonedDateTime
进行处理。两个步骤:(1)将字符串解析为
LocalDateTime
,(2)将
LocalDateTime
对象和
ZoneId
对象提供给
ZonedDateTime
。为了正确处理那天带有“20:54”的输入字符串,我们需要将其解析为
LocalDateTime
,然后要求
ZonedDateTime
使用指定的时区进行调整(我认为结果为“03:54”--请阅读类文档以获取有关调整行为的详细信息和逻辑)。
因此,我们需要在您的代码中添加对ZonedDateTime
的调用。使用您创建的LocalDateTime
对象,我们需要为ZonedDateTime
指定一个ZoneId
对象,以便在完成转换为ZonedDateTime
时使用。
正确的时区名称
你说输入的字符串处于“
东部时间”中。很抱歉告诉你,这种说法是不存在的。“EST”、“EDT”和其他类似的3-4个字母的代码都不是官方的、标准化的,也不是唯一的。你需要学会使用
正确的时区名称。也许你指的是
America/New_York
(注意下划线),或者
America/Montreal
等时区。我将任意选择纽约。
变量命名
请注意我已更改了你的变量名称。为了清晰和后期维护,命名变量通常非常重要,但在日期时间工作中更加重要。
ISO 8601
By the way, a better way to exchange data of date-time values via strings is to use the
ISO 8601 formats such as
2015-10-15T13:21:09Z
. These formats include an offset-from-UTC such as the
Z
(Zulu, UTC) shown in previous sentence. The java.time framework wisely extends the ISO 8601 formats by appending the name of the time zone in brackets. Passing around date-time strings with no offset or time zone info is asking for trouble.
Example code.
Here is some sample code in Java 8. First we parse the string into a LocalDateTime
object.
// Parse input string into a LocalDateTime object.
String input = "2009/10/09 11:00";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern ( "yyyy/MM/dd HH:mm" );
LocalDateTime localDateTime = LocalDateTime.parse ( input , formatter );
将那个无形的
LocalDateTime
转化为时间轴上的实际时刻,只需指定一个时区即可。我们假设输入字符串表示
挂钟时间在
Poughkeepsie,该地使用纽约时区。因此,我们获取了一个
ZoneId
对象,代表纽约时区。
// Specify the time zone we expect is implied for this input string.
ZoneId zoneId = ZoneId.of ( "America/New_York" );
ZonedDateTime zdtNewYork = ZonedDateTime.of ( localDateTime , zoneId );
你可以轻松地适应其他时区。我将随意展示印度时间,以提供两种对比:超前于UTC而不是落后,并且它的偏移量不是整小时(+05:30
)。
// For fun, adjust into India time, five and a half hours ahead of UTC.
ZonedDateTime zdtKolkata = zdtNewYork.withZoneSameInstant ( ZoneId.of ( "Asia/Kolkata" ) );
我们可以进行日期时间计算,例如添加四个小时。因为我们拥有一个
ZonedDateTime
,该类处理所需的调整以适应异常情况,比如
夏令时。
// Get a moment four hours later.
ZonedDateTime later = zdtNewYork.plusHours ( 4 )
对于UTC时区,您可以采取以下两种方式之一。
- 像为任何其他时区分配时区一样,但请注意在
ZoneOffset
(ZoneId
的子类)中定义的便捷常量。
- 或者,从
ZonedDateTime
中提取Instant
。根据定义,Instant
始终处于UTC。
两种方法都表示时间轴上的相同时刻。但请注意,在下面的输出中,它们各自的toString
实现中默认使用了不同的格式。
// To get the same moment in UTC time zone, either adjust time zone or extract Instant.
ZonedDateTime zdtUtc = zdtNewYork.withZoneSameInstant ( ZoneOffset.UTC );
Instant instant = zdtNewYork.toInstant ();
将内容输出到控制台。
System.out.println ( "input: " + input );
System.out.println ( "localDateTime: " + localDateTime );
System.out.println ( "zdtNewYork: " + zdtNewYork );
System.out.println ( "zdtKolkata: " + zdtKolkata );
System.out.println ( "zdtUtc: " + zdtUtc );
System.out.println ( "instant: " + instant );
System.out.println ( "later: " + later );
运行时。
input: 2009/10/09 11:00
localDateTime: 2009-10-09T11:00
zdtNewYork: 2009-10-09T11:00-04:00[America/New_York]
zdtKolkata: 2009-10-09T20:30+05:30[Asia/Kolkata]
zdtUtc: 2009-10-09T15:00Z
instant: 2009-10-09T15:00:00Z
later: 2009-10-09T15:00-04:00[America/New_York]
数据库查询
关于查询数据库,可以在StackOverflow上搜索,因为这已经被详尽地解决了。总之,在将来,JDBC应该能够使用此处显示的java.time数据类型。在那之前,将其转换为java.sql.Timestamp
对象。为您提供方便的转换方法,例如java.sql.Timestamp.from(Instant instant)
。
java.sql.Timestamp ts = java.sql.Timestamp.from( zdtNewYork.toInstant () );