What is the most clean solution for adding a month to specified day using LocalDateTime
or LocalDate
?
Nice answers are In java.time, how is the result of adding a month calculated? and Find next occurrence of a day-of-week in JSR-310 so I hope that this problem can be solved with similar clean solution.
Lets say that user selects a day of month (any from 1 to 31) for some periodic task. After selection timestamp is generated and is increased each month. Only current month's timestamp (there is no track of previous month, timestamp is updated each month) and selected day of month are stored.
If selected day is 31st and we start with January 31st adding a month with dateTime.plusMonths(1)
method gives February 29th in 2016. If dateTime.plusMonths(1)
method is used again the result is March 29th while expected is March 31st. However, it works for days from 1st to 28th.
A workaround for 31st day of month could be using dateTime.with(TemporalAdjusters.lastDayOfMonth())
but this does not cover days from 28th to 30th which can also be last days of month.
Example for lastDayOfMonth()
for selected day of month 30th: January 30th, February 29th (plusMonth method behaviour selects last day if previous day is higher in previous month), March 31st (expected is 30th this is selected day of month). This method does not take into account selected days of month.
Sure this problem has many potential solutions that include some checks and calculating differences between months and days but I am looking for a solution using java.time capabilities only if this is possible.
I am looking for a way to move a date to the next month with selected day of month. Like dateTime.plusMonths(1).withDayOfMonth(selectedDayOfMonth)
. This will throw an exception for dates like February 31st as withDayOfMonth
overrides plusMonths
's adjustment to the last valid date.
Example of iteration of using plusMonth
method. In iteration value from previous one is taken - imagine recursion.
+-----------+------------------+---------------+
| 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 |
+-----------+------------------+---------------+
And another working example for day of month from 1st to 28th.
+-----------+-------------------+--------------+
| 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 |
+-----------+-------------------+--------------+
29th day of month is ok for leap years but not for common years.
+-----------+--------------------+---------------+
| 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 |
+-----------+--------------------+---------------+
The plusMonths() method of LocalDate class in Java is used to add the number of specified months in this LocalDate and return a copy of LocalDate. This method adds the months field in the following steps: Add the months to the month-of-year field. Check if the date after adding months is valid or not.
Use the add() method of the calendar class to add days to the date. The add method() takes two parameter, i.e., calendar field and amount of time that needs to be added. Get the new date from the calendar and set the format of the SimpleDateFormat class to show the calculated new date on the screen.
The plusDays() method of a LocalDate class in Java is used to add the number of specified day in this LocalDate and return a copy of LocalDate. For example, 2018-12-31 plus one day would result in 2019-01-01. This instance is immutable and unaffected by this method call.
Set the day of month to min(selectedDayOfMonth, lastDayOfNextMonth)
public static LocalDate next(LocalDate current, int selectedDayOfMonth) {
LocalDate next = current.plusMonths(1);
return next.withDayOfMonth(Math.min(selectedDayOfMonth, next.lengthOfMonth()));
}
Usage:
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();
}
Output for test(4)
:
2001-01-04
2001-02-04
2001-03-04
2001-04-04
2001-05-04
2001-06-04
Output for test(29)
:
2001-01-29
2001-02-28
2001-03-29
2001-04-29
2001-05-29
2001-06-29
Output for test(31)
:
2001-01-31
2001-02-28
2001-03-31
2001-04-30
2001-05-31
2001-06-30
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With