我需要找到不。两个月之间的月份,包括两个月。我正在使用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);
}
}
预计: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);
}
}
预计:7月(1月,2月,3月,4月,5月,6月,7月) 实际:7
你看到同样的行为吗?有没有其他方法可以做到这一点?
由于
答案 0 :(得分:3)
就日历而言,您的问题是可以理解的:两次比较代表完整的一个月
但是java.time.temporal.ChronoUnit.between
并没有以这种方式推理,而是以完整单位。
根据其javadoc预期结果:
计算返回一个整数,表示数字 两个时间之间的完整单位。例如,金额 11:30到13:29之间的时间只有一个小时 一分钟不到两个小时。
由LocalDate.until
引用的ChronoUnit.between()
javadoc对您的案例更为明确,并且还提供了一个有关between()
与MONTHS
一起用作TemporalUnit
的有趣示例1}}:
计算返回一个整数,表示数字 两个日期之间的完整单位。例如,金额 2012-06-15和2012-08-14之间的月份仅为一个月 是短短两个月的一天。
在您的工作示例中:
LocalDate startDate=LocalDate.of(2018,01,31);
LocalDate endDate=LocalDate.of(2018,7,31);
long noOfMonths = MONTHS.between(startDate,endDate);
您有31 days of month / 31 days of month, on 6 months
=> 6 months
。
在你失败的例子中:
LocalDate startDate=LocalDate.of(2018,01,31);
LocalDate endDate=LocalDate.of(2018,6,30);
long noOfMonths = MONTHS.between(startDate,endDate);
您有30 days of month / 31 days of month, on 5 months
=> 4 months + 1 month short of 1 day
< => 4 months
(四舍五入)。
如果您要写下这样的内容:
LocalDate startDate=LocalDate.of(2018,01,30);
LocalDate endDate=LocalDate.of(2018,6,30);
long noOfMonths = MONTHS.between(startDate,endDate);
你会30 days of month / 30 days of month, on 5 months
=> 5 months
。
关于您的问题:
有没有其他方法可以做到这一点?
最简单:将日期设置为1
或两个日期中的任意相同数字
如果它不适合您的要求,您可以检查这两个日期是否是当月的最后一天,在这种情况下,将其设置为相同的月份值。
你可以这样写:
int lastMonthStart = startDate.getMonth()
.length(startDate.isLeapYear());
int lastMonthEnd = endDate.getMonth()
.length(endDate.isLeapYear());
if (startDate.getDayOfMonth() == lastMonthStart && endDate.getDayOfMonth() == lastMonthEnd) {
startDate = startDate.withDayOfMonth(1);
endDate = endDate.withDayOfMonth(1);
}
答案 1 :(得分:2)
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);
输出:
*********两个月之间的月数(含):6
这显然也消除了几个月长度不等的问题。
如果您已经拥有LocalDate
个对象,则可能值得转换:
LocalDate startDate = LocalDate.of(2018, Month.JANUARY, 31);
YearMonth startMonth = YearMonth.from(startDate);
另外,请勿使用两位数的01
一个月。对于1月它是有效的,但08
和09
都不适用于8月或9月,所以这是一个习惯性的习惯。 Java(以及许多其他语言)将以0
开头的数字理解为the other answer。
答案 2 :(得分:2)
另外两个答案by davidxxx和by Ole V.V.都是正确且信息丰富的。
我想补充一下在日期工作中常用的另一种方法,用于定义一段时间:半开放。在半开放方法中,开头是包含,而结尾是独占。因此,今年上半年(1月,2月,3月,4月和6月)的月份将从1月1日开始跟踪,但不包括 7月 1。
有时我们在日常生活中直观地使用这种方法。例如,如果一个孩子的教室从中午12:00到下午1点吃午饭,那么学生应该在的座位之前下午1点钟敲响。午休时间最长,但不包括下午1点。
我相信您会发现半开放方法的一致使用使您的代码更易于阅读,调试和维护。
现代 java.time 类在定义时间范围时使用此方法。
LocalDateRange
Ole V.V.的回答明智地建议你看YearMonth
课来帮助你的工作。
另一个有用的课程来自ThreeTen-Extra项目:org.threeten.extra.LocalDateRange
。您可以在一个对象中表示整个日期范围。
您可以使用开始日期和六个月的Period
进行实例化。
LocalDateRange ldr = LocalDateRange.of(
LocalDate.of( 2018 , Month.JANUARY , 1 ) ,
Period.ofMonths( 6 )
) ;
或指定开始和结束日期。
LocalDateRange ldr = LocalDateRange.of(
LocalDate.of( 2018 , Month.JANUARY , 1 ) ,
LocalDate.of( 2018 , Month.JULY , 1 ) // Half-open.
) ;
虽然我不推荐它,但如果你坚持使用完全封闭的方法,其中开头和结尾都是包含,LocalDateRange
确实提供了LocalDateRange.ofClosed()
。
LocalDateRange ldr = LocalDateRange.ofClosed( // `ofClosed` vs `of`.
LocalDate.of( 2018 , Month.JANUARY , 1 ) ,
YearMonth( 2018 , Month.JUNE ).atEndOfMonth() // Fully-closed.
) ;
您可以通过LocalDateRange
课程从Period
获得几个月的数量。
LocalDateRange ldr = LocalDateRange.of(
LocalDate.of( 2018 , Month.JANUARY , 1 ) ,
Period.ofMonths( 6 )
) ;
Period p = ldr.toPeriod() ;
long totalMonths = p.toTotalMonths() ; // 6
java.time框架内置于Java 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.Date
,Calendar
和& SimpleDateFormat
现在位于Joda-Time的maintenance mode项目建议迁移到java.time类。
要了解详情,请参阅Oracle Tutorial。并搜索Stack Overflow以获取许多示例和解释。规范是JSR 310。
您可以直接与数据库交换 java.time 对象。使用符合JDBC driver或更高版本的JDBC 4.2。不需要字符串,不需要java.sql.*
类。
从哪里获取java.time类?
ThreeTen-Extra项目使用其他类扩展java.time。该项目是未来可能添加到java.time的试验场。您可以在此处找到一些有用的课程,例如Interval
,YearWeek
,YearQuarter
和more。