Joda-Time中两个日期之间的天数


335

如何找到两个Joda-Time DateTime实例之间的天数差异?“天数差异”是指如果开始时间是星期一,结束时间是星期二,则无论返回日期和开始时间/结束日期的小时/分钟/秒如何,我都希望返回值为1。

Days.daysBetween(start, end).getDays() 如果开始时间是晚上,结束时间是上午,则给我0。

我在其他日期字段上也遇到了同样的问题,因此我希望有一种通用的方法可以“忽略”重要性较低的字段。

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

Answers:


393

令人讨厌的是,withTimeAtStartOfDay答案是错误的,但只是偶尔出现。你要:

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

事实证明,“午夜/一天的开始时间”有时是指凌晨1点(某些地方以这种方式实现夏令时),而Days.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

通过走出去LocalDate回避了整个问题。


您能否提供一个示例案例,其中包含您认为Days.daysBetween不正确的特定数据?
罗勒·布尔克

当您说使用时Instant,您不仅在谈论start.toInstant(),是吗?
Patrick M

@PatrickM是的,我是。关于反思,目前尚不清楚这是要施加什么约束,因此我将删除最后一句话。谢谢!
爱丽丝·珀赛尔

@chrispy daysBetween医生说,返回的数量WHOLE天。就我而言,2天零1个小时应该让我3天。在您的示例中,它返回2天。有没有办法做到这一点?
Sujit Joshi

@SujitJoshi恐怕我不明白你的问题。我建议发布完整的Stack Overflow问题并在此处为我粘贴一个链接:)
Alice Purcell

186

Days

Days类与withTimeAtStartOfDay方法一起使用应该可以:

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

1
谢谢!我试图用joda实现android.text.format.DateUtils.getRelativeTimeSpanString()的行为,这确实很有用。
gosho_ot_pochivka 2013年

1
主席先生,关于此帖子toDateMidnight()不建议使用DateTime类型的方法。
Aniket Kulkarni

2
您现在应该使用.withTimeAtStartOfDay()而不是.toDateMidnight()
bgolson 2013年

2
如果end之前的start,会返回负数天怎么办?
akhy 2015年

7
@akhyar:为什么不尝试呢?
Michael Borgwardt'2

86

您可以使用LocalDate

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

主席先生,关于此帖子toDateMidnight()不建议使用DateTime类型的方法。
Aniket Kulkarni

为什么用完new LocalDate(date)date.toLocalDate()
SARose17年

9

tl; dr

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

…要么…

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

java.time

仅供参考,Joda-Time项目现在处于维护模式,团队建议迁移到java.time类。

乔达-时间的等效DateTimeZonedDateTime

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

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

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

使用ChronoUnit枚举来计算经过的天数或其他单位。

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

截短

至于您要问的是一种更通用的计数方法,请使用该方法,在该方法中,您对小时增量表示为小时,而不是完整小时数表示为60分钟truncatedTo

这是您当天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个

这几天无法使用。在这种情况下,请使用toLocalDate()。


关于java.time

java.time框架是建立在Java 8和更高版本。这些类取代麻烦的老传统日期时间类,如java.util.DateCalendar,和SimpleDateFormat

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

要了解更多信息,请参见Oracle教程。并在Stack Overflow中搜索许多示例和说明。规格为JSR 310

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

在哪里获取java.time类?

ThreeTen-额外项目与其他类扩展java.time。该项目为将来可能向java.time添加内容提供了一个试验场。你可能在这里找到一些有用的类,比如IntervalYearWeekYearQuarter,和更多

ThreeTen-额外项目与其他类扩展java.time。该项目为将来可能向java.time添加内容提供了一个试验场。你可能在这里找到一些有用的类,比如IntervalYearWeekYearQuarter,和更多


您的TL; DR使用truncate的几天时间会成为接受的答案中详细说明的错误的牺牲品,即,当一天的凌晨1点时,它会被一对一关闭。我已更新它,以使用toLocalDate使用您进一步给出的正确答案。
爱丽丝·珀赛尔

2

可接受的答案将生成两个LocalDate对象,如果您正在读取大量数据,则这将是非常昂贵的。我用这个:

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

通过调用,getMillis()您可以使用已有的变量。
MILLISECONDS.toDays()然后,使用简单的算术计算,不创建任何对象。


1
仅当您对“天”的定义恰好是24小时长而没有考虑时区和日期时,此答案才有效。如果是这样,请使用此方法。如果不是,请查看针对时区的其他答案。
罗勒·布尔克

@BasilBourque,你是对的,不过,我还是会寻求一种基于算术计算的解决方案,而不是为每个方法调用构建昂贵的对象,如果您正在从网页中读取输入内容,那么这种计算就有很多选择,也许还可以,但是如果您正在处理包含成千上万行的日志文件,这只会使其非常慢
JBoy

1
如果循环很热,Java将优化对象分配。请参阅docs.oracle.com/javase/7/docs/technotes/guides/vm/…–
爱丽丝·珀塞尔

1

java.time.Period

java.time.Period全班数天

由于Java 8计算差值更直观使用LocalDateLocalDateTime来表示两个日期

    LocalDate now = LocalDate.now();
    LocalDate inputDate = LocalDate.of(2018, 11, 28);

    Period period = Period.between( inputDate, now);
    int diff = period.getDays();
    System.out.println("diff = " + diff);

0
DateTime  dt  = new DateTime(laterDate);        

DateTime newDate = dt.minus( new  DateTime ( previousDate ).getMillis());

System.out.println("No of days : " + newDate.getDayOfYear() - 1 );    

-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
问题是专门寻找乔达时间。
kapad 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.