java 1.6夏令时

时间:2015-05-25 16:00:24

标签: java date timezone dst

我正致力于关注夏令时变化的关键应用程序 我试图通过比较跨越夏令时变化的thow日期来手动模拟运行时会发生什么,所以我进行了以下测试。 我目前的位置是意大利,所以今年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:30 CEST是在欧洲中部时间2:30之后,但恰恰相反。

我不知道这是不是真正的考验 有没有办法解决这个问题? 我也尝试过joda时间,但结果是一样的。
提前谢谢。

4 个答案:

答案 0 :(得分:3)

您的问题是"Europe/Rome DST"无法识别getTimeZone(timeZoneId) 如果它不理解您的输入,it returns the GMT timezone by default。您可以使用getAvailableIDs(上面链接中的getTimeZone下面的方法)查看可用的TimeZone ID列表。

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

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

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

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

答案 1 :(得分:2)

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

使用好的日期时间库

避免乱七八糟的java.util.Date/.Calendard类。众所周知,它们在设计和实施方面都很麻烦。

这些类已被java.time package及更晚(Java 8)中的新Tutorial取代。该软件包的灵感来自Joda-Time库。虽然类似,但java.time和Joda-Time并不完全相同。每个都有其他缺乏的功能。您可以使用其中之一或两者。

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

CET&等代码CEST既不是标准化也不是唯一的。避免他们。

使用full time zone names。其中大多数是“大陆/城市”或“大陆/地区”。

您似乎正在使用此代码来管理DST, Daylight Saving Time的问题。将这种繁重的工作留给日期时间库,例如java.time或Joda-Time。时区将UTC的偏移量与DST和其他异常的过去,现在和将来的规则组合在一起。因此,您指定时区名称,让库完成DST生效时的工作。

DST

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

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

示例代码

以下是Java 8的java.time中的一些示例代码,以了解如何处理DST。首先,我们采用一些“本地”日期时间,其中“本地”表示没有附加任何特定时区。然后我们分配罗马时区。之后我们采用分区值(罗马值)并加上或减去小时数。

歧义

注意10月25日“罗马凌晨2点30分”的概念是如何毫无意义的。您必须知道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]

答案 2 :(得分:0)

我的建议是使用IANA time zone database中的完整标识符。在您的情况下,您应该使用"Europe/Rome"

before.setTimeZone(TimeZone.getTimeZone("Europe/Rome"));

这将确保为该区域使用适当的时区信息,包括何时需要触发夏令时模式以及可能的区域异常。

不要添加DST

答案 3 :(得分:0)

“欧洲/罗马”不是CET,“欧洲/罗马DST”不是CEST。

“欧洲/罗马”是意大利的时区。它不仅包括夏令时的变化,还包括罗马/意大利的偏移的任何其他变化。因此,当罗马在CET时,“欧洲/罗马”是CET,当罗马参加CET时,它就是CEST。像“欧洲/罗马”这样的时区的全部意义在于你不必关心夏令时,时区会为你处理它。