SimpleDateFormat - 时区不正确

SimpleDateFormat - Timezone not correct

我在解析 Java 中的时间戳时遇到问题。

我希望两个时间戳都在同一时区 (CET)。

    SimpleDateFormat sdaf = new SimpleDateFormat ("dd.MM.yyyy HH:mm:ss");

    String str = "30.03.2013 06:00:00";
    sdaf.setTimeZone (TimeZone.getTimeZone ("CET"));
    java.util.Date dat = sdaf.parse (str);
    System.out.println (str + " = " + dat);

    str = "31.03.2013 05:00:00";
    sdaf.setTimeZone (TimeZone.getTimeZone ("CET"));
    dat = sdaf.parse (str);
    System.out.println (str + " = " + dat);

但事实并非如此 - 查看输出。

30.03.2013 06:00:00 = Sat Mar 30 06:00:00 CET 2013
31.03.2013 05:00:00 = Sun Mar 31 05:00:00 CEST 2013

编辑:

如果我将 CET 更改为 GMT+1,我会得到这个。

 03.2013 06:00:00 = Sat Mar 30 06:00:00 CET 2013
 31.03.2013 05:00:00 = Sun Mar 31 06:00:00 CEST 2013

似乎是正确的。但为什么 CET 不起作用?

UTC+1 产生

30.03.2013 06:00:00 = Sat Mar 30 07:00:00 CET 2013
31.03.2013 05:00:00 = Sun Mar 31 07:00:00 CEST 2013

与 GMT+1 有何不同 - ???

您无法获得 31.03.2013 05:00:00CET 时区,因为它已不在该时区。要理解,只需检查两个时区的名称

  • CET:欧洲中部时间UTC+1GMT+1
  • CEST:欧洲中部夏令时UTC+2GMT+2

那是 2013 年 3 月 31 日 02:00:00 发生的夏令时。所以你无法获得第二个日期的 CET 时区,因为它是在夏季 "time zone"。

如果你解析 31.03.2013 02:00:00 你会得到

31.03.2013 02:00:00 = Sun Mar 31 03:00:00 CEST 2013

因为那天02:00:00,正值夏令时,变成了03:00:00。

您可以使用 TimeZone.inDaylightTime(Date)

检查
String str = "30.03.2013 06:00:00";
java.util.Date dat = sdaf.parse (str);
System.out.println (str + " = " + dat);
System.out.println("SDT : " + TimeZone.getTimeZone("CET").inDaylightTime(dat));

30.03.2013 06:00:00 = Sat Mar 30 06:00:00 CET 2013
SDT : false

str = "31.03.2013 02:00:00";
dat = sdaf.parse (str);
System.out.println (str + " = " + dat);
System.out.println("SDT : " + TimeZone.getTimeZone("CET").inDaylightTime(dat));

31.03.2013 02:00:00 = Sun Mar 31 03:00:00 CEST 2013
SDT : true

由于 CETUTC+1GMT+1 相同并且 CEST 变为 UTC+2GMT+2,当您强制日期在 GMT+1 上,您得到 CET 的等价物,但不考虑 SDT 参数。

注意:这是 LocalDateTime 不用 TimeZone 进行大部分处理的原因之一。

可以用Java8,比这个更清晰简单。

DateTimeFormatter parse = DateTimeFormatter.ofPattern("dd.MM.yyyy hh:mm:ss.XXX");
LocalDate localDate = LocalDate.of(2013,3,30);
LocalTime localTime = LocalTime.of(6,0);

LocalDateTime dateTime = LocalDateTime.of(localDate, localTime);
ZonedDateTime zonedDateTime = dateTime.atZone(ZoneId.of("CET"));

System.out.println(zonedDateTime.format(parse));

输出:30.03.2013 06:00:00.+01:00

是正确的。因此,我认为您真的不需要中欧(标准)时间作为 3 月 31 日的约会时间。

我想再说三点:

  • 切勿依赖任何三字母或四字母的时区缩写,例如 CET。
  • 如果您真的想要中欧时间,可能的,因为阿尔及利亚和突尼西亚这两个非洲国家全年都使用 CET。
  • 避免使用过时的 SimpleDateFormatTimeZoneDate,而是使用 java.time、现代的 Java 日期和时间 API .

所以我的代码建议是:

    ZoneId cetAllYear = ZoneId.of("Africa/Algiers");
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd.MM.uuuu HH:mm:ss");
    DateTimeFormatter demoFormatter 
            = DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss z yyyy", Locale.ROOT);

    String str = "31.03.2013 05:00:00";
    ZonedDateTime dateTime = LocalDateTime.parse(str, formatter).atZone(cetAllYear);
    System.out.println (str + " = " + dateTime 
                            + " = " + dateTime.format(demoFormatter));

输出:

31.03.2013 05:00:00 = 2013-03-31T05:00+01:00[Africa/Algiers] = Sun Mar 31 05:00:00 CET 2013

Africa/Tunis 时区也是如此。

避免使用三个字母的时区缩写。中欧时间是标准时间的通用名称,偏移量为 +01:00,一年中有 5 个月用于大量时间欧洲时区。所以在某种程度上它只是半个时区,在另一种方式下它有很多,而且你不知道你得到的是哪个。当你给出一个属于夏令时(一年中的 DST 时间)的日期时间时更是如此。很多三字母和四字母的缩写是不明确的。而是给出时区,例如 Europe/Rome 或 Africa/Tunis,如 region/city.

避免使用 SimpleDateFormat 及其过时的朋友。 class 是出了名的麻烦。 java.time 更好用。

链接