【问题标题】:Converting Java Date to OffsetDateTime将 Java 日期转换为 OffsetDateTime
【发布时间】:2019-03-09 00:55:37
【问题描述】:

我有一个 eta 值,它是 OffsetDateTime,我有一个 scheduleDate,它是 Date 类型。如果未设置 eta,我想回退到日期。

日期的一个例子是Tue Jul 21 10:32:28 PDT 2020。为了转换它,我尝试这样做: OffsetDateTime.ofInstant(dto.getScheduledTime().ToInstant(), ZoneOffset.UTC) 感觉 utc 偏移量是错误的,因为 Date 中已经有 PDT,但同时它也不是像“America/Los_Angeles”这样的 timezoneId。

我对如何处理这个问题有点困惑。

【问题讨论】:

  • 日期 not 中是否包含 PDT。 Date 的字符串形式在系统的本地时区中显示,但 Date 对象本身包含自 1970 年 1 月 1 日以来的毫秒数,仅此而已。这就是为什么您必须提供时区。 UTC 可能是最好的选择,除非您的应用程序需要使用特定时区来显示它。
  • 啊,好吧。那么出现 PDT 的唯一原因是因为我的系统时区来自那里?通过使用 .ofInstant 函数并尝试将 scheduleTime 转换为 Instant,我是否走在正确的轨道上?
  • 是的,我相信您使用正确的方法将日期转换为 OffsetDateTime。
  • 好吧,所以我不知道为什么,但是今天那行没有返回空值......也许我没有保存在 ide 中。但是,如果您说字符串中的时区对其他位置无效,我不应该使用 ZoneOffset.UTC 对吗?
  • 在我看来,你应该使用 UTC。忽略 Date 对象的 String 形式;它不是实际数据,只是表示日期的一种方便方式。

标签: java spring java-time date


【解决方案1】:

tl;博士

OffsetDateTime target, eta;    // Modern java.time class.
java.util.Date scheduledDate;  // Terrible legacy class.


if(Objects.isNull(eta)) {   // If no eta, fall back to scheduledDate.
    target = scheduledDate.toInstant().atOffset( ZoneOffset.UTC ); // Never use legacy class `java.util.Date` -- when encountered, immediately convert to modern `java.time.Instant`. 
} else {  // Else not null.
    target = eta;
}
return target;

最好完全避免java.util.Date。遇到时,立即转换成现代类Instant,忘掉Date对象。

OffsetDateTime target, eta, scheduled;
scheduled = incomingJavaUtilDate.toInstant().atOffset(ZoneOffset.UTC);

target = Objects.isNull(eta) ? scheduledDate : eta;  // Ternary operator. Short way of saying: If eta is null, go with scheduledDate, otherwise go with eta. 
return target;

Date::toString骗你

首先,请了解java.util.Date 代表 UTC 中的时刻,始终采用 UTC。然而,它的toString 方法具有良好的意图,即动态应用JVM当前默认时区的反特性非常令人困惑。这会造成Date 有时区的错误印象,而实际上它没有

避免使用旧的日期时间类

其次,您将非常好的现代 java.time 类 (OffsetDateTime) 与非常糟糕的传统日期时间类 (Date) 不必要地和可悲地混合在一起。不要这样做。完全避免遗留类。早在 2014 年,JSR 310 就已经过时了。

使用java.time

如果传递了java.util.Date 对象,立即转换为java.time。调用添加到旧类的新转换方法。 Instant 类直接替换 Date,作为 UTC 中的一个时刻,但具有比毫秒更精细的纳秒分辨率。

Instant instant = myJavaUtilDate.toInstant();  // Convert from legacy class to modern class.

您通常应该在 UTC 中跟踪时刻。您可以将其作为InstantOffsetDateTime 执行此操作,并将其偏移设置为ZoneOffset.UTC 常量。

OffsetDateTime odt = instant.atOffset(ZoneOffset.UTC);  // Same moment, no change in meaning whatsoever. 

对于 UTC,在我们的代码中 instantodt 之间没有区别。它们都代表 UTC 中的一个时刻。不同之处在于OffsetDateTime (a) 可以携带与 UTC 的替代偏移值(小时-分钟-秒),并且 (b) 更灵活,例如以标准 ISO 8601 以外的格式生成文本。

了解offset-from-UTC 只是几个小时、分钟和秒。而已。相比之下,time zone更多。时区是特定地区的人们使用的偏移量的过去、现在和未来变化的历史。例如,使用America/Los_Angeles 时区的地区的人们每年两次更改他们的偏移量,即Daylight Saving Time (DST),从-08:00 到-07:00 再返回。

因此,通常时区比单纯的偏移更可取。例如,要查看您的Date,我们通过美国西海岸大多数人使用的挂钟时间变成Instant,请将时区America/Los_Angeles (ZoneId) 应用于Instant获取ZonedDateTime

ZoneId z = ZoneId.of("America/Los_Angeles");
ZonedDateTime zdt = instant.atZone(z);

您可以通过提取Instant 返回UTC。

Instant instant = zdt.toInstant();

然后从那里返回java.util.Date(如果必须,否则避免)。

java.util.Date d = java.util.Date.from(instant);

实际上,java.time.Date确实有一个深埋在里面的时区。缺少任何访问器(get/set)方法,它是无法访问的。它的行为与我们在这里的讨论无关。令人困惑?是的。避免可怕的遗留日期时间类的众多原因中的另一个。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-07
    • 1970-01-01
    • 1970-01-01
    • 2017-11-02
    相关资源
    最近更新 更多