The labs on this site need JavaScript to run. The same content is written out below.
Learn › Java › Commonly used classes
Dates and times
What date do you get when you add one month to January 31?
Like a String, the date and time objects of java.time (LocalDate, ZonedDateTime, and so on) do not change their contents. A method such as plusMonths returns a new object, and because the months have different numbers of days, the result of "one month later" is sometimes not what you expect. The same instant is written as a different date and time in each time zone, and the amount of time between two points in time is measured separately in date-based units (Period) and in time-based units (Duration).
Checked against real execution · The 135 combinations (11 distinct cases) were run on JDK 21 and checked against the results
Concepts in this lab
- java.time (Date-Time API)
- This is the set of date and time classes that has been part of Java since Java 8. It includes LocalDate, which holds only a date; LocalDateTime, which holds a date and a time; ZonedDateTime, which holds a time zone as well; and Instant, which holds a single point in time that does not depend on any time zone. All of these objects are immutable, so methods such as plusDays and plusMonths do not change the original object. They return a new object. You therefore have to assign the return value to a variable to use the result. When the result of adding months is a date that is not in the calendar (for example, February 31), no exception is thrown, and the date is adjusted to the last day of that month.
- Time zone and UTC
- UTC is the time that the whole world uses as its reference, and a time zone is the set of rules for how far the time used in a region differs from UTC. Korea (Asia/Seoul) is 9 hours ahead of UTC. The same instant is written as a different time in each time zone, and sometimes the date is different as well. In a region that uses daylight saving time, the offset from UTC changes with the season, so it is safer to specify a time zone by a region name such as America/New_York than by a fixed offset. The usual practice is to store a point in time as a value that does not depend on a time zone (an Instant) and to convert it to the time zone of the viewer when it is displayed.
- Period and Duration
- These are the two classes in java.time that represent the amount of time between two points in time. A Period expresses the amount in calendar years, months, and days, as in "1 year, 2 months, and 3 days", and a Duration expresses the length of the elapsed time in seconds, as in "90 minutes". The length of a month differs from month to month, so a Period cannot be converted to a fixed length of time, and a Duration has no unit such as a month or a year. The getDays() method of Period returns only the value of the days part, not the total number of days, so get the total number of days with ChronoUnit.DAYS.between. Duration.between must be given values that have a time, and passing LocalDate values, which have only a date, throws an exception at run time.
- Immutable object
- This is an object whose state does not change after it is created. Because its value never changes, it is safe for several threads to use at the same time, you can pass it to other code without worrying that it will be changed, and it is well suited for caching or for use as a key in a hash collection. Declare the fields private final, provide no methods that change the state, and make the class final so that a subclass cannot change its behavior. If a field refers to a mutable object (such as a List or a Date), the constructor and the getters pass copies in and out (defensive copying) so that outside code cannot change the internals.
Try these
- Default settings — Adding one month to January 31 gives the last day of February.
- Adding and ignoring the return value — plusMonths is called, but the date is unchanged.
- Adding one month, twice — The result is March 29, not March 31.
- Adding two months at once — The result is March 31. This differs from adding one month twice.
- New Year in Seoul, in New York time — It is the same instant, but in New York it is still the previous day.
- An amount measured with Period — 30 days have passed, but getDays() is 1.
- Measuring a Duration from dates only — The code compiles, but an exception is thrown at run time.
Running with the default settings
- LocalDate d = LocalDate.of(2024, 1, 31): A date object that represents January 31, 2024 is created, and d refers to it. A LocalDate holds only a date, with no time and no time zone.
- LocalDate first = d: first refers to the same object as well. first is kept so that you can see later whether the first object changes.
- d = d.plusMonths(1): One month after January 31 should be February 31, but there is no such day. In this case the date is adjusted to the last day of that month. February 2024 has 29 days, so the result is 2024-02-29. d refers to this new object.
- System.out.println(d): The line 2024-02-29 is printed.
- System.out.println(first): The line 2024-01-31 is printed. The contents of the first object did not change.
- d == first: d refers to the newly created object and first refers to the first object, so the result is false.
Result of this run
- When the result of an addition is a date that is not in the calendar, no exception is thrown. The date is adjusted to the last day of that month.
- So adding months in several separate steps can give a different result.
Code
LocalDate d = LocalDate.of(2024, 1, 31);
LocalDate first = d; // remember the first object
d = d.plusMonths(1);
System.out.println(d);
System.out.println(first);
System.out.println(d == first); // same object?
Assumptions of this model
The output of the 135 combinations (11 distinct cases, because each topic uses only one of the settings) was checked against the results of running the same code on JDK 21 on this PC. The code uses only fixed dates and time zones specified by name, so it gives the same result regardless of the current date or the time zone setting of the PC, and running it with the default time zone of the PC changed to other values confirmed that the output is the same. The time in each time zone follows the time zone data included in the JDK. New York being 5 hours behind UTC applies to this date (winter), and the difference is 4 hours during daylight saving time. Calculations for the days on which daylight saving time starts or ends are not covered by this model. Results for years that are not leap years (for example, February 28) are not within the range that was run and checked.
Previous lab: String, StringBuilder · Next lab: Optional