Java 8:计算两个 ZonedDateTime 之间的差异

Posted

技术标签:

【中文标题】Java 8:计算两个 ZonedDateTime 之间的差异【英文标题】:Java 8: Calculate difference between two ZonedDateTime 【发布时间】:2017-04-25 22:03:52 【问题描述】:

我正在尝试编写一个方法来打印两个 ZonedDateTime 之间的时差,关于时区之间的差异。

我找到了一些解决方案,但它们都是为使用 LocalDateTime 而编写的。

【问题讨论】:

贴一个你想要达到的具体例子,并得到结果。 Michał 的回答正是我所期望的。非常感谢! 【参考方案1】:

这为您提供了两个 ZonedDateTimes 之间的分钟数。

int minutes = (int) (end.toEpochSecond() - start.toEpochSecond()) / 60;

【讨论】:

感谢您的贡献。抱歉,这不是推荐的方式,在特殊情况下会给出完全错误的结果。【参考方案2】:

tl;博士

小时、分钟、秒:

Duration.between( zdtA , zdtB )  // Represent a span-of-time in terms of days (24-hour chunks of time, not calendar days), hours, minutes, seconds. Internally, a count of whole seconds plus a fractional second (nanoseconds).

年、月、日:

Period.between(                  // Represent a span-of-time in terms of years-months-days. 
    zdtA.toLocalDate() ,         // Extract the date-only from the date-time-zone object. 
    zdtB.toLocalDate() 
)

详情

Answer by Michal S 正确,显示为ChronoUnit

Duration & Period

另一条路线是DurationPeriod 类。第一个用于较短的时间跨度(小时、分钟、秒),第二个用于较长的时间(年、月、日)。

Duration d = Duration.between( zdtA , zdtB );

通过调用toString 在standard ISO 8601 format 中生成一个字符串。格式为PnYnMnDTnHnMnS,其中P 标记开头,T 分隔两部分。

String output = d.toString();

在 Java 9 及更高版本中,调用 to…Part 方法来获取各个组件。在another Answer of mine讨论。

示例代码

ZoneId z = ZoneId.of( "America/Montreal" );
ZonedDateTime zdtStart = ZonedDateTime.now( z );
ZonedDateTime zdtStop = zdtStart.plusHours( 3 ).plusMinutes( 7 );

Duration d = Duration.between( zdtStart , zdtStop );

2016-12-11T03:07:50.639-05:00[美国/蒙特利尔]/2016-12-11T06:14:50.639-05:00[美国/蒙特利尔]

PT3H7M

见live code in IdeOne.com。

Interval & LocalDateRange

ThreeTen-Extra 项目为 java.time 类添加了功能。其中一个方便的类是Interval,将时间跨度表示为时间轴上的一对点。另一个是LocalDateRange,用于一对LocalDate 对象。相比之下,PeriodDuration 类各自代表一个时间跨度,因为没有附加到时间线。

Interval 的工厂方法采用一对 Instant 对象。

Interval interval = Interval.of( zdtStart.toInstant() , zdtStop.toInstant() );

您可以从Interval 获取Duration

Duration d = interval.toDuration();


关于java.time

java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧 legacy 日期时间类,例如 java.util.DateCalendarSimpleDateFormat

Joda-Time 项目现在位于maintenance mode,建议迁移到java.time 类。

要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310。

您可以直接与您的数据库交换 java.time 对象。使用符合JDBC 4.2 或更高版本的JDBC driver。不需要字符串,不需要java.sql.* 类。

从哪里获得 java.time 类?

Java SE 8Java SE 9Java SE 10 及更高版本 内置。 标准 Java API 的一部分,带有捆绑实现。 Java 9 添加了一些小功能和修复。 Java SE 6Java SE 7 大部分 java.time 功能都在ThreeTen-Backport 中向后移植到 Java 6 和 7。 Android java.time 类的 android 捆绑包实现的更高版本。 对于早期的 Android (ThreeTenABP 项目适应 ThreeTen-Backport(如上所述)。见How to use ThreeTenABP…

ThreeTen-Extra 项目通过附加类扩展了 java.time。该项目是未来可能添加到 java.time 的试验场。您可以在这里找到一些有用的类,例如IntervalYearWeekYearQuarter 和more。

【讨论】:

您在哪里看到需要两个瞬间的 Duration 上的 of 方法?我只看到public static Duration of(long amount, TemporalUnit unit) @BrianGordon 已修复,将 .of 替换为 .between。谢谢。 Duration 与 ZonedDateTime 一起使用,而 Period 与 LocalDate 一起使用。需要在我们的 zdt 对象上调用 toLocalDate() 方法。 @guillaumeguerin 你是对的。修复了我的代码示例。谢谢。 哦,非常感谢,你的例子非常好!!!【参考方案3】:

您可以使用ChronoUnit中的方法between

此方法将这些时间转换为相同的区域(来自第一个参数的区域),然后调用 Temporal 接口中声明的 until 方法:

static long zonedDateTimeDifference(ZonedDateTime d1, ZonedDateTime d2, ChronoUnit unit)
    return unit.between(d1, d2);

由于 ZonedDateTimeLocalDateTime 都实现了 Temporal 接口,您还可以为这些日期时间类型编写通用方法:

static long dateTimeDifference(Temporal d1, Temporal d2, ChronoUnit unit)
    return unit.between(d1, d2);

但请记住,混合 LocalDateTimeZonedDateTime 调用此方法会导致 DateTimeException。 希望对您有所帮助。

【讨论】:

有没有办法在两个 ZDT 之间的 ZoneId 不同的情况下获得两种 ZDT 类型之间的差异? @stanlick 是:Duration.between( zdtA , zdtB ) 如my Answer 所示。我想between 方法只是在两者上调用ZonedDateTime::toInstant()

以上是关于Java 8:计算两个 ZonedDateTime 之间的差异的主要内容,如果未能解决你的问题,请参考以下文章

Java 8:如何从 Epoch 值创建 ZonedDateTime?

Java 8 Time API - ZonedDateTime - 解析时指定默认 ZoneId

将 java.sql.Timestamp 转换为 Java 8 ZonedDateTime?

ZonedDateTime 到早期 Android 中 Java 8 之前的日期

Java 8 - ZonedDateTime 的 DateTimeFormatter 和 ISO_INSTANT 问题

Java - 比较两个 ZonedDateTime 时的结果与预期不符