简短的回答:
JSR-310 设计者不希望人们通过静态 from() 方法在机器时间和人类时间之间进行转换,例如ZoneId
、ZoneOffset
、等类型。如果您仔细研究 javadoc,则会明确指定这一点OffsetDateTime
。ZonedDateTime
而是使用:
OffsetDateTime#toInstant():Instant
ZonedDateTime#toInstant():Instant
Instant#atOffset(ZoneOffset):OffsetDateTime
Instant#atZone(ZoneId):ZonedDateTime
静态 from() 方法的问题在于,否则人们可以在不考虑时区的情况下在 aInstant
和例如 a之间进行转换。LocalDateTime
长答案:
无论是考虑Instant
作为计数器还是作为字段元组,JSR-310-team 给出的答案是严格区分所谓的机器时间和人工时间。事实上,他们打算进行严格的分离——参见他们的指导方针。所以最后他们只想Instant
被解释为机器时间计数器。所以他们故意设计了一个你不能询问Instant
年份、小时等字段的设计。
但事实上,JSR-310 团队并不一致。他们已经将该方法实现Instant.toString()
为字段元组视图,包括年、...、小时、...和偏移符号 Z(用于 UTC 时区)(脚注:在 JSR-310 之外,这很常见,有一个字段-基于此类机器时间的外观 - 例如参见 Wikipedia 或其他有关TAI 和 UTC的网站)。一旦规范负责人 S. Colebourne 在关于threeten-github-issue的评论中说:
“如果我们真的是强硬路线,Instant 的 toString 将只是从 1970-01-01Z 开始的秒数。我们选择不这样做,并输出更友好的 toString 来帮助开发人员,但它不会改变Instant 只是几秒钟的基本事实,如果没有某种时区,就无法转换为年/月/日。”
人们可以喜欢或不喜欢这个设计决定(像我一样),但结果是你不能要求Instant
一年,...,小时,...和偏移量。另请参阅支持字段的文档:
NANO_OF_SECOND
MICRO_OF_SECOND
MILLI_OF_SECOND
INSTANT_SECONDS
有趣的是,这里缺少什么,首先是缺少与区域相关的字段。作为一个原因,我们经常听到对象喜欢Instant
或java.util.Date
没有时区的说法。在我看来,这是一种过于简单化的观点。虽然这些对象在内部确实没有时区状态(也不需要具有这样的内部值),但这些对象必须与 UTC 时区相关,因为这是每个时区偏移计算和转换为本地类型的基础. 所以正确的答案是:AnInstant
是一个机器计数器,计算自 UNIX 纪元以来在时区 UTC 中的秒数和纳秒数(根据规范)。最后一部分 - 与 UTC 区域的关系 - JSR-310-team 没有很好地指定,但他们不能否认。设计师希望取消时区方面,Instant
因为它看起来与人类时间相关。但是,他们不能完全取消它,因为这是任何内部偏移计算的基本部分。所以你的观察关于
“而且 anInstant.atZone(zone)
和 anInstant.atOffset(offset)
都产生一个与将 Instant 视为具有隐含 UTC 时区/'零'偏移量一致的值。”
是正确的。
虽然它可能非常直观,但它ZoneOffset.from(anInstant)
会ZoneOffset.UTC
引发异常,因为它的 from() 方法搜索不存在的OFFSET_SECONDS 字段。JSR-310 的设计者出于同样的原因决定在规范中这样做,即让人们认为 anInstant
与 UTC 时区正式无关,即“没有时区”(但在内部他们必须接受这个基本事实所有内部计算!)。
出于同样的原因,OffsetDateTime.from(anInstant)
也ZoneId.from(anInstant)
失败了。
关于ZonedDateTime.from(anInstant)
我们阅读:
“转换将首先从时间对象获取 ZoneId,必要时回退到 ZoneOffset。然后它将尝试获取 Instant,必要时回退到 LocalDateTime。结果将是 ZoneId 或 ZoneOffset 与即时或本地日期时间。”
因此,由于相同的原因,此转换将再次失败,因为既不能ZoneId
也不ZoneOffset
能从Instant
. 异常消息内容如下:
“无法从 TemporalAccessor 获取 ZoneId:1970-01-01T00:00:00Z 类型为 java.time.Instant”
最后,我们看到所有静态 from() 方法都无法在人类时间和机器时间之间进行转换,即使这看起来很直观。在某些情况下,假设LocalDate
和之间的转换Instant
是有问题的。此行为已指定,但我预测您的问题不是此类问题的最后一个问题,许多用户将继续感到困惑。
在我看来,真正的设计问题是:
a) 人力时间和机器时间之间不应有明显的区别。像这样的时间对象Instant
应该更好地表现得像两者一样。量子力学中的一个类比:您可以将电子视为粒子和波。
b) 所有静态 from() 方法都太公开了。在我看来,Ihat 太容易访问了,最好从公共 API 中删除或使用比TemporalAccessor
. 这些方法的缺点是人们可能会忘记在此类转换中考虑相关的时区,因为他们以本地类型开始查询。例如考虑:(LocalDate.from(anInstant)
在哪个时区???)。但是,如果您直接询问Instant
它的日期,例如instant.getDate()
,我个人会认为 UTC-timezone 中的日期是有效答案,因为这里查询是从 UTC 角度开始的。
c) 结论:我绝对与 JSR-310 团队分享避免在Instant
不指定时区的情况下在本地类型和全局类型之间进行转换的好主意。我只是在 API 设计方面有所不同,以防止用户进行这种无时区转换。我首选的方法是限制 from() 方法,而不是说全局类型不应该与日历日期或墙壁时间或 UTC 时区偏移等人类时间格式有任何关系。
无论如何,由于保留了向后兼容性,这种(无关紧要的)机器时间和人类时间分离的设计现在已经确定下来,每个想要使用新的 java.time-API 的人都必须接受它。
抱歉,回答很长,但是很难解释 JSR-310 的选择设计。