Java 时区数据库与 IANA 数据

MRE*_*RED 4 java timezone dst

IANA 数据库和非洲/卡萨布兰卡时区的 Java tzdb.dat 2019c 数据库之间存在夏令时差异,这让我很困惑。可能还有其他的,但我找到了这个。据我所知,IANA 时区数据库清楚地表明摩洛哥(非洲/卡萨布兰卡)支持 DST 不幸的是,2019c 版本中的 Java 时区数据库 tzdb.dat 不同意。这已经并将给我带来无限的悲伤。我在这里错过了什么,或者其他人看到过这种东西

非洲/卡萨布兰卡的 IANA 表 2019c

注:下表部分显示正常时间为 UTC+1,夏令时为 UTC 斜线 (/) 分隔标准和夏令时缩写

区域名称 STDOFF 规则格式 [直到]

非洲区/卡萨布兰卡 -0:30:20 - LMT 1913 年 10 月 26 日

         0:00   Morocco +00/+01 1984 Mar 16
         1:00   -   +01 1986
         0:00   Morocco +00/+01 2018 Oct 28  3:00    
         1:00   Morocco +01/+00
Run Code Online (Sandbox Code Playgroud)

从 2018 年 10 月 28 日到现在,标准的偏移量是 +1,夏令时的偏移量是 +0(斜线 (/) 分隔标准和日光缩写。) STDOFF 1:00 所以添加到 UT 以获得标准时间的时间量,没有夏令时的任何调整都对应于 UTC +1,即当前的摩洛哥时间。因此,我们采用 UTC 区域非洲/卡萨布兰卡,并根据斋月添加偏移量 +01/+00。

考试

我编写了一个简单的 Java 类来检查 2019c TZDB。这个类(如下所示)表明最新的 Java 时区数据文件 tzdb.dat 文件有问题。此测试使用 IBM SR5FP40 和时区 2019c 数据文件运行。我使用 2019c 数据文件在 OpenJDK 上得到了相同的结果。

tzdb.dat 的第一行显示 2019c TZDB 2019cX Africa/Abidjan Africa/Accra Africa/Addis_Ababa Africa/Algiers

测试显示问题

时区 = Africa/Cas​​ablanca 支持夏令时 = false Date Mon May 20 00:00:00 WET 2019 当前处于 DST false 时区名称 西欧时区 ID Africa/Cas​​ablanca

还运行了基线测试以显示是否支持 DST,代码将显示它。

时区 = 欧洲/罗马 支持夏令时 = 真 日期 Mon May 20 00:00:00 CEST 2019 当前处于 DST 真时区名称 中欧时区 ID 欧洲/罗马

代码在这里供参考

import java.util.*; 
import java.text.SimpleDateFormat;
import java.text.ParseException;

public class checkdaylight{ 

    public static void main(String[] args) 
    { 

        // Create TimeZone object
        //Europe/Rome
        //Pacific/Pago_Pago
        //Africa/Casablanca
        String TimezoneToTest = "Africa/Casablanca";
        System.out.println("Time Zone = " + TimezoneToTest);
        TimeZone obj = TimeZone.getTimeZone(TimezoneToTest); 
        TimeZone.setDefault(TimeZone.getTimeZone(TimezoneToTest)); //to avoid confusion

        // Checking day light time 
        // and displaying the result 
        System.out.println("Supports Day light Savings time  = "
                        + obj.useDaylightTime()); 
        String pattern = "yyyy-MM-dd";
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern);
        try{
            simpleDateFormat.setTimeZone(TimeZone.getTimeZone(TimezoneToTest));
            Date checkdate = simpleDateFormat.parse("2019-05-20");
            System.out.println("Date " + checkdate.toString()+ " is currently in DST "+ obj.inDaylightTime(checkdate));
         } catch (ParseException e) {
            e.printStackTrace();
        }

        System.out.println("Time zone name " +obj.getDisplayName());
        System.out.println("Time zone ID " + obj.getID());

    } 
} 
Run Code Online (Sandbox Code Playgroud)

Bas*_*que 11

摩洛哥现在永久采用夏令时

自 2018 年 10 月 26 日起,摩洛哥永久改用夏令时

摩洛哥政府给了只有2天通知(!),在法令2.18.855。显然,政府打算停止所有时钟转换,不再更改夏令时斋月。该法令意味着全年抵消+01:00

该法令还意味着摩洛哥不再“处于夏令时”。新常态是 UTC 前一小时的偏移量,而在过去,偏移量通常是 UTC(零时分秒的偏移量)。所以请注意,在下面的代码中,现在(2020 年初)调用ZoneRules::isDaylightSavings返回false

有关详细信息,请参阅维基百科:摩洛哥的夏令时

当我说“永久”时,请持保留态度。世界各地的政治家都表现出频繁更改各自时区偏移量的倾向。这种“永久采用夏令时”只是最近引起政界人士青睐的时尚。总是期待进一步的变化。

避免遗留的日期时间类

您正在使用多年前被JSR 310 中定义的现代java.time类取代的糟糕的日期时间类。不再有任何理由使用那些糟糕的遗留类。

Java 中所有日期时间类型的表,包括现代的和传统的

时间

指定您想要的时区。

ZoneId zCasablanca = ZoneId.of( "Africa/Casablanca" ) ;
Run Code Online (Sandbox Code Playgroud)

获取区域规则。

ZoneRules rulesCasablanca = zCasablanca.getRules() ;
Run Code Online (Sandbox Code Playgroud)

询问适用于该区域特定时刻的规则。

ZoneOffset offset = rulesCasablanca.getOffset( Instant.now() ) ;
boolean isDst = rules.isDaylightSavings( instant ) ;
Run Code Online (Sandbox Code Playgroud)

或者将其折叠为一行。

ZoneOffset offset = ZoneId.of( "Africa/Casablanca" ).getRules().getOffset( Instant.now() ) ;
Run Code Online (Sandbox Code Playgroud)

验证您的 Java 版本。

System.out.println( "Java vendor and version:" ) ;
System.out.println( "    " + System.getProperty("java.vendor") ) ;
System.out.println( "    " + Runtime.version() ) ;

String tzdataVersion = 
    java.time.zone.ZoneRulesProvider
              .getVersions("UTC")
              .lastEntry()
              .getKey() 
;
System.out.println( "tzdata: " + tzdataVersion ) ;
System.out.println( "" ) ;
Run Code Online (Sandbox Code Playgroud)

查看此代码在 IdeOne.com 上实时运行

Java 供应商和版本:

甲骨文公司

12.0.1+12

tzdata:2018g

offset.toString(): +01:00

isDst: 假

具体日期

让我们试试你的具体日期。

    LocalDate localDate = LocalDate.parse( "2019-05-20" ) ;
    ZonedDateTime zdt = localDate.atStartOfDay( z ) ;
    System.out.println( "zdt.toString(): " + zdt ) ;
    System.out.println(
        "offset: " + rules.getOffset( zdt.toInstant() ) + 
        "  |  is in DST: " + rules.isDaylightSavings( zdt.toInstant() ) 
    );
Run Code Online (Sandbox Code Playgroud)

zdt.toString(): 2019-05-20T00:00+01:00[非洲/卡萨布兰卡]

偏移量:+01:00 | 在夏令时:假

数据

Oracle列出了Java 运行时中内置的tzdata文件。

该列表显示摩洛哥切换到永久 DST 已在tzdata2018g. 该 tzdata 文件与 Java 版本 11.0.2、8u201 和 7u211 捆绑在一起。至少 Oracle 捆绑了它,而我认为OpenJDK项目也是如此(我没有验证)。

摩洛哥于 2018 年 10 月 27 日切换为永久 +01。

摩洛哥从 2018 年 10 月 27 日起从 +00/+01 切换为永久 +01,因此其时钟不会按原计划回退到 2018 年 10 月 28 日。

要获取 JVM 正在使用的 tzdata 数据文件的版本,请参阅关于问题Java - find tzdata version in use 而不是JRE version 的这个答案


关于java.time

java.time框架是建立在Java 8和更高版本。这些类取代了麻烦的旧的遗留日期时间类,例如java.util.Date, Calendar, & SimpleDateFormat

要了解更多信息,请参阅Oracle 教程。并在 Stack Overflow 上搜索许多示例和解释。规范是JSR 310

现在处于维护模式Joda-Time项目建议迁移到java.time类。

您可以直接与您的数据库交换java.time对象。使用符合JDBC 4.2或更高版本的JDBC 驱动程序。不需要字符串,不需要类。java.sql.*

从哪里获得 java.time 类?

ThreeTen-额外项目与其他类扩展java.time。该项目是未来可能添加到 java.time 的试验场。你可能在这里找到一些有用的类,比如IntervalYearWeekYearQuarter,和更多