3

当我使用java.time实现将代码转换为time4j 时,我想将 a 添加Duration到 aMoment但出现编译错误。在java.time下,我会执行以下操作:

val startTime: ZonedDateTime = ...
val duration: TemporalAmount = ...
val endTime: ZonedDateTime = startTime.plus(duration)

但是使用time4j,同样不起作用:

val startTime: Moment = ...
val duration: Duration[ClockUnit] = ...
val endTime: Moment = startTime.plus(duration)

编译器抱怨Duration和之间的泛型交互Moment。无论我以哪种方式创建Duration(至少我发现),它都需要有一个关联的泛型java.util.concurrent.TimeUnit,因为Moment实现TimePoint[java.util.concurrent.TimeUnit]Moment#plus方法因此需要具有与时间单位Duration[java.util.concurrent.TimeUnit]相同的关联泛型。Moment

这让我感到惊讶,java.util.concurrent.TimeUnit因为这不是time4j类型。我是否错过了有关此选择的更多细节?我的印象是这是设计决定的。

一种可行的方法是,如果我使用 aPlainTimestamp并添加 a Duration[ClockUnit | CalendarUnit | IsoUnit],因为前一种类型实现TimePoint[IsoUnit, PlainTime]并重载了额外支持的单元。然后我可以将其转换为PlainTimestamp我之后需要的任何时区。这是预期的设计吗?

(仍然:)Duration使用Moment.plus方法的正确类型是什么?

我正在使用 time4j 版本 4.27.2

4

1 回答 1

3

简短回答如何迁移zonedDateTime.plus(Duration.ofHours(24))

Moment.from(zonedDateTime.toInstant()).plus(MachineTime.of(24, TimeUnit.HOURS));

另请参阅全局持续时间类型MachineTime的 API 。

详细的长答案

java.time只知道一个类ChronoUnit来表示适用于任意时间实体(和通用接口)的最常用时间单位TemporalUnit的 -API 相比,Time4J 库的单位和持续时间设计更加细粒度。

  • 每个带有时间线的日历或时间类型都有自己的单位类型,它也以不同的方式表征时间类型的时间线,因此即使编译器也会告诉您不要混淆不同实体的不同单位类型,例如Moment(counterpart to java.time.Instant) 或PlainTimestamp(对应LocalDateTime)。
  • 这种区别被设计编码为超类TimePoint中的泛型类型参数 U 。对于具体类:Moment主要与java.util.concurrent.TimeUnitwhilePlainTimestamp与 的任何实现一起使用IsoUnit,尤其是与枚举CalendarUnitClockUnit.
  • 广义持续时间概念由接口TimeSpan描述,该接口也由要使用的单元类型 U 指定。如果单位类型为 ,则只能为时刻添加时间跨度java.util.concurrent.TimeUnit。这是由特殊的实现类提供的MachineTime。此外,调用的其他持续时间实现net.time4j.Duration仅与本地类型兼容,例如PlainTimestamp.
  • 特殊的时间实体有自己的一组合适的单位。例如,纪元的单位只存在于日本日历中,而对于其他有零个、一个或只有两个纪元的日历则不存在。

为什么Moment和的单位类型不同PlainTimestamp

关于合适的单元集的最后一项已经部分地回答了这个问题。例如,月份和年份对于类似机器的类型(如Moment. 因此,选择的枚举java.util.concurrent.TimeUnit涵盖了作为单位所需的内容Moment

此外,Time4J 的不同单位类型有助于区分。Anet.time4j.Duration<ClockUnit>是在本地上下文中计算的,而 aMachineTime<TimeUnit>是作为全局持续时间计算的。这不仅适用于与时钟相关的单位(如小时),也适用于日历单位。一年不仅仅是一年。我们有 ISO 日历年(对应于公历年)。我们有基于 ISO 周的年(长度为 364 或 371 天)。我们有伊斯兰年(354 或 355 天)等等。因此 Time4J 知道很多不同的日历单元(注意日历模块的 API)。因此 Time4J 最终采用了一种设计,以防止将持续时间与不同的单位类型进行比较(就像比较苹果和橙子一样)。

以下示例是在萨摩亚更改国际日期变更线的罕见案例(2011-12-30 被忽略)表明单位类型的区别可能是多么重要:

在 Time4J 中,我们只是使用不同的单位类型来表示算术是发生在本地时间线上还是全局时间线上。结论:在 Java-8 中,我们必须仔细研究上下文,在 Time4J 中,单元类型提供了有价值的额外信息。

ZonedDateTime zdt = ZonedDateTime.of(2011, 12, 29, 0, 0, 0, 0, "Pacific/Apia");
Moment m1 = Moment.from(zdt.toInstant());
Moment m2 = m1.plus(MachineTime.of(24, TimeUnit.HOURS));
assertThat(m2.isSimultaneous(m1.plus(MachineTime.of(1, TimeUnit.DAYS))), is(true));
System.out.println(m2); // 2011-12-30T10:00:00Z
System.out.println(m2.toZonalTimestamp(PACIFIC.APIA)); // 2011-12-31T00
System.out.println(m1.toZonalTimestamp(PACIFIC.APIA).plus(2, CalendarUnit.DAYS)); // 2011-12-31T00

对象TimeUnit.DAYSCalendarUnit.DAYS显然不一样。它们甚至需要不同的数量(1 对 2)才能产生相同的结果。

边注:

我现在已经缩短了我的答案的第一个版本 - 主要省略了与 Java-8 相关的内容,因为我认为在你的问题的狭隘范围内很容易写太多关于单元/持续时间设计的主题(和我什至没有给出任何完整的答案)。像这里这样的教程页面或额外的文档页面确实是一个更好的地方。

但至少我的回答中没有提到的另外两点对你来说可能也很有趣(关注net.time4j.Duration):符号处理和专门的 timezone-metricMachineTime在某些情况下,最后一个甚至可以替代。

于 2017-07-12T08:32:30.303 回答