使用LocalDateTime
或LocalDate
在指定日期添加月份的最干净解决方案是什么?
好的答案是In java.time, how is the result of adding a month calculated?和Find next occurrence of a day-of-week in JSR-310,所以我希望通过类似的清洁解决方案解决这个问题。
让我们假设用户选择一个月中的某一天(任何从1到31)进行某些定期任务。选择时间戳生成后,每月增加。仅存储当前月份的时间戳(没有上个月的跟踪,每月更新时间戳)和选定的某一天。
如果选择的日期 31日,我们从 1月31日开始,使用dateTime.plusMonths(1)
方法添加一个月,在2016年为 2月29日 。如果再次使用dateTime.plusMonths(1)
方法,则结果为 3月29日,而预期为 3月31日 。但是,从1日到28日,它可以工作几天。
每月31日的解决方法可能是使用dateTime.with(TemporalAdjusters.lastDayOfMonth())
,但这不包括从28日到30日的日期,也可能是一个月的最后几天。
lastDayOfMonth()
所选日期 30日: 1月30日, 2月29日的示例(plusMonth方法行为选择最后一天如果前一天比前一个月更高,则 3月31日(预计 30th 这是每月的选定日期)。此方法未考虑月中的选定日期。
当然这个问题有许多潜在的解决方案,其中包括一些检查和计算月和日之间的差异,但我正在寻找一种只有在可能的情况下使用java.time功能的解决方案。
我正在寻找一种方法,将日期移至下个月的选定日期。像dateTime.plusMonths(1).withDayOfMonth(selectedDayOfMonth)
一样。这会因为withDayOfMonth
覆盖plusMonths
对上一个有效日期的调整而对2月31日这样的日期抛出异常。
使用plusMonth
方法的迭代示例。在迭代中,从前一个值开始 - 想象递归。
+-----------+------------------+---------------+
| iteration | Day-of-month: 31 | Expected |
+-----------+------------------+---------------+
| 1 | January 31st | January 31st |
| 2 | February 29th | February 29th |
| 3 | March 29th | March 31st |
| 4 | April 29th | April 30th |
+-----------+------------------+---------------+
另一个工作示例,从1日到28日的月份。
+-----------+-------------------+--------------+
| iteration | Day-of-month: 4th | Expected |
+-----------+-------------------+--------------+
| 1 | January 4th | January 4th |
| 2 | February 4th | February 4th |
| 3 | March 4th | March 4th |
| 4 | April 4th | April 4th |
+-----------+-------------------+--------------+
一个月的第29天对于闰年是可以的,但是对于普通年份不是。
+-----------+--------------------+---------------+
| iteration | Day-of-month: 29th | Expected |
+-----------+--------------------+---------------+
| 1 | January 29th | January 29th |
| 2 | February 28th | February 28th |
| 3 | March 28th | March 29th |
| 4 | April 28th | April 29th |
+-----------+--------------------+---------------+
答案 0 :(得分:4)
将月份日期设置为min(selectedDayOfMonth, lastDayOfNextMonth)
public static LocalDate next(LocalDate current, int selectedDayOfMonth) {
LocalDate next = current.plusMonths(1);
return next.withDayOfMonth(Math.min(selectedDayOfMonth, next.lengthOfMonth()));
}
用法:
public static void test(int selectedDayOfMonth) {
LocalDate date = LocalDate.of(2001, Month.JANUARY, selectedDayOfMonth);
System.out.println(date);
for (int i = 0; i < 5; i++) {
date = next(date, selectedDayOfMonth);
System.out.println(date);
}
System.out.println();
}
test(4)
的输出:
2001-01-04
2001-02-04
2001-03-04
2001-04-04
2001-05-04
2001-06-04
test(29)
的输出:
2001-01-29
2001-02-28
2001-03-29
2001-04-29
2001-05-29
2001-06-29
test(31)
的输出:
2001-01-31
2001-02-28
2001-03-31
2001-04-30
2001-05-31
2001-06-30
答案 1 :(得分:0)
你可以尝试
myDate.plusDays(YearMonth.of(myDate.getYear, myDate.getMonth + 1).lengthOfMonth())
将下个月的天数添加到当前日期。这仍然会导致一些奇怪的行为,尽管像1月1日那样 - > 1月29日 - &gt; 3月1日,但在可能的情况下,它仍会一直恢复。
添加额外的支票,例如
myDate.plusDays(YearMonth.of(myDate.getYear, myDate.getMonth + (myDate.getDayOfMonth() > 15 ? 1 : 0)).lengthOfMonth())
会解决这个问题并使其更加一致。现在它将添加当月的天数,如果它在上半部分或下个月的天数,如果它在下半部分应该工作我认为。
答案 2 :(得分:0)
一种解决方案可能是花费最后一天并添加一天:
firstDayOfNextMonth = lastDayOfThisMonth.plusDays(1);
然后添加一个月:
firstDayOfNextNextMonth = firstDayOfNextMonth.plusMonths(1);
然后减去一天:
lastDayOfNextMonth = firstDayOfNextNextMonth.minusDays(1);
所以,你得到:
lastDayOfNextMonth = lastDayOfThisMonth.plusDays(1).plusMonths(1).minusDays(1);
您可以将其转换为方法:
LocalDate addMonthToDate(LocalDate date) {
return date.plusDays(1).plusMonths(1).minusDays(1);
}
答案 3 :(得分:0)
YearMonth
如果您希望将月末作为目标,请考虑月份而非特定日期。
YearMonth
类代表任何特定月份。
YearMonth ym = YearMonth.of( 2016 , 1 ); // January
您可以为该对象添加月份。
YearMonth nextMonth = ym.plusMonths( 1 ); // February
当您需要该月末的日期时,请致电atEndOfMonth
询问。
LocalDate ld = ym.atEndOfMonth();
顺便说一下,Month
enum有预定义的对象来代表一年中十二个月中的每一个,即一月到十二月。
YearMonth ym = YearMonth.of( 2016 , Month.JANUARY );
如果您想申请某个月(第29-31天)可能无效的特定日期,请尝试申请此类日期。如果抛出异常,则回到要求月末。
int dayOfMonth = 30;
LocalDate ld = null;
try {
ld = ym.atDay( dayOfMonth );
} catch ( DateTimeException e ) {
ld = ym.atEndOfMonth();
}
执行if( dayOfMonth > 28 )
而不是捕获异常。
答案 4 :(得分:-1)
我只是不得不自己解决此问题,尽管输入的内容略有不同,最终得到以下解决方案。正如其他人指出的那样,在这种情况下,YearMonth可能会有所帮助,但我认为它的真正威力在于将其用作TemporalAdjuster。就我而言,我需要保留从初始日期开始的每月的某天:
public static LocalDate next(LocalDate initial, LocalDate current) {
return initial.with(YearMonth.from(current.plusMonths(1)));
}
这将向前调整日期,同时保留从初始日期起的每月日期,但如有必要,将使用该月的最后一天。
如果只有一个月的日期,则可以从31天的任何月份开始:
public static LocalDate next(LocalDate current, int selectedDayOfMonth) {
return LocalDate.of(2020, 1, selectedDayOfMonth).with(YearMonth.from(current.plusMonths(1)));
}
或使用org.threeten的DayOfMonth:
public static LocalDate next(LocalDate current, int selectedDayOfMonth) {
return DayOfMonth.of(selectedDayOfMonth).atYearMonth(YearMonth.from(current.plusMonths(1)));
}