How to implement date calculation in Java

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.

For Java 8

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-

For Java 7 or lower

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)

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. ```

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.

What should i do?

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

How to implement date calculation in Java
How to implement Kalman filter in Java
How to implement coding conventions in Java
How to get the date in java
[Java] How to implement multithreading
Summary of how to implement default arguments in Java
How to learn JAVA in 7 days
How to use classes in Java?
How to name variables in Java
Try to implement Yubaba in Java
How to concatenate strings in java
How to implement search functionality in Rails
Multilingual Locale in Java How to use Locale
How to get date data in Ruby
Try to implement n-ary addition in Java
How to do base conversion in Java
[Java] How to set the Date time to 00:00:00
How to fix system date in JUnit
How to embed Janus Graph in Java
How to implement ranking functionality in Rails
How to implement asynchronous processing in Outsystems
How to implement a job that uses Java API in JobScheduler
Date manipulation in Java 8
How to display a web page in Java
I tried to implement deep learning in Java
How to get Class from Element in Java
How to hide null fields in response in Java
How to implement a like feature in Rails
How to implement optimistic locking in REST API
[Java] How to substitute Model Mapper in Jackson
How to solve an Expression Problem in Java
How to write Java String # getBytes in Kotlin?
How to implement Pagination in GraphQL (for ruby)
[Java] How to use Calendar class and Date class
How to implement UICollectionView in Swift with code only
[Java] How to use compareTo method of Date class
[Java] How to use Map
How to create a Java environment in just 3 seconds
[Java] How to omit the private constructor in Lombok
How to implement guest login in 5 minutes in rails portfolio
How to lower java version
[Java] How to use Map
How to implement a like feature in Ajax in Rails
How to uninstall Java 8 (Mac)
I tried to implement Firebase push notification in Java
Java --How to make JTable
How to input / output IBM mainframe files in Java?
How to use java Optional
How to minimize Java images
How to write java comments
Implement two-step verification in Java
How to use java class
[Java] How to use Optional ②
How to create a data URI (base64) in Java
[Java] How to use removeAll ()
[Java] How to display Wingdings
Implement Basic authentication in Java
[Java] How to use string.format
How to use Java Map
Implement math combinations in Java
How to write a date comparison search in Rails