为什么 UTC(不是时区)被视为 Java 中的时区(不仅如此)?

Why is UTC (which is not a time zone) considered a time zone in Java (and not only there)?

鉴于 UTC 不是时区,而是时间标准(如所述,例如 here),为什么在我的 Java 应用程序中我可以像使用 UTC 一样使用 UTC时区(请参阅下面的代码片段)?

SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
format.setTimeZone(TimeZone.getTimeZone("UTC"));

如果 UTC 不是时区,为什么 TimeZone.getTimeZone("UTC") 能够 return 时区对象?


顺便说一下,在我的 Windows 机器上,UTC 也在时区列表中(见屏幕截图)。

"UTC is not a time zone"这个说法在现实中是错误的吗?

因为将 UTC 视为时区比将其视为其他时区要简单得多,基本上。

这是 "Yeah, strictly speaking it's not" 场景之一。对于除 "Which region of the world is this observed?" 之外的所有内容,您都可以将 UTC 视为时区并且它工作正常。因此,将其稍微弯曲变形比拥有一个完整的单独概念更简单。

如果您将时区视为从 "instant in time" 到 "UTC offset" 的映射(或等价物,从 "instant in time" 到 "locally observed time"),那么可以考虑使用 UTC作为时区 - 这就是我们在软件中所做的大部分工作。

如果您将时区与该映射一起视为地理区域,那么不,它也不起作用 - 但这在软件中很少有用。 (你总是可以通过说它是一个空白区域来伪造它:)

Is the statement "UTC is not a time zone" in reality wrong?

从技术上和严格上来说,这个说法并没有错。 UTC is a standard, not a timezone(正如您已经链接的那样)。

一个时区对应于世界上的某个地区,并且有很多关于该地区的不同规则:

  • 夏令时和非夏令时的 UTC 偏移量(与 UTC 的差异)是多少
  • 夏令时开始和结束的时间
  • 该区域在其历史期间的偏移量和 DST 的所有变化

示例:在 1985 年,巴西的 Acre 州有标准偏移量 UTC-05:00(在 DST 期间为 UTC-04:00),然后在 1988 年它在 UTC-05:00 没有 DST,然后在2008 年标准更改为 UTC-04:00(没有 DST),自 2013 年起又回到 UTC-05:00 并且没有 DST。

虽然时区跟踪所有这些变化,但 UTC 没有这样的规则。您可以用许多不同的方式来思考 UTC:

  • a "base" date/time,从其他人相对的地方 - 这种与 UTC 的差异称为 "offset"。今天,圣保罗在 UTC-03:00(偏移 减 3 小时,或比 UTC 晚 3 小时),而东京在 UTC+09:00(偏移 加 9 小时,或 UTC 时间提前 9 小时)。
  • a "special" 永远不变的时区。它始终处于相同的偏移量(零),它永远不会改变,并且永远不会有 DST 偏移。

由于 "offset of UTC"(不确定该术语在技术上有多准确)始终为零,通常写为 UTC+00:00Z

UTC 和时区之间的另一个区别是时区由政府和法律定义并且可以更改 anytime/anywhere。上面描述的阿卡的所有变化都是由政治家定义的,无论他们当时认为什么原因。 (因此,即使一个地区今天在其时区中遵循 UTC,也不能保证它在未来会保持不变,这就是为什么即使这些地区有自己的时区,即使它们看起来多余)。

但是无论政治家们多少次改变他们的地区偏移量,他们必须相对于 UTC(当然 until a new standard comes up)。


现在,当您看到像 TimeZone.getTimeZone("UTC") 这样的实现时,您可以用两种不同的方式来思考它:

  • 一个设计缺陷,因为它混合了 2 个不同的概念并导致人们认为它们是同一件事
  • a shortcut/simplification/nice-trick/workaround,这使事情变得更容易(如 )。

对我来说,它是两者的结合 (fifty/fifty)。


但是,

new java.time API 将两个 class 中的概念分开:ZoneRegionZoneOffset(实际上两者都是 class 的子classes =22=],但 ZoneRegion 不是 public 所以实际上我们使用 ZoneIdZoneOffset):

  • 如果您将 ZoneIdIANA timezones names 一起使用(始终采用 Continent/City 格式,如 America/Sao_PauloEurope/Berlin),它将创建一个 ZoneRegion 对象 - "real" 具有所有 DST 规则和历史偏移量的时区。因此,根据您在此 ZoneId.
  • 中使用的日期,您可以有不同的偏移量
  • 如果您使用 ZoneOffset(与 ZUTC+03:00 等),它将 return 只是一个表示偏移量:与 UTC 的差异,但没有任何 DST 规则。无论您将此对象用于什么日期,它始终与 UTC 具有相同的差异。

因此,ZoneId(实际上是ZoneRegion)与某些区域(在某些时区)的偏移量随时间变化的想法是一致的(由于 DST 规则,政治家改变事情,因为等等)。而ZoneOffset代表了与UTC不同的想法,它没有DST规则并且永远不会改变。

还有一个特殊常量 ZoneOffset.UTC,它表示与 UTC(即 UTC 本身)的 差异。请注意,新的 API 采用了不同的方法:不是说一切都是时区,UTC 是特殊类型,而是说 UTC 是一个 ZoneOffset,其偏移值为零。

您仍然可以认为这是一个 "wrong" 设计决策或使事情变得更容易的简化(或两者兼而有之)。 IMO,与旧的 java.util.TimeZone 相比,这个决定是一个很大的改进,因为它清楚地表明 UTC 不是时区(从某种意义上说,它没有 DST 规则并且永远不会改变),它与UTC 标准(一种非常技术性的说法 "it's UTC")。

而且还把timezone和offset的概念分开了(这不是一回事,虽然很相关)。我看到它把 UTC 定义为一个特殊的偏移量 "implementation detail"。创建另一个 class 只是为了处理 UTC 将是多余和混乱的,将其保留为 ZoneOffset 是一个很好的决定,它简化了事情并且没有弄乱 API (对我来说,一个公平权衡)。

我相信许多其他系统决定采用类似的方法(这解释了为什么 Windows 在时区列表中有 UTC)。