繁体   English   中英

如何获得几个日期之间正确的月份和天数?

[英]How to get right number of months and day between several dates?

我需要计算间隔列表之间正确的月数和天数:

31/03/2017  30/09/2017
01/10/2017  31/03/2018
01/04/2018  30/09/2018
01/10/2018  31/12/2019

如果我计算这些间隔之间的所有天数,然后除以30,我得到

MONTHS: 33 DAYS: 12

但我想要的是:

MONTHS: 33 DAYS: 1 (the only day to compute is the first day of first interval)

Ole VVAnswer是正确的,并且明智地使用了现代的java.time类。

LocalDateRange

作为奖励,我将提到将出色的ThreeTen-Extra库添加到您的项目中以访问LocalDateRange类。 此类将时间跨度表示为一对LocalDate对象。 这似乎与您的业务问题匹配。

LocalDate start = … ;
LocalDate stop = … ;
LocalDateRange range = LocalDateRange.of( start , stop ) ;

从此LocalDateRange对象中,您可以获取Ole VV在该Answer中使用的Period对象

Period period = range.toPeriod() ;

请注意, LocalDateRange具有方便进行比较的方法,例如abutscontainsoverlaps等等。

半开

您可能对如何处理月末/月初感到困惑。

通常,在日期时间处理中,最好使用Half-Open方法表示时间跨度。 在这种方法中,开始是包容性的,而结尾是排他性的

所以一个月开始的第一个月跑出来,但包括第一下个月。

LocalDateRange rangeOfMonthOfNovember2019 = 
    LocalDateRange.of(
        LocalDate.of( 2019 , Month.NOVEMBER , 1 ) ,
        LocalDate.of( 2019 , Month.DECEMBER , 1
    )
;

您可以询问每个LocalDateRange经过的天数: LocalDateRange::lengthInDays

DateTimeFormatter f = DateTimeFormatter.ofPattern( "dd/MM/uuuu" );
List < LocalDateRange > ranges = new ArrayList <>( 4 );
ranges.add(
        LocalDateRange.of(
                LocalDate.parse( "31/03/2017" , f ) ,
                LocalDate.parse( "30/09/2017" , f )
        )
);
ranges.add(
        LocalDateRange.of(
                LocalDate.parse( "01/10/2017" , f ) ,
                LocalDate.parse( "31/03/2018" , f )
        )
);
ranges.add(
        LocalDateRange.of(
                LocalDate.parse( "01/04/2018" , f ) ,
                LocalDate.parse( "30/09/2018" , f )
        )
);
ranges.add(
        LocalDateRange.of(
                LocalDate.parse( "01/10/2018" , f ) ,
                LocalDate.parse( "31/12/2019" , f )
        )
);

// Sum the periods, one from each range.
Period period = Period.ZERO;
int days = 0;
for ( LocalDateRange range : ranges )
{
    days = ( days + range.lengthInDays() );
    period = period.plus( range.toPeriod() ).normalized();
}

转储到控制台。

System.out.println( "ranges: " + ranges );
System.out.println( "days: " + days + "  |  pseudo-months: " + ( days / 30 ) + " and days: " + ( days % 30 ) );
System.out.println( "period: " + period );

范围:[2017-03-31 / 2017-09-30,2017-10-01 / 2018-03-31,2018-04-01 / 2018-09-30,2018-10-01 / 2019-12-31 ]

天:1002 | 伪月:33,天:12

期间:P2Y5M119D

如果您坚持全封闭而不是半开放,则LocalDateRange仍可以通过其ofClosed方法为您提供帮助。 但是我强烈建议您改用半开放式,因为我相信您会发现,在所有代码中使用一致的方法将使您的工作变得更轻松。 并教育您的用户。 我看到办公室工作人员在对包含/不包含日期进行错误的假设时感到很困惑。 您跟踪月末至月末的做法可能会引起越来越多的困惑。

YearMonth

您可能会发现有用的另一个类内置于Java中: YearMonth 此类表示整个特定月份。

YearMonth yearMonth = YearMonth.of( 2019 , Month.NOVEMBER ) ;

通知的方法,如atDay以产生LocalDateYearMonth

ISO 8601

提示:将日期时间值序列化为文本时,养成仅使用ISO 8601标准格式而不是本地化格式的习惯。

因此,对于日期,请使用YYYY-MM-DD。

解析/生成文本时, java.time类默认使用这些格式。 因此,无需指定格式化模式。

LocalDate.parse( "2019-01-23" ) 

整个月的标准ISO 8601格式为YYYY-MM,例如2019-11

YearMonth yearMonth = YearMonth.parse( "2019-11" ) ;  // Entire month of November 2019.

关于java.time

java.time框架内置于Java 8及更高版本中。 这些类取代了麻烦的旧的旧式日期时间类,例如java.util.DateCalendarSimpleDateFormat

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

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

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

在哪里获取java.time类?

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

您可以忽略之间的日期,而只需计算第一个日期与最后一个日期之间的差值即可:

    LocalDate first = LocalDate.of(2017, 3, 31);
    LocalDate lastInclusive = LocalDate.of(2019, 12, 31);
    LocalDate lastExclusive = lastInclusive.plusDays(1);
    Period between = Period.between(first, lastExclusive);

    System.out.format("Months: %s days: %d%n",
            between.toTotalMonths(), between.getDays());

在这种情况下,输出为:

月:33天:1

由于Period.between()计算差值到结束日期独家要包含的结束日期,我添加一天的结束日期。

月可以是28、29、30和31天,平均超过30天,这就是为什么除以30会给您太多的时间。 您可以阅读Period.between()的确切工作Period.between()并检查它是否始终为您提供所需的内容。

链接

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM