java.util.Calendar - 年份 1947

10

我在使用日历将字符串转换为日期时,在WSO2平台中发现了一个有趣的行为。 WSO2使用java.util.Calendar作为最终结果。 我发现,如果代码片段中使用了1947年1月1日至1947年2月23日之间的日期,则结果总是会提前一天。 我发现这是由于我们的时区导致的,因为它向前推进了一小时。这意味着日历会将结果减少一小时并且我们得到的将是11PM之前的前一天。

我们已经通过不将其转换为日期来解决问题。所以这不是问题所在。

问题是,为什么只有01.01.1947 - 23.02.1947这些日期会出现这种情况?

以下是可以模拟此问题的示例代码片段:

    int year = 1947;
    int month = 2;
    int day = 23;
    int timezoneOffset = TimeZone.getDefault().getRawOffset();
    Calendar calendar = Calendar.getInstance();
    calendar.clear();
    calendar.set(1, year);
    calendar.set(2, month - 1);
    calendar.set(5, day);
    calendar.set(15, timezoneOffset);

    System.out.println(calendar.getTime());

如果您想测试我的时区(捷克共和国),那么我的时区偏移量的结果是3600000。

需要注意的是,这是在WSO2中编写的代码,我无法更改它,如果有任何问题,请理解。

我使用的Java版本是:openjdk 11.0.8 2020-07-14

链接: WSO2平台


4
1947年2月23日,捷克斯洛伐克(当时为捷克斯洛伐克)从格林威治标准时间转换为中欧标准时间,时钟提前了1小时。相反的变化发生在1946年12月1日(中欧标准时间->格林威治标准时间,时差缩短1小时)。有关原因,请参见此处 - Federico klez Culloca
谢谢您的回复。这听起来很正规。 - Marcel Ouška
你能把它发布为答案,这样我就可以标记它为正确的吗? - Marcel Ouška
1
虽然这可能不在您的控制范围内,但我建议您不要使用TimeZoneCalendar。这些类设计得很差且过时了。相反,请使用ZonedDateTimeZoneId,或者对于日期,请使用LocalDate。所有这些都来自于Java时间和日期API(java.time) - Ole V.V.
2
代码存在很多问题。不仅如我所说,Calendar类设计得很差且过时,而且代码本身也有缺陷。将区域偏移量(字段15)设置为原始偏移量是错误的,可能是你所询问问题的根源。很可能最初的程序员无法正确使用Calendar,正是因为它的设计如此混乱和繁琐。 - Ole V.V.
显示剩余4条评论
2个回答

11

非常有趣!我会在最后一句话中加入“据说”这个词。夏令时所承诺的节能效果从未得到证明。 - Basil Bourque

3

Federico klez Culloca的回答是正确的,而且有历史意义。

此外,我想指出你正在使用过时的日期时间类,这些类已经被现代的JSR 310中定义的java.time类取代多年了。我们可以使用java.time来查看在其他答案中解释的偏移量变化。

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

这是您的代码重写,以避免使用那些过时的类。
首先,确定日期。
int year = 1947;
int month = 2;
int day = 23;
LocalDate localDate = LocalDate.of(year,month,day);

接下来,选择一些时间值。凌晨1点是在凌晨2点跳到凌晨3点之前,而凌晨4点是在跳之后。让我们一起看看。

LocalTime beforeJump = LocalTime.of( 1 , 0 ) ;
LocalTime afterJump = LocalTime.of( 4 , 0 ) ;

将时区定义为 ZoneId

ZoneId zoneId = ZoneId.of( "Europe/Prague" ) ;

结合以确定一个时刻,得到一个ZonedDateTime对象。

ZonedDateTime zdtBeforeJump = ZonedDateTime.of( localDate , beforeJump , z );
ZonedDateTime zdtAfterJump = ZonedDateTime.of( localDate , afterJump , z );

当运行时:
zdtBeforeJump.toString() = 1947-02-23T01:00Z[Europe/Prague]
zdtAfterJump.toString() = 1947-02-23T04:00+01:00[Europe/Prague]

我们可以看到在生成的文本中,偏移量从零小时分钟秒(由Z表示为Zulu时间)变为比UTC提前1小时的偏移量。
我们可以通过代码查询偏移量。从ZoneId对象中获取区域规则作为ZoneRules对象。这提供了该特定地区人民过去、现在和未来使用的偏移量的历史记录。

将我们的时区调整为零偏移量,生成Instant对象,该对象是ZoneRules#getOffset所需的。然后询问规则,在该时区的该时刻实际生效的UTC偏移量。我们得到一个ZoneOffset对象作为结果。

ZoneRules rules = z.getRules();
ZoneOffset offsetBeforeJump = rules.getOffset( zdtBeforeJump.toInstant() );
ZoneOffset offsetAfterJump = rules.getOffset( zdtAfterJump.toInstant() );

运行时。

offsetBeforeJump.toString() = Z
offsetAfterJump.toString() = +01:00

你可以在 IdeOne.com上实时运行查看所有代码。
➥ 与生成的文本相同的结果:跳跃前为零偏移,跳跃后为+01:00。

关于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.*类。Hibernate 5和JPA 2.2支持java.time

如何获取java.time类?


另一个答案是正确的,但没有将读者与Java日期时间API联系起来。这个答案填补了这个空白。 - Arvind Kumar Avinash

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