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

Posted

技术标签:

【中文标题】Joda-Time 中两个日期之间的天数【英文标题】:Number of days between two dates in Joda-Time 【发布时间】:2011-04-17 16:52:42 【问题描述】:

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

Days.daysBetween(start, end).getDays() 如果开始在晚上并在早上结束,则给我 0。

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

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

【问题讨论】:

【参考方案1】:

令人讨厌的是,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(),对吗? @PatrickM 是的,我是。仔细想想,目前还不清楚这究竟是要强加什么限制,所以我将删除最后一句话。谢谢! @chrispy daysBetween doc 说它返回 WHOLE 天数。在我的情况下 2 天 1 小时应该返回我 3 天。在您的示例中,它返回 2 天。有没有办法做到这一点? @SujitJoshi 恐怕我不明白你的问题。我建议发布一个完整的 Stack Overflow 问题并在此处为我粘贴一个链接 :)【参考方案2】:

Days

使用Days 类和withTimeAtStartOfDay 方法应该可以工作:

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

【讨论】:

谢谢!我试图用 joda 实现 android.text.format.DateUtils.getRelativeTimeSpanString() 的行为,这真的很有用。 先生,关于这个post,日期时间类型的方法toDateMidnight()已被弃用。 你现在应该使用 .withTimeAtStartOfDay() 而不是 .toDateMidnight() 如果endstart 之前,会返回负数吗? @akhyar:你为什么不试试呢?【参考方案3】:

你可以使用LocalDate:

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

【讨论】:

先生,关于这个post,日期时间类型的方法toDateMidnight()已被弃用。 为什么使用new LocalDate(date) 而不是date.toLocalDate()【参考方案4】:

tl;博士

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 项目现在位于maintenance mode,团队建议迁移到java.time 类。

Joda-Time DateTime 的等价物是 ZonedDateTime

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

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

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

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

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

截断

至于您询问一种更通用的方法来计算您感兴趣的小时增量作为时钟时间而不是完整小时作为 60 分钟的时间跨度,请使用 @987654368 @方法。

这是您在同一天 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 及更高版本中。这些类取代了麻烦的旧 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。

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

【讨论】:

您的 TL;DR 几天使用 truncate 成为已接受答案中详述的错误的牺牲品,即当一天的开始时间是凌晨 1 点时,它是错误的。我已经使用 toLocalDate 更新了它以使用您进一步给出的正确答案。【参考方案5】:

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

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

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

【讨论】:

只有当您对“天”的定义正好是 24 小时长而不考虑时区和日期时,此答案才有效。如果是这样,请使用此方法。如果没有,请查看解决时区问题的其他答案。 @BasilBourque,你是对的,不过,我会寻求基于算术计算的解决方案,而不是为每个方法调用构建昂贵的对象,如果你是这样的计算,有很多口味从网页读取输入可能没问题,但如果您正在处理包含数十万行的日志文件,这只会使其非常慢 如果您的循环很热,Java 将优化对象分配。见docs.oracle.com/javase/7/docs/technotes/guides/vm/…【参考方案6】:

java.time.Period

java.time.Period 类用于count days。

由于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);

【讨论】:

【参考方案7】:

(KOTLIN) 对于固定日期和当前日期之间的差异 (Joda)

您可以使用Days.daysBetween(jodaDate1,jodaDate2)

这是一个例子:

        val dateTime: DateTime = DateTime.parse("14/09/2020",
            DateTimeFormat.forPattern("dd/MM/yyyy"))
        val currentDate = DateTime.now()
        //To calculate the days in between
        val dayCount = Days.daysBetween(dateTime,currentDate).days
        //Set Value to TextView 
        binding.daysCount.text = dayCount.toString()

【讨论】:

【参考方案8】:
DateTime  dt  = new DateTime(laterDate);        

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

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

【讨论】:

【参考方案9】:
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;

【讨论】:

问题是专门寻找joda-time。

以上是关于Joda-Time 中两个日期之间的天数的主要内容,如果未能解决你的问题,请参考以下文章

日期格式化:SimpleDateFormat线程不安全FastDateFormat和Joda-Time后两个都是线程安全

Joda-Time、夏令时更改和日期时间解析

Java日期计算之Joda-Time

Java日期简化工具Joda-Time

使用 Joda-Time 计算持续时间

JAVA秒会技术之Joda-Time满足你所有关于日期的处理