ZonedDateTime.toInstant().toEpochMilli() 丢失区域数据
ZonedDateTime.toInstant().toEpochMilli() losing zone data
我正在使用系统时区 returns Date
的 TrueTime 库。当转换为毫秒时,我在将此 Date
转换为 UTC 日期时遇到问题。
这是我所做的:
// getting true time in GMT [ex : 2020-07-13T18:00:57.192+03:00 [Europe/Istanbul]]
Date now = getTrueNowTimeInGMT();
// I like to use `LocalDateTime`, so I am converting `Date` to `LocalDateTime`:
LocalDateTime ldtOfSystem = LocalDateTime.ofInstant(now.toInstant(), ZoneId.systemDefault());
// Converting system default zone to UTC
ZonedDateTime zdtOfSystem = ldtOfSystem.atZone(ZoneId.systemDefault());
ZonedDateTime zdtOfSystemInUTC = zdtOfSystem.withZoneSameInstant(ZoneId.of("UTC"));
// Making sure that it is in the UTC. It is returning correct UTC time [ex: 2020-07-13T15:00:57.192 [UTC]]
System.out.println(zdtOfSystemInUTC);
// converting zdtOfSystemInUTC to milliseconds - problem is occurring here!
zdtOfSystemInUTC.toInstant().toEpochMilli();
我正在丢失区域数据,它根据本地区域(系统区域)返回毫秒数。我再次将这些毫秒转换为日期,结果在 GMT+3 [2020-07-13T18:00:57.192+03:00]
我错过了什么吗?我在一个 post 中读到 toInstant()
方法不关心时区。如何从我在 ZonedDateTime
中指出的特定时区获得 毫秒 ?
编辑 感谢@MenuHochSchild 指出这一点,我更改了变量名称(希望改成更好的名称)。
澄清。为什么我需要 UTC 时间?
我无法控制用户的时间。 He/she 可以轻松更改 his/her 设备的日期和时间,这给我们带来了问题。因此,为了解决这个问题,我们找到了一个名为 TrueTime 的库,它提供实时 Date
对象,如下所示:
Tue Jul 14 00:32:46 GMT+03:00 2020
为了与我们的服务器同步并做一些与时间相关的操作,我需要将这个Date
(GMT)转换为UTC,并将其转换为毫秒。
你好像误会了一两件事。
// getting true time in GMT [ex : 2020-07-13T18:00:57.192+03:00 [Europe/Istanbul]]
Date now = getTrueNowTimeInGMT();
A Date
没有任何时区或与 UTC 或 GMT 的偏移量。所以它不能在格林威治标准时间。一个Date
就是一个时间点,不多也不少。顺便说一句,我通常建议您根本不要使用 class,因为它设计不佳且早已过时;但是如果 TrueTime 还不能给你一个 Instant
或其他现代的 class,我们现在必须使用 Date
。
I am losing zone data and it is returning milliseconds based on the
local zone (system zone). …
自纪元以来的毫秒计数与时区无关。纪元通常以UTC定义,但它是一个时间点。因此毫秒从不基于任何时区(或者你可以说它们总是基于UTC,这取决于你喜欢如何使用这些词,意思是一样的)。
… I am converting these milliseconds to Date again and the result is
in GMT+3 [2020-07-13T18:00:57.192+03:00]
这部分我没看懂。您引用的日期、时间和 UTC 偏移量与您从 TrueTime 获得的日期、时间和 UTC 偏移量相同,所以怎么会错呢? Java 将 UTC 和 GMT 视为同义词(在现实世界中,UTC 和 GMT 之间总是相差不到一秒,所以这可能没问题)。
仍然假设你只能从你的日期和时间库中获取老式的Date
,在我看来,获取自纪元以来的当前毫秒数的简单方法是:
getTrueNowTimeInGMT().getTime();
由于我上面所说的,无论设备的时区设置如何,这都是可靠的。
正确。我会添加一些想法和图表。
从不使用 java.util.Date
您的代码:
Date now = getTrueNowTimeInGMT();
查看您的图书馆是否已更新 java.time。 java.util.Date
class 多年前被 JSR 310 取代。应使用 java.time.Instant
class。
Instant
表示 UTC 中的时刻
收到 Date
对象后,立即从遗留 class 转换为现代 class。使用添加到旧 classes 的新转换方法。
Instant instant = myJavaUtilDate.toInstant() ;
你说:
Am I missing something? I read in one post that toInstant() method doesn't care about the timezone.
java.time.Instant
不关心时区,因为根据定义,它始终代表 UTC 中的时刻。
- 通过
Instant.now
捕获当前时刻将始终以UTC视角显示。
- Adding/subtracting 通过
Instant::plus
和 Instant::minus
的时间跨度将始终采用 UTC,并且涉及不遇到政治时间异常,例如 Daylight Saving Time (DST).
从纪元参考开始计数
这两个 classes,传统的和现代的,都代表了 UTC 中的一个时刻。两者都在内部记录自 1970 年第一刻以来的时间,UTC 时间为 1970-01-01T00:00Z。 Instant
在其计数中使用更精细的纳秒分辨率,而在 Date
中使用毫秒。
显然您想要计算自 1970-01-01T00:00Z 以来的毫秒数。
long millisSinceEpoch = instant.toEpochMilli() ;
然后又回来了。
Instant instant = Instant.ofEpochMilli( millisSinceEpoch ) ;
我不建议将跟踪时间作为计数。这样的计数对于人类 reader 来说本质上是模棱两可的,并且容易出错或误解。我强烈建议改用标准 ISO 8601 字符串来传达日期时间值。
String output = instant.toString() ;
然后又回来了。
Instant instant = Instant.parse( input ) ;
ZonedDateTime
你说:
How can I get the milliseconds from the specific time zone I pointed out in ZonedDateTime?
一个 ZonedDateTime
对象表示通过特定区域的人们使用的挂钟时间看到的时刻。想象一下,两个人在打长途 phone 电话,一个在美国西海岸,一个在突尼斯突尼斯。
捕捉他们通话开始的瞬间。
Instant callStartedUtc = Instant.now() ;
当美国人在拨打 phone 时瞥了一眼墙上的时钟,他们看到的是什么时间?
ZonedDateTime zdtLosAngeles = callStartedUtc.atZone( ZoneId.of( "America/Los_Angeles" ) ) ;
当突尼斯人拿起 phone 回答时瞥了一眼挂在墙上的时钟,他们看到的是什么时间?
ZonedDateTime zdtTunis = callStartedUtc.atZone( ZoneId.of( "Africa/Tunis" ) ) ;
三个对象都代表同一时刻。您可以从两个 ZonedDateTime
对象中提取一个 Instant
对象。
Instant instant = zdtLosAngeles.toInstant() ;
这些都是平等的。在此示例代码中,eq
将是 true
.
boolean eq =
callStartedUtc.equals( zdtLosAngeles.toInstant() )
&&
callStartedUtc.equals( zdtTunis.toInstant() )
;
所有三个在内部都具有完全相同的纪元参考计数。
UTC 作为唯一真实时间
提示:在工作编程或系统管理员工作中学会用 UTC 来思考。 将 UTC 视为唯一真实时间,时区只是变化。
不要在脑海中来回转换,坚持使用 UTC。就像学习一门外语一样,脑子里不停地翻译会让你抓狂。
在程序或系统之间交换日期时间值、日志记录、调试等都应该在 UTC 中完成。我建议将您办公桌上的第二个时钟设置为 UTC。
LocalDateTime
不是片刻
你说:
// I like to use LocalDateTime
, so I am converting Date
to LocalDateTime
:
LocalDateTime ldtOfSystem = LocalDateTime.ofInstant(now.toInstant(), ZoneId.systemDefault());
你不应该“喜欢”LocalDateTime
。那个class不能代表一个时刻。它包含一个日期和一天中的时间,但缺少时区或偏离 UTC 的上下文。所以它可能会说“2021 年 1 月 23 日中午”,但我们不知道这意味着日本东京的中午、法国图卢兹的中午,还是美国俄亥俄州托莱多的中午——所有这些都是非常不同的时刻,相隔几个小时。
无论何时跟踪时刻,时间轴上的特定点,都不能使用 LocalDateTime
。而是使用 Instant
(对于 UTC)、OffsetDateTime
(对于与 UTC 的偏移量,一个数字小时-分钟-秒)或 ZonedDateTime
(对于时区,在 Continent/Region
名称,如Europe/Paris
).
如有疑问,请不要使用LocalDateTime
。我发现大部分时间在商业应用程序中,我们都在跟踪特定时刻。最大的例外是在预订未来活动时,例如需要 LocalDateTime
的约会。
搜索以了解更多信息,因为这已在 Stack Overflow 上多次介绍。例如,.
时间服务器
你说:
Clarification. Why do I have the need for the UTC time?
我假设您真的想说,“为什么我需要从受信任的远程时间服务器获取当前时刻?”。如果是这样,为了清楚起见,我要求您编辑您的问题。
这种需求是合理的。如果本地时钟不可信,并且您的值必须接近当前时刻,那么您确实应该联系可信的时间服务器(如果可用)。
术语 UTC
在这里并不相关。时间服务器最有可能 return 一个 UTC 值。但是使用 UTC 与时间来源是本地还是远程无关。
GMT 与 UTC
你说:
I need to convert this Date which is GMT to UTC
不,你几乎可以肯定没有。
GMT 和各种 UTC 的确切定义是冗长、复杂、学术性的,对几乎所有程序员来说都没有实际意义。
在会计和工作流程等常规业务应用方面,您可以将 GMT 和 UTC 视为同义词。差异实际上不到一秒钟。 Java 使用的默认时钟确实忽略了这种差异。
除非您使用火箭遥测技术、GPS/Galileo 卫星或原子钟,否则您真的不应该关心 GMT 与 UTC。
此外,在您使用远程时间服务器的情况下,GMT 与 UTC 更不适用,因为检索到的值在遍历网络时会损失大量时间。
我正在使用系统时区 returns Date
的 TrueTime 库。当转换为毫秒时,我在将此 Date
转换为 UTC 日期时遇到问题。
这是我所做的:
// getting true time in GMT [ex : 2020-07-13T18:00:57.192+03:00 [Europe/Istanbul]]
Date now = getTrueNowTimeInGMT();
// I like to use `LocalDateTime`, so I am converting `Date` to `LocalDateTime`:
LocalDateTime ldtOfSystem = LocalDateTime.ofInstant(now.toInstant(), ZoneId.systemDefault());
// Converting system default zone to UTC
ZonedDateTime zdtOfSystem = ldtOfSystem.atZone(ZoneId.systemDefault());
ZonedDateTime zdtOfSystemInUTC = zdtOfSystem.withZoneSameInstant(ZoneId.of("UTC"));
// Making sure that it is in the UTC. It is returning correct UTC time [ex: 2020-07-13T15:00:57.192 [UTC]]
System.out.println(zdtOfSystemInUTC);
// converting zdtOfSystemInUTC to milliseconds - problem is occurring here!
zdtOfSystemInUTC.toInstant().toEpochMilli();
我正在丢失区域数据,它根据本地区域(系统区域)返回毫秒数。我再次将这些毫秒转换为日期,结果在 GMT+3 [2020-07-13T18:00:57.192+03:00]
我错过了什么吗?我在一个 post 中读到 toInstant()
方法不关心时区。如何从我在 ZonedDateTime
中指出的特定时区获得 毫秒 ?
编辑 感谢@MenuHochSchild 指出这一点,我更改了变量名称(希望改成更好的名称)。
澄清。为什么我需要 UTC 时间?
我无法控制用户的时间。 He/she 可以轻松更改 his/her 设备的日期和时间,这给我们带来了问题。因此,为了解决这个问题,我们找到了一个名为 TrueTime 的库,它提供实时 Date
对象,如下所示:
Tue Jul 14 00:32:46 GMT+03:00 2020
为了与我们的服务器同步并做一些与时间相关的操作,我需要将这个Date
(GMT)转换为UTC,并将其转换为毫秒。
你好像误会了一两件事。
// getting true time in GMT [ex : 2020-07-13T18:00:57.192+03:00 [Europe/Istanbul]] Date now = getTrueNowTimeInGMT();
A Date
没有任何时区或与 UTC 或 GMT 的偏移量。所以它不能在格林威治标准时间。一个Date
就是一个时间点,不多也不少。顺便说一句,我通常建议您根本不要使用 class,因为它设计不佳且早已过时;但是如果 TrueTime 还不能给你一个 Instant
或其他现代的 class,我们现在必须使用 Date
。
I am losing zone data and it is returning milliseconds based on the local zone (system zone). …
自纪元以来的毫秒计数与时区无关。纪元通常以UTC定义,但它是一个时间点。因此毫秒从不基于任何时区(或者你可以说它们总是基于UTC,这取决于你喜欢如何使用这些词,意思是一样的)。
… I am converting these milliseconds to Date again and the result is in GMT+3 [2020-07-13T18:00:57.192+03:00]
这部分我没看懂。您引用的日期、时间和 UTC 偏移量与您从 TrueTime 获得的日期、时间和 UTC 偏移量相同,所以怎么会错呢? Java 将 UTC 和 GMT 视为同义词(在现实世界中,UTC 和 GMT 之间总是相差不到一秒,所以这可能没问题)。
仍然假设你只能从你的日期和时间库中获取老式的Date
,在我看来,获取自纪元以来的当前毫秒数的简单方法是:
getTrueNowTimeInGMT().getTime();
由于我上面所说的,无论设备的时区设置如何,这都是可靠的。
从不使用 java.util.Date
您的代码:
Date now = getTrueNowTimeInGMT();
查看您的图书馆是否已更新 java.time。 java.util.Date
class 多年前被 JSR 310 取代。应使用 java.time.Instant
class。
Instant
表示 UTC 中的时刻
收到 Date
对象后,立即从遗留 class 转换为现代 class。使用添加到旧 classes 的新转换方法。
Instant instant = myJavaUtilDate.toInstant() ;
你说:
Am I missing something? I read in one post that toInstant() method doesn't care about the timezone.
java.time.Instant
不关心时区,因为根据定义,它始终代表 UTC 中的时刻。
- 通过
Instant.now
捕获当前时刻将始终以UTC视角显示。 - Adding/subtracting 通过
Instant::plus
和Instant::minus
的时间跨度将始终采用 UTC,并且涉及不遇到政治时间异常,例如 Daylight Saving Time (DST).
从纪元参考开始计数
这两个 classes,传统的和现代的,都代表了 UTC 中的一个时刻。两者都在内部记录自 1970 年第一刻以来的时间,UTC 时间为 1970-01-01T00:00Z。 Instant
在其计数中使用更精细的纳秒分辨率,而在 Date
中使用毫秒。
显然您想要计算自 1970-01-01T00:00Z 以来的毫秒数。
long millisSinceEpoch = instant.toEpochMilli() ;
然后又回来了。
Instant instant = Instant.ofEpochMilli( millisSinceEpoch ) ;
我不建议将跟踪时间作为计数。这样的计数对于人类 reader 来说本质上是模棱两可的,并且容易出错或误解。我强烈建议改用标准 ISO 8601 字符串来传达日期时间值。
String output = instant.toString() ;
然后又回来了。
Instant instant = Instant.parse( input ) ;
ZonedDateTime
你说:
How can I get the milliseconds from the specific time zone I pointed out in ZonedDateTime?
一个 ZonedDateTime
对象表示通过特定区域的人们使用的挂钟时间看到的时刻。想象一下,两个人在打长途 phone 电话,一个在美国西海岸,一个在突尼斯突尼斯。
捕捉他们通话开始的瞬间。
Instant callStartedUtc = Instant.now() ;
当美国人在拨打 phone 时瞥了一眼墙上的时钟,他们看到的是什么时间?
ZonedDateTime zdtLosAngeles = callStartedUtc.atZone( ZoneId.of( "America/Los_Angeles" ) ) ;
当突尼斯人拿起 phone 回答时瞥了一眼挂在墙上的时钟,他们看到的是什么时间?
ZonedDateTime zdtTunis = callStartedUtc.atZone( ZoneId.of( "Africa/Tunis" ) ) ;
三个对象都代表同一时刻。您可以从两个 ZonedDateTime
对象中提取一个 Instant
对象。
Instant instant = zdtLosAngeles.toInstant() ;
这些都是平等的。在此示例代码中,eq
将是 true
.
boolean eq =
callStartedUtc.equals( zdtLosAngeles.toInstant() )
&&
callStartedUtc.equals( zdtTunis.toInstant() )
;
所有三个在内部都具有完全相同的纪元参考计数。
UTC 作为唯一真实时间
提示:在工作编程或系统管理员工作中学会用 UTC 来思考。 将 UTC 视为唯一真实时间,时区只是变化。
不要在脑海中来回转换,坚持使用 UTC。就像学习一门外语一样,脑子里不停地翻译会让你抓狂。
在程序或系统之间交换日期时间值、日志记录、调试等都应该在 UTC 中完成。我建议将您办公桌上的第二个时钟设置为 UTC。
LocalDateTime
不是片刻
你说:
// I like to use
LocalDateTime
, so I am convertingDate
toLocalDateTime
:
LocalDateTime ldtOfSystem = LocalDateTime.ofInstant(now.toInstant(), ZoneId.systemDefault());
你不应该“喜欢”LocalDateTime
。那个class不能代表一个时刻。它包含一个日期和一天中的时间,但缺少时区或偏离 UTC 的上下文。所以它可能会说“2021 年 1 月 23 日中午”,但我们不知道这意味着日本东京的中午、法国图卢兹的中午,还是美国俄亥俄州托莱多的中午——所有这些都是非常不同的时刻,相隔几个小时。
无论何时跟踪时刻,时间轴上的特定点,都不能使用 LocalDateTime
。而是使用 Instant
(对于 UTC)、OffsetDateTime
(对于与 UTC 的偏移量,一个数字小时-分钟-秒)或 ZonedDateTime
(对于时区,在 Continent/Region
名称,如Europe/Paris
).
如有疑问,请不要使用LocalDateTime
。我发现大部分时间在商业应用程序中,我们都在跟踪特定时刻。最大的例外是在预订未来活动时,例如需要 LocalDateTime
的约会。
搜索以了解更多信息,因为这已在 Stack Overflow 上多次介绍。例如,
时间服务器
你说:
Clarification. Why do I have the need for the UTC time?
我假设您真的想说,“为什么我需要从受信任的远程时间服务器获取当前时刻?”。如果是这样,为了清楚起见,我要求您编辑您的问题。
这种需求是合理的。如果本地时钟不可信,并且您的值必须接近当前时刻,那么您确实应该联系可信的时间服务器(如果可用)。
术语 UTC
在这里并不相关。时间服务器最有可能 return 一个 UTC 值。但是使用 UTC 与时间来源是本地还是远程无关。
GMT 与 UTC
你说:
I need to convert this Date which is GMT to UTC
不,你几乎可以肯定没有。
GMT 和各种 UTC 的确切定义是冗长、复杂、学术性的,对几乎所有程序员来说都没有实际意义。
在会计和工作流程等常规业务应用方面,您可以将 GMT 和 UTC 视为同义词。差异实际上不到一秒钟。 Java 使用的默认时钟确实忽略了这种差异。
除非您使用火箭遥测技术、GPS/Galileo 卫星或原子钟,否则您真的不应该关心 GMT 与 UTC。
此外,在您使用远程时间服务器的情况下,GMT 与 UTC 更不适用,因为检索到的值在遍历网络时会损失大量时间。