Temporal Fields – Date and Time

Temporal Fields

The java.time.temporal.TemporalField interface represents a specific field of a temporal object. The java.time.temporal.ChronoField enum type implements this interface, defining the fields by constant names so that a specific field can be conveniently accessed. Selected constants from the ChronoField enum type include SECOND_OF_MINUTE, MINUTE_OF_DAY, DAY_OF_MONTH, MONTH_OF_YEAR, and YEAR.

The output from Example 17.4 shows a table with all the temporal fields defined by the ChronoField enum type.

Analogous to a ChronoUnit enum constant, a ChronoField enum constant can be queried by the following selected methods:

TemporalUnit getBaseUnit()

Gets the unit that the field is measured in. For example, ChronoField.DAY_OF_ MONTH.getBaseUnit() returns ChronoUnit.DAYS.

boolean isDateBased()
boolean isTimeBased()

Check whether this field represents a date or a time field, respectively. For example, ChronoField.HOUR_OF_DAY.isDateBased() is false, but ChronoField.SECOND_ OF_MINUTE.isTimeBased() is true.

Click here to view code image

boolean isSupportedBy(TemporalAccessor temporal)

Checks whether this field is supported by the specified temporal object. For example, ChronoField.YEAR.isSupportedBy(LocalTime.MIDNIGHT) is false.

static ChronoField[] values()

Returns an array containing the field constants of this enum type, in the order they are declared. This method is called at (7) in Example 17.4.

The temporal classes provide the method isSupported(field) to determine whether a temporal field is valid for a temporal object. In Example 17.4, this method is used at (8), (9), and (10) to determine whether each temporal field defined by the ChronoField enum type is a valid field for the different temporal classes.

The following methods of the temporal classes all accept a temporal field that designates a specific field of the temporal object:

Click here to view code image

LocalDate date = LocalDate.of(2021, 8, 13);
int monthValue = date.get(ChronoField.MONTH_OF_YEAR);
System.out.print(“Date ” + date + ” has month of the year: ” + monthValue);
// Date 2021-08-13 has month of the year: 8

Click here to view code image

LocalDateTime dateTime = LocalDateTime.of(2021, 8, 13, 20, 20);
System.out.print(“Date-time ” + dateTime);
dateTime = dateTime.with(ChronoField.DAY_OF_MONTH, 11)
                   .with(ChronoField.MONTH_OF_YEAR, 1)
                   .with(ChronoField.YEAR, 2022);
System.out.println(” changed to: ” + dateTime);
// Date-time 2021-08-13T20:20 changed to: 2022-01-11T20:20

In Example 17.4, the code at (1) and at (6) prints tables that show which ChronoUnit and ChronoField constants are valid in which temporal-based object. A LocalTime instance supports time-based units and fields, and a LocalDate instance supports date-based units and fields. A LocalDateTime or a ZonedDateTime supports both time-based and date-based units and fields. Using an invalid enum constant for a temporal object will invariably result in an UnsupportedTemporalTypeException being thrown.

Example 17.4 Valid Temporal Units and Temporal Fields

Click here to view code image

import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoField;
import java.time.temporal.ChronoUnit;
public class ValidTemporalUnitsAndFields {
  public static void main(String[] args) {
    // Temporals:
    LocalTime time = LocalTime.now();
    LocalDate date = LocalDate.now();
    LocalDateTime dateTime = LocalDateTime.now();
    ZonedDateTime zonedDateTime = ZonedDateTime.now();
    Instant instant = Instant.now();
    // Print supported units:                                            // (1)
    System.out.printf(“%29s %s %s %s %s %s%n”,
        “ChronoUnit”, “LocalTime”, “LocalDate”, “LocalDateTime”,
        ” ZDT “, “Instant”);
    ChronoUnit[] units =  ChronoUnit.values();                           // (2)
    for (ChronoUnit unit : units) {
      System.out.printf(“%28S: %7b %9b %10b %9b %7b%n”,
          unit.name(), time.isSupported(unit), date.isSupported(unit),   // (3)
          dateTime.isSupported(unit), zonedDateTime.isSupported(unit),   // (4)
          instant.isSupported(unit));                                    // (5)
      }
    System.out.println();
    // Print supported fields:                                           // (6)
    System.out.printf(“%29s %s %s %s %s %s%n”,
        “ChronoField”, “LocalTime”, “LocalDate”, “LocalDateTime”,
        ” ZDT “, “Instant”);
    ChronoField[] fields =  ChronoField.values();                        // (7)
    for (ChronoField field : fields) {
      System.out.printf(“%28S: %7b %9b %10b %9b %7b%n”,
          field.name(), time.isSupported(field), date.isSupported(field),// (8)
          dateTime.isSupported(field), zonedDateTime.isSupported(field), // (9)
          instant.isSupported(field));                                   // (10)
    }
    System.out.println();
  }
}

Output from the program (ZDT stands for ZonedDateTime in the output):

Click here to view code image

                   ChronoUnit LocalTime LocalDate LocalDateTime  ZDT  Instant
                       NANOS:    true     false       true      true    true
                      MICROS:    true     false       true      true    true
                      MILLIS:    true     false       true      true    true
                     SECONDS:    true     false       true      true    true
                     MINUTES:    true     false       true      true    true
                       HOURS:    true     false       true      true    true
                   HALF_DAYS:    true     false       true      true    true
                        DAYS:   false      true       true      true    true
                       WEEKS:   false      true       true      true   false
                      MONTHS:   false      true       true      true   false
                       YEARS:   false      true       true      true   false
                     DECADES:   false      true       true      true   false
                   CENTURIES:   false      true       true      true   false
                   MILLENNIA:   false      true       true      true   false
                        ERAS:   false      true       true      true   false
                     FOREVER:   false     false      false     false   false
                  ChronoField LocalTime LocalDate LocalDateTime  ZDT  Instant
              NANO_OF_SECOND:    true     false       true      true    true
                 NANO_OF_DAY:    true     false       true      true   false
             MICRO_OF_SECOND:    true     false       true      true    true
                MICRO_OF_DAY:    true     false       true      true   false
             MILLI_OF_SECOND:    true     false       true      true    true
                MILLI_OF_DAY:    true     false       true      true   false
            SECOND_OF_MINUTE:    true     false       true      true   false
               SECOND_OF_DAY:    true     false       true      true   false
              MINUTE_OF_HOUR:    true     false       true      true   false
               MINUTE_OF_DAY:    true     false       true      true   false
                HOUR_OF_AMPM:    true     false       true      true   false
          CLOCK_HOUR_OF_AMPM:    true     false       true      true   false
                 HOUR_OF_DAY:    true     false       true      true   false
           CLOCK_HOUR_OF_DAY:    true     false       true      true   false
                 AMPM_OF_DAY:    true     false       true      true   false
                 DAY_OF_WEEK:   false      true       true      true   false
ALIGNED_DAY_OF_WEEK_IN_MONTH:   false      true       true      true   false
 ALIGNED_DAY_OF_WEEK_IN_YEAR:   false      true       true      true   false
                DAY_OF_MONTH:   false      true       true      true   false
                 DAY_OF_YEAR:   false      true       true      true   false
                   EPOCH_DAY:   false      true       true      true   false
       ALIGNED_WEEK_OF_MONTH:   false      true       true      true   false
        ALIGNED_WEEK_OF_YEAR:   false      true       true      true   false
               MONTH_OF_YEAR:   false      true       true      true   false
             PROLEPTIC_MONTH:   false      true       true      true   false
                 YEAR_OF_ERA:   false      true       true      true   false
                        YEAR:   false      true       true      true   false
                         ERA:   false      true       true      true   false
             INSTANT_SECONDS:   false     false      false      true    true
              OFFSET_SECONDS:   false     false      false      true   false

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

Using Temporal Units and Temporal Fields – Date and Time

17.3 Using Temporal Units and Temporal Fields

Temporal units and temporal fields allow temporal objects to be accessed and manipulated in a human-readable way.

For temporal units and temporal fields supported by the Period and Duration classes, see §17.5, p. 1057, and §17.6, p. 1064, respectively.

Temporal Units

The java.time.temporal.TemporalUnit interface represents a unit of measurement, rather than an amount of such a unit—for example, the unit years to qualify that an amount of time should be interpreted as number of years. The java.time.temporal.ChronoUnit enum type implements this interface, defining the temporal units by constant names to provide convenient unit-based access to manipulate a temporal object. Constants defined by the ChronoUnit enum type include the following temporal units, among others: SECONDS, MINUTES, HOURS, DAYS, MONTHS, and YEARS.

The output from Example 17.4 shows a table with all the temporal units defined by the ChronoUnit enum type. It is not surprising that not all temporal units are valid for all types of temporal objects. The time units, such as SECONDS, MINUTES, and HOURS, are valid for temporal objects that are time-based, such as LocalTime, LocalDateTime, and Instant. Likewise, the date units, such as DAYS, MONTHS, and YEARS, are valid units for temporal objects that are date-based, such as LocalDate, LocalDateTime, and ZonedDateTime.

A ChronoUnit enum constant can be queried by the following selected methods:

Duration getDuration()

Gets the estimated duration of this unit in the ISO calendar system. For example, ChronoUnit.DAYS.getDuration() has the duration PT24H (i.e., 24 hours).

boolean isDateBased()
boolean isTimeBased()

Check whether this unit is a date unit or a time unit, respectively. For example, ChronoUnit.HOURS.isDateBased() is false, but ChronoUnit.SECONDS.isTimeBased() is true.

Click here to view code image

boolean isSupportedBy(Temporal temporal)

Checks whether this unit is supported by the specified temporal object. For example, ChronoUnit.YEARS.isSupportedBy(LocalTime.MIDNIGHT) is false.

Click here to view code image

static ChronoUnit[] values()

Returns an array containing the unit constants of this enum type, in the order they are declared. This method is called at (2) in Example 17.4.

The temporal classes provide the method isSupported(unit) to determine whether a temporal unit is valid for a temporal object. In Example 17.4, this method is used at (3), (4), and (5) to determine whether each temporal unit defined by the ChronoUnit enum type is a valid unit for the different temporal classes.

The following methods of the temporal classes all accept a temporal unit that qualifies how a numeric quantity should be interpreted:

  • minus(amount, unit) and plus(amount, unit), (p. 1040)

Click here to view code image

LocalDate date = LocalDate.of(2021, 10, 23);
System.out.print(“Date ” + date);
date = date.minus(10, ChronoUnit.MONTHS).minus(3, ChronoUnit.DAYS);
System.out.println(” minus 10 months and 3 days: ” + date);
// Date 2021-10-23 minus 10 months and 3 days: 2020-12-20

LocalTime time = LocalTime.of(14, 15);
System.out.print(“Time ” + time);
time = time.plus(9, ChronoUnit.HOURS).plus(70, ChronoUnit.MINUTES);
System.out.println(” plus 9 hours and 70 minutes is ” + time);
// Time 14:15 plus 9 hours and 70 minutes is 00:25

  • until(temporalObj, unit), (p. 1040)

Click here to view code image

LocalDate fromDate = LocalDate.of(2021, 3, 1);
LocalDate xmasDate = LocalDate.of(2021, 12, 25);
long tilChristmas = fromDate.until(xmasDate, ChronoUnit.DAYS);
System.out.println(“From ” + fromDate + “, days until Xmas: ” + tilChristmas);
// From 2021-03-01, days until Xmas: 299

Accessing Date Units in a Period – Date and Time

Accessing Date Units in a Period

The Period class provides the obvious getXXX() methods to read the values of date units of a Period object, where XXX can be Years, Months, or Days.

Click here to view code image

Period period5 = Period.of(2, 4, -10);
System.out.println(“Period: ” + period5);             // Period: P2Y4M-10D
System.out.println(“Years:  ” + period5.getYears());  // Years:  2
System.out.println(“Months: ” + period5.getMonths()); // Months: 4
System.out.println(“Days:   ” + period5.getDays());   // Days:   -10

Reading the value of date units of a Project object can also be achieved using the get(unit) method, where only the date units shown in the code below are allowed. A list of these valid temporal units can be obtained by calling the getUnits() method of the Period class.

The class also has methods to check if any date unit of a period has a negative value or if all date units of a period have the value zero.

Click here to view code image

System.out.println(“Years:  ” + period5.get(ChronoUnit.YEARS)); // Years:  2
System.out.println(“Months: ” + period5.get(ChronoUnit.MONTHS));// Months: 4
System.out.println(“Days:   ” + period5.get(ChronoUnit.DAYS));  // Days: -10
List<TemporalUnit> supportedUnits = period5.getUnits(); // [Years, Months, Days]
System.out.println(“Total months: ” + period5.toTotalMonths()); // 28 months
System.out.println(period5.isNegative());                       // true
System.out.println(period5.isZero());                           // false

The class Period provides the method toTotalMonths() to derive the total number of months in a period. However, this calculation is solely based on the number of years and months in the period; the number of days is not considered. A Period just represents an amount of time, so it has no notion of a date. Conversion between months and years is not a problem, as 1 year is 12 months. However, conversion between the number of days and the other date units is problematic. The number of days in a year and in a month are very much dependent on whether the year is a leap year and on a particular month in the year, respectively. A Period is oblivious to both the year and the month in the year, as it represents an amount of time and not a point on the timeline.

int getYears()
int getMonths()
int getDays()

Return the value of a specific date unit of this period, indicated by the name of the method.

long get(TemporalUnit unit)

Returns the value of the specified unit in this Period. The only supported date ChronoUnits are YEARS, MONTHS, and DAYS (p. 1044). All other units throw an exception.

List<TemporalUnit> getUnits()

Returns the list of date units supported by this period: YEARS, MONTHS, and DAYS (p. 1044). These date units can be used in the get(TemporalUnit) method.

long toTotalMonths()

Returns the total number of months in this period, based on the values of the years and months units. The value of the days unit is not considered.

boolean isNegative()

Determines whether the value of any date units of this period is negative.

boolean isZero()

Determines whether the values of all date units of this period are zero.

Accessing Temporal Fields in an Instant – Date and Time

Accessing Temporal Fields in an Instant

The Instant class provides the following selected methods to access temporal fields in an instance of the class:

int getNano()
long getEpochSecond()

Return the number of nanoseconds and the number of seconds represented by this instant from the start of the epoch, respectively. Note that the method names are without the s at the end.

Click here to view code image

int get(TemporalField field)
long getLong(TemporalField field)

The get(field) method will return the value of the specified field in this Instant as an int. Only the following ChronoField constants are supported: NANO_OF_SECOND, MICRO_OF_SECOND, MILLI_OF_SECOND, and INSTANT_SECONDS (p. 1046). The first three fields will always return a valid value, but the INSTANT_SECONDS field will throw a DateTimeException if the value does not fit into an int. All other fields result in an UnsupportedTemporalTypeException.

As the getLong(field) method returns the value of the specified field in this Instant as a long, there is no problem with overflow in returning a value designated by any of the four fields mentioned earlier.

Click here to view code image

boolean isSupported(TemporalField field)

The isSupported(field) determines whether the specified field is supported by this instant.

long toEpochMilli()

Returns the number of milliseconds that represent this Instant from the start of the epoch. The method throws an ArithmeticException in case of number overflow.

The code below shows how the getNano() and getEpochSecond() methods of the Instant class read the value of the nanosecond and the epoch-second fields of an Instant object, respectively.

Click here to view code image

Instant inst = Instant.ofEpochSecond(24L*60*60,    // 1 day and
                                     555_555_555L);// 555555555 ns after epoch.
out.println(inst);                   // 1970-01-02T00:00:00.555555555Z
out.println(inst.getNano());         // 555555555 ns
out.println(inst.getEpochSecond());  // 86400 s

Reading the nanosecond and epoch-second fields of an Instant in different units can be done using the get(field) method. Note the value of the nanosecond field expressed in different units using ChronoField constants. To avoid a DateTimeException when number overflow occurs, the getLong(field) method is used instead of the get(field) method in accessing the epoch-second field.

Click here to view code image

out.println(inst.get(ChronoField.NANO_OF_SECOND));      // 555555555 ns
out.println(inst.get(ChronoField.MICRO_OF_SECOND));     // 555555 micros
out.println(inst.get(ChronoField.MILLI_OF_SECOND));     // 555 ms
out.println(inst.getLong(ChronoField.INSTANT_SECONDS)); // 86400 s
//out.println(inst.get(ChronoField.INSTANT_SECONDS));   // DateTimeException
//out.println(inst.get(ChronoField.HOUR_OF_DAY));       // UnsupportedTemporal-
                                                        // TypeException

The Instant class provides the toEpochMilli() method to derive the position of the instant measured in milliseconds from the epoch; that is, the second and nanosecond fields are converted to milliseconds. Converting 1 day (86400 s) and 555555555 ns results in 86400555 ms.

Click here to view code image

out.println(inst.toEpochMilli());                        // 86400555 ms

Summarizing – Streams

Summarizing

The summarizing collector performs a functional reduction to produce summary statistics (count, sum, min, max, average) on the numeric results of applying a numeric-valued function to the input elements.

Click here to view code image

static <T> Collector<T,?,
NumType
SummaryStatistics> summarizing
NumType
(
       To
NumType
Function<? super T> mapper)

Returns a collector that applies a numtype-valued mapper function to the input elements, and returns the summary statistics for the resulting values.

NumType is Int (but it is Integer when used as a type name), Long, or Double, and the corresponding numtype is int, long, or double.

The collector Collectors.summarizingInt() is used at (1) as a standalone collector to summarize the statistics for the number of tracks on the CDs. The mapper function CD::noOfTracks passed as an argument extracts the number of tracks from each CD on which the functional reduction is performed.

Click here to view code image

IntSummaryStatistics stats1 = CD.cdList.stream()
    .collect(
      Collectors.summarizingInt(CD::noOfTracks)      // (1) Standalone collector
     );
System.out.println(stats1);
// IntSummaryStatistics{count=5, sum=42, min=6, average=8.400000, max=10}

The IntSummaryStatistics class provides get methods to access the individual results (p. 974).

In the pipeline below, the CDs are grouped by musical genre, and the downstream collector created by the Collectors.summarizingInt() method at (2) summarizes the statistics for the number of tracks on the CDs in each group.

Click here to view code image

Map<Genre, IntSummaryStatistics> grpByGenre = CD.cdList.stream()
  .collect(Collectors.groupingBy(
     CD::genre,
     Collectors.summarizingInt(CD::noOfTracks)));    // (2) Downstream collector
System.out.println(grpByGenre);
//{POP=IntSummaryStatistics{count=2, sum=18, min=8, average=9.000000, max=10},
// JAZZ=IntSummaryStatistics{count=3, sum=24, min=6, average=8.000000, max=10}}

System.out.println(grpByGenre.get(Genre.JAZZ));   // Summary stats for Jazz CDs.
// IntSummaryStatistics{count=3, sum=24, min=6, average=8.000000, max=10}

The collector returned by the Collectors.summarizingInt() method performs effectively the same functional reduction as the IntStream.summaryStatistics() terminal operation (p. 974) at (3).

Click here to view code image

IntSummaryStatistics stats2 = CD.cdList.stream()
    .mapToInt(CD::noOfTracks)
    .summaryStatistics();                         // (3)
System.out.println(stats2);
// IntSummaryStatistics{count=5, sum=42, min=6, average=8.400000, max=10}

Working with Instants – Date and Time

17.4 Working with Instants

The temporal classes LocalTime, LocalDate, LocalDateTime, and ZonedDateTime are suitable for representing human time in terms of year, month, day, hour, minute, second, and time zone. The Instant class can be used for representing computer time, specially timestamps that identify to a higher precision when an event occurred on the timeline. Instants are suitable for persistence purposes—for example, in a database.

An Instant represents a point on the timeline, measured with nanosecond precision from a starting point or origin which is defined to be at 1970-01-01T00:00:00Z—that is, January 1, 1970, at midnight—and is called the epoch. Instants before the epoch have negative values, whereas instants after the epoch have positive values. The Z represents the time zone designator for the zero UTC offset, which is the time zone offset for all instants in the UTC standard (p. 1072). The text representation of the epoch shown above is in the ISO standard format used by the toString() method of the Instant class.

An Instant is modeled with two values:

  • A long value to represent the epoch-second
  • An int value to represent the nano-of-second

The nano-of-second must be a value in the range [0, 999999999]. This representation is reflected in the methods provided for dealing with instants. The Instant class shares many of the method name prefixes and the common method names in Table 17.2, p. 1026, and Table 17.3, p. 1026, with the other temporal classes, respectively. Although the Instant class has many methods analogous to the other temporal classes, as we shall see, there are also differences. Instant objects are, like objects of the other temporal classes, immutable and thread-safe.

Creating Instants

The Instant class provides the following predefined instants:

static Instant EPOCH
static Instant MAX
static Instant MIN

These static fields of the Instant class define constants for the epoch (1970-01-01T00:00:00Z), the maximum (1000000000-12-31T23:59:59.999999999Z), and the minimum instants (-1000000000-01-01T00:00Z), respectively.

Following are selected methods for creating and converting instances of the Instant class:

static Instant now()

Returns the current instant based on the system clock.

Click here to view code image

static Instant ofEpochMilli(long epochMilli)
static Instant ofEpochSecond(long epochSecond)
static Instant ofEpochSecond(long epochSecond, long nanoAdjustment)

These static factory methods return an Instant based on the millisecond, second, and nanosecond specified.

Nanoseconds are implicitly set to zero. The argument values can be negative. Note that the amount is specified as a long value.

String toString()

Returns a text representation of this Instant, such as “2021-01-11T14:18:30Z”. Formatting is based on the ISO instant format for date-time:

Click here to view code image

uuuu-MM-ddTHH:mm:ss.SSSSSSSSSZ

where Z designates the UTC standard (also known as Coordinated Universal Time).

Click here to view code image

static Instant parse(CharSequence text)

Returns an Instant parsed from a character sequence, such as “2021-04-28T14:18:30Z”, based on the ISO instant format. A DateTimeParse-Exception is thrown if the text cannot be parsed to an instant.

Click here to view code image

ZonedDateTime atZone(ZoneId zone)

Returns a ZonedDateTime by combining this instant with the specified time zone (p. 1072).

Analogous to the other temporal classes, the Instant class also provides the now() method to obtain the current instant from the system clock.

Click here to view code image

Instant currentInstant = Instant.now();      // 2021-03-09T10:48:01.914826Z

The Instant class provides the static factory method ofEpochUNIT() to construct instants from seconds and nanoseconds. There is no method to construct an instant from just nanoseconds.

Click here to view code image

Instant inst1 = Instant.ofEpochMilli(-24L*60*60*1000);// Date 1 day before epoch.
Instant inst2 = Instant.ofEpochSecond(24L*60*60);     // Date 1 day after epoch.
Instant inst3 = Instant.ofEpochSecond(24L*60*60 – 1,  // Date 1 day after epoch.
                                      1_000_000_000L);
out.println(“A day before: ” + inst1); // Date 1 day before: 1969-12-31T00:00:00Z
out.println(“A day after:  ” + inst2); // Date 1 day after : 1970-01-02T00:00:00Z
out.println(“A day after:  ” + inst3); // Date 1 day after : 1970-01-02T00:00:00Z

Note that the amount specified is a long value. The last statement above also illustrates that the nanosecond is adjusted so that it is always between 0 and 999,999,999. The adjustment results in the nanosecond being set to 0 and the second being incremented by 1.

The toString() method of the Instant class returns a text representation of an Instant based on the ISO standard. The code shows the text representation of the instant 500 nanoseconds after the epoch.

Click here to view code image

Instant inst4 = Instant.ofEpochSecond(0, 500);
out.println(“Default format:  ” + inst4);       // 1970-01-01T00:00:00.000000500Z

The Instant class also provides the parse() static method to create an instant from a string that contains a text representation of an instant, based on the ISO standard. Apart from treating the value of the nanosecond as optional, the method is strict in parsing the string. If the format of the string is not correct, a DateTimeParseException is thrown.

Click here to view code image

Instant instA = Instant.parse(“1970-01-01T00:00:00.000000500Z”);
Instant instB = Instant.parse(“1949-03-01T12:30:15Z”);
Instant instC = Instant.parse(“-1949-03-01T12:30:15Z”);
Instant instD = Instant.parse(“-1949-03-01T12:30:15”); // DateTimeParseException!

The code below illustrates creating an Instant by combining a LocalDateTime object with a time zone offset. Three different zone-time offsets are specified at (2), (3), and (4) to convert the date-time created at (1) to an Instant on the UTC timeline, which has offset zero. Note that an offset ahead of UTC is subtracted and an offset behind UTC is added to adjust the values of the date/time from the LocalDateTime object to the UTC timeline.

Click here to view code image

LocalDateTime ldt = LocalDate.of(2021, 12, 25).atStartOfDay();  //(1)
Instant i1 = ldt.toInstant(ZoneOffset.of(“+02:00”));     // (2) Ahead of UTC
Instant i2 = ldt.toInstant(ZoneOffset.UTC);              // (3) At UTC
Instant i3 = ldt.toInstant(ZoneOffset.of(“-02:00”));     // (4) Behind UTC
System.out.println(“ldt: ” + ldt);
System.out.println(“i1:  ” + i1);
System.out.println(“i2:  ” + i2);
System.out.println(“i3:  ” + i3);

Output from the code:

ldt: 2021-12-25T00:00
i1:  2021-12-24T22:00:00Z
i2:  2021-12-25T00:00:00Z
i3:  2021-12-25T02:00:00Z

Click here to view code image

// LocalDateTime
default Instant toInstant(ZoneOffset offset)

Converts a date-time to an instant by combining this LocalDateTime object with the specified time zone. The valid offset in Java is in the range from –18 to +18 hours. The absolute value of the offset is added to or subtracted from the date-time depending on whether it is specified as a negative or positive value, respectively, keeping in mind that an Instant represents a point in time on the UTC timeline.

This method is inherited by the LocalDateTime class from its superinterface java.time.chrono.ChronoLocalDateTime.

Temporal Arithmetic with Instants – Date and Time

Temporal Arithmetic with Instants

The Instant class provides plus and minus methods that return a copy of the original instant that has been incremented or decremented by a specific amount specified in terms of either seconds, milliseconds, or nanoseconds. Each amounts below is explicitly designated as a long to avoid problems if the amount does not fit into an int.

Click here to view code image

Instant event =
    Instant.EPOCH                    //            1970-01-01T00:00:00Z
           .plusSeconds(7L*24*60*60) // (+7days)   1970-01-08T00:00:00Z
           .plusSeconds(6L*60*60)    // (+6hrs)    1970-01-08T06:00:00Z
           .plusSeconds(5L*60)       // (+5mins)   1970-01-08T06:05:00Z
           .plusSeconds(4L)          // (+4s)      1970-01-08T06:05:04Z
           .plusMillis(3L*100)       // (+3ms)     1970-01-08T06:05:04.003Z
           .plusNanos(2L*1_000)      // (+2micros) 1970-01-08T06:05:04.003002Z
           .plusNanos(1L);           // (+1ns)     1970-01-08T06:05:04.003002001Z

However, it is more convenient to express the above calculation using the plus(amount, unit) method, which also allows the amount to be qualified by a unit. This is illustrated by the statement below, which is equivalent to the one above.

Click here to view code image

Instant ptInTime =
     Instant.EPOCH                          // 1970-01-01T00:00:00Z
            .plus(7L, ChronoUnit.DAYS)      // 1970-01-08T00:00:00Z
            .plus(6L, ChronoUnit.HOURS)     // 1970-01-08T06:00:00Z
            .plus(5L, ChronoUnit.MINUTES)   // 1970-01-08T06:05:00Z
            .plus(4L, ChronoUnit.SECONDS)   // 1970-01-08T06:05:04Z
            .plus(3L, ChronoUnit.MILLIS)    // 1970-01-08T06:05:04.003Z
            .plus(2L, ChronoUnit.MICROS)    // 1970-01-08T06:05:04.003002Z
            .plus(1L, ChronoUnit.NANOS);    // 1970-01-08T06:05:04.003002001Z

The code below shows the plus() method of the Instant class that takes a Duration (p. 1064) as the amount to add.

Click here to view code image

Instant start = Instant.EPOCH
                       .plus(20, ChronoUnit.MINUTES);// 1970-01-01T00:20:00Z
Duration length = Duration.ZERO.plusMinutes(90);     // PT1H30M (90 mins)
Instant end = start.plus(length);                    // 1970-01-01T01:50:00Z

The until() method calculates the amount of time between two instants in terms of the unit specified in the method.

Click here to view code image

long eventDuration1 = start.until(end, ChronoUnit.MINUTES);  // 90 minutes
long eventDuration2 = start.until(end, ChronoUnit.HOURS);    // 1 hour

As an Instant does not represent an amount of time, but a point on the timeline, it cannot be used in temporal arithmetic with other temporal objects. Although an Instant incorporates a date, it is not possible to access it in terms of year and month.

Click here to view code image

Instant plusSeconds/minusSeconds(long seconds)
Instant plusMillis/minusMillis(long millis)
Instant plusNanos/minusNanos(long nanos)

Return a copy of this instant, with the specified amount added or subtracted. Note that the argument type is long.

The methods throw a DateTimeException if the result is not a valid instant, and an ArithmeticException if numeric flow occurs during the operation.

Click here to view code image

Instant plus(long amountToAdd, TemporalUnit unit)
Instant minus(long amountToSub, TemporalUnit unit)

Return a copy of this instant with the specified amount added or subtracted, respectively, where the specified TemporalUnit qualifies the amount (p. 1044).

The following units, defined as constants by the ChronoUnit class, can be used to qualify the amount: NANOS, MICROS, MILLIS, SECONDS, MINUTES, HOURS, HALF_DAYS, and DAYS (p. 1044).

A method call can result in any one of these exceptions: DateTimeException (if the operation cannot be performed), UnsupportedTemporalTypeException (if the unit is not supported), or ArithmeticException (if numeric overflow occurs).

Click here to view code image

Instant isSupported(TemporalUnit unit)

Returns true if the specified unit is supported (p. 1044), in which case, the unit can be used in plus/minus operations on an instant. If the specified unit is not supported, the plus/minus methods that accept a unit will throw an exception.

Click here to view code image

Instant plus(TemporalAmount amountToAdd)
Instant minus(TemporalAmount amountToSubtract)

Return a copy of this instant, with the specified amount added or subtracted. The amount is typically defined as a Duration.

A method call can result in any one of these exceptions: DateTimeException (if the operation cannot be performed) or ArithmeticException (if numeric overflow occurs).

Click here to view code image

long until(Temporal endExclusive, TemporalUnit unit)

Calculates the amount of time between two temporal objects in terms of the specified TemporalUnit (p. 1044). The start and end points are this temporal object and the specified temporal argument, where the end point is excluded.

The start point is an Instant, and the end point temporal is converted to an Instant, if necessary.

The following units, defined as constants by the ChronoUnit class, can be used to indicate the unit in which the result should be returned: NANOS, MICROS, MILLIS, SECONDS, MINUTES, HOURS, HALF_DAYS, and DAYS (p. 1044).

The until() method can result in any one of these exceptions: DateTimeException (the temporal amount cannot be calculated or the end temporal cannot be converted to the appropriate temporal object), UnsupportedTemporalTypeException (the unit is not supported), or ArithmeticException (numeric overflow occurred).

Comparing Instants – Date and Time

Comparing Instants

The methods isBefore() and isAfter() can be used to determine if one instant is before or after the other on the timeline, respectively.

Click here to view code image

// instA is  1970-01-01T00:00:00.000000500Z
// instB is  1949-03-01T12:30:15Z
// instC is -1949-03-01T12:30:15Z
out.println(instA.isBefore(instB));              // false
out.println(instA.isAfter(instC));               // true

The Instant class also overrides the equals() method and the hashCode() method of the Object class, and implements the Comparable<Instant> interface. Instants can readily be used in collections. The code below illustrates comparing instants.

Click here to view code image

out.println(instA.equals(instB));                 // false
out.println(instA.equals(instC));                 // false
List<Instant> list = Arrays.asList(instA, instB, instC);
Collections.sort(list);             // Natural order: position on the timeline.
// [-1949-03-01T12:30:15Z, 1949-03-01T12:30:15Z, 1970-01-01T00:00:00.000000500Z]

Click here to view code image

boolean isBefore(Instant other)
boolean isAfter(Instant other)

Determine whether this Instant is before or after the other instant on the timeline, respectively.

boolean equals(Object other)

Determines whether this Instant is equal to the other instant, based on the timeline position of the instants.

int hashCode()

Returns a hash code for this Instant.

int compareTo(Instant other)

Compares this Instant with the other instant, based on the timeline position of the instants.

Creating Modified Copies of Instants

The Instant class provides the with(field, newValue) method that returns a copy of this instant with either the epoch-second or the nano-of-second set to a new value, while the other one is unchanged.

Click here to view code image

Instant with(TemporalField field, long newValue)

Returns a copy of this instant where either the epoch-second or the nano-of-second is set to the specified value. The value of the other is retained.

This method only supports the following ChronoField constants: NANO_OF_SECOND, MICRO_OF_SECOND, MILLI_OF_SECOND, and INSTANT_SECONDS (p. 1046). For the first three fields, the nano-of-second is replaced by appropriately converting the specified value, and the epoch-second will be unchanged in the copy returned by the method. For the INSTANT_SECONDS field, the epoch-second will be replaced and the nanosecond will be unchanged in the copy returned by the method. Valid values that can be specified with these constants are [0–999999999], [0–999999], [0–999], and a long, respectively.

This method throws a DateTimeException if the field cannot be set, an Unsupported-TemporalTypeException if the field is not supported, and an ArithmeticException if number overflow occurs.

In the code below, the three instants i1, i2, and i3 will have the nano-of-second set to 5,000,000,000 nanoseconds using the with() method, but the epoch-second will not be changed.

Click here to view code image

Instant i0, i1, i2, i3;
i0 = Instant.now();
out.println(i0);                             // 2021-02-28T08:43:35.864Z
i1 = i0.with(ChronoField.NANO_OF_SECOND,  500_000_000);// 500000000 ns.
i2 = i0.with(ChronoField.MICRO_OF_SECOND, 500_000);    // 500000×1000 ns.
i3 = i0.with(ChronoField.MILLI_OF_SECOND, 500);        // 500×1000000 ns.
out.println(i1);                             // 2021-02-28T08:43:35.500Z
out.println(i1.equals(i2));                  // true
out.println(i1.equals(i3));                  // true

In the code below, oneInstant has the nano-of-second set to 500,000,000 nanoseconds and the epoch-second set to 1 day after the epoch.

Click here to view code image

Instant oneInstant = Instant.now()
                            .with(ChronoField.MILLI_OF_SECOND, 500)
                            .with(ChronoField.INSTANT_SECONDS, 24L*60*60);
out.println(oneInstant);                     // 1970-01-02T00:00:00.500Z

Converting Instants – Date and Time

Converting Instants

Each of the classes LocalTime, LocalDate, LocalDateTime, and ZonedDateTime provides the ofInstant() method to obtain a temporal object from an Instant. The code below shows how instants can be converted to other temporal objects for a given time zone. For date/time represented by this particular instant, the offset for the time zone “America/New_York” is -4 hours from UTC.

Click here to view code image

Instant instant = Instant.parse(“2021-04-28T03:15:00Z”);
ZoneId zid = ZoneId.of(“America/New_York”);
LocalTime lt = LocalTime.ofInstant(instant, zid);           // 10:18:30
LocalDate ld = LocalDate.ofInstant(instant, zid);           // 2021-04-27
LocalDateTime ldt = LocalDateTime.ofInstant(instant, zid);  // 2021-04-27T23:15
ZonedDateTime zdt = ZonedDateTime.ofInstant(instant, zid);
    // 2021-04-27T23:15-04:00[America/New_York]

Click here to view code image

static
TemporalType
 ofInstant(Instant instant, ZoneId zone)

Creates a TemporalType object from the given Instant and ZoneId (p. 1072), where TemporalType can be LocalTime, LocalDate, LocalDateTime, or ZonedDateTime.

17.5 Working with Periods

For representing an amount of time, the Date and Time API provides the two classes Period and Duration. We will concentrate on the Period class in this section and discuss the Duration class in §17.6, p. 1064.

The Period class essentially represents a date-based amount of time in terms of years, months, and days, whereas, the Duration class represents a time-based amount of time in terms of seconds and nanoseconds.

The date-based Period class can be used with the LocalDate class, and not surprisingly, the time-based Duration class can be used with the LocalTime class. Of course, the LocalDateTime class can use both temporal amount classes.

The Period and Duration classes are in the same package (java.time) as the temporal classes, and the repertoire of methods they provide should look familiar, as they share many of the method prefixes with the temporal classes (Table 17.2, p. 1026).

The mantra of immutable and thread-safe objects also applies to both the Period and the Duration classes.