ZonedDateTime 的 GMT 和 UTC 是否相同?

Is GMT and UTC same for ZonedDateTime?

我已经实现了一个 API 供国际使用,我正在尝试 return ISO 8601 格式的格林威治标准时间对象日期。现在我通过

ZonedDateTime.withZoneSameInstant(ZoneOffset.UTC)

但我知道 UTC 和 GMT 不是一回事。

稍后我将为每个用户分配他们的首选 Locale 并 return 分配给他们的 Locale ,但现在我想在 GMT 时执行此操作,但我不知道如何以正确的方式进行

TL;DR

ZonedDateTime UTC 和 GMT 的名称不同,但时区规则相同,因此 UTC 和 GMT 的时间始终相同。

理论

最初 GMT 和 UTC 的定义不同。 UTC 时间和 GMT 时间之间的时间从来不会超过一秒。由于 UTC 已成为无处不在的时间锚点,因此 GMT 有时被宽松地用来表示 UTC,因此区分更加模糊。

练习:ZonedDateTme

A ZonedDateTime 使用 ZoneId 作为其时区,因此您的问题等于询问 ZoneId GMT 和 UTC 是否相同。让我们先看看它们的外观:

    ZoneId utc = ZoneId.of("Etc/UTC");
    System.out.println("Etc/UTC:        " + utc);

    ZoneId gmt = ZoneId.of("Etc/GMT");
    System.out.println("Etc/GMT:        " + gmt);

    ZoneId utcNormalized = utc.normalized();
    System.out.println("UTC normalized: " + utcNormalized);

    ZoneOffset utcAsOffset = ZoneOffset.UTC;
    System.out.println("UTC as offset:  " + utcAsOffset);

    ZoneId gmtNormalized = gmt.normalized();
    System.out.println("GMT normalized: " + gmtNormalized);

输出为:

Etc/UTC:        Etc/UTC
Etc/GMT:        Etc/GMT
UTC normalized: Z
UTC as offset:  Z
GMT normalized: Z

前两行告诉我们ZoneId确实可以区分GMT和UTC。它们都是有效的时区,每个时区都有它们的时区 ID。

我在第 3 行和第 5 行使用的 normalized() 方法承诺将 ZoneId 转换为 ZoneOffset if 时间zone 使用常量偏移量。在这两种情况下,我们得到的偏移量都为零。所以这里ZoneId不区分

进一步核实此事:

    System.out.println("Are GMT and UTC the same? " + utc.equals(gmt));
    System.out.println("Are GMT and UTC the same? "
            + utcNormalized.equals(gmtNormalized));
    System.out.println("Are GMT and UTC the same? "
            + utc.getRules().equals(utcAsOffset.getRules()));
Are GMT and UTC the same? false
Are GMT and UTC the same? true
Are GMT and UTC the same? true

因此 ZoneId 对象不相等,使用这两者的 ZonedDateTime 对象即使具有相同的日期和时间也不会相等。但是我们从他们那里得到相同的偏移量并且他们有相同的区域规则,也就是说,他们总是有相同的时间。

Java 代码中的结果

这一行来自你的问题:

ZonedDateTime.withZoneSameInstant(ZoneOffset.UTC)

这是正确的,没有令人信服的理由说明您不应该这样做。如果您愿意,因为 Java 将 UTC 视为时区偏移量,您可以改为转换为 OffsetDateTime。我以某种方式认为它不如 ZonedDateTime.

重量级

如果您希望 ZonedDateTime 以 GMT 而不是 UTC 打印,请使用 ZoneId.of("Etc/GMT")。在这里,我使用的是保持该时区之前的变量:

    ZonedDateTime zdtGmt = ZonedDateTime.now(ZoneId.of("Asia/Istanbul"))
            .withZoneSameInstant(gmt);
    System.out.println("Time in GMT: " + zdtGmt);
    DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL)
            .withLocale(Locale.forLanguageTag("tr"));
    System.out.println("Time in GMT: " + zdtGmt.format(formatter));

刚才的输出:

Time in GMT: 2020-05-16T09:34:05.253696Z[Etc/GMT]
Time in GMT: 16 Mayıs 2020 Cumartesi 09:34:05 Greenwich Ortalama Saati

您还写道:

Later I will be assigning every user with their preferred Locale and return with their Locale …

Locale 与此处无关。语言环境和时区是正交的概念。例如,访问上海的土耳其人可能想要使用土耳其语言环境和上海时区。无法从语言环境推断出时区(至少没有可靠的方法)。您可能需要考虑为每个用户分配一个首选时区。