Java - 从本地时间转换为 UTC 历史时间

标签 java time timezone

除了时钟回滚一小时的情况外,在给定国家代码和时区更改时间列表的情况下,可以通过编程方式确定时区。

例如,如果您知道英国的时钟在每年 3 月的最后一个星期日凌晨 1 点和 10 月的最后一个星期日凌晨 2 点发生变化,那么给定历史或 future 的“本地”时间,您就可以确定 UTC 时间。

我的问题是,Java 中是否有任何内置方法可以确定此信息 - 不仅可以确定当前时间,还可以确定历史时间?

最佳答案

事情没那么简单,因为时区的规则(可能/可以/做)一直在变化。

... if you know that on the last Sunday of every March at 1am and October at 2am the clocks change in the UK

并非所有历史日期都是如此。 In the 70's您会注意到,一些 DST 更改发生在 3 月中旬,而不是最后一个星期日,例如 1974 年和 1975 年,DST 分别于 3 月 17 日和 16 日开始。另请注意,它从凌晨 2 点开始,而不是凌晨 1 点 ( only in 1981 it was changed to start at 1 AM )。

无论如何,Java 包含来自 IANA 的所有时区数据(包括历史数据),因此您只需使用内置类,API 即可完成所有数学运算:

// London timezone
ZoneId londonTimezone = ZoneId.of("Europe/London");

// the historical local date/time I want to check (March 16th 1975, 3 AM)
LocalDateTime localDt = LocalDateTime.of(1975, 3, 16, 3, 0, 0);

// convert to UK timezone (1975-03-16T03:00+01:00)
ZonedDateTime londonDt = localDt.atZone(londonTimezone);

// convert to UTC (1975-03-16T02:00:00Z)
Instant instant = londonDt.toInstant();

在上面的代码中,我创建了本地时间 1975 年 3 月 16 日凌晨 3 点(即 after the DST transition, that occurred at 2 AM)

当我将其转换为伦敦时区时,结果为 1975-03-16T03:00+01:00 - 偏移量为 +01:00(比 UTC 早一小时),因为 DST 是有效。将其转换为 UTC,结果为 1975-03-16T02:00:00Z

With the exception of when clocks roll back an hour...

其实,有一个办法可以解决这个问题。使用London's DST transition in 1975 ,夏令时于 10 月 26 日结束:凌晨 3 点,时钟被调回到凌晨 2 点,因此凌晨 2 点到 2:59 之间的所有本地时间都存在两次。如何解决这种歧义?

// London timezone
ZoneId londonTimezone = ZoneId.of("Europe/London");

// October 26th 1975, 2 AM - the ambiguous local time in London, due to DST end
LocalDateTime localDt = LocalDateTime.of(1975, 10, 26, 2, 0, 0);
// convert to UK timezone (default is the local time in DST: 1975-10-26T02:00+01:00)
ZonedDateTime londonDt = localDt.atZone(londonTimezone);

// get the date in DST (1975-10-26T02:00+01:00)
System.out.println(londonDt.withEarlierOffsetAtOverlap());
// get the date after DST ends (1975-10-26T02:00Z)
System.out.println(londonDt.withLaterOffsetAtOverlap());

方法withEarlierOffsetAtOverlap()withLaterOffsetAtOverlap()分别返回DST结束之前和之后对应的日期/时间,解决了歧义。

这些方法中的每一个都会返回一个新的 ZonedDateTime 实例(每个实例对应于不同的 UTC 时刻),您可以对它们调用 toInstant() 来获取对应的UTC 即时。

始终使用这两种方法来获取转换之前和之后的日期/时间。不要尝试增加或减少一小时,因为并非所有转换都会将时钟更改 1 小时:

It is possible to programmatically determine the timezone, given a country-code and a list of times at which the timezone changes

这是可能的,但请注意,同一个国家可以有多个时区,特别是像美国(非洲大陆有 4 个时区,加上阿拉斯加和夏威夷)、俄罗斯(超过 10 个)、巴西(4 - 实际上是有更多,因为有些州有夏令时,但其他州没有),等等。

无论如何,给定国家代码,您可以检查 this file并获取该国家/地区的所有时区。然后,对于每个时区,您运行与此类似的代码来比较发生转换时的所有日期:

ZoneId londonTimezone = ZoneId.of("Europe/London");
ZoneRules rules = londonTimezone.getRules();

// get all the transitions (dates when the offset changes)
rules.getTransitions().forEach(t -> {
    // UTC instant when the change occurs
    Instant instant = t.getInstant();

    // local date/time before the transition
    LocalDateTime before = t.getDateTimeBefore();
    // UTC offset before the transition
    ZoneOffset offsetBefore = t.getOffsetBefore();

    // local date/time after the transition
    LocalDateTime after = t.getDateTimeAfter();
    // UTC offset afger the transition
    ZoneOffset offsetAfter = t.getOffsetAfter();

    // is GAP (clock shifts forward - local times are skipped)
    boolean gap = t.isGap();
    // is overlap (clock shifts backwards - local times can happen twice) 
    boolean overlap = t.isOverlap();

    // *** You can use the data above to check if the transition date matches your list***
});

有些区域有转换规则而不仅仅是转换,因此您还必须检查转换规则列表:

rules.getTransitionRules().forEach(rule -> {
    // get a transition for specific year
    ZoneOffsetTransition transition = rule.createTransition(2018);

    // use the transition the same as above (getInstant(), getDateTimeBefore(), etc)
});

关于Java - 从本地时间转换为 UTC 历史时间,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49671454/

相关文章:

java - 如何获取先科的ZoneId?

java - 在与主体分开的 try-with-resources 中捕获对象构造期间的错误

java - 梯度错误:Execution failed for task ':app:transformClassesWithJarMergingForDebug'

php - mysql查询时区转换

r - 连接键值不完全相等的R data.tables-合并时间最近的行

Python pandas 计算时间,直到列中的值大于当前期间的值

php - 如何将 GMT 时区传递给 PHP DateTime 对象?

java - (Java) 线程在异常捕获处莫名其妙地停止

java - 为什么 GWT 中的多个 RPC 调用会显着降低响应时间?

php - 如何向MySQL数据库插入微秒?