Java 8 epoch-millis 时间戳到格式化日期,如何?

Java 8 epoch-millis time stamp to formatted date, how?

提问人:Harald 提问时间:7/31/2016 最后编辑:TunakiHarald 更新时间:5/31/2018 访问量:12214

问:

在 Java-8 之前,我习惯于自 Epoch 以来始终将任何与日期/时间相关的内容保留为毫秒,并且只在出路时处理人类可读的日期/时间,即在 UI 或日志文件中,或者在解析用户生成的输入时。

我认为这在 Java-8 中仍然是安全的,现在我正在寻找最简洁的方法来从毫秒时间戳中获取格式化日期。我试过了

df = Dateformatter.ofPattern("...pattern...");
df.format(Instant.ofEpochMilli(timestamp))

但它爆炸了,我理解了一半。现在用什么代替?Unsupported field: YearOfEraInstant.getLong(...)Instant

LocalDateTime.ofEpoch(Instant, ZoneId)似乎错了,因为我不在乎有当地时间。我只想在应用格式化程序时查看当地时区。在内部,它应该只是 .Instant

同样,我想只在格式化时应用。但我注意到它本身似乎不再涉及时区,所以我认为我需要使用上述之一。ZonedDateTime.ofInstant(Instant, ZoneId)ZoneIdDateTimeFormatter

哪一个是首选,为什么?或者我应该使用另一种方式将纪元时间戳格式化为带时区的日期/时间?

日期时间 java-8 java-time

评论


答:

1赞 GreyBeardedGeek 8/1/2016 #1

我同意这有点令人困惑,尤其是与它的前身 Joda DateTime 相比。

最令人困惑的是,LocalDateTime 的文档说它是“没有时区的日期时间”,但 LocalDateTime.ofInstant 方法同时采用即时和时区作为参数。

也就是说,我认为您可以通过使用 Instant 和 LocalDateTime.ofInstant 通过使用 UTC 时区来实现您想要的目标。

public LocalDateTime millisToDateTime(long millis) {
  return LocalDateTime.ofInstant(Instant.ofEpochMilli(millis), ZoneId.of("Z");
} 
7赞 Tunaki 8/1/2016 #2

使用使用年份或其他字段构建的格式化程序格式化时,会出现错误;不知道现在是哪一年、哪一个月或哪一天,它只知道自纪元以来已经过去了多少毫秒。在同一时刻,它可能是在地球的两个不同地方的 2 个不同的日子。InstantInstant

因此,如果要打印日期,则需要添加时区信息。使用 ,您可以调用 atZone(zone) 将其与 ZoneId 组合在一起,以形成 ZonedDateTime。这很像一个瞬间,只是它有一个时区信息。如果要使用系统时区(正在运行的 VM 时区之一),可以使用 ZoneId.systemDefault() 获取它。Instant

要打印它,您可以使用两个内置的格式化程序ISO_OFFSET_DATE_TIMEISO_ZONED_DATE_TIME。两者之间的区别在于,分区日期时间格式化程序会将区域 ID 添加到输出中。

Instant instant = Instant.now();
DateTimeFormatter formatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME;
System.out.println(formatter.format(instant.atZone(ZoneId.systemDefault())));
System.out.println(formatter.format(instant.atZone(ZoneId.of("America/Los_Angeles"))));

在我的计算机上运行时,系统时区为 ,您将获得:"Europe/Paris"

2016-07-31T18:58:54.108+02:00
2016-07-31T09:58:54.108-07:00

当然,如果这些格式化程序不适合您,您可以使用 ofPattern 或构建器 DateTimeFormatterBuilder 构建自己的格式化程序。

13赞 JodaStephen 8/1/2016 #3

An 不包含有关时区的任何信息,并且与其他地方不同,不会自动使用默认时区。因此,格式化程序无法确定年份是什么,因此会出现错误消息。Instant

因此,要格式化即时,您必须添加时区。这可以使用 withZone(ZoneId) 直接添加到格式化程序中 - 无需手动转换为 *:ZonedDateTime

ZoneId zone = ZoneId.systemDefault();
DateTimeFormatter df = DateTimeFormatter.ofPattern("...pattern...").withZone(zone);
df.format(Instant.ofEpochMilli(timestamp))

* 遗憾的是,在早期的 Java 8 版本中,该方法不起作用,但现在已修复,因此如果上述代码不起作用,请升级到最新的 Java 8 补丁版本。DateTimeformatter.withZone(ZoneId)

编辑:只是为了补充一点,当您想在没有任何其他上下文的情况下及时存储即时时,这是正确的类。Instant