【问题标题】:java 1.6 daylight savingjava 1.6 夏令时
【发布时间】:2015-05-25 16:00:23
【问题描述】:

我正在开发一个关注夏令时更改的关键应用程序。
我试图通过比较跨越夏令时变化的日期来手动模拟运行时可能发生的事情,所以我进行了以下测试。 我目前的位置是意大利,所以今年从 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 CET 之后,但正好相反。

我不知道这是否是一个真正的测试。
有没有办法解决这个问题?
我也尝试了 joda time,但结果是一样的。
提前致谢。

【问题讨论】:

  • 查看this question. 的答案。根据位置而不是缩写来获取您的时区可能更合适。
  • @augray,我也尝试使用 tz 数据库时区 ID,但结果是一样的。
  • 仅供参考,您的更新在此行有错字before.setTimeZone(TimeZone.getTimeZone(""Europe/Rome DST")"));
  • @augray thx,我纠正了错字

标签: java date timezone dst


【解决方案1】:

您的问题是"Europe/Rome DST" 无法被getTimeZone(timeZoneId) 识别。
当它不理解您的输入时,it returns the GMT timezone by default。你可以看到getAvailableIDs的可用TimeZone id列表(上面链接getTimeZone下面的方法)。

需要注意的是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

【讨论】:

    【解决方案2】:

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

    使用好的日期时间库

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

    这些类已被 Java 8 和更高版本 (Tutorial) 中的新 java.time package 取代。该软件包的灵感来自 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 何时生效。

    夏令时

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

    歧义

    请注意,“罗马凌晨 2:30”的概念在 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]
    

    【讨论】:

      【解决方案3】:

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

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

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

      不要添加DST

      【讨论】:

        【解决方案4】:

        “Europe/Rome”不是 CET,“Europe/Rome DST”不是 CEST。

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2017-11-10
          • 2015-08-15
          • 1970-01-01
          • 2011-08-01
          • 2012-12-21
          • 2012-11-16
          • 2018-08-07
          相关资源
          最近更新 更多