如何将时间戳字符串转换为纪元时间?

How to convert timestamp string to epoch time?

我有 2017-18-08 11:45:30.345 格式的时间戳。
我想把它转换成纪元时间,所以我在下面做:

String timeDateStr = "2017-18-08 11:45:30.345"; 
DateTimeFormatter dtf  = DateTimeFormatter.ofPattern("yyyy-dd-MM HH:mm:ss.SSS");
ZonedDateTime     zdt  = ZonedDateTime.parse(timeDateStr, dtf);        
System.out.println(zdt.toInstant().toEpochMilli());

我遇到以下错误:

java.time.format.DateTimeParseException: Text '2017-18-08 11:45:30.345' could not be parsed: Unable to obtain ZonedDateTime from TemporalAccessor

我也尝试了不同的格式,但仍然出现错误。

更正了有关 yyyy-dd-MM 的代码。分钟值也可以是 1-59 而不是 60。您提供了 60。这是解决问题的另一种简单方法。只需使用 DateFormat class.

String timeDateStr = "2017-18-08 12:59:30.345";
DateFormat df = new SimpleDateFormat("yyyy-dd-MM hh:mm:ss.SSS", Locale.ENGLISH);
try {
    Date d = df.parse(timeDateStr);
    System.out.println(d.toInstant().toEpochMilli());
} catch (ParseException e) {
    e.printStackTrace();
}

注意originally the question had the input 2017-18-08 12:60:30.345 (with 60 in the minutes field), then it was edited时间12:60改为11:45),但我决定继续讨论原始输入 (12:60),因为它也适用于编辑后的版本 (11:45)。


ZonedDateTime 需要时区或偏移量,但输入 String 没有(它只有日期和时间)。

输入中还有一个细节:

  • 分钟值是60,这是不被接受的:有效值是从0到59(实际上有一种方法可以接受这个,参见“Lenient parsing” 下面)
  • hhclock-hour-of-am-pm field,因此它还需要 AM/PM 指示符才能完全解析。因为你没有它,你应该使用 HH 模式来代替

所以模式必须是 yyyy-dd-MM HH:mm:ss.SSS,输入不能有 60 作为分钟值(除非你使用宽松的解析,我将在下面解释)而且你不能direclty 将其解析为 ZonedDateTime 因为它没有 timezone/offset 指示符。

一种替代方法是将其解析为 LocalDateTime,然后定义此日期所在的 timezone/offset。在下面的例子中,我假设它在 UTC:

// change 60 minutes to 59 (otherwise it doesn't work)
String timeDateStr = "2017-18-08 12:59:30.345";
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-dd-MM HH:mm:ss.SSS");
// parse to LocalDateTime
LocalDateTime dt = LocalDateTime.parse(timeDateStr, dtf);

// assume the LocalDateTime is in UTC
Instant instant = dt.toInstant(ZoneOffset.UTC);
System.out.println(instant.toEpochMilli());

这将输出:

1503061170345

相当于 UTC 中的 2017-18-08 12:59:30.345

如果你想要另一个时区的日期,你可以使用 ZoneId class:

// get the LocalDateTime in some timezone
ZonedDateTime z = dt.atZone(ZoneId.of("Europe/London"));
System.out.println(z.toInstant().toEpochMilli());

输出为:

1503057570345

注意结果不一样,因为同一个地方date/time在每个时区代表不同的Instant(在世界的每个地方,地方date/time2017-18-08 12:59:30.345发生在不同的瞬间)。

另请注意,API 使用 IANA timezones names(始终采用 Region/City 格式,如 America/Sao_PauloEurope/Berlin)。 避免使用 3 个字母的缩写(如 CSTPST),因为它们是 ambiguous and not standard.

您可以通过调用 ZoneId.getAvailableZoneIds().

获取可用时区列表(并选择最适合您的系统的时区)

您还可以将 系统的默认时区 ZoneId.systemDefault() 一起使用,但这可以在不通知的情况下更改,即使在运行时也是如此,因此最好明确使用特定的一.


还有将 LocalDateTime 转换为 offset 的选项(如 -05:00+03:00):

// get the LocalDateTime in +03:00 offset
System.out.println(dt.toInstant(ZoneOffset.ofHours(3)).toEpochMilli());

输出将等同于偏移量+03:00中的本地date/time(比UTC提前3小时):

1503050370345


宽松的解析

作为,您可以使用宽松的解析在分钟字段中接受60(使用java.time.format.ResolverStyle class):

String timeDateStr = "2017-18-08 12:60:30.345";
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-dd-MM HH:mm:ss.SSS")
    // use lenient parsing
    .withResolverStyle(ResolverStyle.LENIENT);
// parse to LocalDateTime
LocalDateTime dt = LocalDateTime.parse(timeDateStr, dtf);

在这种情况下,60分钟调整到下一个小时,LocalDateTime将是:

2017-08-18T13:00:30.345


夏令时

如果您决定使用 UTC 或固定偏移量(使用 ZoneOffset class),则可以忽略此部分。

但是如果您决定使用时区(ZoneId class),您还必须注意 DST (Daylight Saving Time) 问题。我将使用我居住的时区作为示例 (America/Sao_Paulo)。

在圣保罗,夏令时从 2017 年 10 月 15 日开始:午夜,时钟从午夜向前移动 1 小时到凌晨 1 点.所以 00:00 和 00:59 之间的所有本地时间在这个时区都不存在。如果我在这个时间间隔内创建本地日期,它会调整到下一个有效时刻:

ZoneId zone = ZoneId.of("America/Sao_Paulo");

// October 15th 2017 at midnight, DST starts in Sao Paulo
LocalDateTime d = LocalDateTime.of(2017, 10, 15, 0, 0, 0, 0);
ZonedDateTime z = d.atZone(zone);
System.out.println(z);// adjusted to 2017-10-15T01:00-02:00[America/Sao_Paulo]

夏令时结束时:2018 年 2 月 18 日th 午夜,时钟偏移 1 小时,从午夜到 [=23 PM 17。所以从23:00到23:59的所有本地时间都存在两次(夏令时和非夏令时),你必须决定你想要哪个:

// February 18th 2018 at midnight, DST ends in Sao Paulo
// local times from 23:00 to 23:59 at 17th exist twice
LocalDateTime d = LocalDateTime.of(2018, 2, 17, 23, 0, 0, 0);
// by default, it gets the offset before DST ends
ZonedDateTime beforeDST = d.atZone(zone);
System.out.println(beforeDST); // before DST end: 2018-02-17T23:00-02:00[America/Sao_Paulo]

// get the offset after DST ends
ZonedDateTime afterDST = beforeDST.withLaterOffsetAtOverlap();
System.out.println(afterDST); // after DST end: 2018-02-17T23:00-03:00[America/Sao_Paulo]

请注意,DST 结束前后的日期具有不同的偏移量(-02:00-03:00)。这会影响 epochMilli 的值。

您必须检查您选择的时区中 DST 的开始和结束时间,并相应地检查调整。

只是我对 nagendra 547 的回答做了一点改动

请参考以下代码:-

String timeDateStr = "2017-18-08 12:59:30.345";
DateFormat df = new SimpleDateFormat("yyyy-dd-mm hh:mm:ss.SSS", Locale.ENGLISH);
try {
    Date d = df.parse(timeDateStr);
    System.out.println(d.toInstant().toEpochMilli());
} catch (ParseException e) {
    e.printStackTrace();
}

您的代码将因以下 3 个原因而失败。

  1. 您的日期字符串 (2017-18-08 12:60:30.345) 与您使用的格式化程序不匹配。应该是 yyyy-MM-dd HH:mm:ss.SSS 而不是 yyyy-dd-MM hh:mm:ss.SSS
  2. 分钟的范围是(0-59),60不在这个范围内。
  3. 即使您根据上述观点更正了代码,它也不会 运行 ZonedDateTime。因此,您需要先创建一个 LocalDateTime,然后将 ZoneId 传递给它。

代码应如下所示:

String timeDateStr = "2017-18-08 12:59:30.345"; 
DateTimeFormatter dtf  = DateTimeFormatter.ofPattern("yyyy-dd-MM HH:mm:ss.SSS");
LocalDateTime date = LocalDateTime.parse(timeDateStr, dtf);
ZonedDateTime     zdt  = date.atZone(ZoneId.of("Europe/London"));
System.out.println(zdt.toInstant().toEpochMilli());