在您发布该 Java 小应用程序的源代码之前,我们无法准确地为您提供帮助。
跟踪日期时间
您似乎误解的一个一般概念是计算机跟踪的日期时间没有时区。如果您使用的是旧的过时类java.util.Date,它会跟踪自UTC 中1970 年第一刻的epoch reference date 以来的毫秒数。
日期时间!= 字符串
另一个概念:日期时间对象不是字符串。您可以生成一个字符串来表示您的日期时间对象中的日期时间值,但该字符串与日期时间不同。请记住,在内部,日期时间实际上是自 1970 年以来的毫秒数 UTC)。
您的应用可能会调用 java.util.Date 类的 toString 方法。该方法会混淆地将 JVM 的当前默认时区应用于存储的日期时间,从而产生错误的错觉,即 java.util.Date 已分配了该时区。
默认时区
默认时区通常在 JVM 启动时从主机操作系统中获取,但并非总是如此。 JVM 的配置标志可能会默认指示另一个时区。 JVM 内任何应用程序的任何线程中的任何代码都可以在任何时候change the JVM’s default time zone,在运行时!由于不可靠,我建议您避免使用默认值,而是始终指定所需/预期的时区。
通常最佳做法是将服务器保持在 UTC。但我不希望我的应用程序容易受到诸如某些系统管理员更改服务器时区等外部因素的影响,我总是在我的 Java 代码中指定所需/预期的时区(如下所示)。
没问题
所以您没有问题需要解决。 19:13:3x 的巴黎时间 (CEST) 比 UTC 早两个小时,您的 Java 应用程序正确显示为 17:13:3x UTC 时区。这些值是有意义的。这两个日期时间字符串是时间轴上同一时刻的两种不同表示。
如果您想要在您的 Java 应用程序中使用巴黎时间,请询问巴黎时间(如下所示)。如果您需要 UTC,请在您的 Java 应用程序中请求 UTC(也显示在下方)。
在您向我们展示您的源代码之前,为什么您的 Java 应用程序以 UTC 显示时间仍然是个谜。
同时,我可以展示捕捉当前时间和调整到所需时区的基础知识。
java.time
您使用的是旧的日期时间类,这些类已被证明是麻烦、混乱和有缺陷的。避开他们。这些旧类已被 Java 8 及更高版本中内置的 java.time 框架所取代。大部分 java.time 功能已在 ThreeTen-Backport 中向后移植到 Java 6 和 7,并在 ThreeTenABP 中进一步适应 Android。
Instant 是 UTC 时间线上的当前时刻,分辨率为纳秒(比 java.util.Date 中的毫秒更精细)。
Instant instant = Instant.now();
通常最适合您的大部分业务逻辑、数据库存储、其他存储和 UTC 数据交换。所以要经常使用Instant。
挂钟时间
应用时区以获取某个地区的wall-clock time。申请ZoneId 以获得ZonedDateTime。
ZoneId zoneId = ZoneId.of( "Europe/Paris" );
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId );
生成 ISO 8601 字符串
java.time 中的toString 方法默认生成标准ISO 8601 格式的字符串。 ZonedDateTime 类的 toString 方法通过在方括号中附加时区名称来扩展标准。
String output = instant.toString();
2016-05-21T00:52:53.375Z
String output = zdt.toString();
2016-05-21T02:52:53.375+02:00[欧洲/巴黎]
您可以调整到另一个时区。请注意日期早于巴黎,20 与 5 月的 21(在蒙特利尔仍然是“昨天”)。
ZonedDateTime zdt_Montréal = zdt.withZoneSameInstant( ZoneId.of( "America/Montreal" ) ) ;
2016-05-20T20:52:53.375-04:00[美国/蒙特利尔]
因此,我们为时间轴上的同一时刻(UTC、巴黎和蒙特利尔)生成了三种不同的文本表示。
时区
避免使用 3-4 个字母的区域缩写,例如 CEST。这些既不是标准化的,也不是唯一的(!)。此外,它们不是真正的时区。始终使用proper time zone names,格式为continent/region。