我产生了以下好奇心:-
TimeZone.getTimeZone
认为PST
("太平洋标准时间")等同于America/Los_Angeles
,后者观测夏令时- 但它认为
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
Ideone演示
我们可以证明,使用美国四个主要时区的三个字母缩写获得的时区在夏令时的使用上是不一致的:
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());
}
输出:
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
Ideone演示
除了三个字母的缩写无论如何都最好避免(Javadoc说"然而,它们的使用是不推荐的……")之外,这似乎非常令人困惑,也是微妙错误的一个很好的来源,无论你是否期望在多个时区使用夏令时。
这种行为差异有什么原因吗?美国时区是否具有微妙的特性,这意味着这种行为是意料之中的事?
首先要说:我们必须了解时区标识符和时区名称之间的区别。通常"MST"或"PST"代表(缩写)区域名称,应解释为不处于夏令时状态(后缀ST=标准时间)。
然而,旧的Java版本也(错误地)使用了这些名称作为标识符,也就是说,允许使用这些字符串作为工厂方法(如TimeZone.getTimeZone(...)
)的参数。这就引出了一个问题:什么是有效的标识符
区域数据的基本来源(规则,而不是名称)来自TZDB,该数据库现在由IANA及其实际管理员Paul Eggert维护。它包含一个名为northamerica的文件。该文件中的以下条目告诉我们MST是一个标识符,但不是PST(而是"PST8PDT")。
Zone EST -5:00 - EST
Zone MST -7:00 - MST
Zone HST -10:00 - HST
Zone EST5EDT -5:00 US E%sT
Zone CST6CDT -6:00 US C%sT
Zone MST7MDT -7:00 US M%sT
Zone PST8PDT -8:00 US P%sT
然而,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文件sun.util.calendar.ZoneInfoFile
。因此,"太平洋标准时间"被映射到美国/洛杉矶。"PST8PDT"被TZDB超越并映射到固定偏移。"MST"呢?在这里你可以设置一个属性如何解释它,即布尔系统属性"sun.timezone.ids.oldmapping"。它的默认值是"false"。上述文件ZoneInfoFile
还包含以下开关,具体取决于系统属性的值:
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也写了一篇关于这个主题的文章。所有这些奇怪之处只是为了向后兼容性,用户应该避免。