Java 为什么美国时区的三个字母缩写与夏令时不一致?

Java 为什么美国时区的三个字母缩写与夏令时不一致?,java,timezone,Java,Timezone,我产生了以下好奇心:- 时区。getTimeZone认为PST()等同于遵守夏令时制度的美国/洛杉矶 但它认为MST()等同于不遵守夏令时的UTC-7 这可以通过以下代码片段进行演示: System.out.println( TimeZone.getTimeZone("MST").hasSameRules(TimeZone.getTimeZone("GMT-7"))); System.out.println( TimeZone.getTimeZone("PST").hasSa

我产生了以下好奇心:-

  • 时区。getTimeZone
    认为
    PST
    ()等同于遵守夏令时制度的
    美国/洛杉矶
  • 但它认为
    MST
    ()等同于不遵守夏令时的
    UTC-7
这可以通过以下代码片段进行演示:

System.out.println(
    TimeZone.getTimeZone("MST").hasSameRules(TimeZone.getTimeZone("GMT-7")));
System.out.println(
    TimeZone.getTimeZone("PST").hasSameRules(TimeZone.getTimeZone("America/Los_Angeles")));
输出:

true
true
EST is Eastern Standard Time; observes DST: false
CST is Central Standard Time; observes DST: true
MST is Mountain Standard Time; observes DST: false
PST is Pacific Standard Time; observes DST: true

我们可以证明,使用美国四个主要时区的三个字母缩写获得的时区在使用夏令时时不一致:

for (String id : Arrays.asList("EST", "CST", "MST", "PST")) {
  TimeZone tz = TimeZone.getTimeZone(id);
  System.out.printf("%s is %s; observes DST: %s%n",
      id, tz.getDisplayName(false, TimeZone.LONG), tz.useDaylightTime());
}
输出:

true
true
EST is Eastern Standard Time; observes DST: false
CST is Central Standard Time; observes DST: true
MST is Mountain Standard Time; observes DST: false
PST is Pacific Standard Time; observes DST: true

除了最好避免使用三个字母的缩写外(上面写着“但是,它们的用法不推荐…”),这似乎非常令人困惑,而且是一个很好的漏洞来源,无论您是否希望在多个时区使用夏令时


这种行为上的差异有什么原因吗?美国时区是否具有微妙的特性,这意味着这种行为是可以预期的?

首先,我们必须理解时区标识符和时区名称之间的差异。通常,“MST”或“PST”代表(缩写)区域名称,应解释为不处于夏令时状态(后缀ST=标准时间)

但是,旧的Java版本(错误地)将这些名称也用作标识符,也就是说,允许将这些字符串用作工厂方法的参数,如
TimeZone.getTimeZone(…)
。这就引出了一个问题:什么是有效标识符?

区域数据(规则,而不是名称)的基础源于TZDB,现在由IANA及其实际管理员Paul Eggert维护。它包含一个名为的文件。该文件中的以下条目告诉我们MST是标识符,而不是PST(而不是“PST8PDT”)

但是,Java仍然使用“PST”作为标识符——通过硬连线的旧映射:

private static String[][] oldMappings =
    {
        { "ACT", "Australia/Darwin" },
        { "AET", "Australia/Sydney" },
        { "AGT", "America/Argentina/Buenos_Aires" },
        { "ART", "Africa/Cairo" },
        { "AST", "America/Anchorage" },
        { "BET", "America/Sao_Paulo" },
        { "BST", "Asia/Dhaka" },
        { "CAT", "Africa/Harare" },
        { "CNT", "America/St_Johns" },
        { "CST", "America/Chicago" },
        { "CTT", "Asia/Shanghai" },
        { "EAT", "Africa/Addis_Ababa" },
        { "ECT", "Europe/Paris" },
        { "IET", "America/Indiana/Indianapolis" },
        { "IST", "Asia/Kolkata" },
        { "JST", "Asia/Tokyo" },
        { "MIT", "Pacific/Apia" },
        { "NET", "Asia/Yerevan" },
        { "NST", "Pacific/Auckland" },
        { "PLT", "Asia/Karachi" },
        { "PNT", "America/Phoenix" },
        { "PRT", "America/Puerto_Rico" },
        { "PST", "America/Los_Angeles" },
        { "SST", "Pacific/Guadalcanal" },
        { "VST", "Asia/Ho_Chi_Minh" }
    };
只需检查JDK文件。因此“PST”被映射到美国/洛杉矶。“PST8PDT”被TZDB取代并映射到一个固定偏移量。“MST”呢?在这里您可以设置一个属性如何解释它,即布尔系统属性“sun.timezone.ids.oldmapping”。其默认值为“false”“。同样提到的文件
ZoneInfo文件
也包含以下开关,具体取决于系统属性的值:

private static void addOldMapping() {
      for (String[] arrayOfString1 : oldMappings) {
        aliases.put(arrayOfString1[0], arrayOfString1[1]);
      }
      if (USE_OLDMAPPING) {
        aliases.put("EST", "America/New_York");
        aliases.put("MST", "America/Denver");
        aliases.put("HST", "Pacific/Honolulu");
      } else {
        zones.put("EST", new ZoneInfo("EST", -18000000));
        zones.put("MST", new ZoneInfo("MST", -25200000));
        zones.put("HST", new ZoneInfo("HST", -36000000));
      }
}

IBM也写过关于这个主题的文章。所有这些奇怪之处都是为了向后兼容,用户应该避免使用。

我想,这是一个关于的问题。谢谢@Meno,这是一个非常彻底的答案。