与 java.time.Instant 对象一起使用时的 Java SE 8 TemporalAccessor.from 问题
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/23641846/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Java SE 8 TemporalAccessor.from issues when used with a java.time.Instant object
提问by user3627702
java.time
has an Instantclass which encapsulates a position (or 'moment') on the timeline. While I understand that this is a seconds/nanoseconds value so not directly related to time zones or times offsets, its toString
returns a date and time formatted as a UTC date/time, eg 2014-05-13T20:05:08.556Z. Also anInstant.atZone(zone)
and anInstant.atOffset(offset)
both produce a value that is consistent with treating the Instant
as having an implied UTC time-zone/'zero' offset.
java.time
有一个Instant类,它封装了时间轴上的位置(或“时刻”)。虽然我知道这是一个秒/纳秒值,因此与时区或时间偏移量没有直接关系,但它toString
返回格式为 UTC 日期/时间的日期和时间,例如 2014-05-13T20:05:08.556Z。此外anInstant.atZone(zone)
,anInstant.atOffset(offset)
两者都产生一个与将Instant
视为具有隐含的 UTC 时区/“零”偏移量一致的值。
I would have expected therefore:
因此,我会期望:
ZoneOffset.from(anInstant)
to produce a 'zero'ZoneOffset
OffsetDateTime.from(anInstant)
to produce a date/time with a 'zero' offsetZoneId.from(anInstant)
(probably) to produce a UTCZoneId
ZonedDateTime.from(anInstant)
(probably) to produce aZonedDateTime
with a UTCZoneId
ZoneOffset.from(anInstant)
产生一个“零”ZoneOffset
OffsetDateTime.from(anInstant)
生成具有“零”偏移量的日期/时间ZoneId.from(anInstant)
(可能)产生一个UTCZoneId
ZonedDateTime.from(anInstant)
(可能)产生ZonedDateTime
一个 UTCZoneId
The documentation for ZonedDateTime.from
, as I read it, appears to endorse this.
的文档ZonedDateTime.from
,正如我所读到的,似乎支持这一点。
In fact ZoneOffset.from(anInstant)
fails with DateTimeException
, and I suppose for that reason OffsetDateTime.from(anInstant)
also fails, as do the other two.
实际上ZoneOffset.from(anInstant)
失败了DateTimeException
,我想因为这个原因OffsetDateTime.from(anInstant)
也失败了,其他两个也是如此。
Is this the expected behaviour?
这是预期的行为吗?
回答by Idan Arye
The Instant
class does not have a time zone. It gets printed like it's in the UTC zone because it has to be printed in some zone(you wouldn't want it to be printed in ticks, would you?), but that's not it's time zone - as demonstrated in the following example:
该Instant
课程没有时区。它的打印就像在 UTC 区域中一样,因为它必须在某个区域中打印(您不希望它以刻度打印,是吗?),但这不是时区 - 如以下示例所示:
Instant instant=Instant.now();
System.out.println(instant);//prints 2014-05-14T06:18:48.649Z
System.out.println(instant.atZone(ZoneId.of("UTC")));//prints 2014-05-14T06:18:48.649Z[UTC]
The signatures of ZoneOffset.from
and OffsetDateTime.from
accept any temporal object, but they fail for some types. java.time
does not seem to have an interface for temporals that have a timezone or offset. Such an interface could have declare getOffset
and getZone
. Since we don't have such interface, these methods are declared separately in multiple places.
ZoneOffset.from
和OffsetDateTime.from
接受任何时间对象的签名,但它们对某些类型失败。java.time
似乎没有具有时区或偏移量的时间接口。这样的接口可以有声明getOffset
和getZone
。由于我们没有这样的接口,这些方法在多处单独声明。
If you had a ZonedDateTime
you could call it's getOffset
. If you had an OffsetDateTime
you could also call it's offset - but these are two different getOffset
methods, as the two classes don't get that method from a common interface. That means that if you have a Temporal
object that could be either you would have to test if it's an instanceof
both of them to get the offset.
如果你有一个,ZonedDateTime
你可以称之为getOffset
。如果您有一个,OffsetDateTime
您也可以将其称为偏移量 - 但这是两种不同的getOffset
方法,因为这两个类不会从公共接口获取该方法。这意味着,如果您有一个Temporal
可能是其中之一的对象,您将必须测试它是否是instanceof
他们两个来获得偏移量。
OffsetDateTime.from
simplifies the process by doing that for you - but since it also can't rely on a common interface, it has to accept any Temporal
object and throw an exception for those that don't have an offset.
OffsetDateTime.from
通过为您执行此操作来简化流程 - 但由于它也不能依赖公共接口,因此它必须接受任何Temporal
对象并为那些没有偏移的对象抛出异常。
回答by Meno Hochschild
Short answer:
简短的回答:
The JSR-310-designers don't want people to do conversions between machine time and human time via static from()-methods in types like ZoneId
, ZoneOffset
, OffsetDateTime
, ZonedDateTime
etc. This is explicitly specified if you carefully study the javadoc. Instead use:
该JSR-310-设计师不希望人们从通过静态做机时间和人类的时间之间的转换() -类中的方法,如ZoneId
,ZoneOffset
,OffsetDateTime
,ZonedDateTime
等如果你仔细研究的Javadoc这是明确指定。而是使用:
OffsetDateTime#toInstant():Instant
ZonedDateTime#toInstant():Instant
Instant#atOffset(ZoneOffset):OffsetDateTime
Instant#atZone(ZoneId):ZonedDateTime
The problem with the static from()-methods is that otherwise people are able to do conversions between an Instant
and for example a LocalDateTime
without thinking about the timezone.
静态 from() 方法的问题在于,否则人们可以在不考虑时区Instant
的LocalDateTime
情况下在 a和 a之间进行转换。
Long answer:
长答案:
Whether to consider an Instant
as counter or as field tuple, the answer given by JSR-310-team was a strict separation between so-called machine time and human time. Indeed they intend to have a strict separation - see their guidelines. So finally they want Instant
to be only interpreted as a machine time counter. So they intentionally have a design where you cannot ask an Instant
for fields like year, hour etc.
无论是Instant
作为计数器还是字段元组,JSR-310-team 给出的答案是所谓的机器时间和人类时间之间的严格分离。事实上,他们打算进行严格的分离——参见他们的指导方针。所以最后他们只想Instant
被解释为机器时间计数器。所以他们故意设计了一个设计,你不能要求Instant
年、小时等字段。
But indeed, the JSR-310-team is not consistent at all. They have implemented the method Instant.toString()
as a field tuple view including year, ..., hour, ... and offset-symbol Z (for UTC-timezone) (footnote: Outside of JSR-310 this is quite common to have a field-based look on such machine times - see for example in Wikipedia or on other sites about TAI and UTC). Once the spec lead S. Colebourne said in a comment on a threeten-github-issue:
但事实上,JSR-310 团队根本不是一致的。他们已将该方法实现Instant.toString()
为字段元组视图,包括年、...、小时、...和偏移符号 Z(用于 UTC 时区)(脚注:在 JSR-310 之外,具有字段的情况很常见-基于此类机器时间的外观 - 例如参见维基百科或其他有关TAI 和 UTC 的网站)。一旦规范负责人 S. Colebourne 在对一个Threeten-github-issue的评论中说:
"If we were really hard line, the toString of an Instant would simply be the number of seconds from 1970-01-01Z. We chose not to do that, and output a more friendly toString to aid developers, But it doesn't change the basic fact that an Instant is just a count of seconds, and cannot be converted to a year/month/day without a time-zone of some kind."
“如果我们真的强硬,那么 Instant 的 toString 将只是从 1970-01-01Z 开始的秒数。我们选择不这样做,并输出一个更友好的 toString 来帮助开发人员,但它不会改变Instant 只是秒数的基本事实,如果没有某种时区,就不能转换为年/月/日。”
People can like this design decision or not (like me), but the consequence is that you cannot ask an Instant
for year, ..., hour, ... and offset. See also the documentation of supported fields:
人们可以喜欢或不喜欢这个设计决定(像我一样),但结果是你不能要求Instant
年、...、小时、...和偏移量。另请参阅支持字段的文档:
NANO_OF_SECOND
MICRO_OF_SECOND
MILLI_OF_SECOND
INSTANT_SECONDS
Here it is interesting what is missing, above all a zone-related field is missing. As a reason, we often hear the statement that objects like Instant
or java.util.Date
have no timezone.In my opinion this is a too simplistic view. While it is true that these objects have no timezone state internally (and there is also no need for having such an internal value), those objects MUST be related to UTC timezone because this is the basis of every timezone offset calculation and conversion to local types. So the correct answer would be: An Instant
is a machine counter counting the seconds and nanoseconds since UNIX epoch in timezone UTC (per spec). The last part - relationship to UTC zone - is not well specified by JSR-310-team but they cannot deny it. The designers want to abolish the timezone aspect from Instant
because it looks human-time-related.However, they can't completely abolish it because that is a fundamental part of any internal offset calculation. So your observation regarding
有趣的是缺少什么,最重要的是缺少与区域相关的字段。因此,我们经常听到对象喜欢Instant
或java.util.Date
没有时区的说法。在我看来,这是一个过于简单化的观点。虽然这些对象在内部确实没有时区状态(并且也不需要具有这样的内部值),但这些对象必须与 UTC 时区相关,因为这是每个时区偏移计算和转换为本地类型的基础. 所以正确的答案是:AnInstant
是一个机器计数器,它计算自 UNIX 时代在 UTC 时区(根据规范)以来的秒数和纳秒数。最后一部分 - 与 UTC 区域的关系 - JSR-310-team 没有很好地说明,但他们不能否认。设计师希望取消时区方面,Instant
因为它看起来与人类时间相关。但是,他们不能完全废除它,因为这是任何内部偏移计算的基本部分。所以你的观察
"Also an Instant.atZone(zone)
and an Instant.atOffset(offset)
both produce a value that is consistent with treating the Instant as having an implied UTC time-zone/'zero' offset."
“而且 anInstant.atZone(zone)
和 anInstant.atOffset(offset)
都产生一个值,该值与将 Instant 视为具有隐含的 UTC 时区/'零'偏移量一致。”
is right.
是对的。
While it might be very intuitive that ZoneOffset.from(anInstant)
might produce ZoneOffset.UTC
, it throws an exception because its from()-method searches for a non-existent OFFSET_SECONDS-field. The designers of JSR-310 have decided to do that in the specification for the same reason, namely to make people think that an Instant
has officially nothing to do with UTC timezone i.e. "has no timezone" (but internally they must accept this basic fact in all internal calculations!).
虽然它可能是很直观的是ZoneOffset.from(anInstant)
可能会产生ZoneOffset.UTC
,它抛出一个异常,因为它由() -对一个不存在的方法搜索OFFSET_SECONDS场。JSR-310 的设计者出于同样的原因决定在规范中这样做,即让人们认为 anInstant
与 UTC 时区正式无关,即“没有时区”(但在内部他们必须接受这个基本事实所有内部计算!)。
For the same reason, OffsetDateTime.from(anInstant)
and ZoneId.from(anInstant)
fail, too.
出于同样的原因,OffsetDateTime.from(anInstant)
也ZoneId.from(anInstant)
失败了。
About ZonedDateTime.from(anInstant)
we read:
关于ZonedDateTime.from(anInstant)
我们阅读:
"The conversion will first obtain a ZoneId from the temporal object, falling back to a ZoneOffset if necessary. It will then try to obtain an Instant, falling back to a LocalDateTime if necessary. The result will be either the combination of ZoneId or ZoneOffset with Instant or LocalDateTime."
“转换将首先从时间对象中获取 ZoneId,必要时回退到 ZoneOffset。然后它会尝试获取 Instant,如有必要回退到 LocalDateTime。结果将是 ZoneId 或 ZoneOffset 与即时或本地日期时间。”
So this conversion will fail again due to the same reasons because neither ZoneId
nor ZoneOffset
can be obtained from an Instant
. The exception message reads as:
因此,由于相同的原因,此转换将再次失败,因为既不能ZoneId
也不ZoneOffset
能从Instant
. 异常消息如下:
"Unable to obtain ZoneId from TemporalAccessor: 1970-01-01T00:00:00Z of type java.time.Instant"
“无法从 TemporalAccessor 获取 ZoneId:1970-01-01T00:00:00Z,类型为 java.time.Instant”
Finally we see that all static from()-methods suffer from being unable to do a conversion between human time and machine time even if this looks intuitive. In some cases a conversion between let's say LocalDate
and Instant
is questionable. This behaviour is specified, but I predict that your question is not the last question of this kind and many users will continue to be confused.
最后,我们看到所有静态 from() 方法都无法在人类时间和机器时间之间进行转换,即使这看起来很直观。在某些情况下,可以说LocalDate
和之间的转换Instant
是有问题的。此行为已指定,但我预测您的问题不是此类问题的最后一个,许多用户将继续感到困惑。
The real design problem in my opinion is that:
在我看来,真正的设计问题是:
a) There should not be a sharp separation between human time and machine time. Temporal objects like Instant
should better behave like both. An analogy in quantum mechanics: You can view an electron both as a particle and a wave.
a) 人的时间和机器的时间之间不应有明显的区别。像这样的时间对象Instant
应该更好地表现两者。量子力学中的一个类比:您可以将电子视为粒子和波。
b) All static from()-methods are too public. Ihat is too easily accessible in my opinion and should better have been removed from public API or use more specific arguments than TemporalAccessor
. The weakness of these methods is that people can forget to think about related timezones in such conversions because they start the query with a local type. Consider for example: LocalDate.from(anInstant)
(in which timezone???). However, if you directly ask an Instant
for its date like instant.getDate()
, personally I would consider the date in UTC-timezone as valid answer because here the query starts from an UTC perspective.
b) 所有静态 from() 方法都太公开了。我认为 Ihat 太容易访问了,最好从公共 API 中删除,或者使用比TemporalAccessor
. 这些方法的弱点在于人们可能会忘记在此类转换中考虑相关的时区,因为它们以本地类型开始查询。考虑例如:(LocalDate.from(anInstant)
在哪个时区???)。但是,如果您直接询问Instant
日期,例如instant.getDate()
,我个人会将 UTC 时区中的日期视为有效答案,因为这里的查询是从 UTC 角度开始的。
c) In conclusion: I absolutely share with the JSR-310-team the good idea to avoid conversions between local types and global types like Instant
without specifying a timezone. I just differ when it comes to the API-design to prevent users from doing such a timezone-lessconversion. My preferred way would have been to restrict the from()-methods rather than saying that global types should not have any relation to human-time-formats like calendar-date or wall-time or UTC-timezone-offset.
c) 结论:我绝对与 JSR-310 团队分享了避免在本地类型和全局类型之间进行转换的好主意,例如Instant
不指定时区。我只是在 API 设计方面有所不同,以防止用户进行这种无时区转换。我的首选方法是限制 from() 方法,而不是说全局类型不应与人类时间格式(例如日历日期或挂墙时间或 UTC 时区偏移)有任何关系。
Anyway, this (inconsequent) design of separation between machine time and human time is now set in stone due to preserving backward compatibility, and everyone who wants to use the new java.time-API has to live with it.
无论如何,由于保持向后兼容性,这种(无关紧要的)分离机器时间和人类时间的设计现在已经一成不变,每个想要使用新 java.time-API 的人都必须接受它。
Sorry for a long answer, but it is pretty tough to explain the chosen design of JSR-310.
抱歉回答太长,但很难解释 JSR-310 的所选设计。
回答by rohtakdev
Just an example w.r.t conversions, i believe some folks will get below exception
只是一个例子wrt转换,我相信有些人会得到以下例外
(java.time.DateTimeException: Unable to obtain LocalDateTime from TemporalAccessor: 2014-10-24T18:22:09.800Z of type java.time.Instant)
(java.time.DateTimeException:无法从 TemporalAccessor 获取 LocalDateTime:2014-10-24T18:22:09.800Z 类型为 java.time.Instant)
if they try -
如果他们尝试——
LocalDateTime localDateTime = LocalDateTime.from(new Date().toInstant());
LocalDateTime localDateTime = LocalDateTime.from(new Date().toInstant());
to resolve the issue, please pass in zone -
要解决此问题,请传入 zone -
LocalDateTime localDateTime = LocalDateTime.from(new Date().toInstant().atZone(ZoneId.of("UTC")));
LocalDateTime localDateTime = LocalDateTime.from(new Date().toInstant().atZone(ZoneId.of("UTC")));