【问题标题】:How to get correct number of hours between Joda dates?如何在 Joda 日期之间获得正确的小时数?
【发布时间】:2013-11-08 14:41:32
【问题描述】:

我想获取两个日期之间的所有 Daylight Saving Time (DST) 小时。

这是我的示例代码:

public static void main(String[] args) {

    Date startDate = new Date();
    Calendar startCalendar = Calendar.getInstance();
    startCalendar.setTime(startDate);
    startCalendar.set(2014, 2, 1, 0, 0, 0);
    startCalendar.set(Calendar.MILLISECOND, 0);

    Date endDate = new Date();
    Calendar endCalendar = Calendar.getInstance();
    endCalendar.setTime(endDate);
    endCalendar.set(2014, 2, 31, 0, 0, 0);
    endCalendar.set(Calendar.MILLISECOND, 0);

    DateTime startDateTime = new DateTime(startCalendar);
    DateTime endDateTime = new DateTime(endCalendar).plusDays(1);

    Hours hours = Hours.hoursBetween(startDateTime, endDateTime);

    // actual is 744
    System.out.println("Expected: 743, actual: " + hours.getHours());
}

好吧,我显然遗漏了一些东西,但我无法发现我的错误。

【问题讨论】:

  • 使用 timeanddate.com/date/… 并加上 1 天 = 24 小时我得到 744。我不明白这一点
  • 743 是正确答案,因为 2014 年 3 月 9 日(在许多地方)只有 23 小时。

标签: java jodatime dst


【解决方案1】:

主要问题是您未能指定时区

当我在西雅图运行您的代码时,我得到了 2014 年 3 月的 743 小时。为什么?因为我的默认时区。在美国西海岸,Daylight Saving Time 于 2014 年 3 月 9 日星期日 02:00 开始。请参阅此页面,Time change dates in 2014。因此,第 9 天实际上是 23 小时而不是 24 小时。

但如果冰岛有人运行完全相同的代码,她会得到744。为什么?因为冰岛人太聪明了,不会理会夏令时的废话。

此外,作为一个好习惯,您应该在尝试使用天数时调用 Joda-Time 方法withTimeAtStartOfDay()。以前我们使用 Joda-Time 的 midnight 方法,但这些方法已被弃用,因为某些日历中的某些日子 do not have a midnight

提示:请注意以 standard 命名的 Joda-Time 方法,文档解释说这意味着假设每天 24 小时。换句话说,这些方法忽略了夏令时转换。

这是在 Java 7 中使用 Joda-Time 2.3 的一些示例代码。

// © 2013 Basil Bourque. This source code may be used freely forevery by anyone taking full responsibility for doing so.

// Joda-Time - The popular alternative to Sun/Oracle's notoriously bad date, time, and calendar classes bundled with Java 7 and earlier.
// http://www.joda.org/joda-time/

// Joda-Time will become outmoded by the JSR 310 Date and Time API introduced in Java 8.
// JSR 310 was inspired by Joda-Time but is not directly based on it.
// http://jcp.org/en/jsr/detail?id=310

// By default, Joda-Time produces strings in the standard ISO 8601 format.
// https://en.wikipedia.org/wiki/ISO_8601

// Time Zone list: http://joda-time.sourceforge.net/timezones.html
org.joda.time.DateTimeZone seattleTimeZone = org.joda.time.DateTimeZone.forID("America/Los_Angeles");
org.joda.time.DateTimeZone icelandTimeZone = org.joda.time.DateTimeZone.forID("Atlantic/Reykjavik");

// Switch between using 'seattleTimeZone' and 'icelandTimeZone' to see different results (23 vs 24).
org.joda.time.DateTime theNinth = new org.joda.time.DateTime( 2014, 3, 9, 0, 0, seattleTimeZone ) ; // Day when DST begins.
org.joda.time.DateTime theTenth = theNinth.plusDays( 1 ); // Day after DST begins.

// Using "hoursBetween()" method with a pair of DateTimes.
org.joda.time.Hours hoursObject = org.joda.time.Hours.hoursBetween( theNinth.withTimeAtStartOfDay(), theTenth.withTimeAtStartOfDay() );
int hoursInt = hoursObject.getHours();
System.out.println( "Expected 23 from hoursInt, got: " + hoursInt );

// Using an Interval.
org.joda.time.Interval interval = new Interval( theNinth.withTimeAtStartOfDay(), theTenth.withTimeAtStartOfDay() );
System.out.println( "Expected 23 from interval, got: " + org.joda.time.Hours.hoursIn(interval).getHours() );

// Using a Period with Standard days.
org.joda.time.Period period = new org.joda.time.Period( theNinth.withTimeAtStartOfDay(), theTenth.withTimeAtStartOfDay() );
org.joda.time.Hours standardHoursObject = period.toStandardHours();
System.out.println( "Expected 24 from standardHoursObject, got: " + standardHoursObject.getHours() );

【讨论】:

  • 明确指定时区并使用.withTimeAtStartOfDay() 会产生正确的结果
【解决方案2】:

欢迎来到日期/时间处理的混乱。你的问题是时区。具体来说,无论您处于哪个时区,都会遵守夏令时,并且在您的间隔期间会发生切换(弹簧向前),从而将其缩短一个小时。请参阅此代码。

public class DateTimeTest {
    public static void main(String[] args) {
    DateTime startDateTime = new DateTime()
        .withYear(2014)
        .withMonthOfYear(3)
        .withDayOfMonth(1)
        .withHourOfDay(0)
        .withMinuteOfHour(0)
        .withSecondOfMinute(0)
        .withMillisOfSecond(0)
        .withZone(DateTimeZone.forID("US/Eastern"));

    DateTime endDateTime = new DateTime()
        .withYear(2014)
        .withMonthOfYear(3)
        .withDayOfMonth(31)
        .withHourOfDay(0)
        .withMinuteOfHour(0)
        .withSecondOfMinute(0)
        .withMillisOfSecond(0)
        .withZone(DateTimeZone.forID("US/Eastern"))
        .plusDays(1);

    System.out.println("Expected 744, got: " 
        + Hours.hoursBetween(startDateTime, endDateTime).getHours());  // 743

    DateTime startUtc = startDateTime.withZoneRetainFields(DateTimeZone.UTC);
    DateTime endUtc = endDateTime.withZoneRetainFields(DateTimeZone.UTC);

    System.out.println("Expected 744, got: " 
        + Hours.hoursBetween(startUtc, endUtc).getHours());  // 744
    }
}

【讨论】:

  • toDateMidnight 已弃用。有替代品吗?
  • 我只是将它用作将分钟、秒和毫秒设置为 0 以保持代码简短的快捷方式。我针对 joda-time 1.6.2 编写了这个代码,它现在已经很老了。我没有意识到他们已经弃用它了。
  • 我也无法重现您的结果。它总是返回 744。我正在使用 Joda-Time-2.3
  • 尝试更新。第一对现在使用遵守 DST 的时区,第二对没有。很可能,您已经在时钟设置不同的几台不同的机器上运行了它。时区会让你为这样的事情发疯。通常最好对它们进行适当的控制。
  • 这是一个关于如何设置类型的工件。每DateTime.withZone():“返回具有不同时区的此日期时间的副本,保留毫秒瞬间。” .... 换句话说(正如您已经注意到的),当您当前的时区不是“目的地”时区时,您会得到不同的“本地”时间。这个例子要么需要使用DateTime.withZoneRetainFields()(它将保留目标区域的本地时间,但并非在所有情况下都是安全的),或者以更安全的时区方式设置(例如指定时区和本地时间在构造函数中)。
【解决方案3】:

我也想不通(但在 Joda 时代?),但你可以解决。毫秒时间的差除以3600000L 应该是准确的小时数。我试过了,得到了 743。

【讨论】:

  • 夏令时在美国从该月开始。所以你失去了一个小时。 ( 744 - 1 ) = 743
猜你喜欢
  • 2012-12-29
  • 2020-02-08
  • 2013-04-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-26
相关资源
最近更新 更多