在转换和扩充值时,使用 ZoneDateTime 与 Calendar 有什么影响?

What are the implications of using ZoneDateTime versus Calendar, when converting and augmenting values?

提问人:Reilas 提问时间:5/29/2023 最后编辑:Reilas 更新时间:6/21/2023 访问量:82

问:

我不确定在使用 Calendar 对象与使用 ZonedDateTime 对象时会发生什么不可预见的影响,特别是失效。

我想知道是否可以深入了解为什么必须使用一个框架而不是另一个框架来完成值的转换或解析。

我已经查看了 Java 教程,参考了“遗留日期时间代码,但我无法获得可能发生失效的地方。

套用一下,本教程提到了以下内容。

“Calendar 类不是类型安全的”。

我不关心类型安全,解析只是几个步骤。

“因为这些类是可变的,所以它们不能在多线程应用程序中使用”。

可变状态不是一个主题。

“由于月份编号异常和缺乏类型安全性,应用程序代码中的错误很常见”。

我不关心索引与枚举。

这些观点都不是抽象的对立面。

作为演示,我将同时使用这两个类,以尝试讨论它们的逻辑。

我可以使用 SimpleDateFormatCalendar 解析格式化的 String 值。

String string = "MMMM dd, yyyy '@' h:mm:ss a z";
SimpleDateFormat format = new SimpleDateFormat(string);
String value = "May 28, 2023 @ 6:50:01 pm EDT";
Calendar calendar = Calendar.getInstance();
calendar.setTime(format.parse(value));

如果我从日历中打印数据,我会收到一个有效的结果。

System.out.println(calendar.getTimeInMillis());
System.out.printf("%tc", calendar.getTimeInMillis());
1685314201000
Sun May 28 18:50:01 EDT 2023

而且,我可以使用 DateTimeFormatterZonedDateTime 解析相同的格式化 String 值。
当然,我必须在中将“PM”大写——这不是挫折。
DateTimeParseException 甚至指定了字符索引。

DateTimeFormatter format = DateTimeFormatter.ofPattern(string);
ZonedDateTime zonedDateTime = ZonedDateTime.parse(value, format);

同样,如果我从 zonedDateTime 打印数据,我会得到一个有效的结果。

System.out.println(zonedDateTime.toInstant().toEpochMilli());
System.out.printf("%tc%n", zonedDateTime.toInstant().toEpochMilli());
System.out.println(zonedDateTime);
1685314201000
Sun May 28 18:50:01 EDT 2023
2023-05-28T18:50:01-04:00[America/New_York]

如果并排查看数据,您会发现它们是等效的,因此两者都有效。

如果我想增加这些值,比如说,通过添加 4 小时来得出 UTC 值,因为 EDT 是 UTC-4;我可以通过两个班级实现这一点。

对于 Calendar 类,我可以使用 add 方法。
其中,具有 Calendar 类中的可扩展规范

calendar.add(Calendar.HOUR_OF_DAY, 4);
1685328601000
Sun May 28 22:50:01 EDT 2023

同样,对于 zonedDateTime,我可以使用 plusHours 方法。

zonedDateTime = zonedDateTime.plusHours(4);
1685328601000
Sun May 28 22:50:01 EDT 2023
2023-05-28T22:50:01-04:00[America/New_York]

同样,我得出的值相等,就 CalendarZonedDateTime 而言。

如果我从 1,685,328,601,000 中减去 1,685,314,201,000,我得到 14,400,000
14,400,000 相当于 4 小时×(60 分钟 ×(60 秒× 1000 毫秒))。

这使我得出了我的结论,即在基本解析和增强方面,使用一个类而不是另一个类的不利影响。

使用一个类与另一个类时将计算的不可预见的误差是什么?

例如,一个类是否使用更准确的年份度量值(例如 365.25 天)而不是 365.2425

除了 Java 所陈述的设计含义之外,一个类在哪些方面被证明是惯用的——考虑到两个类将产生等效的值。

我的意思是从哲学的角度来看惯用的,而不是以范式的、意识形态的、新教的方式说的惯用语。

经验证据不一定适用。

Java 时间 解析 逻辑 日期算术

评论

0赞 Anonymous 5/29/2023
无论是使用旧的繁琐还是现代的,添加 4 小时从 EDT(北美东部夏令时)转换为 UTC 都是错误的。您将在 4 小时后获得一个时刻,并且仍然是东部时间。这两个类都可以为您进行转换,但是,您只需要对它们应用 UTC 时区即可。CalendarZonedDateTime
0赞 Anonymous 5/29/2023
您可以自己决定是喜欢 5 行还是 2 行,或者您觉得哪个更清晰。可能没什么大不了的:不能保证从你那里得到格里高利历。由于在我能想到的所有日历系统中在时区之间转换或添加 4 小时都是一样的,因此对于您的特定用例来说应该无关紧要。Calendar.getInstance()
0赞 Reilas 5/29/2023
@OleV.V.,我不关心设计的影响,我已经清楚地概述了这一点。我感兴趣的是,为什么一个抽象被认为比另一个抽象产生更合乎逻辑的结果。如您所见,它们的结果没有区别,它们是完全相同的值。这是现在的第二个回应,这意味着我会遇到某种设计缺陷。缺陷在哪里?我提供了生成结果所需的完整代码;我忘了输入什么,不需要更多的逻辑。请应用这个话题。
0赞 Reilas 5/29/2023
@OleV.V.,此外,“......添加 4 小时转换...UTC是错误的。...4 小时后,你会得到一个时刻,而且仍然是东部时间”。这是不正确的,例如,现在是美国东部时间上午 11 点,UTC 时间下午 3 点。即 EDT 加 4。如果我理解正确的话,你是说这是因为时区仍然说 EDT。不过,我没有更改时区,我只是想得出一个 UTC 值。你说这是逻辑上的错误?这有点多,几乎是自命不凡。

答:

1赞 Louis Wasserman 5/29/2023 #1

不,他们没有使用“更准确的年份测量”。没有任何方面是 Calendar 不正确的,或者会导致值完全错误。

日历已经过时,因为它难以正确使用,例如将月份编号从 0 到 11 而不是 1 到 12。 它还将物理时间和民事时间混为一谈,区分两者很重要。

使用 java.util.Calendar 可以编写 100% 正确的代码。不过,大多数人不会。

评论

0赞 Reilas 5/29/2023
所以,我不是在寻找实证评估。我对“大多数人”的考虑不感兴趣。我对一个值的解析很感兴趣,它似乎有某种与之相关的耻辱感;其中有首选的抽象。而且,除了错综复杂的经验停滞之外,我无法理解惯用原则。如果没有逻辑上的理由表明一个基本过程不能用一个抽象来完成另一个抽象,那么为什么会有延迟呢?
1赞 Louis Wasserman 5/29/2023
嘿,如果你想用汇编来写所有东西,因为每个过程都可以在原始汇编中完成,请成为我的客人。我知道你不是在寻找“实证评估”,但我告诉你的是为什么人们避免使用 java.util.Calendar,以至于大多数人甚至不愿意帮助调试使用它的代码。什么是“惯用语”,什么是社区推荐使用的东西,应该清晰易读且易于使用,而 Calendar 则不是,这足以成为不使用它的理由。
0赞 Louis Wasserman 5/29/2023
换句话说,java.util.Calendar 存在“污名”,因为它的使用与不正确的代码明显相关,即使使用它的特定代码示例是正确的。
0赞 Reilas 5/29/2023
我想情况是怎样的,与其说是误判的暗示,不如说是某种不可避免的超越,而是修辞和坚持的结果。有趣的是,有些人选择它,而不是逻辑。瞧瞧它有多偏离!在谈论计算毫秒时,你非常肯定一种方法比另一种更好。感谢您抽出宝贵时间,谢谢。
1赞 Louis Wasserman 5/30/2023
“有趣的是,有些人选择它,而不是逻辑。当由于微妙的不正确而造成数百万美元的错误时,我认为这在任何方面都不足为奇。我的团队发现了数百个由不正确使用日期和时间 API 导致的严重错误,并发现使用 java.time 大大降低了它们的频率。我否认这是令人惊讶的、错误的或奇怪的。