java - 在东部时间中获取日期和时间输入,并在Java中转换为UTC时间戳

标签 java datetime epoch milliseconds

我有一个简单的Web界面,其日期和时间的格式为“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”中。我通过添加4小时将其从美国东部时间转换为UTC。然后,我尝试创建一个即时对象并解析该字符串并获得纪元毫秒,但我得到的异常是:

“无法解析文本'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 进行检查是正确的,这会使它变得有些困惑。从逻辑上讲,我们应该能够直接从输入String解析为 ZonedDateTime 。但是存在一个问题,即由于调整规则,没有任何时区信息的输入字符串可能不适用于特定的时区。例如,在 Spring ,当我们采用夏时制“提前”时,在美国,凌晨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对象,我们需要为ZoneId指定ZonedDateTime对象,以用于完成到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(祖鲁语,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转换为时间线上的实际时刻。我们假设输入字符串表示使用纽约时区的wall-clock time中的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 );

您可以轻松调整到其他时区。我将以两种方式任意显示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 的子类)中定义的方便的常量。
  • 或者,从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,因为已经对其进行了详尽的处理。总结:将来,JDBC应该能够使用此处显示的java.time数据类型。在此之前,将其转换为 java.sql.Timestamp 对象。为您提供的便捷转换方法,例如 java.sql.Timestamp.from( Instant instant )
    java.sql.Timestamp ts = java.sql.Timestamp.from( zdtNewYork.toInstant () );
    

    关于java - 在东部时间中获取日期和时间输入,并在Java中转换为UTC时间戳,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33179166/

    相关文章:

    python - Django 日期时间字段 - 在 View 中转换为时区

    ios - 将 NSDate 转换为毫秒纪元时间

    php - 在 PHP 中将 TIMESTAMP 转换为 unix 时间?

    vb.net - 从字符串 yyyyMMdd 到整数类型的转换无效

    date - 配置单元日期转换不起作用

    Java 堆栈到 GUI

    java - 提取所有出现的模式K,并在一遍检查字符串是否匹配 "K*"

    javascript - 如何确定从今天到过去日期的纪元时间范围,然后确定过去时间的等量?

    java - 如何开始使用 Java 服务器端技术?

    java - Android 中的线程等待