使用 new java.util.Date() Returns 比 DST 晚一个小时

Using new java.util.Date() Returns An Hour Behind With DST

我有一个 Java 程序作为 cron 作业在 linux 虚拟机上运行。当前存在问题,以下行用作日志记录的一部分以存储当前日期和时间:

String date = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss").format(new Date());

今天夏令时后,确定系统日期设置不正确,因为它没有更新。使用以下命令修复此问题并重新启动 linux 环境后,日期现在是正确的,并且已更新为夏令时。

ln -s /usr/share/zoneinfo/America/New_York /etc/localtime

然而,当 Java 程序作为 cron 作业的一部分执行时,日期仍然晚了一个小时。这会导致内部计时问题,因为 new Date() 用于获取当前系统时间,这会导致进程中的后续问题。例如在 5:00 PM,Java 程序输出 4:00 PM。这个实现是否有额外的考虑? new Date() 不应该总是 return 当前系统日期吗?

向 cron 作业添加调试行,这会在预期的正确时间执行。

tl;博士

  • Instant.now()
  • Update your JVM’s tzdata
  • 使用 UTC 工作,尤其是日志记录
  • 序列化为 ISO 8601 格式

Date class

Shouldn't new Date() always return the current system date?

是的,Date class 始终捕获 UTC 中的当前时刻。让我重复一遍:Date 以 UTC 捕捉瞬间。

问题来自 class' toString 方法的一个善意但不幸的“特性”,其中 JVM 当前的默认时区用于生成该字符串。避免这种过时且麻烦的众多原因之一 class.

我怀疑您的“tzdata”时区数据文件已过时,无法识别 DST 转换的新日期。

重新启动 JVM

Java 实现通常在启动时从主机 OS 中获取它们当前的默认时区。在JVM之后,更改主机OS'时区对JVM没有影响。

重新启动 JVM 以获取主机 OS 中的新区域设置。

在服务器上使用 UTC

最佳做法通常是将您的服务器设置为 UTC。那么你不必担心夏令时的废话。

在 UTC 工作

您的大部分编程、日志记录、cron 作业、数据交换和数据序列化都应该使用 UTC。

请注意,例如,Stack Overflow 如何使用 UTC 报告 "today" 和 "yesterday" 的 activity。学会将 UTC 视为唯一真实时间;所有区域都是该主题的变体。

仅在关键时应用时区,例如在向期望特定时区的用户进行演示时。

请记住,您的问题不是由于 space 时间弯曲造成的。秒、分和小时继续正常递增,就 UTC 而言,滴答滴答。您的问题在于错误翻译成具有过时时区规则更改数据的时区。

在 Java 代码中指定您想要的区域

您的 Java 应用应始终明确指定其 desired/intended 时区。如果省略,则隐式应用 JVM 当前的默认时区。

JVM 中的任何应用程序的任何线程中的任何代码都可以随时更改 JVM 默认设置,并立即影响该 JVM 中的所有其他代码。所以永远不要依赖当前的默认值来做任何重要的事情。与用户确认,并在您的代码中明确指定。

避免遗留日期时间 classes

包括 DateCalendar 在内的旧日期时间 classes 真是一团糟。避开他们。它们现在是遗留的,被 java.time classes 取代。

Instant class 是时间线上的一个时刻,分辨率以纳秒为单位,始终采用 UTC。

Instant instant = Instant.now() ;

要生成标准 ISO 8601 格式的字符串,请调用 toString。最后的 Z 是 Zulu 的缩写,表示 UTC。

String output = instant.toString() ;

2017-01-23T12:34:56.123456789Z

使用这两行简单的代码就可以避免整个问题。

但是您应该定期更新以下位置的 tzdata:

  • 主机OS
  • JVM
  • 数据库系统(Postgres等)
  • Joda-Time 等库