在东部时间输入日期和时间并在Java中转换为UTC时间戳

我有一个简单的网络界面,以“2009/10/09 11:00”或“yyyy / MM / dd HH:mm”的形式显示日期和时间.时间(从用户的角度来看)是在东部时间.

我希望能够获取此字符串,将其转换为UTC时间戳,因此我可以根据指定的时间获取此时间戳并查询我们的NoSQL数据库.

我的代码如下:

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm");
    LocalDateTime dateTime = LocalDateTime.parse(startSearchTime, formatter);
    System.out.println(dateTime);
    LocalDateTime utcTime = dateTime.plusHours(4);
    Instant instant = Instant.parse(utcTime.toString());
    System.out.println(instant.toEpochMilli());

我从UI获取字符串并将其存储在’startSearchTime’中.我将其从东部时间转换为UTC,增加4小时.然后我尝试创建一个即时对象并解析字符串并获得纪元毫秒,但我得到的异常是:

“文字’2015-10-16T14:00z’无法解析”

有了这个新的Java 8 DateTime API,我认为这个任务很简单,我缺少什么?!

最佳答案
Yasmani Llanes的answer基本上是正确的.我会解释.

LocalDateTime!= UTC时刻

LocalDateTime不是真正的日期时间,它与时间线无关.在将其调整到时区以确定时间线上的点之前,它没有任何实际意义.您的代码LocalDateTime utcTime,您可以选择变量名称,表示您将“本地”日期时间与UTC时间混为一谈.它不是.一个是模糊的想法,另一个是真实的. (好吧,牛顿意义上的真实,而不是爱因斯坦相对论意义上的那些;-))

因此,LocalDateTime::toString的输出不是Instant.parse方法所预期的完全形成的字符串.具体来说,它没有关于offset-from-UTCtime zone的数据.前一段解释了为什么这是一个特征而不是一个bug.

你想要的是一个ZonedDateTime,它基本上是一个Instant(在UTC时间轴上的一个时刻)加上一个ZoneId(一个时区).

ZonedDateTime = Instant + ZoneId

时区是UTC(小时和分钟)的偏移量加上过去,现在和将来调整的一组规则和异常(例如Daylight Saving Time, DST).

ZoneId = offset-from-UTC + adjustment-rules

你在java.time框架中通过LocalDateTime是正确的,这是让它有点混乱的地方.从逻辑上讲,我们应该能够直接从输入字符串解析到ZonedDateTime.但是由于调整规则,存在输入字符串没有任何时区信息可能对特定区域无效的问题.例如,在春天,当我们在夏令时“蹦蹦跳跳”时,在美国上午凌晨2点前进一小时,当天没有“02:38”或“20:54” .时钟从01:59.59.x跳到03:00:00.0.

我的理解是java.time框架想要通过传递给ZonedDateTime的LocalDateTime对象来处理这种调整,而不是让ZonedDateTime在解析时直接处理它.两个步骤:(1)将字符串解析为LocalDateTime,(2)将LocalDateTime对象和ZoneId对象解析为ZonedDateTime.为了正确处理当天“20:54”的输入字符串,我们需要将其解析为LocalDateTime,然后要求ZonedDateTime使用指定的时区进行调整(结果是“03:54”,我认为 – 读取用于调整行为的细节和逻辑的类doc.

所以我们需要添加你的代码,调用ZonedDateTime.使用您创建的LocalDateTime对象,我们需要为ZonedDateTime指定一个ZoneId对象,以用于完成到ZonedDateTime的转换.

适当的时区名称

你说输入字符串是“Eastern Time”.我不敢告诉你没有这样的事情. “EST”,“EDT”和其他3-4个字母代码不是官方的,不是标准化的,也不是唯一的.您需要学习使用proper time zone names.也许您的意思是America/New_York(注意下划线)或America/Montreal或某些此类区域.我会任意去纽约.

变量命名

请注意我是如何更改变量名称的.命名变量对于清晰度和后期维护通常非常重要,但对于日期时间工作更是如此.

ISO 8601

顺便说一下,通过字符串交换日期时间值数据的更好方法是使用ISO 8601格式,例如2015-10-15T13:21:09Z.这些格式包括UTC的偏移量,例如前一句中显示的Z(Zulu,UTC). java.time框架明智地通过在括号中附加时区名称来扩展ISO 8601格式.传递没有偏移或时区信息的日期 – 时间字符串是一个麻烦.

示例代码.

下面是Java 8中的一些示例代码.首先,我们将字符串解析为LocalDateTime对象.

// 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中使用纽约时区的wall-clock time.所以我们得到纽约时区的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 );

您可以轻松调整到其他时区.我将任意显示India time以两种方式提供对比:在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,所以该类处理异常所需的调整,例如Daylight Saving Time.

// Get a moment four hours later.
ZonedDateTime later = zdtNewYork.plusHours ( 4 );  // DST and other anomalies handled by ZDT when adding hours.

对于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. Upshot:将来JDBC应该能够使用此处显示的java.time数据类型.在此之前,转换为java.sql.Timestamp对象.为您提供方便的转换方法,例如java.sql.Timestamp.from( Instant instant ).

java.sql.Timestamp ts = java.sql.Timestamp.from( zdtNewYork.toInstant () );

转载注明原文:在东部时间输入日期和时间并在Java中转换为UTC时间戳 - 代码日志