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 中跟踪时刻。您可以将其作为Instant 或OffsetDateTime 执行此操作,并将其偏移设置为ZoneOffset.UTC 常量。
OffsetDateTime odt = instant.atOffset(ZoneOffset.UTC); // Same moment, no change in meaning whatsoever.
对于 UTC,在我们的代码中 instant 和 odt 之间没有区别。它们都代表 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)方法,它是无法访问的。它的行为与我们在这里的讨论无关。令人困惑?是的。避免可怕的遗留日期时间类的众多原因中的另一个。