我有 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
我也尝试了不同的格式,但仍然出现错误。
最佳答案
注意 :originally 问题有输入 2017-18-08 12:60:30.345
(在分钟字段中带有 60
),然后 it was edited( 时间 从 12:60
更改为 11:45
),但我决定保留这个输入的原始答案(讨论)因为它也适用于编辑后的版本 ( 12:60
)。11:45
需要时区或偏移量,但输入 ZonedDateTime
没有它(它只有日期和时间)。
输入中还有另一个细节:
String
,这是不被接受的:有效值是从 0 到 59(实际上有一种方法可以接受这个,请参阅下面的“宽松解析”) 60
是 clock-hour-of-am-pm field ,因此还需要完全解析 AM/PM 指示符。由于您没有它,您应该使用 hh
模式而不是 所以模式必须是
HH
,输入不能有 yyyy-dd-MM HH:mm:ss.SSS
作为分钟值(除非你使用宽松的解析,我将在下面解释)并且你不能直接将它解析为 60
因为它没有时区/偏移指示符。一种替代方法是将其解析为
ZonedDateTime
,然后定义此日期所在的时区/偏移量。在下面的示例中,我假设它在 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 中的
LocalDateTime
。如果你想要另一个时区的日期,你可以使用
2017-18-08 12:59:30.345
类:// get the LocalDateTime in some timezone
ZonedDateTime z = dt.atZone(ZoneId.of("Europe/London"));
System.out.println(z.toInstant().toEpochMilli());
输出是:1503057570345
请注意,结果是不同的,因为相同的本地日期/时间在每个时区代表不同的
ZoneId
(在世界的每个部分,本地日期/时间 Instant
发生在不同的时刻)。另请注意,API 使用 IANA timezones names(始终采用
2017-18-08 12:59:30.345
格式,如 Region/City
或 America/Sao_Paulo
)。避免使用 3 个字母的缩写(如
Europe/Berlin
或 CST
),因为它们是 ambiguous and not standard 。您可以通过调用
PST
获取可用时区列表(并选择最适合您系统的时区)。您还可以将 系统的默认时区 与
ZoneId.getAvailableZoneIds()
一起使用,但这可以在不通知的情况下更改,即使在运行时也是如此,因此最好明确使用特定的时区。还有将
ZoneId.systemDefault()
转换为 offset 的选项(如 LocalDateTime
或 -05:00
):// get the LocalDateTime in +03:00 offset
System.out.println(dt.toInstant(ZoneOffset.ofHours(3)).toEpochMilli());
输出将等同于偏移量 +03:00
中的本地日期/时间(UTC 前 3 小时):1503050370345
宽松解析
作为 @MenoHochschild reminded me in the comments ,您可以使用宽松解析来接受分钟字段中的
+03:00
(使用 60
类):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 分钟被调整到下一小时,java.time.format.ResolverStyle
将是:2017-08-18T13:00:30.345
夏令时
如果您决定使用 UTC 或固定偏移量(使用
LocalDateTime
类),则可以忽略此部分。但是如果您决定使用时区(使用
ZoneOffset
类),您还必须处理 DST (Daylight Saving Time) 问题。我将使用我居住的时区作为示例( ZoneId
)。在圣保罗,夏令时从 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]
DST 结束时:2018 年 2 月 18 日午夜,时钟将 向后移动 1 小时,从午夜到 17 日 的下午 23 点。因此,从 23:00 到 23:59 的所有本地时间都存在 两次 (在 DST 和非 DST 中),您必须决定您想要哪一个:// 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 结束前后的日期具有不同的偏移量( America/Sao_Paulo
和 -02:00
)。这会影响 epochMilli 的值。您必须检查 DST 在您选择的时区的开始和结束时间,并相应地检查调整。
关于java - 如何将时间戳字符串转换为纪元时间?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45756595/