Java时区数据库和IANA数据的区别

4
在 IANA 数据库和 Java tzdb.dat 2019c 数据库的 Africa/Casablanca 时区之间存在夏令时差异,这让我感到非常困惑。也许还有其他情况,但我只发现了这一个。从我所知道的情况来看,IANA 时区数据库清楚地显示摩洛哥 (Africa/Casablanca) 支持夏令时,不幸的是,Java 时区数据库 tzdb.dat 在 2019c 版本中并不同意。这已经并将会给我带来无限的痛苦。我错过了什么,或者其他人是否遇到过类似的情况。
IANA 表格 2019c 的 Africa/Casablanca 区域
注意:下面表格的部分显示正常时间为 UTC+1,夏令时时间为UTC,斜杠(/)分隔标准和夏令时的缩写

区域 名称 标准偏移 规则 格式 [直至]

区域 Africa/Casablanca -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

自2018年10月28日至今,标准时间偏移量为+1,夏令时偏移量为+0(斜杠/分隔标准和夏令时缩写)。 STDOFF 1:00指将其添加到UT以获得标准时间的时间量,没有任何关于夏令时的调整,这对应于UTC +1,即当前的摩洛哥时间。 因此,我们采用Zone Africa / Casablanca,它是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/Casablanca 支持夏令时=false 日期Mon May 20 00:00:00 WET 2019目前不在DST中 时区名西欧时间 时区ID Africa/Casablanca
还运行了基准测试以显示代码是否支持夏令时。
时区=Europe/Rome 支持夏令时=true 日期Mon May 20 00:00:00 CEST 2019目前在DST中 时区名中欧时间 时区ID Europe/Rome 代码供参考。
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());

    } 
} 

不回答你的问题,我建议你不要使用 TimeZoneSimpleDateFormatDate。这些类设计得很糟糕并且非常过时,尤其是 SimpleDateFormat 经常出问题。相反,使用 java.time,现代 Java 日期和时间 API 中的 ZoneIdZonedDateTimeDateTimeFormatter - Ole V.V.
1个回答

11

摩洛哥现在永久进入夏令时

自2018年10月26日起,摩洛哥永久进入DST

摩洛哥政府只提前通知了2天(!),发布了法令2.18.855。 显然政府打算停止所有的时钟转换,不再改变DSTRamadan。这项法令意味着全年偏移+01:00

这项法令也意味着摩洛哥不再处于“夏令时”状态。新常态是比协调世界时提前一小时,而在过去,偏移通常是在协调世界时(零小时分钟秒的偏移)。因此请注意下面的代码,在现在(2020年初)调用ZoneRules::isDaylightSavings返回false

详见维基百科: 摩洛哥的夏令时

当我说“永久性”时,请带着一点怀疑。全球各地的政治家们经常改变其所在时区的偏移量,这种“永久性夏令时”只是最近引起政治家们兴趣的最新潮流。请始终预期进一步的变化。

避免使用旧版日期时间类

您正在使用可怕的日期时间类,这些类已经被多年前在JSR 310中定义的现代java.time类取代。不再有任何理由使用那些可怕的旧版类。

Table of all date-time types in Java, both modern and legacy

java.time

请指定您所在的时区。

ZoneId zCasablanca = ZoneId.of( "Africa/Casablanca" ) ;

获取区域规则。

ZoneRules rulesCasablanca = zCasablanca.getRules() ;

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

ZoneOffset offset = rulesCasablanca.getOffset( Instant.now() ) ;
boolean isDst = rules.isDaylightSavings( instant ) ;

或将其折叠为一行。

ZoneOffset offset = ZoneId.of( "Africa/Casablanca" ).getRules().getOffset( Instant.now() ) ;

验证您的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( "" ) ;

请查看此代码在IdeOne.com上的实时运行run live at IdeOne.com

Java供应商和版本:

Oracle Corporation

12.0.1+12

tzdata: 2018g

offset.toString(): +01:00

isDst: false

特定日期

让我们尝试您指定的日期。

    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() ) 
    );

zdt.toString(): 2019-05-20T00:00+01:00[Africa/Casablanca] 偏移量: +01:00 | 是否在 DST 中: false

tzdata

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

该列表显示摩洛哥永久切换到夏令时已在 tzdata2018g 中考虑。该 tzdata 文件与 Java 版本 11.0.2、8u201 和 7u211 捆绑在一起。至少 Oracle 进行了捆绑,而我认为 OpenJDK 项目也这样做了(我没有验证)。

摩洛哥在 2018-10-27 切换到永久 +01。

摩洛哥从 +00/+01 切换到永久 +01,有效日期为 2018-10-27,因此其时钟不会像之前计划的那样在 2018-10-28 回退。

要获取JVM正在使用的tzdata数据文件版本,请参见此答案中的问题,Java - find tzdata version in use regardless of JRE version

关于 java.time

java.time 框架是内置于 Java 8 及更高版本中的。这些类替代了麻烦的旧日期时间类,如 legacy java.util.DateCalendarSimpleDateFormat

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

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

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

如何获取java.time类?

ThreeTen-Extra项目通过添加额外的类来扩展java.time。该项目是java.time可能未来增加内容的试验场。您可能会在这里找到一些有用的类,例如IntervalYearWeekYearQuartermore


谢谢您提供的新课程,我已经尝试了,但不幸的是它们与旧课程一致,混淆仍然存在。我的困惑来自于2019c版IANA表格与上面的评论不一致。是否有可能忽略了Ramadon日期和相关的回退计算(请查看IANA数据中的非洲地区),这些数据在这里无法适用。 请参阅以下评论: Naoufal Semlali(2019-04-16)和Paul Eggert(2019-05-20)。 - MRED
运行更多的测试显示新的调用在拉姆丹期间不会显示夏令时,而正常情况下会显示夏令时。 - MRED

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接