使用 java.time 将月份添加到每月的特定日期
Adding month to specific day of month with java.time
使用 LocalDateTime
或 LocalDate
将月份添加到指定日期的最干净的解决方案是什么?
很好的答案是 In java.time, how is the result of adding a month calculated? and Find next occurrence of a day-of-week in JSR-310 所以我希望这个问题可以用类似的干净解决方案来解决。
假设用户选择一个月中的某一天(从 1 到 31 中的任意一天)执行某个周期性任务。选择时间戳后生成并每月增加。仅存储当前月份的时间戳(没有上个月的轨迹,时间戳每月更新)和所选日期。
如果选择的日期是 31st 并且我们从 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 日(预期是 30 日 这个被选中月中的某天)。此方法不考虑一个月中选定的几天。
当然这个问题有很多潜在的解决方案,包括一些检查和计算月和日之间的差异,但我正在寻找一个解决方案,只有在可能的情况下才使用 java.time 功能。
澄清
我正在寻找一种方法来将日期移到下个月的选定日期。喜欢dateTime.plusMonths(1).withDayOfMonth(selectedDayOfMonth)
。这将引发像 2 月 31 日这样的日期的异常,因为 withDayOfMonth
覆盖 plusMonths
对最后有效日期的调整。
使用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 |
+-----------+--------------------+---------------+
你可以试试
myDate.plusDays(YearMonth.of(myDate.getYear, myDate.getMonth + 1).lengthOfMonth())
将下个月的天数添加到当前日期。这仍然会在月初导致一些奇怪的行为,比如 1 月 1 日 -> 1 月 29 日 -> 3 月 1 日,但它仍然会在可能的情况下始终自行恢复。
添加额外的检查,例如
myDate.plusDays(YearMonth.of(myDate.getYear, myDate.getMonth + (myDate.getDayOfMonth() > 15 ? 1 : 0)).lengthOfMonth())
会解决这个问题并使其更一致地工作。现在它将添加当月的天数,如果它在上半年,或者如果它在下半年,我认为应该工作的下个月的天数。
一个解决方案可能是取最后一天并添加一天:
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);
}
YearMonth
如果您希望以月末为目标,请考虑月份而不是特定日期。
YearMonth
class 代表任何特定的月份。
YearMonth ym = YearMonth.of( 2016 , 1 ); // January
您可以向该对象添加月份。
YearMonth nextMonth = ym.plusMonths( 1 ); // February
当您需要该月的月底日期时,请致电atEndOfMonth
。
LocalDate ld = ym.atEndOfMonth();
顺便说一下,Month
枚举有预定义的对象来表示一年中的十二个月,即一月到十二月。
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();
}
Of 执行 if( dayOfMonth > 28 )
而不是捕获异常。
将月中的第几天设置为 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
我不得不自己解决这个问题,虽然输入略有不同,但最终得到了以下解决方案。正如其他人指出的那样,YearMonth can be helpful in this case, but I think its true power is its use as a 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)));
}
使用 LocalDateTime
或 LocalDate
将月份添加到指定日期的最干净的解决方案是什么?
很好的答案是 In java.time, how is the result of adding a month calculated? and Find next occurrence of a day-of-week in JSR-310 所以我希望这个问题可以用类似的干净解决方案来解决。
假设用户选择一个月中的某一天(从 1 到 31 中的任意一天)执行某个周期性任务。选择时间戳后生成并每月增加。仅存储当前月份的时间戳(没有上个月的轨迹,时间戳每月更新)和所选日期。
如果选择的日期是 31st 并且我们从 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 日(预期是 30 日 这个被选中月中的某天)。此方法不考虑一个月中选定的几天。
当然这个问题有很多潜在的解决方案,包括一些检查和计算月和日之间的差异,但我正在寻找一个解决方案,只有在可能的情况下才使用 java.time 功能。
澄清
我正在寻找一种方法来将日期移到下个月的选定日期。喜欢dateTime.plusMonths(1).withDayOfMonth(selectedDayOfMonth)
。这将引发像 2 月 31 日这样的日期的异常,因为 withDayOfMonth
覆盖 plusMonths
对最后有效日期的调整。
使用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 |
+-----------+--------------------+---------------+
你可以试试
myDate.plusDays(YearMonth.of(myDate.getYear, myDate.getMonth + 1).lengthOfMonth())
将下个月的天数添加到当前日期。这仍然会在月初导致一些奇怪的行为,比如 1 月 1 日 -> 1 月 29 日 -> 3 月 1 日,但它仍然会在可能的情况下始终自行恢复。
添加额外的检查,例如
myDate.plusDays(YearMonth.of(myDate.getYear, myDate.getMonth + (myDate.getDayOfMonth() > 15 ? 1 : 0)).lengthOfMonth())
会解决这个问题并使其更一致地工作。现在它将添加当月的天数,如果它在上半年,或者如果它在下半年,我认为应该工作的下个月的天数。
一个解决方案可能是取最后一天并添加一天:
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);
}
YearMonth
如果您希望以月末为目标,请考虑月份而不是特定日期。
YearMonth
class 代表任何特定的月份。
YearMonth ym = YearMonth.of( 2016 , 1 ); // January
您可以向该对象添加月份。
YearMonth nextMonth = ym.plusMonths( 1 ); // February
当您需要该月的月底日期时,请致电atEndOfMonth
。
LocalDate ld = ym.atEndOfMonth();
顺便说一下,Month
枚举有预定义的对象来表示一年中的十二个月,即一月到十二月。
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();
}
Of 执行 if( dayOfMonth > 28 )
而不是捕获异常。
将月中的第几天设置为 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
我不得不自己解决这个问题,虽然输入略有不同,但最终得到了以下解决方案。正如其他人指出的那样,YearMonth can be helpful in this case, but I think its true power is its use as a 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)));
}