Parallel Streams – Streams

16.9 Parallel Streams

The Stream API makes it possible to execute a sequential stream in parallel without rewriting the code. The primary reason for using parallel streams is to improve performance, but at the same time ensuring that the results obtained are the same, or at least compatible, regardless of the mode of execution. Although the API goes a long way to achieve its aim, it is important to understand the pitfalls to avoid when executing stream pipelines in parallel.

Building Parallel Streams

The execution mode of an existing stream can be set to parallel by calling the parallel() method on the stream (p. 933). The parallelStream() method of the Collection interface can be used to create a parallel stream with a collection as the data source (p. 897). No other code is necessary for parallel execution, as the data partitioning and thread management for a parallel stream are handled by the API and the JVM. As with any stream, the stream is not executed until a terminal operation is invoked on it.

The isParallel() method of the stream interfaces can be used to determine whether the execution mode of a stream is parallel (p. 933).

Parallel Stream Execution

The Stream API allows a stream to be executed either sequentially or in parallel— meaning that all stream operations can execute either sequentially or in parallel. A sequential stream is executed in a single thread running on one CPU core. The elements in the stream are processed sequentially in a single pass by the stream operations that are executed in the same thread (p. 891).

A parallel stream is executed by different threads, running on multiple CPU cores in a computer. The stream elements are split into substreams that are processed by multiple instances of the stream pipeline being executed in multiple threads. The partial results from processing of each substream are merged (or combined) into a final result (p. 891).

Parallel streams utilize the Fork/Join Framework (§23.3, p. 1447) under the hood for executing parallel tasks. This framework provides support for the thread management necessary to execute the substreams in parallel. The number of threads employed during parallel stream execution is dependent on the CPU cores in the computer.

Figure 16.12, p. 963, illustrates parallel functional reduction using the three-argument reduce(identity, accumulator, combiner) terminal operation (p. 962).

Figure 16.14, p. 967, illustrates parallel mutable reduction using the three-argument collect(supplier, accumulator, combiner) terminal operation (p. 966).

Working with Durations – Date and Time

17.6 Working with Durations

The java.time.Duration class implements a time-based amount of time in terms of seconds and nanoseconds, using a long and an int value for these time units, respectively. Although the Duration class models an amount of time in terms of seconds and nanoseconds, a duration can represent an amount of time in terms of days, hours, and minutes. As these time units have fixed lengths, it makes interoperability between these units possible. The time-based Duration class can be used with the LocalTime and LocalDateTime classes, as these classes have time fields. In contrast, the Period class essentially represents a date-based amount of time in terms of years, months, and days (p. 1057).

The Period and Duration classes are in the same package (java.time) as the temporal classes. The Period and the Duration classes provide similar methods, as they share many of the method prefixes and common methods with the temporal classes (Table 17.2, p. 1026, and Table 17.3, p. 1026). Their objects are immutable and thread-safe. However, there are also differences between the two classes (p. 1072). Familiarity with one would go a long way toward understanding the other.

Creating Durations

Like the Period class, the Duration class provides the static factory methods ofUNIT() to construct durations with different units.

Click here to view code image

Duration d1 = Duration.ofDays(1L);                               // PT24H
Duration d2 = Duration.ofHours(24L);                             // PT24H
Duration d3 = Duration.ofMinutes(24L*60);                        // PT24H
Duration d4 = Duration.ofSeconds(24L*60*60);                     // PT24H
Duration d5 = Duration.ofMillis(24L*60*60*1000);                 // PT24H
Duration d6 = Duration.ofSeconds(24L*60*60 – 1, 1_000_000_000L); // (1) PT24H
Duration d7 = Duration.ofNanos(24L*60*60*1_000_000_000); // (2) PT24H
Duration d8 = Duration.ofNanos(24*60*60*1_000_000_000);  // (3) PT-1.857093632S

The durations created above all have a length of 1 day, except for the one in the last declaration statement. Note that the amount specified should be a long value. It is a good defensive practice to always designate the amount as such in order to avoid inadvertent problems if the amount does not fit into an int. The designation L should be placed such that there is no danger of any previous operation in the expression causing a rollover. This problem is illustrated at (3), where the int value of the argument expression is rolled over, as it is greater than Integer.MAX_VALUE.

The statement at (1) above also illustrates that the value of the nanoseconds is adjusted so that it is always between 0 and 999,999,999. The adjustment at (1) results in the value 0 for nanoseconds and the number of seconds being incremented by 1.

Calling the toString() method on the first seven declarations above, the result is the string “PT24H” (a duration of 24 hours), whereas for the last duration at (3), the result string is “PT-1.857093632S”, which clearly indicates that the int amount was not interpreted as intended.

The previous declarations are equivalent to the ones below, where the amount is qualified with a specific unit in the call to the of(value, unit) method.

Click here to view code image

Duration d11 = Duration.of(1L, ChronoUnit.DAYS);              // P24H
Duration d22 = Duration.of(24L, ChronoUnit.HOURS);            // P24H
Duration d33 = Duration.of(24L*60, ChronoUnit.MINUTES);       // P24H
Duration d44 = Duration.of(24L*60*60, ChronoUnit.SECONDS);    // P24H
Duration d88 = Duration.of(24L*60*60*1000, ChronoUnit.MILLIS);// P24H
Duration d77 = Duration.of(24L*60*60*1_000_000_000,
                           ChronoUnit.NANOS);                 // P24H

The code snippet below does not create a duration of 8 days—it creates a duration of 24 hours. The first method call is invoked with the class name, and the subsequent method call is on the new Duration object returned as a consequence of the first call. The of() method creates a new Duration object based on its argument.

Click here to view code image

Duration duration = Duration.ofDays(7).ofHours(24);   // PT24H. Logical error.

Like the Period class, we can create a duration that represents the amount of time between two temporal objects by calling the static method between() of the Duration class.

Click here to view code image

LocalTime startTime = LocalTime.of(14, 30);                  // 14:30
LocalTime endTime   = LocalTime.of(17, 45, 15);              // 17:45:15
Duration interval1 = Duration.between(startTime, endTime);   // PT3H15M15S
Duration interval2 = Duration.between(endTime, startTime);   // PT-3H-15M-15S

Note the exception thrown in the last statement below because a LocalDateTime object cannot be derived from a LocalTime object, whereas the converse is true.

Click here to view code image

LocalDateTime dateTime = LocalDateTime.of(2021, 4, 28,
                                          17, 45, 15);    // 2021-04-28T17:45:15
Duration interval3 = Duration.between(startTime, dateTime);  // PT3H15M15S
Duration interval4 = Duration.between(dateTime, startTime);  // DateTimeException!

The Duration class also provides the parse() static method to create a duration from a text representation of a duration based on the ISO standard. If the format of the string is not correct, a DateTimeParseException is thrown. Formatting according to the toString() method is shown in parentheses.

Click here to view code image

Duration da = Duration.parse(“PT3H15M10.1S”);// 3hrs. 15mins. 10.1s.(PT3H15M10.1S)
Duration db = Duration.parse(“PT0.999S”);    // 999000000 nanos.    (PT0.999S)
Duration dc = Duration.parse(“-PT30S”);      // -30 seconds.        (PT-30S)
Duration dd = Duration.parse(“P-24D”);       // -24 days            (PT-576H)
Duration dd = Duration.parse(“P24H”);        // Missing T. DateTimeParseException!

static Duration ZERO

This constant defines a Duration of length zero (PT0S).

Click here to view code image

static Duration ofDays(long days)
static Duration ofHours(long hours)
static Duration ofMinutes(long minutes)
static Duration ofMillis(long millis)
static Duration ofSeconds(long seconds)
static Duration ofSeconds(long seconds, long nanoAdjustment)
static Duration ofNanos(long nanos)

These static factory methods return a Duration representing an amount of time in seconds and nanoseconds that is equivalent to the specified amount, depending on the method. Nanoseconds are implicitly set to zero. The argument value can be negative. Standard definitions of the units are used. Note that the amount is specified as a long value.

Click here to view code image

static Duration of(long amount, TemporalUnit unit)

This static factory method returns a Duration representing an amount of time in seconds and nanoseconds that is equivalent to the specified amount in the specified temporal unit. The amount is specified as a long value, which can be negative.

Valid ChronoUnit constants to qualify the amount specified in the method call are the following: NANOS, MICROS, MILLIS, SECONDS, MINUTES, HOURS, HALF_DAYS, and DAYS (p. 1044). These units have a standard or an estimated duration.

Click here to view code image

static Duration between(Temporal startInclusive, Temporal endExclusive)

This static method returns the duration between two temporal objects that must support the seconds unit and where it is possible to convert the second temporal argument to the first temporal argument type, if necessary. Otherwise, a DateTimeException is thrown. The result of this method can be a negative period if the end temporal is before the start temporal.

String toString()

Returns a text representation of a Duration according to the ISO standard: PThHmMd.dS—that is, h Hours, m Minutes, and d.d Seconds, where the nanoseconds are formatted as a fraction of a second.

Click here to view code image

static Duration parse(CharSequence text)

This static method returns a Duration parsed from a character sequence. The formats accepted are based on the ISO duration format PTnHnMn.nS—for example, “PT2H3M4.5S” (2 hours, 3 minutes, and 4.5 seconds). A java.time.format.Date-TimeParseException is thrown if the text cannot be parsed to a duration.

Differences between Periods and Durations – Date and Time

Differences between Periods and Durations

Table 17.4 summarizes the differences between selected methods of the Period and the Duration classes, mainly in regard to the temporal units supported, representation for parsing and formatting, and comparison. N/A stands for Not Applicable.

Table 17.4 Some Differences between the Period Class and the Duration Class

MethodsThe Period classThe Duration class
of(amount, unit)N/AValid ChronoUnits: NANOS, MICROS, MILLIS, SECONDS, MINUTES, HOURS, HALF_DAYS, DAYS (p. 1065).
parse(text) toString()Representation based on: PnYnMnD and PnW (p. 1057).Representation based on: PnDTnHnMn.nS (p. 1065).
get(unit)Supported ChronoUnits: YEARS, MONTHS, DAYS (p. 1059).Supported ChronoUnits: NANOS, SECONDS (p. 1067).
getUnits()Supported ChronoUnits: YEARS, MONTHS, DAYS (p. 1059).Supported ChronoUnits: NANOS, SECONDS (p. 1067).
equals(other)Based on values of individual units (p. 1059).Based on total length (p. 1067).
compareTo(other)N/ANatural order: total length (p. 1067).
minus(amount, unit) plus(amount, unit)N/AValid ChronoUnits: NANOS, MICROS, MILLIS, SECONDS, MINUTES, HOURS, HALF_DAYS, DAYS (p. 1069).
abs()N/AReturns copy with positive length (p. 1069).
dividedBy(divisor)N/AReturns copy after dividing by divisor (p. 1069).
normalized()Only years and months normalized (p. 1061).N/A

17.7 Working with Time Zones and Daylight Savings

The following three classes in the java.time package are important when dealing with date and time in different time zones and daylight saving hours: ZoneId, ZoneOffset, and ZonedDateTime.

UTC (Coordinated Universal Time) is the primary time standard used for keeping time around the world. UTC/Greenwich is the time at Royal Observatory, Greenwich, England. It is the basis for defining time in different regions of the world.

Time Zones and Zone Offsets

A time zone defines a region that observes the same standard time. The time observed in a region is usually referred to as the local time. A time zone is described by a zone offset from UTC/Greenwich and any rules for applying daylight saving time (DST). Time zones that practice DST obviously have variable offsets during the year to account for DST.

In Java, each time zone has a zone ID that is represented by the class java.time.ZoneId. The class java.time.ZoneOffset, which extends the ZoneId class, represents a zone offset from UTC/Greenwich. For example, the time zone with US/Eastern as the zone ID has the offset -04:00 during daylight saving hours—that is, it is 4 hours behind UTC/Greenwich when DST is in effect.

The time zone offset at UTC/Greenwich is represented by ZoneOffset.UTC and, by convention, is designated by the letter Z. GMT (Greenwich Mean Time) has zero offset from UTC/Greenwich (UTC+0), thus the two are often used as synonyms; for example, GMT-4 is equivalent to UTC-4. However, GMT is a time zone, whereas UTC is a time standard.

Java uses the IANA Time Zone Database (TZDB) maintained by the Internet Assigned Numbers Authority (IANA) that updates the database regularly, in particular, regarding changes to the rules for DST practiced by a time zone (p. 1073).

A set with names of available time zones can readily be obtained by calling the ZoneId.getAvailableZoneIds() method. Time zones have unique names of the form Area/Location—for example, US/Eastern, Europe/Oslo. The following code prints a very long list of time zones that are available to a Java application.

Click here to view code image

ZoneId.getAvailableZoneIds()                 // Prints a long list of zone names.
      .stream()
      .sorted()
      .forEachOrdered(System.out::println);  // Output not shown intentionally.

The ZoneId.of() method creates an appropriate zone ID depending on the format of its argument:

  • UTC-equivalent ID, if only “Z”, “UTC”, or “GMT” is specified. As these designations are equivalent, the result is ZoneOffset.UTC; that is, it represents the offset UTC+0.
  • Offset-based ID, if the format is “+hh:mm” or “-hh:mm”—for example, “+04:00”, “-11:30”. The result is an instance of the ZoneOffset class with the parsed offset.
  • Prefix offset-based ID, if the format is the prefix UTC or GMT followed by a numerical offset—for example, “UTC+04:00”, “GMT-04:00”. The result is a time zone represented by a ZoneId with the specified prefix and a parsed offset.
  • Region-based ID, if the format is “Area/Location”—for example, “US/Eastern”, “Europe/Oslo”. The result is a ZoneId that can be used, among other things, to look up the underlying zone rules associated with the zone ID. In the examples in this section, a zone ID is specified in the format of a region-based ID.

The code below creates a region-based zone ID. The method ZoneId.systemDefault() returns the system default zone ID.

Click here to view code image

ZoneId estZID = ZoneId.of(“US/Eastern”);              // Create a time zone ID.
System.out.println(estZID);                           // US/Eastern
System.out.println(ZoneId.systemDefault());           // Europe/Oslo

Selected methods in the ZoneId abstract class are presented below. The concrete class ZoneOffset extends the ZoneId class.

Click here to view code image

static ZoneId of(String zoneId)

Returns an appropriate zone ID depending on the format of the zone ID string. See the previous discussion on zone ID.

Click here to view code image

String          toString()
abstract String getId()

Return a string with the zone ID, typically in one of the formats accepted by the of() method.

abstract ZoneRules getRules()

Retrieves the associated time zone rules for this zone ID. The rules determine the functionality associated with a time zone, such as daylight savings (p. 1082).

Click here to view code image

static Set<String> getAvailableZoneIds()

Returns a set with the available time zone IDs.

static ZoneId systemDefault()

Returns the zone ID of the default time zone of the system.

The LocalDateTime Class – Date and Time

The LocalDateTime Class

The class LocalDateTime allows the date and the time to be combined into one entity, which is useful for representing such concepts as appointments that require both a time and a date. The of() methods in the LocalDateTime class are combinations of the of() methods from the LocalTime and LocalDate classes, taking values of both time and date fields as arguments. The toString() method of this class will format the temporal fields according to the ISO standard (§18.6, p. 1134):

uuuu-MM-ddTHH:mm:ss.SSSSSSSSS

The letter T separates the values of the date fields from those of the time fields.

Click here to view code image

// 2021-04-28T12:15
LocalDateTime dt1 = LocalDateTime.of(2021, 4, 28, 12, 15);
// 2021-08-19T14:00
LocalDateTime dt2 = LocalDateTime.of(2021, Month.AUGUST, 19, 14, 0);

The LocalDateTime class also provides an of() method that combines a LocalDate object and a LocalTime object. The first declaration in the next code snippet combines a date and a time. The static field LocalTime.NOON defines the time at noon. In addition, the LocalTime class provides the instance method atDate(), which takes a date as an argument and returns a LocalDateTime object. The second declaration combines the time at noon with the date referred to by the reference date1. Conversely, the LocalDate class provides the overloaded instance method atTime() to combine a date with a specified time. In the last two declarations, the atTime() method is passed a LocalTime object and values for specific time fields, respectively.

Click here to view code image

// LocalDate date1 is 1969-07-20.
LocalDateTime dt3 = LocalDateTime.of(date1, LocalTime.NOON); // 1969-07-20T12:00
LocalDateTime dt4 = LocalTime.of(12, 0).atDate(date1);       // 1969-07-20T12:00
LocalDateTime dt5 = date1.atTime(LocalTime.NOON);            // 1969-07-20T12:00
LocalDateTime dt6 = date1.atTime(12, 0);                     // 1969-07-20T12:00

As a convenience, each temporal class provides a static method now() that reads the system clock and returns the values for the relevant temporal fields in an instance of the target class.

Click here to view code image

LocalTime currentTime = LocalTime.now();
LocalDate currentDate = LocalDate.now();
LocalDateTime currentDateTime = LocalDateTime.now();

Example 17.1 includes the different ways to create temporal objects that we have discussed so far.

Click here to view code image

// LocalTime
LocalDateTime atDate(LocalDate date)

Returns a LocalDateTime that combines this time with the specified date.

Click here to view code image

// LocalDate
LocalDateTime atTime(LocalTime time)
LocalDateTime atTime(int hour, int minute)
LocalDateTime atTime(int hour, int minute, int second)
LocalDateTime atTime(int hour, int minute, int second, int nanoOfSecond)
LocalDateTime atStartOfDay()

Return a LocalDateTime that combines this date with the specified values for time fields. The second and nanosecond fields are set to zero, if their values are not specified. In the last method, this date is combined with the time at midnight.

Click here to view code image

// LocalDateTime
ZonedDateTime atZone(ZoneId zone)

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

Click here to view code image

// LocalTime, LocalDate, LocalDateTime, respectively.
static LocalTime now()
static LocalDate now()
static LocalDateTime now()

Each temporal class has this static factory method, which returns either the current time, date, or date-time from the system clock.

Example 17.1 Creating Local Dates and Local Times

Click here to view code image

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Month;
public class CreatingTemporals {
  public static void main(String[] args) {
    // Creating a specific time from time-based values:
    LocalTime time1 = LocalTime.of(8, 15, 35, 900);// 08:15:35.000000900
    LocalTime time2 = LocalTime.of(16, 45);        // 16:45
//  LocalTime time3 = LocalTime.of(25, 13, 30);    // DateTimeException
    System.out.println(“Surveillance start time: ” + time1);
    System.out.println(“Closing time: ” + time2);
    // Creating a specific date from date-based values:
    LocalDate date1 = LocalDate.of(1969, 7, 20);            // 1969-07-20
    LocalDate date2 = LocalDate.of(-3113, Month.AUGUST, 11);// -3113-08-11
//  LocalDate date3 = LocalDate.of(2021, 13, 11);           // DateTimeException
//  LocalDate date4 = LocalDate.of(2021, 2, 29);            // DateTimeException
    System.out.println(“Date of lunar landing:        ” + date1);
    System.out.println(“Start Date of Mayan Calendar: ” + date2);
    // Creating a specific date-time from date- and time-based values.
    // 2021-04-28T12:15
    LocalDateTime dt1 = LocalDateTime.of(2021, 4, 28, 12, 15);
    // 2021-08-17T14:00
    LocalDateTime dt2 = LocalDateTime.of(2021, Month.AUGUST, 17, 14, 0);
    System.out.println(“Car service appointment: ” + dt1);
    System.out.println(“Hospital appointment:    ” + dt2);
    // Combining date and time objects.
    // 1969-07-20T12:00
    LocalDateTime dt3 = LocalDateTime.of(date1, LocalTime.NOON);
    LocalDateTime dt4 = LocalTime.of(12, 0).atDate(date1);
    LocalDateTime dt5 = date1.atTime(LocalTime.NOON);
    LocalDateTime dt6 = date1.atTime(12, 0);
    System.out.println(“Factory date-time combo: ” + dt3);
    System.out.println(“Time with date combo:    ” + dt4);
    System.out.println(“Date with time combo:    ” + dt5);
    System.out.println(“Date with explicit time combo: ” + dt6);
    // Current time:
    LocalTime currentTime = LocalTime.now();
    System.out.println(“Current time:      ” + currentTime);
    // Current date:
    LocalDate currentDate = LocalDate.now();
    System.out.println(“Current date:      ” + currentDate);
    // Current date and time:
    LocalDateTime currentDateTime = LocalDateTime.now();
    System.out.println(“Current date-time: ” + currentDateTime);
  }
}

Possible output from the program:

Click here to view code image Surveillance start time: 08:15:35.000000900
Closing time: 16:45
Date of lunar landing:        1969-07-20
Start Date of Mayan Calendar: -3113-08-11
Car service appointment: 2021-04-28T12:15
Hospital appointment:    2021-08-17T14:00
Factory date-time combo: 1969-07-20T12:00
Time with date combo:    1969-07-20T12:00
Date with time combo:    1969-07-20T12:00
Date with explicit time combo: 1969-07-20T12:00
Current time:      10:55:41.296744
Current date:      2021-03-05
Current date-time: 2021-03-05T10:55:41.299318

The LocalTime Class – Date and Time

The LocalTime Class

The declaration statements below show examples of creating instances of the LocalTime class to represent time on a 24-hour clock in terms of hours, minutes, seconds, and nanoseconds.

Click here to view code image

LocalTime time1 = LocalTime.of(8, 15, 35, 900);   // 08:15:35.000000900
LocalTime time2 = LocalTime.of(16, 45);           // 16:45
// LocalTime time3 = LocalTime.of(25, 13, 30);    // DateTimeException

The ranges of values for time fields hour (0–23), minute (0–59), second (0–59), and nanosecond (0–999,999,999) are defined by the ISO standard. The toString() method of the class will format the time fields according to the ISO standard as follows:

HH:mm:ss.SSSSSSSSS

Omitting the seconds (ss) and fractions of seconds (SSSSSSSSS) in the call to the of() method implies that their value is zero. (More on formatting in §18.6, p. 1134.) In the second declaration statement above, the seconds and the nanoseconds are not specified in the method call, resulting in their values being set to zero. In the third statement, the value of the hour field (25) is out of range, and if the statement is uncommented, it will result in a DateTimeException.

The LocalDate Class

Creating instances of the LocalDate class is analogous to creating instances of the LocalTime class. The of() method of the LocalDate class is passed values for date fields: the year, month of the year, and day of the month.

Click here to view code image

LocalDate date1 = LocalDate.of(1969, 7, 20);            // 1969-07-20
LocalDate date2 = LocalDate.of(-3113, Month.AUGUST, 11);// -3113-08-11
//  LocalDate date3 = LocalDate.of(2021, 13, 11);       // DateTimeException
//  LocalDate date4 = LocalDate.of(2021, 2, 29);        // DateTimeException

The ranges of the values for date fields year, month, and day are (–999,999,999 to +999,999,999), (1–12), and (1–31), respectively. The month can also be specified using the enum constants of the java.time.Month class, as in the second declaration statement above. A DateTimeException is thrown if the value of any parameter is out of range, or if the day is invalid for the specified month of the year. In the third declaration, the value of the month field 13 is out of range. In the last declaration, the month of February cannot have 29 days, since the year 2021 is not a leap year.

The toString() method of the LocalDate class will format the date fields according to the ISO standard (§18.6, p. 1134):

uuuu-MM-dd

The year is represented as a proleptic year in the ISO standard, which can be negative. A year in CE (Current Era, or AD) has the same value as a proleptic year; for example, 2021 CE is the same as the proleptic year 2021. However, for a year in BCE (Before Current Era, or BC), the proleptic year 0 corresponds to 1 BCE, the proleptic year –1 corresponds to 2 BCE, and so on. In the second declaration in the preceding set of examples, the date -3113-08-11 corresponds to 11 August 3114 BCE.

Working with Dates and Times – Date and Time

17.2 Working with Dates and Times

The classes LocalTime, LocalDate, and LocalDateTime in the java.time package represent time-based, date-based, and combined date-based and time-based temporal objects, respectively, that are all time zone agnostic. These classes represent human time that is calendar-based, meaning it is defined in terms of concepts like year, month, day, hour, minute, and second, that humans use. The Instant class can be used to represent machine time, which is defined as a point measured with nanosecond precision on a continuous timeline starting from a specific origin (p. 1049).

Time zones and daylight savings are discussed in §17.7, p. 1072.

Creating Dates and Times

The temporal classes in the java.time package do not provide any public constructors to create temporal objects. Instead, they provide overloaded static factory methods named of which create temporal objects from constituent temporal fields. We use the term temporal fields to mean both time fields (hours, minutes, seconds, nanoseconds) and date fields (year, month, day). The of() methods check that the values of the arguments are in range. Any invalid argument results in a java.time.DateTimeException.

Click here to view code image

// LocalTime
static LocalTime of(int hour, int minute)
static LocalTime of(int hour, int minute, int second)
static LocalTime of(int hour, int minute, int second, int nanoOfSecond)
static LocalTime ofSecondOfDay(long secondOfDay)

These static factory methods in the LocalTime class return an instance of Local-Time based on the specified values for the specified time fields. The second and nanosecond fields are set to zero, if not specified.

The last method accepts a value for the secondOfDay parameter in the range [0, 24 * 60 * 60 – 1] to create a LocalTime.

Click here to view code image

// LocalDate
static LocalDate of(int year, int month, int dayOfMonth)
static LocalDate of(int year, Month month, int dayOfMonth)
static LocalDate ofYearDay(int year, int dayOfYear)

These static factory methods in the LocalDate class return an instance of LocalDate based on the specified values for the date fields. The java.time.Month enum type allows months to be referred to by name—for example, Month.MARCH. Note that month numbering starts with 1 (Month.JANUARY).

The last method creates a date from the specified year and the day of the year.

Click here to view code image

// LocalDateTime
static LocalDateTime of(int year, int month, int dayOfMonth,
                        int hour, int minute)
static LocalDateTime of(int year, int month, int dayOfMonth,
                        int hour, int minute, int second)
static LocalDateTime of(int year, int month, int dayOfMonth, int hour,
                        int minute, int second, int nanoOfSecond)
static LocalDateTime of(int year, Month month, int dayOfMonth,
                        int hour, int minute, int second)
static LocalDateTime of(int year, Month month, int dayOfMonth,
                        int hour, int minute)
static LocalDateTime of(int year, Month month, int dayOfMonth,
                        int hour, int minute, int second, int nanoOfSecond)

These static factory methods in the LocalDateTime class return an instance of LocalDateTime based on the specified values for the time and date fields. The second and nanosecond fields are set to zero, if not specified. The java.time.Month enum type allows months to be referred to by name—for example, Month.MARCH (i.e., month 3 in the year).

Click here to view code image

static LocalDateTime of(LocalDate date, LocalTime time)

Combines a LocalDate and a LocalTime into a LocalDateTime.

All code snippets in this subsection can be found in Example 17.1, p. 1031, ready for running and experimenting. An appropriate import statement with the java.time package should be included in the source file to access any of the temporal classes by their simple name.

Comparing Dates and Times – Date and Time

Comparing Dates and Times

It is also possible to check whether a temporal object represents a point in time before or after another temporal object of the same type. In addition, the LocalDate and LocalDateTime classes provide an isEqual() method that determines whether a temporal object is equal to another temporal object of the same type. In contrast, the equals() method allows equality comparison with an arbitrary object.

Click here to view code image

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

The temporal classes implement the Comparable<E> interface, providing the compareTo() method so that temporal objects can be compared in a meaningful way. The temporal classes also override the equals() and the hashCode() methods of the Object class. These methods make it possible to both search for and sort temporal objects.

Click here to view code image

// LocalTime
boolean isBefore(LocalTime other)
boolean isAfter(LocalTime other)

Determine whether this LocalTime represents a point on the timeline before or after the other time, respectively.

Click here to view code image

// LocalDate
boolean isBefore(ChronoLocalDate other)
boolean isAfter(ChronoLocalDate other)
boolean isEqual(ChronoLocalDate other)
boolean isLeapYear()

The first two methods determine whether this LocalDate represents a point on the timeline before or after the other date, respectively. The LocalDate class implements the ChronoLocalDate interface.

The third method determines whether this date is equal to the specified date.

The last method checks for a leap year according to the ISO proleptic calendar system rules.

Click here to view code image

// LocalDateTime
boolean isBefore(ChronoLocalDateTime<?> other)
boolean isAfter(ChronoLocalDateTime<?> other)
boolean isEqual(ChronoLocalDateTime<?> other)

The first two methods determine whether this LocalDateTime represents a point on the timeline before or after the specified date-time, respectively. The Local-DateTime class implements the ChronoLocalDateTime<LocalDateTime> interface.

The third method determines whether this date-time object represents the same point on the timeline as the other date-time.

Click here to view code image

int compareTo(LocalTime other)                // LocalTime
int compareTo(ChronoLocalDate other)          // LocalDate
int compareTo(ChronoLocalDateTime<?> other)   // LocalDateTime

Compare this temporal object to another temporal object. The three temporal classes implement the Comparable<E> functional interface. The compareTo() method returns 0 if the two temporal objects are equal, a negative value if this temporal object is less than the other temporal object, and a positive value if this temporal object is greater than the other temporal object.

Creating Periods – Date and Time

Creating Periods

Like the temporal classes, the Period class does not provide any public constructors, but rather provides an overloaded static factory method of() to construct periods of different lengths, based on date units.

Click here to view code image

Period p = Period.of(2, 4, 8);         // (1)
System.out.println(p);                 // (2) P2Y4M8D (2 Years, 4 Months, 8 Days)
Period p1 = Period.ofYears(10);        // P10Y, period of 10 years.
Period p2 = Period.ofMonths(14);       // P14M, period of 14 months.
Period p3 = Period.ofDays(40);         // P40D, period of 40 days.
Period p4 = Period.ofWeeks(2);         // P14D, period of 14 days (2 weeks).

The most versatile of() method requires the amount of time for all date units: years, months, and days, as at (1). Other of() methods create a period based on a particular date unit, as shown in the examples above.

The toString() method of the Period class returns a text representation of a Period according to the ISO standard: PyYmMdD—that is, y Years, m Months, and d Days. The output from (2) above, P2Y4M8D, indicates a period of 2 years, 4 months, and 8 days.

The code snippet below does not create a period of 3 years, 4 months, and 5 days— it creates a period of only 5 days. The first method call is invoked with the class name, and the subsequent method calls are on the new Period object returned as a consequence of the previous call. The of() method creates a new Period object based on its argument.

Click here to view code image

Period period = Period.ofYears(3).ofMonths(4).ofDays(5); // P5D. Logical error.

As we would expect, we can create a period that represents the amount of time between two dates by calling the static method between() of the Period class.

Click here to view code image

LocalDate d1 = LocalDate.of(2021, 3, 1);  // 2021-03-01
LocalDate d2 = LocalDate.of(2022, 3, 1);  // 2022-03-01
Period period12 = Period.between(d1, d2); // P1Y
Period period21 = Period.between(d2, d1); // P-1Y

The Period class also provides the static method parse() to create a period from a string that contains a text representation of a period in the ISO standard. If the format of the string is not correct, a java.time.format.DateTimeParseException is thrown.

Click here to view code image

Period period2 = Period.parse(“P1Y15M20D”); // 1 year, 15 months, 20 days
Period period3 = Period.parse(“P20D”);      // 20 days
Period period4 = Period.parse(“P5W”);       // 35 days (5 weeks)
//  Period pX = Period.parse(“P24H”); // java.time.format.DateTimeParseException

static Period ZERO

This constant defines a Period of length zero (P0D).

Click here to view code image

static Period of(int years, int months, int days)
static Period ofYears(int years)
static Period ofMonths(int months)
static Period ofWeeks(int weeks)
static Period ofDays(int days)

These static factory methods return a Period representing an amount of time equal to the specified value of a date unit. Date units that are implicit are set to zero. A week is equal to 7 days. The argument value can be negative.

Click here to view code image

static Period between(LocalDate startDateInclusive,
                      LocalDate endDateExclusive)

This static method returns a Period consisting of the number of years, months, and days between the two dates. The calculation excludes the end date. The result of this method can be a negative period if the end date is before the start date.

String toString()

Returns a text representation of a Period according to the ISO standard. Typical formats are PyYmMdD and PnW—that is, y Years, m Months, and d Days, or n Weeks.

Click here to view code image

static Period parse(CharSequence text)

This static method returns a Period parsed from a character sequence—for example, “P3Y10M2D” (3 years, 10 months, 2 days). A java.time.format.Date-TimeParseException is thrown if the text cannot be parsed to a period.

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.