There are many pitfalls that people tend to get hooked on if they don't know the implementation of date calculation in Java, and there are some obstacles around them, so let's sort them out. For example, suppose you want to get the date of the last day of the next month.
It is better to use the standard API, Date and Time API.
LocalDate nextMonth = LocalDate.now().plusMonths(1L);
LocalDate lastDayOfNextMonth = nextMonth.withDayOfMonth(nextMonth.lengthOfMonth());
For example, if you do this on 8/31/2016, the code on the first line will add 1 month by .plusMonths (1L)
, but 9/31 doesn't exist, so it's last valid in September. It will be adjusted to the day 9/30. (The second line also sets the last day of September, 30th, and lastDayOfNextMonth will be 2016/9/30)
cf.) https://docs.oracle.com/javase/jp/8/docs/api/java/time/LocalDate.html#plusMonths-long-
The standard API can be implemented using java.util.Calendar.
Calendar calendar = new GregorianCalendar();
calendar.add(Calendar.MONTH, 1);
calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
Date lastDayOfNextMonth = calendar.getTime();
Now you can get the date of the last day of the next month. For example, if you do this on 8/31/2016, adding 1 to 9/31 will be adjusted to 9/30, which is the last valid day in September because it is a non-existent date. So far, it seems that the usability is not so different from the Date and Time API, but let's compare the explanation in the API document. (Line breaks are added as appropriate for readability)
Date and Time API LocalDate # plusMonths From https://docs.oracle.com/javase/jp/8/docs/api/java/time/LocalDate.html#plusMonths-long-
Returns a copy of this LocalDate plus the specified number of months.
This method adds the specified amount to the month field in three steps.
Adds the number of months entered to the month field Checks if the resulting date is invalid Adjust "Month Day" to the last valid day if necessary
For example, 2007-03-If you add January to 31, 2007-04-There will be an invalid date of 31. Instead of returning an invalid result 2007, the last valid day of the month-04-30 is selected.
This instance is immutable and is not affected by this method call. ```
GregorianCalendar#add From http://docs.oracle.com/javase/jp/7/api/java/util/GregorianCalendar.html#add(int,%20int)
Specified based on the rules of the calendar(Signed)Adds the amount of time to the specified calendar field.
Add Rule 1. The value of field after subtracting the value of field in the call before the call becomes the modulo overflow amount that occurred in field. An overflow will cause the value of the field to exceed the range, resulting in the next larger field being incremented or decremented. Occurs when the value of a field is adjusted to fall within that range.
Add Rule 2. If a small field is expected to be invariant, the minimum or maximum value has changed since the field was changed. If it is not equal to its previous value, the field's value is adjusted to be as close as possible to its expected value. Small fields Represents a small unit of time. HOUR is DAY_OF_A field smaller than MONTH. Small fields that are not expected to be invariant are not adjusted. The calendar system determines which fields are expected to be invariant. ```
While the behavior of LocalDate # plusMonths is clearly stated, I'm not sure what GregorianCalendar # add is saying. .. .. Add rules 1 and 2 are also explained at the beginning of http://docs.oracle.com/javase/jp/7/api/java/util/Calendar.html, where they are as follows: Some examples are given in a way that is a little easier to understand, but I think it's still difficult to understand.
Example:First consider the GregorianCalendar set on August 31, 1999. add(Calendar.MONTH, 13)When you call
The calendar is set to September 30, 2000. Adding 13 months to August results in September of the following year, so Add Rule 1
The MONTH field is set to September. DAY_OF_MONTH cannot be done on the 31st of September in Gregorian Calendar, so by Add Rule 2
DAY_OF_MONTH is set to the closest possible value of 30. This is a small field, but when the month changes in GregorianCalendar
DAY as changes are planned_OF_WEEK is not adjusted by Rule 2.
Also, if the month of the next month has already been calculated in the previous process and you want to get only the last day, if you try to set the next month with Calendar # set, you may be disappointed with the unexpected behavior as follows.
Calendar calendar = new GregorianCalendar();
calendar.set(Calendar.MONTH,Next month);
calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
Date lastDayOfNextMonth = calendar.getTime();
The only difference from the above is that the second line is set instead of add. If you run this on 7/31, you will get the correct result of 8/31, but if you run it on 8/31, you will get the result of 10/1. This is because in the case of set, unlike add, a carry occurs when the date does not exist. Another thing to note here is that the result is 10/1 instead of 10/31. If there is a carry when the next month is set, it seems natural that it will be 10/31. In order to check the value at the time of setting the next month, add the code (3rd line) that outputs the value by getTime after set as shown below.
Calendar calendar = new GregorianCalendar();
calendar.set(Calendar.MONTH,Next month);
System.out.println(calendar.getTime());
calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
Date lastDayOfNextMonth = calendar.getTime();
The result changes to 10/31. The Calendar class internally holds fields such as date, and each field value is recalculated when the get method, add method, getTime method, etc. are executed, while the set method is a field. It will not be recalculated just by setting the value to, but will be recalculated collectively when get etc. is executed later. In other words, at the time of set, it is not yet reflected in the date and time set in Calendar. Therefore, if getTime is inserted in the middle of set, the result will be different. I think this is different from what you would expect intuitively, so if you don't understand the behavior well, you may inadvertently create a bug. The behavior of this set method is also explained at the beginning of the API documentation http://docs.oracle.com/javase/jp/7/api/java/util/Calendar.html. (Line breaks are added as appropriate for readability)
set(f, value)Will change the calendar field f to value. In addition, to indicate that the calendar field f has changed
Internal member variables are set. The calendar field f changes immediately, but the calendar time value(millisecond)Is
get()、getTime()、getTimeInMillis()、add(), Or roll()Will not be recalculated until the next call.
Like this, set()Calling multiple times does not result in unnecessary calculations. set()When you change the calendar field using
Other fields may change depending on the calendar field, calendar field value, and calendar system.
In addition, get(f)However, after recalculating the calendar field, the value set by calling the set method is not always returned.
These details are determined by the concrete calendar class.
Example:First consider the GregorianCalendar set on August 31, 1999. set(Calendar.MONTH, Calendar.SEPTEMBER)When you call
The date is set to September 31, 1999. This is a temporary internal representation, getTime()Calling it will be October 1, 1999.
However, getTime()Before calling set(Calendar.DAY_OF_MONTH, 30)When you call set()Because the recalculation is done after itself
The date is set to September 30, 1999.
In the case of Java8 Date and Time API,
LocalDate nextMonth = LocalDate.now().withMonth(Next month);
LocalDate lastDayOfNextMonth = nextMonth.withDayOfMonth(nextMonth.lengthOfMonth());
Even if the code that specifies the next month is executed on 8/31 as in the case of add, the result will be 9/30. The API document https://docs.oracle.com/javase/jp/8/docs/api/java/time/LocalDate.html#withMonth-int- also has the following description, so you can rest assured.
If that "month day" is invalid for that year, it will be changed to the last valid day of the month.
Another non-intuitive calendar is a mistake that beginners tend to make, with the intention of setting September.
calendar.set(Calendar.MONTH, 9);
There is also a case where it is written. Actually, this means that October was set. The month of the calendar is 0-11, not 1-12, so if you want to specify September
calendar.set(Calendar.MONTH, 8);
Must be written. It's hard to read and understand intuitively, so
calendar.set(Calendar.MONTH, Calendar.SEPTEMBER);
It is better to write using constants like.
If you have a good grasp of the behavior of the Calendar class as described above, you can use Calendar, but it's complicated. Using Joda-Time http://www.joda.org/joda-time/, which was a standard date library in Java before Java7, you can write straightforwardly as follows.
Date lastDayOfNextMonth = LocalDate.now().plusMonths(1).dayOfMonth().withMaximumValue().toDate();
To specify the month of the next month, it will be as follows.
Date lastDayOfNextMonth = LocalDate.now().withMonthOfYear(Next month).dayOfMonth().withMaximumValue().toDate();
If you are not using Joda-Time and are considering migrating to Java 8 in the future, make it a Joda-Time developer and read the specifications of the Date and Time API (JSR-310). Stephen Colebourne has published ThreeTen-Backport http://www.threeten.org/threetenbp/ as a library that backports Java8 Date and Time API classes for Java6 / 7, so use that. I think it's also good.
Recommended Posts