两个日期之间的天数

时间:2021-09-06 21:31:13

How do I find the difference in Days between two Joda-Time DateTime instances? With ‘difference in days’ I mean if start is on Monday and end is on Tuesday I expect a return value of 1 regardless of the hour/minute/seconds of the start and end dates.

我如何找到两个Joda-Time DateTime实例之间的时间差异?我的意思是,如果开始是在周一,结束是在周二,那么不管开始日期和结束日期的小时/分钟/秒是多少,返回值都是1。

Days.daysBetween(start, end).getDays() gives me 0 if start is in the evening and end in the morning.

天。如果开始是在晚上,结束是在早上,那么getdays()就是0。

I'm also having the same issue with other date fields so I was hoping there would be a generic way to 'ignore' the fields of lesser significance.

我对其他日期字段也有同样的问题,所以我希望有一种通用的方法来“忽略”不那么重要的字段。

In other words, the months between Feb and 4 March would also be 1, as would the hours between 14:45 and 15:12 be. However the hour difference between 14:01 and 14:55 would be 0.

换句话说,2月至3月4日之间的月份也是1,14点45分至15点12分之间的月份也是1。但是14:01和14:55之间的小时差是0。

6 个解决方案

#1


330  

Annoyingly, the withTimeAtStartOfDay answer is wrong, but only occasionally. You want:

令人恼火的是,“从何时开始到何时”的答案是错误的,但这只是偶尔的。你想要的:

Days.daysBetween(start.toLocalDate(), end.toLocalDate()).getDays()

It turns out that "midnight/start of day" sometimes means 1am (daylight savings happen this way in some places), which Days.daysBetween doesn't handle properly.

事实证明,“午夜/开始的一天”有时意味着凌晨1点(在某些地方,白天储蓄就是这样发生的),也就是白天。daysBetween处理不正常。

// 5am on the 20th to 1pm on the 21st, October 2013, Brazil
DateTimeZone BRAZIL = DateTimeZone.forID("America/Sao_Paulo");
DateTime start = new DateTime(2013, 10, 20, 5, 0, 0, BRAZIL);
DateTime end = new DateTime(2013, 10, 21, 13, 0, 0, BRAZIL);
System.out.println(daysBetween(start.withTimeAtStartOfDay(),
                               end.withTimeAtStartOfDay()).getDays());
// prints 0
System.out.println(daysBetween(start.toLocalDate(),
                               end.toLocalDate()).getDays());
// prints 1

Going via a LocalDate sidesteps the whole issue.

通过LocalDate绕过整个问题。

#2


181  

Days Class

Using the Days class with the withTimeAtStartOfDay method should work:

使用withTimeAtStartOfDay方法的Days类应该工作:

Days.daysBetween(start.withTimeAtStartOfDay() , end.withTimeAtStartOfDay() ).getDays() 

#3


81  

you can use LocalDate:

您可以使用LocalDate:

Days.daysBetween(new LocalDate(start), new LocalDate(end)).getDays() 

#4


6  

tl;dr

java.time.temporal.ChronoUnit.DAYS.between( 
    earlier.truncatedTo( ChronoUnit.DAYS )  , 
    later.truncatedTo( ChronoUnit.DAYS ) 
)

…or…

…或…

java.time.temporal.ChronoUnit.HOURS.between( 
    earlier.truncatedTo( ChronoUnit.HOURS )  , 
    later.truncatedTo( ChronoUnit.HOURS ) 
)

java.time

FYI, the Joda-Time project is now in maintenance mode, with the team advising migration to the java.time classes.

FYI, Joda-Time项目现在处于维护模式,团队建议迁移到java。时间类。

The equivalent of Joda-Time DateTime is ZonedDateTime.

与Joda-Time DateTime等价的是ZonedDateTime。

ZoneId z = ZoneId.of( "Pacific/Auckland" ) ;
ZonedDateTime now = ZonedDateTime.now( z ) ;

Apparently you want to count the days by dates, meaning you want to ignore the time of day. For example, starting a minute before midnight and ending a minute after midnight should result in a single day. For this behavior, extract a LocalDate from your ZonedDateTime. The LocalDate class represents a date-only value without time-of-day and without time zone.

显然,你想按日期计算天数,这意味着你想忽略一天的时间。例如,从午夜前的一分钟开始,到午夜后的一分钟结束,应该是一天。对于这个行为,从ZonedDateTime中提取一个LocalDate。LocalDate类表示一个没有时间和没有时区的日期值。

LocalDate localDateStart = zdtStart.toLocalDate() ;
LocalDate localDateStop = zdtStop.toLocalDate() ;

Use the ChronoUnit enum to calculate elapsed days or other units.

使用ChronoUnit enum计算运行天数或其他单元。

long days = ChronoUnit.DAYS.between( localDateStart , localDateStop ) ;

Truncate

As for you asking about a more general way to do this counting where you are interested the delta of hours as hour-of-the-clock rather than complete hours as spans-of-time of sixty minutes, use the truncatedTo method.

如果你想问一个更一般的方法来计算你感兴趣的小时数,而不是完全的小时数,60分钟,使用truncatedTo方法。

Here is your example of 14:45 to 15:12 on same day.

这是你在同一天的14:45到15:12的例子。

ZoneId z = ZoneId.of( "America/Montreal" ); 
ZonedDateTime start = ZonedDateTime.of( 2017 , 1 , 17 , 14 , 45 , 0 , 0 , z );
ZonedDateTime stop = ZonedDateTime.of( 2017 , 1 , 17 , 15 , 12 , 0 , 0 , z );

long hours = ChronoUnit.HOURS.between( start.truncatedTo( ChronoUnit.HOURS ) , stop.truncatedTo( ChronoUnit.HOURS ) );

1

1

For a count of days by dates, truncate to ChronoUnit.DAYS. Here is an example rolling over midnight from five minutes before to five minutes after, for elapsed days of 1.

按日期计算天数,截断为按时间计算天数。这里有一个例子,从午夜前5分钟滚动到午夜后5分钟,持续了1天。

ZoneId z = ZoneId.of( "America/Montreal" );
ZonedDateTime start = ZonedDateTime.of( 2017 , 1 , 17 , 23 , 55 , 0 , 0 , z );
ZonedDateTime stop = ZonedDateTime.of( 2017 , 1 , 18 , 00 , 05 , 0 , 0 , z );

long days = ChronoUnit.DAYS.between( start.truncatedTo( ChronoUnit.DAYS ) , stop.truncatedTo( ChronoUnit.DAYS ) );

1

1


About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

java。time框架被构建到Java 8以及以后的版本中。这些类取代了麻烦的旧遗留日期时间类(如java.util)。日期,日历,& SimpleDateFormat。

The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

现在处于维护模式的Joda-Time项目建议迁移到java。时间类。

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

要了解更多信息,请参阅Oracle教程。和搜索堆栈溢出的许多例子和解释。规范是JSR 310。

You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.

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

Where to obtain the java.time classes?

在哪里获得java。时间类?

  • Java SE 8, Java SE 9, Java SE 10, and later
    • Built-in.
    • 内置的。
    • Part of the standard Java API with a bundled implementation.
    • 带有捆绑实现的标准Java API的一部分。
    • Java 9 adds some minor features and fixes.
    • Java 9添加了一些次要的特性和修复。
  • Java SE 8, Java SE 9, Java SE 10,以及后来的内置。带有捆绑实现的标准Java API的一部分。Java 9添加了一些次要的特性和修复。
  • Java SE 6 and Java SE 7
    • Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
    • 大部分java。时间功能在三个回端移植到Java 6和7。
  • Java SE 6和Java SE 7大部分的Java。时间功能在三个回端移植到Java 6和7。
  • Android
    • Later versions of Android bundle implementations of the java.time classes.
    • java的Android bundle实现的后续版本。时间类。
    • For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
    • 对于早期的Android (<26), ThreeTenABP项目适用于three - backport(如上所述)。看到如何使用ThreeTenABP ....
  • Android后期版本的java捆绑包实现。时间类。对于早期的Android (<26), ThreeTenABP项目适用于three - backport(如上所述)。看到如何使用ThreeTenABP ....

The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

三个额外的项目扩展了java。时间和额外的类。这个项目是java.time未来可能增加的一个试验场。你可能会在这里找到一些有用的课程,比如时间间隔、YearWeek、YearQuarter等等。

The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

三个额外的项目扩展了java。时间和额外的类。这个项目是java.time未来可能增加的一个试验场。你可能会在这里找到一些有用的课程,比如时间间隔、YearWeek、YearQuarter等等。

#5


2  

The accepted answer builds two LocalDate objects, which are quite expensive if you are reading lot of data. I use this:

所接受的答案构建两个LocalDate对象,如果您正在读取大量数据,那么这些对象将非常昂贵。我用这个:

  public static int getDaysBetween(DateTime earlier, DateTime later)
  {
    return (int) TimeUnit.MILLISECONDS.toDays(later.getMillis()- earlier.getMillis());
  }

By calling getMillis() you use already existing variables.
MILLISECONDS.toDays() then, uses a simple arithmetic calculation, does not create any object.

通过调用getMillis(),您可以使用已经存在的变量。然后,使用简单的算术计算,不会创建任何对象。

#6


-11  

public static int getDifferenceIndays(long timestamp1, long timestamp2) {
    final int SECONDS = 60;
    final int MINUTES = 60;
    final int HOURS = 24;
    final int MILLIES = 1000;
    long temp;
    if (timestamp1 < timestamp2) {
        temp = timestamp1;
        timestamp1 = timestamp2;
        timestamp2 = temp;
    }
    Calendar startDate = Calendar.getInstance(TimeZone.getDefault());
    Calendar endDate = Calendar.getInstance(TimeZone.getDefault());
    endDate.setTimeInMillis(timestamp1);
    startDate.setTimeInMillis(timestamp2);
    if ((timestamp1 - timestamp2) < 1 * HOURS * MINUTES * SECONDS * MILLIES) {
        int day1 = endDate.get(Calendar.DAY_OF_MONTH);
        int day2 = startDate.get(Calendar.DAY_OF_MONTH);
        if (day1 == day2) {
            return 0;
        } else {
            return 1;
        }
    }
    int diffDays = 0;
    startDate.add(Calendar.DAY_OF_MONTH, diffDays);
    while (startDate.before(endDate)) {
        startDate.add(Calendar.DAY_OF_MONTH, 1);
        diffDays++;
    }
    return diffDays;
}

#1


330  

Annoyingly, the withTimeAtStartOfDay answer is wrong, but only occasionally. You want:

令人恼火的是,“从何时开始到何时”的答案是错误的,但这只是偶尔的。你想要的:

Days.daysBetween(start.toLocalDate(), end.toLocalDate()).getDays()

It turns out that "midnight/start of day" sometimes means 1am (daylight savings happen this way in some places), which Days.daysBetween doesn't handle properly.

事实证明,“午夜/开始的一天”有时意味着凌晨1点(在某些地方,白天储蓄就是这样发生的),也就是白天。daysBetween处理不正常。

// 5am on the 20th to 1pm on the 21st, October 2013, Brazil
DateTimeZone BRAZIL = DateTimeZone.forID("America/Sao_Paulo");
DateTime start = new DateTime(2013, 10, 20, 5, 0, 0, BRAZIL);
DateTime end = new DateTime(2013, 10, 21, 13, 0, 0, BRAZIL);
System.out.println(daysBetween(start.withTimeAtStartOfDay(),
                               end.withTimeAtStartOfDay()).getDays());
// prints 0
System.out.println(daysBetween(start.toLocalDate(),
                               end.toLocalDate()).getDays());
// prints 1

Going via a LocalDate sidesteps the whole issue.

通过LocalDate绕过整个问题。

#2


181  

Days Class

Using the Days class with the withTimeAtStartOfDay method should work:

使用withTimeAtStartOfDay方法的Days类应该工作:

Days.daysBetween(start.withTimeAtStartOfDay() , end.withTimeAtStartOfDay() ).getDays() 

#3


81  

you can use LocalDate:

您可以使用LocalDate:

Days.daysBetween(new LocalDate(start), new LocalDate(end)).getDays() 

#4


6  

tl;dr

java.time.temporal.ChronoUnit.DAYS.between( 
    earlier.truncatedTo( ChronoUnit.DAYS )  , 
    later.truncatedTo( ChronoUnit.DAYS ) 
)

…or…

…或…

java.time.temporal.ChronoUnit.HOURS.between( 
    earlier.truncatedTo( ChronoUnit.HOURS )  , 
    later.truncatedTo( ChronoUnit.HOURS ) 
)

java.time

FYI, the Joda-Time project is now in maintenance mode, with the team advising migration to the java.time classes.

FYI, Joda-Time项目现在处于维护模式,团队建议迁移到java。时间类。

The equivalent of Joda-Time DateTime is ZonedDateTime.

与Joda-Time DateTime等价的是ZonedDateTime。

ZoneId z = ZoneId.of( "Pacific/Auckland" ) ;
ZonedDateTime now = ZonedDateTime.now( z ) ;

Apparently you want to count the days by dates, meaning you want to ignore the time of day. For example, starting a minute before midnight and ending a minute after midnight should result in a single day. For this behavior, extract a LocalDate from your ZonedDateTime. The LocalDate class represents a date-only value without time-of-day and without time zone.

显然,你想按日期计算天数,这意味着你想忽略一天的时间。例如,从午夜前的一分钟开始,到午夜后的一分钟结束,应该是一天。对于这个行为,从ZonedDateTime中提取一个LocalDate。LocalDate类表示一个没有时间和没有时区的日期值。

LocalDate localDateStart = zdtStart.toLocalDate() ;
LocalDate localDateStop = zdtStop.toLocalDate() ;

Use the ChronoUnit enum to calculate elapsed days or other units.

使用ChronoUnit enum计算运行天数或其他单元。

long days = ChronoUnit.DAYS.between( localDateStart , localDateStop ) ;

Truncate

As for you asking about a more general way to do this counting where you are interested the delta of hours as hour-of-the-clock rather than complete hours as spans-of-time of sixty minutes, use the truncatedTo method.

如果你想问一个更一般的方法来计算你感兴趣的小时数,而不是完全的小时数,60分钟,使用truncatedTo方法。

Here is your example of 14:45 to 15:12 on same day.

这是你在同一天的14:45到15:12的例子。

ZoneId z = ZoneId.of( "America/Montreal" ); 
ZonedDateTime start = ZonedDateTime.of( 2017 , 1 , 17 , 14 , 45 , 0 , 0 , z );
ZonedDateTime stop = ZonedDateTime.of( 2017 , 1 , 17 , 15 , 12 , 0 , 0 , z );

long hours = ChronoUnit.HOURS.between( start.truncatedTo( ChronoUnit.HOURS ) , stop.truncatedTo( ChronoUnit.HOURS ) );

1

1

For a count of days by dates, truncate to ChronoUnit.DAYS. Here is an example rolling over midnight from five minutes before to five minutes after, for elapsed days of 1.

按日期计算天数,截断为按时间计算天数。这里有一个例子,从午夜前5分钟滚动到午夜后5分钟,持续了1天。

ZoneId z = ZoneId.of( "America/Montreal" );
ZonedDateTime start = ZonedDateTime.of( 2017 , 1 , 17 , 23 , 55 , 0 , 0 , z );
ZonedDateTime stop = ZonedDateTime.of( 2017 , 1 , 18 , 00 , 05 , 0 , 0 , z );

long days = ChronoUnit.DAYS.between( start.truncatedTo( ChronoUnit.DAYS ) , stop.truncatedTo( ChronoUnit.DAYS ) );

1

1


About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

java。time框架被构建到Java 8以及以后的版本中。这些类取代了麻烦的旧遗留日期时间类(如java.util)。日期,日历,& SimpleDateFormat。

The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

现在处于维护模式的Joda-Time项目建议迁移到java。时间类。

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

要了解更多信息,请参阅Oracle教程。和搜索堆栈溢出的许多例子和解释。规范是JSR 310。

You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.

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

Where to obtain the java.time classes?

在哪里获得java。时间类?

  • Java SE 8, Java SE 9, Java SE 10, and later
    • Built-in.
    • 内置的。
    • Part of the standard Java API with a bundled implementation.
    • 带有捆绑实现的标准Java API的一部分。
    • Java 9 adds some minor features and fixes.
    • Java 9添加了一些次要的特性和修复。
  • Java SE 8, Java SE 9, Java SE 10,以及后来的内置。带有捆绑实现的标准Java API的一部分。Java 9添加了一些次要的特性和修复。
  • Java SE 6 and Java SE 7
    • Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
    • 大部分java。时间功能在三个回端移植到Java 6和7。
  • Java SE 6和Java SE 7大部分的Java。时间功能在三个回端移植到Java 6和7。
  • Android
    • Later versions of Android bundle implementations of the java.time classes.
    • java的Android bundle实现的后续版本。时间类。
    • For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
    • 对于早期的Android (<26), ThreeTenABP项目适用于three - backport(如上所述)。看到如何使用ThreeTenABP ....
  • Android后期版本的java捆绑包实现。时间类。对于早期的Android (<26), ThreeTenABP项目适用于three - backport(如上所述)。看到如何使用ThreeTenABP ....

The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

三个额外的项目扩展了java。时间和额外的类。这个项目是java.time未来可能增加的一个试验场。你可能会在这里找到一些有用的课程,比如时间间隔、YearWeek、YearQuarter等等。

The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

三个额外的项目扩展了java。时间和额外的类。这个项目是java.time未来可能增加的一个试验场。你可能会在这里找到一些有用的课程,比如时间间隔、YearWeek、YearQuarter等等。

#5


2  

The accepted answer builds two LocalDate objects, which are quite expensive if you are reading lot of data. I use this:

所接受的答案构建两个LocalDate对象,如果您正在读取大量数据,那么这些对象将非常昂贵。我用这个:

  public static int getDaysBetween(DateTime earlier, DateTime later)
  {
    return (int) TimeUnit.MILLISECONDS.toDays(later.getMillis()- earlier.getMillis());
  }

By calling getMillis() you use already existing variables.
MILLISECONDS.toDays() then, uses a simple arithmetic calculation, does not create any object.

通过调用getMillis(),您可以使用已经存在的变量。然后,使用简单的算术计算,不会创建任何对象。

#6


-11  

public static int getDifferenceIndays(long timestamp1, long timestamp2) {
    final int SECONDS = 60;
    final int MINUTES = 60;
    final int HOURS = 24;
    final int MILLIES = 1000;
    long temp;
    if (timestamp1 < timestamp2) {
        temp = timestamp1;
        timestamp1 = timestamp2;
        timestamp2 = temp;
    }
    Calendar startDate = Calendar.getInstance(TimeZone.getDefault());
    Calendar endDate = Calendar.getInstance(TimeZone.getDefault());
    endDate.setTimeInMillis(timestamp1);
    startDate.setTimeInMillis(timestamp2);
    if ((timestamp1 - timestamp2) < 1 * HOURS * MINUTES * SECONDS * MILLIES) {
        int day1 = endDate.get(Calendar.DAY_OF_MONTH);
        int day2 = startDate.get(Calendar.DAY_OF_MONTH);
        if (day1 == day2) {
            return 0;
        } else {
            return 1;
        }
    }
    int diffDays = 0;
    startDate.add(Calendar.DAY_OF_MONTH, diffDays);
    while (startDate.before(endDate)) {
        startDate.add(Calendar.DAY_OF_MONTH, 1);
        diffDays++;
    }
    return diffDays;
}