【问题标题】:Hours duration calculation issue using two Date objects使用两个 Date 对象的持续时间计算问题
【发布时间】:2018-03-28 02:09:35
【问题描述】:

我的班级有一个方法可以计算两个日期的每小时持续时间。

public static double getDuration(Date startDate, Date endDate){
    return (endDate.getTime()-startDate.getTime()) / (1000.0 * 60 * 60);
}

我输入的日期是28-10-2017 14:0029-10-2017 02:00。在夏令时期间,持续时间为13.0,这是错误的。

如果我用这个输入编写一个简单的程序,我会得到正确的结果。所以只需检查Date 对象。在我的应用程序输入中,它以 dayofWeek 的形式出现,开始日期为 7,结束日期为 1。但是我的简单程序将它作为开始日期的 1 和结束日期的 2。知道这是怎么发生的吗?

【问题讨论】:

  • 如何建立输入日期?
  • 已经为您完成了。见Java Time Trail
  • 为了一切美好的事物,不要试图这样计算日期差异。围绕日期、时间和时区有太多的疯狂,以至于时空结构无法应对另一个“一天总是有 24 小时”的假设。见github.com/kdeldycke/awesome-falsehood#dates-and-time
  • 如果你在 JSE7 上,那么joda.org/joda-time
  • 假设从 2017-10-29T01:30 到 2017-10-29T00:30 有一个“回退”作为 DST 过渡。这意味着 00:30 和 01:30 之间的时间会发生两次。这意味着 2017-10-28T14:00 和 2017-10-29T02:00 之间的 经过 时间为 13 小时。如果您将每个本地时间转换为 UTC,您应该清楚地看到这一点。如果 00:30 到 01:30 由于“弹簧向前”而被跳过,则会发生相反的情况。

标签: java date dst date-difference


【解决方案1】:

tl;博士

对于 Java 6 和 Java 7,请使用 ThreeTen-Backport 项目。

12 小时

Europe/Berlin 时区在这些日期的下午 2 点和凌晨 2 点之间为十二小时。

org.threeten.bp.temporal.ChronoUnit.HOURS.between( 

    org.threeten.bp.LocalDateTime
        .parse( "2017-10-28T14:00:00" )
        .atZone( org.threeten.bp.ZoneId.of( "Europe/Berlin" ) ) ,  // Producing a `ZonedDateTime` instance. 

    org.threeten.bp.LocalDateTime
        .parse( "2017-10-29T02:00:00" )
        .atZone( org.threeten.bp.ZoneId.of( "Europe/Berlin" ) )  // Producing another `ZonedDateTime` instance. 

)

12

13 小时

要查看安排在柏林时间凌晨 3 点的Daylight Saving Time (DST) 切换的效果,请将您的开始-停止时间更改为下午 3 点和凌晨 3 点,以获得 13 小时而不是 12 小时。

02:00-03:00 的时间会重复,如 Answer by Hugo 中所述。

org.threeten.bp.temporal.ChronoUnit.HOURS.between( 

    org.threeten.bp.LocalDateTime
        .parse( "2017-10-28T15:00:00" )
        .atZone( org.threeten.bp.ZoneId.of( "Europe/Berlin" ) ) ,  // Producing a `ZonedDateTime` instance. 

    org.threeten.bp.LocalDateTime
        .parse( "2017-10-29T03:00:00" )
        .atZone( org.threeten.bp.ZoneId.of( "Europe/Berlin" ) )  // Producing another `ZonedDateTime` instance. 

)

13

查看code run live at IdeOne.com,使用 Java 8 中内置的 java.time 类。

java.time

这已在 Stack Overflow 上多次介绍,因此请搜索更多信息。

简单地说……

避免使用Date 类。仅使用 java.time 类。同样,避免将 Joda-Time 项目用于新工作,因为团队建议迁移到 java.time 类。

将您的输入字符串解析为 LocalDateTime 值,因为它们缺少时区指示符或与 UTC 的偏移量。 LocalDateTime 确实代表时间线上的实际时刻,但我们会到达那里;继续阅读。

提示:以标准 ISO 8601 格式编写日期时间字符串,解析/生成字符串时默认在 java.time 类中使用。

为这些值定义一个时区 (ZoneId)。按continent/region 格式的正确名称指定区域,例如Africa/CasablancaEurope/Berlin。切勿使用 3-4 个伪区域名称,例如 PSTCST

应用区域为每个时刻生成一个ZonedDateTime 对象。

计算实例化Duration 对象或调用ChronoUnit.HOURS.between 所用的时间。

ThreeTen-Backport 项目中的大部分 java.time 功能都向后移植到 Java 6 和 Java 7。 java.time 类内置于 Java 8 和 Java 9 中。



关于java.time

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

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

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

从哪里获取 java.time 类?

【讨论】:

  • 在问题的 cmets 中某处有一条说明它是 JSE7,所以没有 java.time
  • @ptomli (A) 作者有责任将重要事实编辑到问题中,而不是埋在 cmets 中。 (B) 在 ThreeTen-Backport 项目中,大部分 java.time 被向后移植到 Java 6 和 7。
【解决方案2】:

夏令时 03:00 切换

你说in the comments 你的JVM default timezoneEurope/Berlin。 2017 年 10 月 28th,柏林位于 Daylight Saving Time (DST),因此偏移量为 +02:00(比 UTC 提前两个小时)。当当地时间到达 2017 年 10 月 29th 凌晨 3 点时,时钟将向后移动 1 小时,到凌晨 2 点(偏移量从+02:00+01:00)。

这意味着,在这个时区,所有当地时间凌晨 2 点到 2:59 之间的时间都存在两次(偏移量为+02:00+01:00)。只是为了更好地说明这一点,这就是时间线在这个时区中每小时的样子:

  • 2017-10-29T01:00+02:00(夏令时凌晨 1 点 - 偏移量 +02:00
  • 2017-10-29T02:00+02:00(1 小时后,凌晨 2 点在 DST - 偏移+02:00
  • 2017-10-29T02:00+01:00(1 小时后,凌晨 2 点 不是 DST - 偏移+01:00
  • 2017-10-29T03:00+01:00(1 小时后,凌晨 3 点不是在 DST - 偏移+01:00

您的 Date 对象的 getTime() 值等于 1509238800000 对应于 second 2 AM(not-in-DST 日期),然后相差 13 小时。


如果我们将时间戳转换为UTC(使用values provided in the comments),这会变得更清楚:

Timestamp (getTime()) | Europe/Berlin timezone              | UTC
----------------------|-------------------------------------|------------------
1509192000000         | 2017-10-28T14:00+02:00 (in DST)     | 2017-10-28T12:00Z
1509238800000         | 2017-10-29T02:00+01:00 (not in DST) | 2017-10-29T01:00Z

请注意,当转换为 UTC 时,很明显差异实际上是 13 小时。

【讨论】:

  • @BasilBourque 我仔细检查了一遍。在欧洲/柏林时区,2017 年 10 月 29 日,挂钟repeats the 02:00-03:00 hour。检查这个:ideone.com/ATOofe
  • 我错了:凌晨 2 点对 3 点切换。删除了我的评论。我假设在美国/加拿大使用 2 AM 切换。然后我了解到Europe/Berlin 使用 3 AM 切换。我会为任何其他太快假设的美国人/加拿大人留下此评论。
【解决方案3】:

首先,read this,哭一下,然后庆幸你不需要编写日期/时间库,因为其他人已经为你完成了

然后将Joda-Time 添加到您的项目中。

如果您知道数据库中所有日期的时区,那么您应该能够使用DurationPeriodInterval,具体取决于您的需要。

【讨论】:

  • 我不能建议一个新的包,因为这是不允许的。
  • 虽然是一个很棒的 AP​​I(我用了很多而且真的很喜欢它),但 Joda-Time 现在处于维护模式并被新的 API 取代,所以我不建议使用它。即使在joda's website 中,它也说:“请注意,Joda-Time 被认为是一个基本上“完成”的项目。没有计划进行重大改进。如果使用 Java SE 8,请迁移到 java.time (JSR-310 )。”。在 Java 7 中,有 ThreeTen Backport,它是 Java 8 新日期/时间类的一个很好的反向移植。
  • @Hugo 你能告诉我上面显示的代码有什么问题吗?以及 dayofweek 如何影响持续时间结果?
  • @JAVA_CAT 对我来说,不清楚星期几应该如何影响结果。也许如果你编辑你的问题并添加更多代码并为其提供更多上下文......我的猜测是星期几是不相关的,虽然
  • @Hugo NP。日期/时间/时区很复杂,即使在元数据中也是如此;-)
【解决方案4】:

首先,我建议使用处理不同时区和时间变化的库(如夏季 +1 小时等)。为此,我建议使用 JodaTime。

 Hours hoursDiff =  Hours.hoursBetween(start.toLocalDate(), end.toLocalDate()).getDays())

然后你就可以了

hoursDiff.getHours(); //returns and int

您可以像这样将日期转换为日期时间(参数中需要):

Date date = new Date();
DateTime dateTime = new DateTime(date);

<!-- https://mvnrepository.com/artifact/joda-time/joda-time -->
<dependency>
    <groupId>joda-time</groupId>
    <artifactId>joda-time</artifactId>
    <version>2.9.7</version>
</dependency>

【讨论】:

  • Joda-Time 项目现在处于维护模式。团队建议迁移到 java.time 类。
猜你喜欢
  • 2013-07-06
  • 1970-01-01
  • 2023-03-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-11-15
  • 1970-01-01
相关资源
最近更新 更多