Java 8 - ChronoUnit.MONTHS.between(fromDate,toDate) 未按预期工作

Dee*_*boy 5 datetime duration java-8

我需要找到没有。两个日期之间的月份数,包括两个月份。我正在使用 ChronoUnit.Months.between,但得到了意想不到的结果。

代码适用于:1 月 31 日 - 7 月 31 日

失败日期:1 月 31 日 - 6 月 30 日

失败的代码:

import java.time.LocalDate;
import static java.time.temporal.ChronoUnit.MONTHS;
public class HelloWorld{

     public static void main(String []args){
        LocalDate startDate=LocalDate.of(2018,01,31);
        LocalDate endDate=LocalDate.of(2018,6,30);
        //Both dates inclusive, hence adding 1
        long noOfMonths = (MONTHS.between(startDate,endDate)) + 1L;
        System.out.println(" ********* No. of months between the two dates : " + noOfMonths);
        }
}
Run Code Online (Sandbox Code Playgroud)

预期:6(1 月、2 月、3 月、4 月、5 月、6 月)实际:5

有效的代码:

import java.time.LocalDate;
import static java.time.temporal.ChronoUnit.MONTHS;
public class HelloWorld{

     public static void main(String []args){
        LocalDate startDate=LocalDate.of(2018,01,31);
        LocalDate endDate=LocalDate.of(2018,7,31);
        //Both dates inclusive, hence adding 1
        long noOfMonths = (MONTHS.between(startDate,endDate)) + 1L;
        System.out.println(" ********* No. of months between the two dates : " + noOfMonths);
        }
}
Run Code Online (Sandbox Code Playgroud)

预计:7(一月、二月、三月、四月、五月、六月、七月)实际:7

你看到同样的行为吗?有没有其他方法可以做到这一点?

谢谢

Ole*_*.V. 5

另一个答案davidxxx已经解释了为什么您的代码给出了意外结果。请允许我补充一点,如果您对月份而不是日期感兴趣,那么该YearMonth课程是您可以使用的正确且正确的课程:

        YearMonth startMonth = YearMonth.of(2018, Month.JANUARY);
        YearMonth endMonth = YearMonth.of(2018, Month.JUNE);
        // Both months inclusive, hence adding 1
        long noOfMonths = ChronoUnit.MONTHS.between(startMonth, endMonth) + 1;
        System.out.println(" ********* No. of months between the two months (inclusive): " + noOfMonths);
Run Code Online (Sandbox Code Playgroud)

输出:

********* 两个月(含)之间的月数:6

这显然也消除了月份长度不等所带来的问题。

如果您已经有LocalDate对象,则可能值得转换:

        LocalDate startDate = LocalDate.of(2018, Month.JANUARY, 31);
        YearMonth startMonth = YearMonth.from(startDate);
Run Code Online (Sandbox Code Playgroud)

顺便说01一句,一个月不要使用两位数。一月它的工作原理,但也08还是09会为八月或九月上班,所以这是一个坏习惯收购。Java(和许多其他语言)理解0八进制数字开头的数字


Bas*_*que 5

半开

davidxxxOle VV的另外两个答案都是正确且信息丰富的。

我想补充一种在日期时间工作中常用的用于定义时间跨度的替代方法:半开放。在半开方法中,开始是包容性的,而结束是排斥性的。因此,上半年的月份(1 月、2 月、3 月、4 月和 6 月)被跟踪为从 1 月 1 日开始,一直到(但不包括)7 月1 日。

有时我们会在日常生活中直观地使用这种方法。例如,如果一个班级的孩子们从中午 12:00 到下午 1 点休息吃午饭,那么学生应该在下午 1 点钟声响起之前就座。午休时间持续到(但不包括)下午 1 点。

我相信您会发现对半开放方法的一致使用使您的代码更易于阅读、调试和维护。

现代java.time类使用这种方法来定义时间跨度。

LocalDateRange

Ole VV 的答案明智地建议你看看YearMonth课堂来帮助你的工作。

另一个有用的类来自ThreeTen-Extra项目:org.threeten.extra.LocalDateRange. 您可以在单个对象中表示整个日期范围。

您可以使用开始日期和Period六个月的时间进行实例化。

LocalDateRange ldr = LocalDateRange.of( 
    LocalDate.of( 2018 , Month.JANUARY , 1 ) ,
    Period.ofMonths( 6 ) 
) ;
Run Code Online (Sandbox Code Playgroud)

或者指定开始和停止日期。

LocalDateRange ldr = LocalDateRange.of( 
    LocalDate.of( 2018 , Month.JANUARY , 1 ) ,
    LocalDate.of( 2018 , Month.JULY , 1 ) // Half-open.
) ;
Run Code Online (Sandbox Code Playgroud)

虽然我不推荐它,但如果您坚持使用完全封闭的方法,其中开始和结束都包含,则LocalDateRange确实提供LocalDateRange.ofClosed().

LocalDateRange ldr = LocalDateRange.ofClosed(     // `ofClosed` vs `of`.
    LocalDate.of( 2018 , Month.JANUARY , 1 ) ,
    YearMonth( 2018 , Month.JUNE ).atEndOfMonth() // Fully-closed.
) ;
Run Code Online (Sandbox Code Playgroud)

您可以LocalDateRange通过Period课程从 a 获得月份数。

LocalDateRange ldr = LocalDateRange.of( 
    LocalDate.of( 2018 , Month.JANUARY , 1 ) ,
    Period.ofMonths( 6 ) 
) ;
Period p = ldr.toPeriod() ; 
long totalMonths = p.toTotalMonths() ;  // 6
Run Code Online (Sandbox Code Playgroud)

关于java.time

java.time框架是建立在Java 8和更高版本。这些类取代了麻烦的旧的遗留日期时间类,例如java.util.Date, Calendar, & 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,和更多