Creating Modified Copies of Dates and Times – Date and Time

Creating Modified Copies of Dates and Times

An immutable object does not provide any set methods that can change its state. Instead, it usually provides what are known as with methods (or withers) that return a copy of the original object where exactly one field has been set to a new value. The LocalTime and LocalDate classes provide with methods to set the value of a time or date field, respectively. Not surprisingly, the LocalDateTime class provides with methods to set the values of both time and date fields individually. A with method changes a specific property in an absolute way, which is reflected in the state of the new temporal object; the original object, however, is not affected. Such with methods are also called absolute adjusters, in contrast to the relative adjusters that we will meet later (p. 1040).

Click here to view code image

// LocalTime, LocalDateTime
LocalTime/LocalDateTime withHour(int hour)
LocalTime/LocalDateTime withMinute(int minute)
LocalTime/LocalDateTime withSecond(int second)
LocalTime/LocalDateTime withNano(int nanoOfSecond)

Return a copy of this LocalTime or LocalDateTime with the value of the appropriate time field changed to the specified value. A DateTimeException is thrown if the argument value is out of range.

Click here to view code image

// LocalDate, LocalDateTime
LocalDate/LocalDateTime withYear(int year)
LocalDate/LocalDateTime withMonth(int month)
LocalDate/LocalDateTime withDayOfMonth(int dayOfMonth)
LocalDate/LocalDateTime withDayOfYear(int dayOfYear)

Return a copy of this LocalDate or LocalDateTime with the value of the appropriate date field changed to the specified value. A DateTimeException is thrown if the specified value is out of range or is invalid in combination with the values of the other time or date fields in the temporal object.

The first and second methods will adjust the day of the month to the last valid day of the month, if the day of the month becomes invalid when the year or the month is changed (e.g., the month value 2 will change the date 2020-03-31 to 2020-02-29).

In contrast, the third method will throw a DateTimeException if the specified day of the month is invalid for the month-year combination (e.g., the day of month 29 is invalid for February 2021), as will the last method if the day of the year is invalid for the year (e.g., the day of year 366 is invalid for the year 2021).

Click here to view code image

// LocalTime, LocalDate, LocalDateTime
LocalTime/LocalDate/LocalDateTime with(TemporalField field, long newValue)

Returns a copy of this temporal object with the specified TemporalField (p. 1046) set to a new value. The ChronoField enum type implements the TemporalField interface, and its enum constants define specific temporal fields (p. 1046).

Using an invalid field in the with() method will result in any one of these exceptions: DateTimeException (field value cannot be set), Unsupported-TemporalTypeException (field is not supported), or ArithmeticException (numeric overflow occurred).

The code lines below are from Example 17.2. In the second assignment statement, the method calls are chained. Three instances of the LocalDate class are created consecutively, as each with method is called to set the value of a specific date field. The last assignment shows the use of temporal fields in the with() method for the same purpose.

Click here to view code image

LocalDate date2 = LocalDate.of(2021, 3, 1);                   // 2021-03-01
date2 = date2.withYear(2024).withMonth(2).withDayOfMonth(28); // 2024-02-28
LocalDate date3 = LocalDate.of(2021, 3, 1);                   // 2021-03-01
date3 = date3
    .with(ChronoField.YEAR, 2024L)
    .with(ChronoField.MONTH_OF_YEAR, 2L)
    .with(ChronoField.DAY_OF_MONTH, 28L);                     // 2024-02-28

The following code contains a logical error, such that the last two LocalDate instances returned by the with methods are ignored, and the reference date2 never gets updated.

Click here to view code image

date2 = date2.withYear(2022);                      // 2022-03-01
date2.withMonth(2).withDayOfMonth(28);             // date2 is still 2022-03-01.

In the next code examples, each call to a with method throws a DateTimeException. The minute and hour values are out of range for a LocalTime object. Certainly the month value 13 is out of range for a LocalDate object. The day of month value 31 is not valid for April, which has 30 days. The day of year value 366 is out of range as well, since the year 2021 is not a leap year.

Click here to view code image

LocalTime time = LocalTime.of(14, 45);       // 14:45
time = time.withMinute(100);       // Out of range. DateTimeException.
time = time.withHour(25);          // Out of range. DateTimeException.
LocalDate date = LocalDate.of(2021, 4, 30);  // 2021-04-30
date = date.withMonth(13);         // Out of range. DateTimeException.
date = date.withDayOfMonth(31);    // Out of range for month. DateTimeException.
date = date.withDayOfYear(366);    // Out of range for year. DateTimeException.

The code snippets below illustrate how the withYear() and withMonth() methods adjust the day of the month, if necessary, when the year or the month is changed, respectively.

Click here to view code image

LocalDate date3 = LocalDate.of(2020, 2, 29);  // Original: 2020-02-29
date3 = date3.withYear(2021);                 // Expected: 2021-02-29
System.out.println(“Date3: ” + date3);        // Adjusted: 2021-02-28
LocalDate date4 = LocalDate.of(2021, 3, 31);  // Original: 2021-03-31
date4 = date4.withMonth(4);                   // Expected: 2021-04-31
System.out.println(“Date4: ” + date4);        // Adjusted: 2021-04-30

The year in the date 2020-02-29 is changed to 2021, resulting in the following date: 2021-02-29. Since the year 2021 is not a leap year, the month of February cannot have 29 days. The withYear() method adjusts the day of the month to the last valid day of the month (i.e., 28). Similarly, the month in the date 2021-03-31 is changed to 4 (i.e., April), resulting in the following date: 2021-04-31. Since the month April has 30 days, the withMonth() method adjusts the day of the month to the last valid day of the month (i.e., 30).

Example 17.2 Using Local Dates and Local Times

Click here to view code image

import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Month;
import java.time.temporal.ChronoField;
public class UsingTemporals {
  public static void main(String[] args) {
    // Date-Time: 1945-08-06T08:15
    LocalDateTime doomsday = LocalDateTime.of(1945, 8, 6, 8, 15);
    LocalDate date = doomsday.toLocalDate();                   // 1945-08-06
    LocalTime time = doomsday.toLocalTime();                   // 08:15
    System.out.println(“Date-Time: ” + doomsday);
    System.out.println();
    // Time: 08:15
    int hourOfDay      = time.getHour();                       // 8
    int minuteOfHour1  = time.getMinute();                     // 15
    int minuteOfHour2  = time.get(ChronoField.MINUTE_OF_HOUR); // 15
    int secondOfMinute = time.getSecond();                     // 0
    System.out.println(“Time of day:      ” + time);
    System.out.println(“Hour-of-day:      ” + hourOfDay);
    System.out.println(“Minute-of-hour 1: ” + minuteOfHour1);
    System.out.println(“Minute-of-hour 2: ” + minuteOfHour2);
    System.out.println(“Second-of-minute: ” + secondOfMinute);
    System.out.println();
    // Date: 1945-08-06
    int year       = date.getYear();                           // 1945
    int monthVal1  = date.getMonthValue();                     // 8
    int monthVal2  = date.get(ChronoField.MONTH_OF_YEAR);      // 8
    Month month    = date.getMonth();                          // AUGUST
    DayOfWeek dow  = date.getDayOfWeek();                      // MONDAY
    int day        = date.getDayOfMonth();                     // 6
    System.out.println(“Date:  ” + date);
    System.out.println(“Year:  ” + year);
    System.out.println(“Month value 1: ” + monthVal1);
    System.out.println(“Month value 2: ” + monthVal2);
    System.out.println(“Month-of-year: ” + month);
    System.out.println(“Day-of-week:   ” + dow);
    System.out.println(“Day-of-month:  ” + day);
    System.out.println();
    // Ordering
    LocalDate d1 = LocalDate.of(1948, 2, 28);                  // 1948-02-28
    LocalDate d2 = LocalDate.of(1949, 3, 1);                   // 1949-03-01
    boolean result1 = d1.isBefore(d2);                         // true
    boolean result2 = d2.isAfter(d1);                          // true
    boolean result3 = d1.isAfter(d1);                          // false
    boolean result4 = d1.isEqual(d2);                          // false
    boolean result5 = d1.isEqual(d1);                          // true
    boolean result6 = d1.isLeapYear();                         // true
    System.out.println(“Ordering:”);
    System.out.println(d1 + ” is before ”   + d2 + “: ” + result1);
    System.out.println(d2 + ” is after ”    + d1 + “: ” + result2);
    System.out.println(d1 + ” is after ”    + d1 + “: ” + result3);
    System.out.println(d1 + ” is equal to ” + d2 + “: ” + result4);
    System.out.println(d1 + ” is equal to ” + d1 + “: ” + result5);
    System.out.println(d1.getYear() + ” is a leap year: ” + result6);
    System.out.println();
    System.out.println(“Using absolute adjusters:”);
    LocalDate date2 = LocalDate.of(2021, 3, 1);
    System.out.println(“Date before adjusting: ” + date2);     // 2021-03-01
    date2 = date2.withYear(2024).withMonth(2).withDayOfMonth(28);
    System.out.println(“Date after adjusting:  ” + date2);     // 2024-02-28
    System.out.println();
    System.out.println(“Using temporal fields:”);
    LocalDate date3 = LocalDate.of(2021, 3, 1);
    System.out.println(“Date before adjusting: ” + date3);     // 2021-03-01
    date3 = date3
        .with(ChronoField.YEAR, 2024L)
        .with(ChronoField.MONTH_OF_YEAR, 2L)
        .with(ChronoField.DAY_OF_MONTH, 28L);
    System.out.println(“Date after adjusting:  ” + date3);     // 2024-02-28
  }
}

Output from the program:

Click here to view code image

Date-Time: 1945-08-06T08:15
Time of day:      08:15
Hour-of-day:      8
Minute-of-hour 1: 15
Minute-of-hour 2: 15
Second-of-minute: 0
Date:  1945-08-06
Year:  1945
Month value 1: 8
Month value 2: 8
Month-of-year: AUGUST
Day-of-week:   MONDAY
Day-of-month:  6
Ordering:
-1004-03-01 is before 1004-03-01: true
1004-03-01 is after -1004-03-01: true
-1004-03-01 is after -1004-03-01: false
-1004-03-01 is equal to 1004-03-01: false
-1004-03-01 is equal to -1004-03-01: true
1004 is a leap year: true
Using absolute adjusters:
Date before adjusting: 2021-03-01
Date after adjusting:  2024-02-28
Using temporal fields:
Date before adjusting: 2021-03-01
Date after adjusting:  2024-02-28

Leave a Reply

Your email address will not be published. Required fields are marked *