Ole VV的答案是正确的。我将添加一些想法和图表。
从不使用java.util.Date
你的代码:
现在日期 = getTrueNowTimeInGMT();
查看您的库是否已针对java.time进行了更新。java.util.Date
几年前,随着JSR 310的采用,该类被取代。java.time.Instant
应该使用该类。
Instant
代表 UTC 的时刻
收到Date
对象后,立即从遗留类转换为现代类。使用添加到旧类的新转换方法。
Instant instant = myJavaUtilDate.toInstant() ;
你说:
我错过了什么吗?我在一篇文章中读到 toInstant() 方法不关心时区。
java.time.Instant
不关心时区,因为根据定义,它始终代表 UTC 中所见的时刻。
Instant.now
始终以 UTC 的角度来捕捉当前时刻。
- 通过添加/减去时间跨度
Instant::plus
并且Instant::minus
将始终使用 UTC,并且涉及通用的 24 小时 UTC 时间,不会遇到诸如夏令时 (DST)之类的政治时间异常。
从纪元参考计数
这两个类,传统的和现代的,代表了 UTC 的一个时刻。自 1970 年第一刻 UTC 1970-01-01T00:00Z 以来,两者都在内部保持时间计数。Instant
在计数中使用更精细的纳秒分辨率,而Date
.
显然,您需要自 1970-01-01T00:00Z 以来的毫秒数。
long millisSinceEpoch = instant.toEpochMilli() ;
又回来了。
Instant instant = Instant.ofEpochMilli( millisSinceEpoch ) ;
我不建议将跟踪时间作为计数。这样的计数对于人类读者来说本质上是模棱两可的,并且容易出现错误或误解。我强烈建议改用标准 ISO 8601 字符串来传达日期时间值。
String output = instant.toString() ;
又回来了。
Instant instant = Instant.parse( input ) ;
ZonedDateTime
你说:
如何从我在 ZonedDateTime 中指出的特定时区获取毫秒数?
一个ZonedDateTime
物体代表了一个特定地区的人们使用的挂钟时间所看到的时刻。想象一下两个人在打长途电话,一个在美国西海岸,一个在突尼斯突尼斯。
捕捉他们通话开始的那一刻。
Instant callStartedUtc = Instant.now() ;
当美国人一边打电话一边看墙上的时钟时,他们看到的是几点?
ZonedDateTime zdtLosAngeles = callStartedUtc.atZone( ZoneId.of( "America/Los_Angeles" ) ) ;
当突尼斯人拿起电话接听时,看了一眼挂在墙上的时钟,他们看到的是几点?
ZonedDateTime zdtTunis = callStartedUtc.atZone( ZoneId.of( "Africa/Tunis" ) ) ;
三个对象都代表同一个时刻。Instant
您可以从两个对象中提取一个ZonedDateTime
对象。
Instant instant = zdtLosAngeles.toInstant() ;
这些都是平等的。在此示例代码中,eq
将为true
.
boolean eq =
callStartedUtc.equals( zdtLosAngeles.toInstant() )
&&
callStartedUtc.equals( zdtTunis.toInstant() )
;
所有三个内部都具有与纪元参考相同的计数。
UTC 作为唯一的真实时间
提示:在工作编程或做系统管理员工作时,学会用 UTC 来思考。将 UTC 视为 One True Time,时区只是变化。
不要在脑海中来回翻译,只要坚持使用 UTC。就像学习一门外语一样,脑子里不断的翻译会让你发疯。
在程序或系统之间交换日期时间值、记录、调试等都应该在 UTC 中完成。我建议将您办公桌上的第二个时钟设置为 UTC。
LocalDateTime
不是片刻
你说:
// 我喜欢使用LocalDateTime
,所以我正在转换Date
为LocalDateTime
:
LocalDateTime ldtOfSystem = LocalDateTime.ofInstant(now.toInstant(), ZoneId.systemDefault());
你不应该“喜欢” LocalDateTime
。那个类不能代表一个时刻。它包含日期和时间,但缺少时区或偏离 UTC 的上下文。所以它可能会说“2021 年 1 月 23 日中午”,但我们不知道这是否意味着日本东京的中午、法国图卢兹的中午或美国俄亥俄州托莱多的中午——所有这些时刻都非常不同,相隔几个小时。
每当您跟踪某个时刻,时间轴上的特定点时,您都不能使用LocalDateTime
. 而是使用Instant
(对于 UTC)、OffsetDateTime
(对于与 UTC 的偏移量,一个数字小时-分钟-秒)或ZonedDateTime
(对于一个时区,在Continent/Region
名称中,例如Europe/Paris
)。
如有疑问,请勿使用LocalDateTime
. 我发现大部分时间在商业应用程序中,我们都在跟踪一个特定的时刻。最大的例外是在LocalDateTime
需要预订未来活动(例如约会)时。
搜索以了解更多信息,因为这已在 Stack Overflow 上多次介绍。例如,Instant 和 LocalDateTime 有什么区别?.
时间服务器
你说:
澄清。为什么我需要 UTC 时间?
我假设您真的想说“为什么我需要从受信任的远程时间服务器获取当前时刻?”。如果是这样,为了清楚起见,我要求您编辑您的问题。
这种需求是合法的。如果本地时钟不可信,并且您必须具有接近当前时刻的值,那么您确实应该联系受信任的时间服务器(如果可用)。
这个词UTC
在这里并不重要。时间服务器最有可能返回 UTC 值。但是在UTC与本地或远程时间的来源无关。
GMT 与 UTC
你说:
我需要将这个 GMT 日期转换为 UTC
不,你没有,几乎可以肯定。
对于几乎所有程序员来说,GMT 和各种 UTC 的确切定义是冗长的、复杂的、学术的和没有实际意义的。
对于会计和工作流程等常规业务应用程序,您可以将 GMT 和UTC视为同义词。差异实际上不到一秒钟。Java 使用的默认时钟确实忽略了这种差异。
除非您使用火箭遥测、GPS/伽利略卫星或原子钟,否则您真的不应该关心 GMT 与 UTC。
此外,在您使用远程时间服务器的情况下,GMT 与 UTC 更不适用,因为检索到的值在通过网络遍历时会损失大量时间。