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

时间:2015-10-16 20:37:07

标签: java datetime epoch milliseconds

我有一个简单的网页界面,以“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,我认为这个任务很简单,我错过了什么?!

2 个答案:

答案 0 :(得分:8)

Yasmani Llanes的answer基本上是正确的。我会解释。

LocalDateTime!= UTC时刻

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

因此,LocalDateTime::toString的输出不是Instant.parse方法所期望的完全形成的字符串。具体而言,它没有与offset-from-UTCtime zone相关的数据。上一段解释了为什么这是一个功能而不是错误

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

  

ZonedDateTime = Instant + ZoneId

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

  

ZoneId =偏离UTC +调整规则

在java.time框架中浏览LocalDateTime是正确的,这是让它有点混乱的地方。从逻辑上讲,我们应该能够直接从输入String解析为ZonedDateTime。但是由于调整规则,存在没有任何时区信息的输入字符串的问题可能对特定区域无效。例如,在春天的时候,我们会提前#34;随着夏令时,美国在凌晨2点前一小时跳了一小时,没有" 02:38"或" 20:54"在那一天。时钟从01:59.59.x跳到03:00:00.0。

我的理解是java.time框架希望通过传递给LocalDateTime的{​​{1}}对象来处理这种调整,而不是在解析时让ZonedDateTime直接处理它。两个步骤:(1)将字符串解析为ZonedDateTime,(2)将LocalDateTime对象和LocalDateTime对象解析为ZoneId。使用" 20:54"正确处理输入字符串那天,我们需要将其解析为ZonedDateTime,然后让LocalDateTime使用指定的时区进行调整(结果为" 03:54",我认为 - 阅读类doc以获取调整行为中使用的细节和逻辑。

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

正确的时区名称

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

变量命名

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

ISO 8601

顺便说一句,通过字符串交换日期时间值数据的更好方法是使用ZonedDateTime格式,例如2015-10-15T13:21:09Z。这些格式包括UTC的偏移量,例如前一句中显示的Z(祖鲁语,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转换为时间线上的实际时刻。我们假设输入字符串代表使用纽约时区的ISO 8601中的wall-clock time。所以我们得到了纽约时区的Poughkeepsie对象。

// 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 );

您可以轻松调整到其他时区。我将任意显示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,该类会处理India time等异常所需的调整。

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

对于UTC时区,您可以采用两种方式之一。

  • 为任何其他时区分配时区,但请注意Daylight Saving TimeZoneOffset的子类)中定义的方便常量。
  • 或者,也可以从Instant中提取ZonedDateTime。根据定义,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数据类型。在此之前,转换为ZoneId对象。为您提供的便捷转换方法,例如java.sql.Timestamp

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

答案 1 :(得分:0)

我相信您尝试解析的文本不是ggmap(Yak_base) + stat_bin2d( aes(x = Yak_dat[,2], y = Yak_dat[,1], colour = Yak_Ind, fill = Yak_Ind), size=.10, bins = 60, alpha = 1/2, data = Yak_dat_fort ) + geom_polygon(aes(x = Yak_coord[,1], y = Yak_coord[,2]), data = Yak_coord, colour = 'black', fill = NA, alpha = 0.4, size = 0.75 ) + labs(x = "Longitude", y = "Latitude") + ggtitle("Yakima Subbasin") + scale_fill_discrete( name = "Indicator for Snow", labels = c("No Snow","Snow") ) 应该是的格式。你错过了秒和纳秒。有效的Instant类似于“2007-12-03T10:15:30.00Z”。

您应该在String上使用toInstant