java 1.6 夏令时

java 1.6 daylight saving

我正在开发一个关心夏令时变化的关键应用程序。
我试图通过比较跨越夏令时变化的日期来手动模拟运行时可能发生的情况,因此我进行了以下测试。 我当前所在的位置是意大利,因此今年从 CEST(中欧夏令时)更改为 CET(中欧时间)发生在 25/10。
我使用 full time zone names,我的时区是 Europe/Rome

这是我做的测试:

Calendar before = Calendar.getInstance(TimeZone.getTimeZone("Europe/Rome DST")); //CEST
before.set(Calendar.DAY_OF_MONTH, 25);
before.set(Calendar.MONTH, Calendar.OCTOBER);
before.set(Calendar.HOUR_OF_DAY, 2);
before.set(Calendar.MINUTE, 30);
before.set(Calendar.SECOND, 0);
before.set(Calendar.MILLISECOND, 0);

System.out.println(before.getTime());

Calendar after = Calendar.getInstance(TimeZone.getTimeZone("Europe/Rome")); //CET
after.set(Calendar.DAY_OF_MONTH, 25);
after.set(Calendar.MONTH, Calendar.OCTOBER);
after.set(Calendar.HOUR_OF_DAY, 2);
after.set(Calendar.MINUTE, 30);
after.set(Calendar.SECOND, 0);
after.set(Calendar.MILLISECOND, 0);

System.out.println(after.getTime());

System.out.println(before.compareTo(after));

输出为:

BEFORE DST CHANGE: Sun Oct 25 03:30:00 CET 2015
AFTER DST CHANGE: Sun Oct 25 02:30:00 CET 2015
before.compareTo(after): 1

比较结果错误,即2:30CEST在2:30CET之后,但恰恰相反

不知道是不是真考
有什么办法可以解决这个问题吗?
我也尝试了 joda 时间,但结果是一样的。
提前致谢。

您的问题是 "Europe/Rome DST" 无法被 getTimeZone(timeZoneId) 识别。
当它不理解您的输入时,it returns the GMT timezone by default。您可以使用 getAvailableIDs 查看可用的时区 ID 列表(getTimeZone 下方的方法在上方 link)。

需要注意的是,CEST也不在列表中。要模拟 CEST 时区,您可以选择以下解决方案之一:

  • 我建议使用 TimeZone.setRawOffset(int offsetInMs) 自己设置 CET 和 CEST 的偏移量。
  • 使用相对于 GMT 定义的时区之一(例如,id "Etc/GMT+1")。这将确保您使用时区 api 能够理解的有效时区偏移量。
  • 在日历实例上设置 DST 偏移量 Calendar.DST_OFFSET

通过使用最后一个解决方案,正确的测试代码是:

Calendar before = Calendar.getInstance(TimeZone.getTimeZone("Europe/Rome"));
before.set(Calendar.DST_OFFSET, 3600000);       
before.set(Calendar.DAY_OF_MONTH, 25);
before.set(Calendar.MONTH, Calendar.OCTOBER);
before.set(Calendar.HOUR_OF_DAY, 2);
before.set(Calendar.MINUTE, 30);
before.set(Calendar.SECOND, 0);
before.set(Calendar.MILLISECOND, 0);

System.out.println("BEFORE DST CHANGE: " + before.getTime());       

Calendar after = Calendar.getInstance(TimeZone.getTimeZone("Europe/Rome"));
after.set(Calendar.DAY_OF_MONTH, 25);
after.set(Calendar.MONTH, Calendar.OCTOBER);
after.set(Calendar.HOUR_OF_DAY, 2);
after.set(Calendar.MINUTE, 30);
after.set(Calendar.SECOND, 0);
after.set(Calendar.MILLISECOND, 0);

System.out.println("AFTER DST CHANGE: " + after.getTime());

System.out.println("before.compareTo(after): " + before.compareTo(after));

并且输出:

BEFORE DST CHANGE: Sun Oct 25 02:30:00 CEST 2015
AFTER DST CHANGE: Sun Oct 25 02:30:00 CET 2015
before.compareTo(after): -1

我的建议是使用 IANA time zone database. In your case you should be using "Europe/Rome":

中的完整标识符
before.setTimeZone(TimeZone.getTimeZone("Europe/Rome"));

这将确保为该地区使用正确的时区信息,包括何时需要触发夏令时模式,以及可能的地区例外情况。

不要添加DST

by augray 是正确的,应该被接受(单击那个大的空白复选标记图标使其变为绿色)。我会添加一些想法和代码。

使用一个好的日期时间库

避免混乱 java.util.Date/.Calendard 类。它们出了名的麻烦,在设计和实现上都有缺陷。

这些 类 已被新的 java.time package in Java 8 and later (Tutorial). That package was inspired by the Joda-Time 库取代。虽然相似,但 java.time 和 Joda-Time 并不相同。每个都有对方所没有的特点。您可以使用一个或两个。

避免使用 3-4 个字母的时区代码

CETCEST 等代码既不标准化也不唯一。避开他们。

使用full time zone names。其中大部分是 "continent/city" 或 "continent/region"。

您似乎正在使用这些代码来解决 DST, Daylight Saving Time. Leave such heavy-lifting to the date-time library, such as java.time or Joda-Time 的问题。时区将 UTC 的偏移量与 DST 和其他异常的过去、现在和未来规则集相结合。因此,您指定时区名称并让库执行确定夏令时生效时间的工作。

夏令时

Daylight Saving Time (DST) for Rome 今年将于 2015 年 10 月 25 日凌晨 3 点结束。时钟回滚以重复凌晨 2 点。所以那天有两次2:30次。您可以在下面的示例代码中看到这两个 2:30 次。

  • 2015-10-25T02:30+02:00[Europe/Rome]
  • 2015-10-25T02:30+01:00[Europe/Rome]

示例代码

这里是 java.time of Java 8 中的一些示例代码,以了解 DST 是如何处理的。首先我们取一些 "local" 日期时间,其中 "local" 表示没有附加任何特定时区。然后我们分配罗马时区。之后,我们采用分区值(罗马值)并增加或减少小时数。

歧义

请注意“2:30 AM in Rome”的概念在 10 月 25 日毫无意义。您必须知道 01:0002:00 的偏移量才能正确解释。

ZoneId zone = ZoneId.of( "Europe/Rome" );

System.out.println( "-----|  Local  |-----------------------------------------\n" );

LocalDateTime local_0130 = LocalDateTime.of( 2015 , Month.OCTOBER , 25 , 1 , 30 );
ZonedDateTime zoned_0130 = ZonedDateTime.of( local_0130 , zone );

LocalDateTime local_0230 = LocalDateTime.of( 2015 , Month.OCTOBER , 25 , 2 , 30 );
ZonedDateTime zoned_0230 = ZonedDateTime.of( local_0230 , zone );

LocalDateTime local_0330 = LocalDateTime.of( 2015 , Month.OCTOBER , 25 , 3 , 30 );
ZonedDateTime zoned_0330 = ZonedDateTime.of( local_0330 , zone );

System.out.println( "local_0130: " + local_0130 + " in zone: " + zone + " is " + zoned_0130 );
System.out.println( "local_0230: " + local_0230 + " in zone: " + zone + " is " + zoned_0230 );
System.out.println( "local_0330: " + local_0330 + " in zone: " + zone + " is " + zoned_0330 + "\n" );

System.out.println( "-----|  Add Hours  |-----------------------------------------\n" );

ZonedDateTime zoned_0130_plus_1H = zoned_0130.plusHours( 1 );
ZonedDateTime zoned_0130_plus_2H = zoned_0130.plusHours( 2 );

System.out.println( "zoned_0130_plus_1H: " + zoned_0130_plus_1H );
System.out.println( "zoned_0130_plus_2H: " + zoned_0130_plus_2H + "\n" );

System.out.println( "-----|  Subtract Hours  |-----------------------------------------\n" );

ZonedDateTime zoned_0330_minus_1H = zoned_0330.minusHours( 1 );
ZonedDateTime zoned_0330_minus_2H = zoned_0330.minusHours( 2 );

System.out.println( "zoned_0330_minus_1H: " + zoned_0330_minus_1H );
System.out.println( "zoned_0330_minus_2H: " + zoned_0330_minus_2H + "\n" );

当运行.

-----|  Local  |-----------------------------------------

local_0130: 2015-10-25T01:30 in zone: Europe/Rome is 2015-10-25T01:30+02:00[Europe/Rome]
local_0230: 2015-10-25T02:30 in zone: Europe/Rome is 2015-10-25T02:30+02:00[Europe/Rome]
local_0330: 2015-10-25T03:30 in zone: Europe/Rome is 2015-10-25T03:30+01:00[Europe/Rome]

-----|  Add Hours  |-----------------------------------------

zoned_0130_plus_1H: 2015-10-25T02:30+02:00[Europe/Rome]
zoned_0130_plus_2H: 2015-10-25T02:30+01:00[Europe/Rome]

-----|  Subtract Hours  |-----------------------------------------

zoned_0330_minus_1H: 2015-10-25T02:30+01:00[Europe/Rome]
zoned_0330_minus_2H: 2015-10-25T02:30+02:00[Europe/Rome]

"Europe/Rome"不是CET,"Europe/Rome DST"不是CEST。

"Europe/Rome" 是意大利的时区。它不仅包括 DST 更改,还包括对 Rome/Italy 的偏移量的任何其他更改。所以 "Europe/Rome" 是 CET 当罗马在 CET 时,它是 CEST 当罗马在 CET 时。像 "Europe/Rome" 这样的时区的全部意义在于您不必关心夏令时,时区会为您处理。