[英]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 VV的Answer是正确的,并且明智地使用了现代的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
具有方便进行比较的方法,例如abuts
, contains
, overlaps
等等。
您可能对如何处理月末/月初感到困惑。
通常,在日期时间处理中,最好使用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
以产生LocalDate
从YearMonth
。
提示:将日期时间值序列化为文本时,养成仅使用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 8及更高版本中。 这些类取代了麻烦的旧的旧式日期时间类,例如java.util.Date
, Calendar
和SimpleDateFormat
。
要了解更多信息,请参见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中添加内容提供了一个试验场。 您可以在这里找到一些有用的类,比如Interval
, YearWeek
, YearQuarter
,和更多 。
您可以忽略之间的日期,而只需计算第一个日期与最后一个日期之间的差值即可:
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()
并检查它是否始终为您提供所需的内容。
链接
Period.bewteen()
文档
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.