[英]Java - ZonedDateTime does not correctly convert to Date object?
I have the following Java code:我有以下 Java 代码:
Date convertedDate = Date.from(zonedDateTime.toInstant());
The Issue I am having is that convertedDate
is not correct in comparison to the zonedDateTime object.我遇到的问题是,与 zonedDateTime 对象相比, convertedDate
不正确。
For Example when I have a zonedDateTime object of:例如,当我有一个 zonedDateTime 对象时:
2021-09-16T12:00
with zone:带区域:
Africa/Abidjan
The code above converts this to:上面的代码将其转换为:
Thu Sep 16 13:00:00 BST 2021
What I would be expecting here is我在这里期待的是
Thu Sep 16 10:00:00 BST 2021
As the Africa/Abidjan
timezone is 2 hours ahead of BST.由于Africa/Abidjan
时区比英国夏令时早 2 小时。
How can I solve this?我该如何解决这个问题?
The java.util
Date-Time API and their formatting API, SimpleDateFormat
are outdated and error-prone. java.util
Date-Time API 及其格式化 API SimpleDateFormat
已过时且容易出错。 It is recommended to stop using them completely and switch to the modern Date-Time API * .建议完全停止使用它们并切换到现代 Date-Time API * 。
Solution using java.time
, the 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);
}
}
Output:输出:
2021-09-16T12:00Z[Africa/Abidjan]
2021-09-16T13:00+01:00[Europe/London]
The Z
in the output is the timezone designator for zero-timezone offset.输出中的Z
是零时区偏移的时区指示符。 It stands for Zulu and specifies the Etc/UTC
timezone (which has the timezone offset of +00:00
hours).它代表祖鲁语并指定Etc/UTC
时区(时区偏移为+00:00
小时)。
From the output, it is clear that 2021-09-16T12:00Z[Africa/Abidjan] is equal to 2021-09-16T13:00+01:00[Europe/London].从输出中可以清楚地看出 2021-09-16T12:00Z[Africa/Abidjan] 等于 2021-09-16T13:00+01:00[Europe/London]。
Learn more about the modern Date-Time API from Trail: Date Time .从Trail: Date Time 中了解有关现代日期时间 API 的更多信息。
* For any reason, if you have to stick to Java 6 or Java 7, you can use ThreeTen-Backport which backports most of the java.time functionality to Java 6 & 7. If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and How to use ThreeTenABP in Android Project . * 出于任何原因,如果您必须坚持使用 Java 6 或 Java 7,您可以使用ThreeTen-Backport,它将大部分java.time功能向后移植到 Java 6 和 7。如果您正在为 Android 项目和您的 Android API 工作级别仍然不符合 Java-8,请检查 通过 desugaring和How to use ThreeTenABP in Android Project 可用的 Java 8+ APIs 。
Answer by Avinash is correct. Avinash 的回答是正确的。 Here are a few more thoughts.这里还有一些想法。
BST
is not a real time zone name . BST
不是真正的时区名称。 Perhaps you meant Europe/London
.也许你的意思是Europe/London
。 And that is not GMT/UTC.这不是GMT/UTC。 The London time zone can vary in its offset, because of Daylight Saving Time (DST) and perhaps other reasons.由于夏令时 (DST)和其他原因,伦敦时区的偏移量可能会有所不同。
Let's look at your moment in each of the three different time zones.让我们来看看您在三个不同时区中的每一个时刻。
First we parse your input as a LocalDateTime
, lacking the context of a time zone or offset-from-UTC.首先,我们将您的输入解析为LocalDateTime
,缺少时区或UTC 偏移量的上下文。 Then we assign a time zone for Abidjan as the context to produce a ZonedDateTime
object.然后我们为阿比让分配一个时区作为上下文来生成一个ZonedDateTime
对象。 We adjust to another time zone, resulting in a second ZonedDateTime
that represents the same moment, the same point on the timeline, but a different wall-clock time.我们调整到另一个时区,导致第二个ZonedDateTime
代表同一时刻,时间线上的同一点,但不同的挂钟时间。 Lastly, we extract a Instant
to effectively adjust into UTC.最后,我们提取一个Instant
来有效地调整为 UTC。 A Instant
represents a moment in UTC, always in 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.
See this code run live at IdeOne.com .查看此代码在 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
The Z
at the end means an offset of zero hours-minutes-seconds, pronounced “Zulu”.末尾的Z
表示零时分秒的偏移量,发音为“Zulu”。 So we can zee that noon on that September day in Côte d'Ivoire is the same as in UTC, having an offset of zero.所以我们可以看到,科特迪瓦 9 月那一天的中午与 UTC 相同,偏移为零。 In contrast, the +01:00
tells us that London time is an hour ahead.相比之下,+01 +01:00
告诉我们伦敦时间提前了一个小时。 So the clock reads 1 PM ( 13:00
) rather than noon.所以时钟显示的是下午 1 点( 13:00
)而不是中午。
You can determine the offset in effect at a particular moment via the ZoneRules
class.您可以通过ZoneRules
类确定特定时刻有效的偏移量。 The offset info is represented by the ZoneOffset
class.偏移量信息由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() ;
Or condense that to:或者将其浓缩为:
ZoneId
.of( "Africa/Abidjan" )
.getRules()
.getOffset( LocalDateTime.parse( "2021-09-16T12:00" ) )
.getTotalSeconds()
When run we see again that Côte d'Ivoire is using an offset of zero at that date-time.运行时,我们再次看到科特迪瓦在该日期时间使用零偏移量。
rules: ZoneRules[currentStandardOffset=Z]
offset: Z
offsetInSeconds: 0
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.