日期时间字符串中的 'z' 在不同的语言环境中是否有不同的输出?

Does a 'z' in a datetime String have different outputs in different locales?

不久前,我回答了一个关于如何从 ZonedDateTime 中提取时区的问题,该 ZonedDateTime 是从 String 解析的。
它总体上有效,但在 OP 的系统和我的系统上,相同代码的输出不同,不知何故不再让我离开。

询问如何从 ZonedDateTime 获取 ZoneId,我提供了一种方法。诚然,这不是公认的答案,但似乎仍然值得某人点赞。

我的回答的特别之处在于用于解析时间 String 的模式中的 'z'String 中有一个时区名称,代表时区 "Australia/Adelaide" "... ACST"(澳大利亚中部标准时间)。

当我在我的系统上解析它并使用 DateTimeFormatter.ISO_ZONED_DATE_TIME print/format ZonedDateTime 时,它会在 "America/Manaus" 中打印时间并提取 ZoneId,还是那个来自南美的。 OP 在我的回答下方的评论中指出,在他的系统上,至少有一个输出行显示 desired/correct ZoneId.

这怎么可能?系统默认语言环境对解析 'z' in datetime Strings 有影响吗?

这是我在问题中的回答的代码加上我的 ZoneId.systemDefault():

的输出
public static void main(String args[]) throws Exception {
    String time = "2 Jun 2019 03:51:17 PM ACST";
    String pattern = "d MMM yyyy hh:mm:ss a z"; // z detects the time zone (ACST here)
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);

    // parse a time object using the formatter and the time String
    ZonedDateTime zdt = ZonedDateTime.parse(time, formatter);
    // print it using a standard formatter
    System.out.println(zdt.format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
    // extract the zone id
    ZoneId zoneId = zdt.getZone();
    ZoneId sysDefault = ZoneId.systemDefault();
    // print the zone id
    System.out.println("Time zone of parsed String is " + zoneId);
    System.out.println("System default time zone is " + sysDefault);

    // retrieve the instant seconds
    Instant instant = zdt.toInstant();
    // print the epoch seconds of another time zone
    System.out.println("Epoch seconds in Australia/Adelaide are " 
            + instant.atZone(ZoneId.of("Australia/Adelaide")).toEpochSecond());
}

它的输出是这样的:

2019-06-02T15:51:17-04:00[America/Manaus]
Time zone of parsed String is America/Manaus
System default time zone is Europe/Berlin
Epoch seconds in Australia/Adelaide are 1559505077

任何人都可以指出我所犯的错误或确认并解释不同系统默认值 ZoneId 对将 String 解析为 ZonedDateTime 的影响吗?

我也遇到过在不同的 JVM 上解析不明确的时区缩写(在提供语言环境时也是如此,所以这不是唯一的问题)。我认为没有记录确切的行为。在某些情况下,选择了 JVM 的默认时区,但我不知道它是否总是匹配。

您可以通过重载DateTimeFormatterBuilder.appendZoneText(TextStyle, Set<ZoneId>)方法来控制不明确情况下的时区选择。

区域设置不同的示例:Europe/Berlin 和许多其他欧洲时区在许多区域设置中将被格式化为 Central European TimeCET。在德语语言环境中,它们变成 Mitteleuropäische ZeitMET.

是正确的。

ISO 8601

此外,"2 Jun 2019 03:51:17 PM ACST" 等字符串不应被解析。这种格式不应该用于交换日期时间值。这样的字符串应该只用于向人类用户展示,而不是用于数据交换。

你会尝试将 USD 23.67 的金额兑换成 twenty three dollars and sixty-seven cents in United States dollars 还是 vingt-trois dollars et soixante-sept cents en dollars des États-Unis

要通过文本交换日期时间值,请始终使用标准 ISO 8601 格式。合理的格式被设计为明确的,易于机器解析,并且易于跨文化的人类阅读。

如果使用时区,请使用ZonedDateTime::toString 方法生成文本。此方法明智地扩展了 ISO 8601 格式,将时区名称附加在方括号中。

ZoneId z = ZoneId.of( "Australia/Adelaide" ) ;
String output = ZonedDateTime.now( z ).toString() ;

2020-02-10T06:30:57.756491+10:30[Australia/Adelaide]

运行 即 code live at IdeOne.com.

正在解析。

ZonedDateTime zdt = ZonedDateTime.parse( output ) ;

更好的是,当时区不直接相关时使用 UTC 值。要以 UTC 格式捕获和传达当前时刻,请使用 Instant

再次调用 toString & parse 来生成和解析标准 ISO 8601 格式的文本。末尾的 Z 表示 UTC,零小时-分钟-秒的偏移量,发音为“Zulu”。

String output = Instant.now().toString() ;

2020-02-09T20:07:42.718473Z