问题描述
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 小时。
我该如何解决这个问题?
解决方法
java.time
java.util
日期时间 API 及其格式化 API SimpleDateFormat
已过时且容易出错。建议完全停止使用它们并切换到 modern Date-Time API*。
使用 java.time
(现代日期时间 API)的解决方案:
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]
-
输出中的
Z
是零时区偏移的 timezone designator。它代表祖鲁语并指定Etc/UTC
时区(时区偏移为+00:00
小时)。 -
从输出中可以明显看出,2021-09-16T12:00Z[Africa/Abidjan] 等于 2021-09-16T13:00+01:00[Europe/London]。
从 Trail: Date Time 了解有关现代 Date-Time API 的更多信息。
* 出于任何原因,如果您必须坚持使用 Java 6 或 Java 7,您可以使用 ThreeTen-Backport,它将大部分 java.time 功能向后移植到 Java 6 & 7. 如果您正在为 Android 项目工作并且您的 Android API 级别仍然不符合 Java-8,请检查 Java 8+ APIs available through desugaring 和 How to use ThreeTenABP in Android Project。
,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