【问题标题】:Java - ZonedDateTime does not correctly convert to Date object?Java - ZonedDateTime 没有正确转换为 Date 对象?
【发布时间】:2021-09-27 05:59:52
【问题描述】:

我有以下 Java 代码:

Date convertedDate = Date.from(zonedDateTime.toInstant());

我遇到的问题是 convertedDate 与 zonedDateTime 对象相比不正确。

例如,当我有一个 zonedDateTime 对象时:

2021-09-16T12:00

带区域:

Africa/Abidjan

上面的代码将其转换为:

Thu Sep 16 13:00:00 BST 2021

我在这里期待的是

 Thu Sep 16 10:00:00 BST 2021

Africa/Abidjan 时区比英国夏令时早 2 小时。

我该如何解决这个问题?

【问题讨论】:

  • “因为非洲/阿比让时区比 BST 早 2 小时。”
  • 好的,那么在这种情况下我的代码是正确的吗?
  • 对我来说看起来是正确的。非洲/阿比让时区的 2021-09-16T12:00 是 2021 年 9 月 16 日星期四 13:00:00 BST。

标签: java date time java-8 zoneddatetime


【解决方案1】:

java.time

java.util 日期时间 API 及其格式化 API SimpleDateFormat 已过时且容易出错。建议完全停止使用,改用modern Date-Time API*

使用现代日期时间 API java.time 的解决方案:

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;

public class Main {
    public static void main(String[] args) {
        // The given ZonedDateTime
        ZonedDateTime zdtAbidjan = ZonedDateTime.of(
                                        LocalDateTime.of(LocalDate.of(2021, 9, 16),
                                        LocalTime.of(12, 0)),
                                        ZoneId.of("Africa/Abidjan")
                                    );
        System.out.println(zdtAbidjan);

        ZonedDateTime zdtLondon = zdtAbidjan.withZoneSameInstant(ZoneId.of("Europe/London"));
        System.out.println(zdtLondon);
    }
}

输出:

2021-09-16T12:00Z[Africa/Abidjan]
2021-09-16T13:00+01:00[Europe/London]

ONLINE DEMO

  • 输出中的Z 是零时区偏移的timezone designator。它代表 Zulu 并指定 Etc/UTC 时区(时区偏移量为 +00:00 小时)。

  • 从输出可以看出,2021-09-16T12:00Z[Africa/Abidjan] 等于 2021-09-16T13:00+01:00[Europe/London]。

Trail: Date Time 了解有关现代日期时间 API 的更多信息。


* 出于任何原因,如果您必须坚持使用 Java 6 或 Java 7,您可以使用 ThreeTen-Backport,它将大部分 java.time 功能向后移植到 Java 6 和 7 . 如果您正在为一个Android项目工作并且您的Android API级别仍然不符合Java-8,请检查Java 8+ APIs available through desugaringHow to use ThreeTenABP in Android Project

【讨论】:

    【解决方案2】:

    Answer by Avinash 是正确的。这里还有一些想法。

    时区名称

    BST 不是real time zone name。也许你的意思是Europe/London。那是不是 GMT/UTC。由于Daylight Saving Time (DST) 和其他原因,伦敦时区的偏移量可能会有所不同。

    UTC

    让我们看看您在三个不同时区的每一刻。

    首先,我们将您的输入解析为 LocalDateTime,缺少时区上下文或与 UTC 的偏移量。然后我们为Abidjan 分配一个时区作为上下文来生成ZonedDateTime 对象。我们调整到另一个时区,产生第二个ZonedDateTime,它代表同一时刻,时间轴上的同一点,但挂钟时间不同。最后,我们提取Instant 以有效地适应UTC。 Instant 代表 UTC 中的时刻,始终采用 UTC。

    LocalDateTime ldt = LocalDateTime.parse( "2021-09-16T12:00" ) ;
    ZonedDateTime zdtAbidjan = ldt.atZone( ZoneId.of( "Africa/Abidjan" ) ) ;
    ZonedDateTime zdtLondon = zdtAbidjan.withZoneSameInstant( ZoneId.of( "Europe/London" ) ) ;
    Instant instant = zdtAbidjan.toInstant() ;  // Adjust to UTC by extracting an `Instant` object.
    

    看到这个code run live at IdeOne.com

    ldt: 2021-09-16T12:00
    zdtAbidjan: 2021-09-16T12:00Z[Africa/Abidjan]
    zdtLondon: 2021-09-16T13:00+01:00[Europe/London]
    instant: 2021-09-16T12:00:00Z
    

    末尾的Z 表示零时分秒的偏移量,发音为“Zulu”。因此,我们可以看到科特迪瓦 9 月那天的中午与 UTC 相同,偏移量为零。相比之下,+01:00 告诉我们伦敦时间提前了一个小时。所以时钟显示为下午 1 点 (13:00) 而不是中午。

    获取偏移量

    您可以通过ZoneRules 类确定在特定时刻生效的偏移量。偏移量信息由ZoneOffset 类表示。

    ZoneId z = ZoneId.of( "Africa/Abidjan" ) ;
    ZoneRules rules = z.getRules() ;
    ZoneOffset offset = rules.getOffset( LocalDateTime.parse( "2021-09-16T12:00" ) ) ;
    int offsetInSeconds = offset.getTotalSeconds() ;
    

    或将其浓缩为:

    ZoneId
    .of( "Africa/Abidjan" )
    .getRules()
    .getOffset( LocalDateTime.parse( "2021-09-16T12:00" ) )
    .getTotalSeconds()
    

    在运行时,我们再次看到科特迪瓦在该日期时间使用零偏移量。

    rules: ZoneRules[currentStandardOffset=Z]
    offset: Z
    offsetInSeconds: 0
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-07-17
      • 2017-05-31
      • 2011-01-31
      • 2022-01-06
      • 2018-12-24
      相关资源
      最近更新 更多