Chapter 4 – Date and Time
-Introduction
--Why a New API?
--A Simple Example
--The ISO-8601 String Format
--Design Considerations
--Understanding the Standard Method Names
---Understanding the of Type Methods
---Understanding the from Method
---Understanding the parse Method
---Understanding the format Method
---Understanding the get Type Method
---Understanding the is Type Methods
---Understanding the with Type Methods
---Understanding the plus Type Methods
---Understanding minus Type Methods
---Understanding the to Type Methods
---Understanding the at Type Methods
--Overview of the Date and Time Packages
-Getting the Current Time
--Working with the Instant Class
---Creating an Instant
---Creating an Instant Based on the Epoch
---Creating an Instant Based on Parsing a UTC String
---Creating an Instant by Adding or Subtracting a Time from an Instant
---Using the Instant Class’ get Type Methods
--Working with Time Intervals
---Creating Offsets Using the of Type Methods
---Creating Offsets Using the with Type Methods
---Creating a Duration Using the parse Method
---Creating a Duration by Adding or Subtracting a Unit
---Using the Duration Class
--Using the Period Class
-Using Date and Time Classes
--Creating Date and Time Classes
---Date and Time get Type Methods
---Adjusting a Date or Time
--Using the YearMonth, MonthDay, and Year Classes
---Comparing MonthDay Instances
--Using the Enumerations DayOfWeek and Month
---Using the DayOfWeek Enumeration
---Using the Month Enumeration
-Working with the Time Zone and Offset Classes
--Obtaining Available Zone Ids
--Working with ZoneOffsets
--Working with Zone Rules
-Formatting, Queries, and Value-Based Classes
--Formatting Dates and Times
--Using Temporal Queries
--Understanding Value-Based Classes
-Conclusion
Introduction
A significant addition to Java 8 is the introduction of the new date and time API intended to replace the existing java.util.Date and java.util.Calendar classes. It is based on the Joda (http://joda-time.sourceforge.net/) effort and is a large and rich API. Complete documentation of the API can be found at http://download.java.net/jdk8/docs/api/.
The date and time API is based on the ISO-8601 standard (http://www.iso.org/iso/home/standards/iso8601.htm) which uses the Gregorian calendar, the de facto date standard used throughout the world. The standard provides a way of expressing dates and handles leap years as well. However, it is not appropriate to use it to represent historical dates. Other calendar systems are supported in the java.time.chrono package.
Coordinate Universal Time (UTC) is the primary time standard in use today. It incorporates time zones and uses a time offset from UTC which is found along zero degrees longitude. The term, temporal-based, refers to classes such as LocalDate and ZonedDateTime which reflect different ways of expressing time.
There are several ways of organizing a discussion of the API. Time can be understood from a human perspective using measurements such as months and seconds. It can also be understood from the perspective of a computer. This measurement typically measures time from the epoch (January 1, 1970) in units such as milliseconds or nanoseconds. Both of these two perspectives are supported by the API giving the developer the option of using the most appropriate approach based on the context of the problem. These two types can be referred to as human time and machine time.
Human time is generally preferred when working with days as found in a calendar. Machine time is often preferred when dealing with durations of time such as the number of milliseconds between two events. However, it could also be expressed in human time such as the difference between two days.
An instant is an instantaneous point along a timeline. You can use this to represent events or at least the point in time the event occurs. It can be thought of as an event timestamp.
We also talk about time in terms of how long an activity occurs. Java 8 uses a Duration class to designate intervals measured in units like minutes or seconds. Duration can be used for a range such as the time it takes to open a valve or the length of an animation cycle. There is also the Period class that deals with intervals measured in days and months.
Many of the examples used in this chapter are dependent on the current date and time. As a result, the output of these examples may not reflect today’s date or time. However, they should reflect the current date and time when executed.
Why a New API?
The date and time classes available before Java 8 had a number of problems. For example, they were mutable which made them difficult to work with across threads. They were often not type safe and the internal numbers used to represent units such as months made them difficult to work with. It has also been difficult to support internationalization with the older Java date and time classes.
A Simple Example
Let’s start with an example that determines whether an individual is currently working. The worker is located in La Paz Bolivia, starts work at 8:00 in the morning and works for 6 hours. We want to call the individual from some other location, but before we call we want to make sure that they are scheduled to work.
First we need to determine the current time. We could use the DateTime class but this class does not include time zone information. So we use the ZonedDateTime class. It possesses a now method that returns an instance of the class representing the current date and time at our location. The following code sequence illustrates how this is done and then displays this date and time:
ZonedDateTime present = ZonedDateTime.now();
System.out.println(present);
The output that follows uses the ISO 8601 date and time representation scheme:
2014-03-05T19:50:14.613-06:00[America/Chicago]
The output string starts with date: March 3, 2014. The 'T' demarks the beginning of the time expression. In this case it represents a time of 7:50 PM plus 14 minutes and 0.613 seconds. The next sequence, -06:00, shows the offset from UTC. The last part of the expression lists the time zone enclosed in brackets.
We need to represent the start time and duration of the worker in La Paz. To get this representation we start by creating a ZoneId object that represents the time zone. This is followed by the start time using a LocalTime object, and the length of the employee’s shift using a Duration object. These objects are created as follows:
ZoneId timeZone = ZoneId.of("America/La_Paz");
LocalTime startWorkDay = LocalTime.of(8,0);
Duration dailyTime = Duration.ofHours(6);
To determine if the employee is working, we need a start time and an end time that is based on his time zone. There are several ways of creating these two times. In the following code sequence, we first create a LocalDate called employeeDate, used to represent a day, month, and year value. It is then used with the startWorkDay and timeZone variables to create a start time of type ZonedDateTime. This date and time is then displayed using the DateFormatter class. The ofLocalizedDateTime method is passed a format style of LONG. This class controls how the date and time is displayed.
LocalDate employeeDate =
LocalDate.of(LocalDate.now().getDayOfYear(),
LocalDate.now().getMonth(),
LocalDate.now().getDayOfMonth());
ZonedDateTime startTime = ZonedDateTime.of(
employeeDate,
startWorkDay, timeZone);
System.out.println("Start: " +
startTime.format(
DateTimeFormatter.
ofLocalizedDateTime(FormatStyle.LONG)));
The output is shown below. The 0064 field is the number of days from the start of the year.
Start: March 5, 0064 8:00:00 AM BOT
The end time is calculated by adding the dailyTime duration to the start time as follows.
ZonedDateTime endTime = startTime.plusHours(
dailyTime.toHours());
System.out.println("End: " +
endTime.format(
DateTimeFormatter.ofLocalizedDateTime(
FormatStyle.LONG)));
The end time is shown below:
End: March 5, 0064 2:00:00 PM BOT
The last step is to use the compareTo method with the start, end, and present times to determine if the employee is working. This method returns a negative value if the time specified by its argument follows either the start or end times. It returns a positive value if it follows the target time. The following expression will return false based upon these sets of dates and times:
System.out.println(
(startTime.compareTo(present) <= 0) &&
(endTime.compareTo(present) >= 0));
The ISO-8601 String Format
This format is used with several methods of the API. Its format is shown below:
PnDTnHnMn.nS
The letters correspond to the following units: Period, Days, Hours, Minutes, and Seconds. The ‘n’ represents the number of units and precedes the corresponding unit symbol. The ‘T’ must be present before the use of an hour, minute, or second field. The letters are case-insensitive. Fields can be left out but must appear in the order listed above. The period separating the seconds from the first part of the string, may use 0 to 9 digits. An optional plus or minus can be used to affect an addition or subtraction. Days are exactly 24 hours in length. Table 1 - Period Format Examples provides several examples of how they can be used.
Table 1 - Period Format Examples
Formatted String | Meaning
"P3DT13H5M" | 3 days, 13 hours and 5 minutes
"PT8H" | 8 hours
"P24D" | 24 days
"PT15.012S" | 15 seconds and 12000000 nanoseconds
"-P-3H+42M" | +3 hours and -42 minutes
Design Considerations
The date and time API is based on four design considerations:
1. Methods should be easy to understand
2. A fluent style of programming should be supported
3. Objects should be immutable
4. The API should be extendable
The methods of the API are written to be easily understood and clear to a user. They should be as consistent between classes as possible. These names themselves have been standardized as much as possible and are shown in Table 2 - Method Naming Convention Summary.
A fluent style of programming is supported to make it easier to use. Fluency is supported by chaining easy-to-understand methods together making them easy to learn.
Most of the objects created using the classes of the API are immutable. The chief advantage of this design decision is that they are thread-safe. Constructors often are not available which means static factory type methods are frequently used. Immutable objects are easy to construct and are good for hash keys.
Most of the API classes possess methods that appear to modify a date or time object. However, these methods use the object they are executing against, perform any adjustment to this date or time, and then return a new object. The original object, being immutable, is not modified.
The API provides the developer with the ability to extend and create unique calendar systems when needed. This allows extension of the API to address unique needs as they arise.
Understanding the Standard Method Names
The purpose of this section is to provide a high level overview of the methods used by the API. Understanding the intent of these names will make them easier to use. It will become more obvious which methods should be used in a given situation.
The API classes and interfaces possess a number of methods with similar, if not identical, names. This is done to promote their readability and ease of use. Table 2 - Method Naming Convention Summary summarizes these methods.
The of, from, and parse type methods are static factory methods and provide the primary tool for the creation of date and time objects. The other methods are instance methods and provide techniques to format, get fields, return modified objects, or otherwise transform one object into a different type.
Many of these methods are overloaded or use the same prefix. To assist you in putting these methods into perspective, various tables are found in Appendix A which have organized these methods by type.
Table 2 - Method Naming Convention Summary
Type | Prefix | Use
Creation | of | Creates an instance based on its parameters
Creation | from | Creates an instance where some information may be lost
Creation | parse | Creates an instance by parsing an input string
Formatting | format | Returns a formatted string
State Information | get | Returns a state of the target object
State Information | is | Returns a boolean value relating to the state of the object
Modification | with | Returns a new object that differs by one element
Modification | plus | Returns a new object with a time unit added
Modification | minus | Returns a new object with a time unit subtracted
Modification | to | Converts the target object to a different type
Modification | at | Combine the object with another to produce a new object
Understanding the of Type Methods
The of type methods are similar in usage. They differ in their arguments and return types. They are all static and return an instance of that class. The of Type Methods section in Appendix A provides a list of type methods that are available. The following code sequence illustrates how an instance of the LocalDate class can be created using this method. The integer arguments correspond to year, month, and day.
LocalDate localDate = LocalDate.of(2014, 3, 6);
Understanding the from Method
The from method is very consistent in its signature from class to class with the exception of the Duration class. Each from method is a static method that returns an instance of the class it executes against. Each from method accepts a TemporalAccessor object as an argument with the exception of the Duration class which uses a TemporalAmount object. The from Method section in Appendix A provides a list from type methods that are available.
The following sequence creates a LocalDate from a LocalDateTime:
LocalDateTime ldt = LocalDateTime.now();
LocalDate localDate = LocalDate.from(ldt);
Understanding the parse Method
The static parse method is fairly consistent between those implementing classes. It uses either one or two arguments. The first argument is always a CharSequence. When the second argument is present, it is a DateTimeFormatter instance. The parse Method section in Appendix A provides a list of parse methods that are available.
In this example, the parse method is executed against an Instant to create a date at midnight January 1, 2010.
Instant instant = Instant.parse(
"2010-01-01T00:00:00.000Z");
Understanding the format Method
The format method is used by classes which support human readable dates and time. It uses a DateTimeFormatter instance which controls how the date is formatted. In the following example, the ISO_DATE format is used:
LocalDate localDate = LocalDate.of(2014, 3, 6);
System.out.println(
localDate.format(DateTimeFormatter.ISO_DATE));
The output appears as follows:
2014-03-06
The following classes possess this method: LocalDate, LocalDateTime, LocalTime, MonthDay., OffsetDateTime, OffsetTime, Year, YearMonth, ZonedDateTime.
Understanding the get Type Method
The get type methods return information about an object. The methods supported depend on the needs of the class. The get Type Methods section in Appendix A provide a list of get type methods that are available.
In the following example, the day of the year is returned based on a LocalDate object:
LocalDate localDate = LocalDate.of(2014, 3, 6);
int dayOfYear = localDate.getDayOfYear();
System.out.println(dayOfYear);
The output will be 65. Specific get methods are discussed in their respective class discussions.
Understanding the is Type Methods
The is type methods all return a boolean value. The actual method supported by a class is dependent on the requirements of the class. The is Type Methods section in Appendix A provides a list of is type methods that are available.
To demonstrate the isAfter method, we create an Instant that represents the current date and time as shown below. This is then compared to the epoch.
Instant now = Instant.now();
System.out.println(now.isAfter(Instant.EPOCH));
The output will be true.
Understanding the with Type Methods
These methods always return an instance of the class they execute against. The methods supported vary by class. In general, they return a modified version of the original class. The with Type Methods section in Appendix A provides a list of with type methods that are available.
In the following code sequence, the last day of the month is determined using a TemporalAdjusters. This class provides a number of interesting methods to adjust a date to a different value.
LocalDate localDate = LocalDate.of(2014, 3, 6);
LocalDate endOfMonth =
localDate.with(
TemporalAdjusters.lastDayOfMonth());
System.out.println(endOfMonth.toString());
The output will be:
2014-03-31
Understanding the plus Type Methods
These methods return a new object after adding a time unit to an existing object. The object returned is of the same type as the object the method was executed against. The methods supported depend on the class. The plus Type Methods section in Appendix A provides a list of plus type methods that are available.
In this example, one day is added to the localDate:
LocalDate localDate = LocalDate.of(2014, 3, 6);
LocalDate nextDay = localDate.plusDays(1);
Specific examples will be provided in the respective class sections.
Understanding minus Type Methods
The minus type methods all return the same type of class that they execute against. The object returned is a new object that is based on the original object where a unit of time has been subtracted. The methods supported are dependent on the class. The minus Type Methods section in Appendix A provides a list of minus type methods that are available.
In this example, one day is subtracted from localDate:
LocalDate localDate = LocalDate.of(2014, 3, 6);
LocalDate previousDay = localDate.minusDays(1);
Understanding the to Type Methods
These types of methods are transformational in nature. They return different types depending on their purpose. The to Type Methods section in Appendix A provides a list of to type methods that are available.
In this example, the number of days from the epoch is calculated based on the localDate:
LocalDate localDate = LocalDate.of(2014, 3, 6);
System.out.println(localDate.toEpochDay());
The output will be 16135. This means that the date occurs 16135 days after the epoch.
Understanding the at Type Methods
The at type methods perform a transformation of one type to another using the provided arguments. The methods are specific to the class. The at Type Methods section in Appendix A provides a list of at type methods that are available.
In the following code sequence the start of the day is calculated and returned:
LocalDate localDate = LocalDate.of(2014, 3, 6);
System.out.println(localDate.atStartOfDay());
The output shown below indicates a time of midnight (“T00:00”):
2014-03-06T00:00
Overview of the Date and Time Packages
Table 3 - Date and Time Packages Overview summarizes the date and time packages introduced to Java 8. The classes of the API are summarized in Table 4 - Date and Time Class Overview. They are organized by their type to provide an overview of what is available and how instances of the class can be used.
Table 3 - Date and Time Packages Overview
Package | Description
java.time | Many of the commonly used classes for date and time are found here. They are all immutable.
java.time.chrono | This package supports non-Gregorian and user-designed calendar systems
java.time.format | This package support the formatting and parsing of date and time information
java.time.temporal | Intended to be used by framework and library writers
java.time.zone | Deals with time zones
Table 4 - Date and Time Class Overview
Category | Class | Use
General | Clock | Provides access to the current date, time, and time zone
General | Instant | A point in time in terms of milliseconds
Dates and Time | LocalDate | Provides access to a date without time
Dates and Time | LocalDateTime | Provides access to a date and time
Dates and Time | LocalTime | Provides access to a time without a date
Dates and Time | OffsetDateTime | Offsets using date and time units
Dates and Time | OffsetTime | Offset using time units
Dates and Time | Year | Represents a year
Dates and Time | YearMonth | Represents a year and month
Duration and Periods | Duration | Measurement of a period of time in nanoseconds
Duration and Periods | Period | Measurement of a period of time in easier to use units
Time Zone Related | ZonedDateTime | Date and time with a time zone
Time Zone Related | ZoneId | Represents a time zone
Time Zone Related | ZoneOffset | Represents a time zone as an offset from UTC
Enumerations | Month | Represents the months of the year: January through December
Enumerations | DayOfWeek | Represents the 7 days of the week: Monday through Sunday
Getting the Current Time
The current time is represented by an instance of the Clock class. This class is used to create a time or date based on a time zone. It is an abstract class that allows alternative clocks to be used instead of the system clock. Many date and time classes possess a now method that uses the system clock along with the current time zone to create an instant.
This class can be used to get a date or time in the future. This can be useful when testing an application. An application can be tested with a specific date to better determine how the application will behave when time sensitive changes are applied.
The Clock class’ static systemDefaultZone method will return an instance of a Clock based on the current system clock. It will use a date and time based on the default time zone. In the following example an instance of Clock is created:
Clock clock = Clock.systemDefaultZone();
System.out.println(clock);
This sequence will output the following indicating that it is a system clock and the current time zone:
SystemClock[America/Chicago]
The next example uses the millis method to return the number of milliseconds that has elapsed from the epoch as specified by the date and time. This number by itself is normally not of immediate use. The example also uses the instant method that returns an Instant object representing the clock time. The Instant class is discussed in the Working with the Instant Class section.
System.out.println(clock.millis());
System.out.println(clock.instant());
The output of this sequence follows:
1393774319282
2014-03-02T15:31:59.282Z
If we want to specify a time zone, we use the static system method with a ZoneID object. The ZoneId class is covered in more depth in the Obtaining Available Zone IDs section. In the following example, the ZoneId class’ of method is supplied a human readable string representing a time zone.
clock = Clock.system(ZoneId.of("Asia/Jakarta"));
System.out.println(clock);
System.out.println(clock.getZone());
System.out.println(clock.instant());
The output of this sequence follows:
SystemClock[Asia/Jakarta]
Asia/Jakarta
2014-03-02T16:15:45.830Z
An alternative technique uses the withZone method. In the following code sequence, this method is applied using the same time zone. The withZone method creates a new object based on the object it is applied against. The same date and time are used but are adjusted to the new time zone.
Clock clock = Clock.systemDefaultZone();
clock = clock.withZone(ZoneId.of("Asia/Jakarta"));
System.out.println(clock);
System.out.println(clock.getZone());
System.out.println(clock.instant());
Its output will be identical to the previous example.
We can also create an instant based on whole minutes or seconds. The following example illustrates creating an instant using the tickMinutes method where the time is expressed in whole minutes:
clock = clock.tickMinutes(ZoneId.of("Asia/Jakarta"));
System.out.println(clock);
System.out.println(clock.instant());
The output follows showing whole minutes with zeroes for seconds:
TickClock[SystemClock[Asia/Jakarta],PT1M]
2014-03-02T16:15:00Z
Working with the Instant Class
An instant is a point in time measured in the number of nanoseconds that have elapsed since the epoch. Negative values represent instants before the epoch and positive values represent time after the epoch. The Instant class supports this concept and provides a way of creating a time event using the machine timeline.
There are three Instant constants as listed in Table 5 - Instant Constants.
Table 5 - Instant Constants
Constant | Meaning
EPOCH | An Instant representing the epoch (1970-01-01T00:00:00Z)
MAX | An Instant representing the largest possible instant in the future(1000000000-12-31T23:59:59.999999999Z)
MIN | An Instant representing the smallest possible instant in the past (-1000000000-01-01T00:00Z)
Creating an Instant
There are numerous methods for creating an Instant. Many of these are static methods found in the Instant class. The time represented by an instant has nanosecond precision and is supported internally with two long values.
We can use the Clock’s instant method to create an Instant object:
Clock clock = Clock.systemDefaultZone();
System.out.println(clock.instant());
The output follows:
2014-03-10T17:48:38.307Z
We can also use the Instant class’ static now method to create an instant based on the current UTC time zone as determined by the system clock. This is illustrated below:
Instant now = Instant.now();
System.out.println(now);
The output of this sequence may appear as follows:
2014-03-02T16:00:14.929Z
Creating an Instant Based on the Epoch
We may need to create an Instant from the epoch to mark an event along the machine timeline. There are three methods that produce an Instant based on the time elapsed from the epoch. They differ in their arguments and the resulting accuracy of the Instant object produced.
Table 6 – An Instant based on the Epoch
Method | Arguments | Meaning of Argument
ofEpochSecond | long epochSecond | Number of seconds from epoch
ofEpochSecond | long epochSecond, long nanoAdjustment | Number of seconds from epoch with a nanosecond adjustment
ofEpochMilli | long epochMilli | Number of milliseconds from epoch
The following illustrates creating an instant that is 500,000,000 milliseconds after the epoch
Instant instant = Instant.ofEpochMilli(500000000);
System.out.println(instant);
The output follows:
1970-01-06T18:53:20Z
Creating an Instant Based on Parsing a UTC String
There is also a parse method which takes a UTC string argument representing a point in time. An exception is thrown if the string is not valid (DateTimeParseException). This convenience method is human friendly.
The following illustrates how this method is used to create an instant based on the date and time of January 1, 2010:
instant = Instant.parse("2010-01-01T00:00:00.000Z");
System.out.println(instant);
The output will be:
2010-01-01T00:00:00Z
Creating an Instant by Adding or Subtracting a Time from an Instant
There are a series of plus and minus type methods that execute against an Instant object. These either add or subtract an increment using the Instant and then return a new Instant object reflecting this time. The unit added or subtracted can be a second, millisecond, nanosecond or a TemporalAmount. A TemporalAmount, as its name implies, is the number of time units such as seconds.
The following code sequence illustrates adding 3600 seconds (1 hour) and then subtracting 500 milliseconds from that time. The example also shows how fluent programming is supported. The chained methods are easy to read and understand.
instant = Instant.now()
.plusSeconds(3600)
.minusMillis(500);
System.out.println(instant);
The output follows:
2014-03-02T17:57:57.758Z
Using the Instant Class’ get Type Methods
The Instant class has several getter methods. These methods return various types of information such as the number of seconds relative to the epoch or whether an instant occurs before or after the epoch. These methods are summarized in Table 7 - Instant Getter Methods.
The following illustrates these methods:
Instant now = Instant.now();
System.out.println(now.getEpochSecond());
System.out.println(now.getNano());
System.out.println(now.isAfter(Instant.EPOCH));
System.out.println(now.isBefore(Instant.EPOCH));
One possible output follows. The value returned by the getNano method returns the number of nanoseconds from the last second and not from the epoch.
1393776014
929000000
true
false
Table 7 - Instant Getter Methods
Return Type | Method | Meaning
long | getEpochSecond() | The number of seconds, positive or negative, relative to the epoch
int | getNano() | The number of nanoseconds since the last second.
boolean | isAfter(Instant otherInstant) | Returns true if the Instant it is applied against comes after other Instant
boolean | isBefore(Instant otherInstant) | Returns true if the Instant it is applied against comes before other Instant
Working with Time Intervals
The Duration class represents an amount of time in terms of days, hours, minutes, seconds and nanoseconds. It possesses static methods to return a Duration object specified in days and hours. However, days are always considered to be exactly 24 hours in length. This may not be adequate when dealing with durations where daylight savings time is important. In this case, the Period class is a better choice.
The Period class provides a more human oriented way of expressing a time interval. The Period class represents time in terms of years, months, and days. This class takes into account daylight savings time when performing operations.
Creating Offsets Using the of Type Methods
A series of overloaded of type methods provides a convenient way of creating durations. They use different units of time as listed in Table 8 - Duration of Methods Summary. The Duration class has one constant, ZERO. It represents a duration of zero.
Table 8 - Duration of Methods Summary
Method | Arguments | Units
of | long amount, TemporalUnit unit | The specified unit
ofDays | long days | Days
ofHours | long hours | Hours
ofMillis | long millis | Milliseconds
ofMinutes | long minutes | Minutes
ofNanos | long nanos | Nanoseconds
ofSeconds | long seconds | Seconds
ofSeconds | long seconds, long nanoAdjustment | Seconds adjustment with nanoseconds
The following illustrates the creation of several different Duration objects using these methods:
duration = Duration.ofSeconds(120);
System.out.println(duration);
duration = Duration.ofMinutes(30);
System.out.println(duration);
duration = Duration.ofHours(5);
System.out.println(duration);
The output is shown below and uses the ISO-8601 formatted string representation of the duration as discussed in The ISO-8601 String Format section. The letters correspond to Period, Time, Minutes, and Hours.
PT2M
PT30M
PT5H
Creating Offsets Using the with Type Methods
A duration can also be created using either of two with type methods: withNanos and withSeconds. The following illustrates the creation of a duration of 35 seconds:
duration = Duration.ofMinutes(30).withSeconds(35);
System.out.println(duration);
The resulting duration is as follows:
PT35S
Creating a Duration Using the parse Method
The parse method is passed an ISO-8601 formatted string. The following illustrates the method using a duration of 3 days, 13 hours and 5 minutes:
duration = Duration.parse("P3DT13H5M");
System.out.println(duration);
System.out.println(duration.getSeconds());
The output shown below echoes the parse string and uses the getSeconds method to return the duration in seconds:
PT85H5M
306300
Creating a Duration by Adding or Subtracting a Unit
There are a series of methods that add or subtract a unit from a duration. Since the Duration object is immutable, the return value is a new Duration object. Table 9 - Duration minus Method Summary and Table 10 - Duration add Method Summary summarizes these methods.
Table 9 - Duration minus Method Summary
Method | Argument
minus | Duration duration
minus | long amountToSubtract, TemporalUnit unit
minusDays | long daysToSubtract
minusHours | long hoursToSubtract
minusMillis | long millisToSubtract
minusMinutes | long minutesToSubtract
minusNanos | long nanosToSubtract
minusSeconds | long secondsToSubtract
subtractFrom | Temporal temporal
Table 10 - Duration add Method Summary
Method | Argument
plus | Duration duration
plus | long amountToAdd, TemporalUnit unit
plusDays | long daysToAdd
plusHours | long hoursToAdd
plusMillis | long millisToAdd
plusMinutes | long minutesToAdd
plusNanos | long nanosToAdd
plusSeconds | long secondsToAdd
addTo | Temporal temporal
A duration is created, below, based on an initial duration of 30 minutes. Ten seconds is then added to it and then 500 milliseconds is subtracted from it.
Duration duration = Duration.ofMinutes(30).
plus(Duration.ofSeconds(10)).
minus(Duration.ofMillis(500));
System.out.println(duration);
The resulting duration is shown below:
PT30M9.5S
Using the Duration Class
The Duration class supports getter methods as listed in Table 11 - Duration Class get Type Methods.
Table 11 - Duration Class get Type Methods
Return Type | Method | Meaning
int | getNano() | The number of nanoseconds since the last second
long | getSeconds() | The length of the Duration in seconds
boolean | isNegative() | Returns true if the Duration is negative
boolean | isZero() | Returns true if the Duration’s length is zero
In addition, it supports a number of methods that manipulate a Duration instance. The following demonstrates calculating 2/3 of the time between two dates. The between method returns the initial duration. The multipliedBy and dividedBy methods compute the fractional part of this duration.
LocalTime time1 = LocalTime.of(10, 30);
LocalTime time2 = LocalTime.of(11, 45);
duration = Duration.between(time1, time2).
multipliedBy(2).
dividedBy(3);
System.out.println(duration);
The result is shown below:
PT50M
Using the Period Class
The Period class is concerned with representing an interval of time in terms of years, months, and days. The class takes into account daylight savings time when performing operations. It is very similar to the Duration class except in terms of the fields that it supports.
The following example shows how to create a Period object based on the number of days or months:
Period period = Period.ofDays(21);
System.out.println(period);
period = Period.ofMonths(3);
System.out.println(period);
The output follows:
P21D
P3M
We can also use the parse method to create a Period. In this sequence a string representing 3 years, 2 months, and 16 days is created:
period = Period.parse("P3Y2M16D");
System.out.println(period);
System.out.println("Years: " + period.getYears() +
" Months: " + period.getMonths() +
" Days: " + period.getDays());
The output is shown below:
P3Y2M16D
Years: 3 Months: 2 Days: 16
The between method will return the Period between two dates as illustrated below:
period = Period.between(LocalDate.now(),
LocalDate.now()
.minusMonths(3)
.plusDays(60));
System.out.println(period);
It will produce the following output:
P-1M-2D
Using Date and Time Classes
The date and time classes do not use time zone information. Time zone classes are discussed in the Working with the Time Zone and Offset Classes section. Date and time classes are summarized in Table 12 - Date and Time Class Summary. These classes are immutable and their names give a good idea of what they represent. In addition to these classes; there are several enumerations which work nicely with these classes.
Table 12 - Date and Time Class Summary
Class | Date | Time | Usage
LocalDate | Yes | No | Represents a date. The primary fields are year, month, and day. Other fields can be accessed such as day-of-week and day-of-year.
LocalTime | No | Yes | Represents time and typically viewed as a combination of an hour, minute and second. A nanosecond component is also supported.
LocalDateTime | Yes | Yes | Represents a date and time. The class possesses methods to return individual parts of the date and time such as the day or hour.
YearMonth | Yes | No | Supports only a month and a year field
MonthDay | Yes | No | Supports only a month and a day
Year | Yes | No | Supports only years
Date and time information is often locale specific. To address this concern, the Date and Time API transparently uses the Unicode Common Locale Data Repository (CLDR) (http://cldr.unicode.org/). This repository consists of data used to format information such as dates. It provides country specific information.
In the next sections we will illustrate how the LocalDate, LocalTime, and LocalDateTime classes can be instantiated and used. Following this section we will examine the YearMonth, MonthDay, and Year classes.
Databases often have specific types for date and time entities. The following shows how the new date and time classes map:
- LocalDate is used with the SQL DATE type
- LocalTime is used with the SQL TIME type
- LocalDateTime is used with the SQL TIMESTAMP type.
Creating Date and Time Classes
There are three common ways of creating instances of these classes:
- Using the now method
- Using an of type method
- Using the from method
These methods, with the exception of the from method, are overloaded. The use of these methods is consistent with their use in other classes. The following code sequence illustrates these methods:
LocalDateTime ldt = LocalDateTime.now();
System.out.println(ldt);
ldt = LocalDateTime.of(1980, 2, 12, 17, 30);
System.out.println(ldt);
LocalDate localDate = LocalDate.now();
System.out.println(localDate);
localDate = LocalDate.now(ZoneId.of("Brazil/West"));
System.out.println(localDate);
localDate = LocalDate.of(2011, Month.MARCH, 12);
System.out.println(localDate);
localDate = LocalDate.from(ldt);
System.out.println(localDate);
LocalTime localTime = LocalTime.now();
System.out.println(localTime);
localTime = LocalTime.of(17, 30, 15);
System.out.println(localTime);
The output is shown below:
2014-03-10T14:35:42.638
1980-02-12T17:30
2014-03-10
2014-03-10
2011-03-12
1980-02-12
14:35:42.769
17:30:15
Date and Time get Type Methods
These classes provide several get type methods to retrieve information about the date or time. The following sequence illustrates how we can use these methods with instances of the LocalDate and LocalTime classes. Similar methods are available for the LocalDateTime class.
localDate = LocalDate.of(2011, Month.MARCH, 12);
System.out.println(localDate);
System.out.println("Year: " + localDate.getYear() +
" Month: " + localDate.getMonth() +
" Day: " + localDate.getDayOfMonth() +
" Day of the week: " +
localDate.getDayOfWeek());
localTime = LocalTime.of(17, 30, 15);
System.out.println(localTime);
System.out.println("Hour: " + localTime.getHour() +
" Minute: " + localTime.getMinute() +
" Second: " + localTime.getSecond() +
" Nanosecond: " + localTime.getNano());
The output is shown below:
2011-03-12
Year: 2011 Month: MARCH Day: 12 Day of the week: SATURDAY
17:30:15
Hour: 17 Minute: 30 Second: 15 Nanosecond: 0
There are several is type methods available, such as the isLeapYear and isBefore illustrated below:
localDate = LocalDate.of(2011, Month.MARCH, 12);
System.out.println(localDate.isLeapYear());
localTime = LocalTime.of(17, 30, 15);
System.out.println(localTime.isBefore(
LocalTime.of(15, 0, 15)));
Both of these methods will return false.
Adjusting a Date or Time
The with method, along with the TemporalAdjusters class, provides a useful technique for finding other related dates. For example, the firstDayOfMonth method will return the first day of the month based on a given date as shown below. Also illustrated is the next method that uses a specific day of the week.
localDate = LocalDate.of(2011, Month.MARCH, 12);
LocalDate firstDayOfTheMonth =
localDate.with(
TemporalAdjusters
.firstDayOfMonth());
System.out.println(firstDayOfTheMonth);
LocalDate nextFriday =
localDate.with(
TemporalAdjusters.next(DayOfWeek.FRIDAY));
System.out.println(nextFriday);
The output follows:
2011-03-01
2011-03-18
The LocalTime class has with type methods that return a new time where only one unit of the time is changed. In the following sequence the hour and then the minute is changed:
localTime = LocalTime.of(17, 30, 15);
System.out.println(localTime.withHour(8));
System.out.println(localTime.withMinute(20));
The output is shown below:
08:30:15
17:20:15
Using the YearMonth, MonthDay, and Year Classes
These classes support limited date information as their names imply. Since they have similar methods we will focus on the MonthDay class.
The MonthDay class represents a month and day combination such as August 4. It is immutable and possesses a number of methods to compare or otherwise use instances of this class. It does not contain information regarding the year or time zone.
This class is useful when we only need information about a month and day of the month. There are several static methods used to create instances of MonthDay. They include overloaded versions of now, of, and parse. Several of these are illustrated below. With the exception of the first declaration using the now method, these will all create a MonthDay object that represents August 4.
MonthDay monthDay = MonthDay.now();
monthDay = MonthDay.of(8, 4);
monthDay = MonthDay.of(Month.AUGUST, 4);
monthDay = MonthDay.parse("--08-04");
The parse method requires a string formatted as “--MM-dd”. If not, it will throw a java.time.format.DateTimeParseException exception.
A MonthDay object can also be created using with type methods as illustrated below. This will create a new MonthDay object that is the copy of the first one but the month has been changed to April. The withMonth version has the same effect.
monthDay = MonthDay.now();
monthDay = monthDay.with(Month.APRIL);
monthDay = monthDay.withMonth(4);
The day can be altered using the withDayOfMonth method in a similar fashion.
We can create a LocalDate from a MonthDay instance using the atYear method as shown below:
LocalDate date = monthDay.atYear(2014);
Be careful when using MonthDay objects. A MonthDay of February 29 is considered to be valid. Whether it is actually valid is dependent on the year. However, a date of February 30 is invalid. A minimum check of possible date ranges is checked when specifying the month.
The isValidYear method can be used to determine if a MonthDay instance is valid for a specific year. In the next example, a MonthDay object is created for February 29. It is then tested against the years 2012 and 2014.
monthDay = MonthDay.of(2, 29);
System.out.println(monthDay.isValidYear(2012));
System.out.println(monthDay.isValidYear(2014));
The results will be true for 2012 but false for 2014.
Comparing MonthDay Instances
The compareTo method returns an integer indicating the relationship between two MonthDay instances. A negative value is returned if the date the method is executing against precedes its date argument, a zero if they are equal, and a positive number if its argument follows the first date.
The following will return a -1 indicating that the first instance precedes the second instance:
MonthDay today = MonthDay.now();
MonthDay nextMonth =
today.with(today.getMonth().plus(1));
System.out.println(today.compareTo(nextMonth));
We can also use the isAfter and isBefore methods which compares two MonthDay instances.
System.out.println(today.isBefore(nextMonth));
System.out.println(nextMonth.isAfter(today));
Both statements will display true.
Using the Enumerations DayOfWeek and Month
These two enumeration classes are used to specify the days of the week and months of the year from a human perspective. They are typically used in conjunction with other classes.
Using the DayOfWeek Enumeration
This enumeration is a convenient way of representing the days of the week. For example, the expression, DayOfWeek.FRIDAY, represents Friday. The values method returns an array of these enumerations as shown below. It also possesses a getValue method that returns the numeric representation for that enumeration.
DayOfWeek days[] = DayOfWeek.values();
for (DayOfWeek day : days) {
System.out.println(day.getValue() + " - " + day);
}
The output of this sequence follows:
1 - MONDAY
2 - TUESDAY
3 - WEDNESDAY
4 - THURSDAY
5 - FRIDAY
6 - SATURDAY
7 – SUNDAY
We can create a DayOfWeek value using either the valueOf or of methods. The valueOf method returns an instance of the enumeration. Its string argument must be all uppercase to be recognized properly.
DayOfWeek day = DayOfWeek.valueOf("TUESDAY");
The of method accepts an integer and returns the corresponding enumeration as shown below:
DayOfWeek day = DayOfWeek.of(2);
System.out.println(day);
The following will be displayed:
TUESDAY
This enumeration can be used with other classes such as the LocalDate class. In the following example, the current day is obtained using the now method. The with method is then applied to it. The result will be a LocalDate for the Tuesday of that week.
LocalDate localDate = LocalDate.now();
System.out.println(localDate.with(DayOfWeek.TUESDAY));
Assuming that today is March 6, 2014, the output of this sequence will be as follows:
2014-03-06
Using the Month Enumeration
The Month enumeration supports the 12 months of the year. Like the DayOfWeek enumeration, it has a values and a getValues method that we can use to display this enumeration:
Month months[] = Month.values();
for (Month month : months) {
System.out.println(month.getValue() +
" - " + month);
}
The output follows:
1 - JANUARY
2 - FEBRUARY
3 - MARCH
4 - APRIL
5 - MAY
6 - JUNE
7 - JULY
8 - AUGUST
9 - SEPTEMBER
10 - OCTOBER
11 - NOVEMBER
12 – DECEMBER
A specific month can be created using the of method which takes an integer value corresponding to the month:
Month month = Month.of(3);
In addition, several methods exist that return that month’s number of days from the first of the year, the first month of the current quarter, and the maximum number of days in the month. These methods are demonstrated below using the previous month declaration. The argument to the firstDayOfYear method is set to true if the year is to be considered a leap year.
System.out.println(month.firstDayOfYear(false));
System.out.println(month.firstMonthOfQuarter());
System.out.println(month.maxLength());
The output of this sequence follows:
60
JANUARY
31
Working with the Time Zone and Offset Classes
A time zone is a region where the time is the same within that area. It can be described as using one of several formats. For example, we can specify CST (Central Standard Time) using a time zone identifier. This identifier consists of a region followed by a forward slash and then a city such as "America/Chicago".
A time zone can also be represented as an offset from Greenwich/UTC time such as -06:00 for Chicago. It will be -05:00 if daylight savings time is in effect. If we are interested in using a time zone identifier in code we use the ZoneId class. If we need to use an offset, the ZoneOffset class should be used.
Temporal based classes that include the time zone include ZonedDateTime, OffsetDateTime, and OffsetTime. Table 13 - Time Zone Classes indicates what information is handled by each class.
Table 13 - Time Zone Classes
Class | Date | Time | Time Zone ID | Offset
ZonedDateTime | Yes | Yes | Yes | Yes
OffsetDateTime | Yes | Yes | No | Yes
OffsetTime | No | Yes | No | Yes
In addition to these classes, there is also the ZoneRules class that specifies a set of rules for defining when zone-offset changes occur. This is useful when working with daylight savings times.
Time zones are also an important part of most calendar systems. To support time zones, the Date and Time API transparently uses the Time-Zone Database (TZDB) (http://www.iana.org/time-zones). This database contains information about time zones and changes to time zones since 1970. To correctly determine the time, the current time zone in often needed.
Obtaining Available Zone IDs
The zone ids available can be obtained using the ZoneId class’ getAvailableZoneIds method which returns a Set. This can be useful if this set, or a subset, needs to be displayed to the user, possibly in the form of a drop down combo box.
The following code sequence uses the method to create a set. The contents of the set are then displayed.
Set<String> zoneIds = ZoneId.getAvailableZoneIds();
for(String zoneId : zoneIds) {
System.out.println(zoneId);
}
A subset of this list is shown below:
Asia/Aden
America/Cuiaba
Etc/GMT+9
Etc/GMT+8
Africa/Nairobi
America/Marigot
Asia/Aqtau
Pacific/Kwajalein
…
If we are interested in obtaining the current ZoneId, we should use the ZoneId class’ systemDefault method as shown below:
System.out.println(ZoneId.systemDefault());
One possible output follows:
America/Chicago
To create a ZoneID for a specific time zone, we should use the time zone identifier as the argument of the of method:
ZoneId zoneId = ZoneId.of("Europe/Vienna");
Working with ZoneOffsets
The ZoneOffset, OffsetDateTime and OffsetTime classes provide a means of specifying an offset from the UTC. The ZoneOffset class represents an offset specified in hours, minutes, or seconds. It does not incorporate a date or time element as do the other two classes. The following illustrates, using the ZoneOffset class, to specify a difference of one hour:
ZoneOffset zoneOffSet = ZoneOffset.ofHours(1);
System.out.println(zoneOffSet);
The output below shows that only the hour and minutes are used:
+01:00
The OffsetDateTime class is illustrated here using the current date and time obtained using the now method:
OffsetDateTime offsetDateTime = OffsetDateTime.now();
System.out.println(offsetDateTime);
As the output below indicates, the date, time, and offset are used. The last value, -5:00, represents the offset from UTC.
2014-03-10T16:02:56.751-05:00
The next sequence illustrates using the now method with zoneOffSet and ZoneId objects:
offsetDateTime = OffsetDateTime.now(zoneOffSet);
System.out.println(offsetDateTime);
offsetDateTime =
OffsetDateTime.now(ZoneId.of("+03:30"));
System.out.println(offsetDateTime);
offsetDateTime =
OffsetDateTime.now(ZoneId.of("-06:30"));
System.out.println(offsetDateTime);
The output shows how the time differs by time zone:
2014-03-10T22:02:56.784+01:00
2014-03-11T00:32:56.785+03:30
2014-03-10T14:32:56.785-06:30
The OffsetTime class uses only time as illustrated below. The plusHours method is one of several methods used to adjust the offset.
OffsetTime offsetTime = OffsetTime.now();
System.out.println(offsetTime);
offsetTime = offsetTime.plusHours(2);
System.out.println(offsetTime);
The output follows:
16:02:56.785-05:00
18:02:56.785-05:00
Using the fluent style we can create a date in the future as follows. This illustrates several other at type methods that are available.
OffsetDateTime futureDate = Year.of(2016)
.atMonth(Month.MARCH).atDay(12)
.atTime(12, 30)
.atOffset(ZoneOffset.of("-10:00"));
System.out.println(futureDate);
The date is shown below:
2016-03-12T12:30-10:00
Working with Zone Rules
The rules used when working with time zones can be complex. We often use ZoneRules with daylight savings time. The following uses two different time zones with a displaySettings method:
zoneId = ZoneId.of("America/Phoenix");
displaySettings(zoneId);
zoneId = ZoneId.of("America/Chicago");
displaySettings(zoneId);
The displaySettings method is listed below. This method uses the isDaylightSavings and getDaylightSavings methods on the time zone passed to it based on the current date and a date 8 months later. These methods indicate whether daylight savings time is in effect and the length of the daylight savings respectively.
public static void displaySettings(ZoneId zoneId) {
ZoneRules rules = zoneId.getRules();
System.out.println("Today");
System.out.println(zoneId + ": " +
rules.isDaylightSavings(Instant.now()));
System.out.println(zoneId + ": " +
rules.getDaylightSavings(Instant.now()));
System.out.println("In Eight Months");
System.out.println(zoneId + ": " +
rules.isDaylightSavings(
Instant.now().plus(8 * 30, ChronoUnit.DAYS)));
System.out.println(zoneId + ": " +
rules.getDaylightSavings(
Instant.now().plus(8 * 30, ChronoUnit.DAYS)));
}
The output follows. Phoenix does not use daylight savings time so the isDaylightSavings returns false in both instances and the length of the time savings is 0. Chicago does use daylight savings time. At the time the code was executed it was in effect with a length of 1 hour.
Today
America/Phoenix: false
America/Phoenix: PT0S
In Eight Months
America/Phoenix: false
America/Phoenix: PT0S
Today
America/Chicago: true
America/Chicago: PT1H
In Eight Months
America/Chicago: false
America/Chicago: PT0S
Formatting, Queries, and Value-Based Classes
This section addresses a few topics to round out our discussion of the API. When a date or time needs to be displayed there are many formats to choose from. These options are presented in the first section.
Temporal queries provide an interesting way of obtaining information about a date or time. This is addressed in the second section. While not unique to the date and time API, understanding value-based classes is important. The last section examines these types of classes.
Formatting Dates and Times
Two classes support the formatting of dates and time:
- DateTimeFormatter - Is used frequently when formatting dates and times. It is used with either predefined formats or custom formats.
- DateTimeFormatterBuilder - Uses a builder pattern to support the creation of custom patterns
The DateTimeFormatter class is illustrated below where several of the predefined formats are used:
LocalDateTime today = LocalDateTime.now();
System.out.println(today.format(
DateTimeFormatter.BASIC_ISO_DATE));
System.out.println(today.format(
DateTimeFormatter.ISO_LOCAL_DATE_TIME));
System.out.println(today.format(
DateTimeFormatter.ISO_LOCAL_DATE));
System.out.println(today.format(
DateTimeFormatter.ISO_WEEK_DATE));
System.out.println(today.format(DateTimeFormatter
.ofLocalizedDate(FormatStyle.SHORT)));
The varied types of output are shown below:
20140310
2014-03-10T16:32:03.561
2014-03-10
2014-W11-1
3/10/14
The ofPattern method allows the developer to customize the appearance of the output as illustrated below. There are a large variety of field specifiers available and can be found in the DateTimeFormatter documentation.
DateTimeFormatter dateFormat =
DateTimeFormatter.ofPattern(
"MMM d, yyyy (hh:mm:ss a)");
System.out.println(today.format(dateFormat));
The date is formatted as shown below:
Mar 10, 2014 (04:32:03 PM)
All date formatters are based on the DateTimeFormatterBuilder class. The class uses a series of append methods to create a pattern. In the following code sequence, a DateTimeFormatterBuilder object is created using the ISO_LOCAL_DATE format plus two literals for the current date:
ZonedDateTime zdt = ZonedDateTime.now();
DateTimeFormatterBuilder formatterBuilder =
new DateTimeFormatterBuilder();
formatterBuilder
.appendLiteral(
"The current date for Time Zone \"")
.appendZoneOrOffsetId()
.appendLiteral("\" is ")
.append(DateTimeFormatter.ISO_LOCAL_DATE);
DateTimeFormatter formatter =
formatterBuilder.toFormatter();
System.out.println(formatter.format(zdt));
The resulting output appears below:
The current date for Time Zone "America/Chicago" is 2014-03-10
Using Temporal Queries
A TemporalQuery is used in conjunction with a temporal-based object to obtain information about that object. Predefined queries are available and custom queries can be created.
The following reworks the example found in the A Simple Example section. That example demonstrated how to determine whether an employee was currently working even though the employee may be in a different time zone.
An Employee class is defined that possesses various date and time classes to represent time related aspects of an employee. The immediate relevant parts of the class are listed below. The class implements the TemporalQuery interface which requires that we provide a queryFrom method. We will implement this method later in this discussion.
public class Employee implements TemporalQuery<Boolean> {
private String name;
private LocalDate birthdate;
private LocalDate hireDate;
private LocalTime startWorkDay;
private Duration dailyTime;
private ZoneId timeZone;
public Employee(String name, LocalDate birthdate,
LocalDate hireDate, LocalTime startWorkDay,
Duration dailyTime, ZoneId timeZone) {
this.name = name;
this.birthdate = birthdate;
this.hireDate = hireDate;
this.startWorkDay = startWorkDay;
this.dailyTime = dailyTime;
this.timeZone = timeZone;
}
...
}
An employee is created as shown below. The fourth argument of the constructor specifies that she starts work at 8 in the morning. The fifth argument indicates that her shift is 6 hours in length. The last argument shows that she is located in La Paz.
Employee employee1 = new Employee("Mary",
LocalDate.of(1985, Month.MAY, 5),
LocalDate.of(2010, Month.JUNE, 13),
LocalTime.of(8, 0),
Duration.ofHours(6),
ZoneId.of("America/La_Paz"));
The present time is calculated using the ZonedDateTime class’ now method. This time will represent the current time in the current time zone. The Employee class’ isWorkingNow method will return true if the employee is currently working or false otherwise.
ZonedDateTime present = ZonedDateTime.now();
System.out.println(employee1.isWorkingNow(present));
The isWorkingNow method’s implementation follows. A LocalDate is created first and is used to create a start and end time based on the worker’s time zone. A ZonedDateTime presenting the present is created using the TemporalAccessor passed to the method. The method then uses these values with the compareTo method to determine if the worker is present.
public Boolean isWorkingNow(TemporalAccessor temporal) {
LocalDate employeeDate =
LocalDate.of(LocalDate.now().getDayOfYear(),
LocalDate.now().getMonth(),
LocalDate.now().getDayOfMonth());
ZonedDateTime startTime = ZonedDateTime.of(
employeeDate,
this.startWorkDay,
this.getTimeZone());
ZonedDateTime present = (ZonedDateTime) temporal;
ZonedDateTime endTime = startTime.plusHours(
this.getDailyTime().toHours());
return (startTime.compareTo(present) <= 0) &&
(endTime.compareTo(present) >= 0);
}
Using a specific method such as the isWorkingNow method allows other methods to be added to the class to support similar query operations. Alternatively, we could have used the queryFrom method from a standard query as follows:
System.out.println(employee1.queryFrom(present));
The queryFrom method duplicates the isWorkingNow method:
@Override
public Boolean queryFrom(TemporalAccessor temporal) {
LocalDate employeeDate =
LocalDate.of(
LocalDate.now().getDayOfYear(),
LocalDate.now().getMonth(),
LocalDate.now().getDayOfMonth());
ZonedDateTime start = ZonedDateTime.of(employeeDate,
this.startWorkDay, this.getTimeZone());
ZonedDateTime present = (ZonedDateTime) temporal;
ZonedDateTime endTime =
start.plusHours(this.getDailyTime().toHours());
return (start.compareTo(present) <= 0) &&
(endTime.compareTo(present) >= 0);
}
We could have also called the isWorkingNow method from the queryFrom method to achieve the same results.
Understanding Value-Based Classes
Many classes in Java 8 are value-based. The LocalDateTime and MonthDay are examples of such classes. These types of classes possess a number of characteristics including:
- They are final
- They are immutable
- They have no public constructors
- Factory methods are needed to create instances of these classes
- Their equals, hashCode and toString methods do not use information from other sources when executed
- We should use the equals method instead of the == operator when performing comparisons
- Interchanging two equivalent objects should not result in any difference in behavior
Identity-sensitive operations on value-based objects should be avoided. Any such operations may result in unpredictable behavior. This is illustrated below where the equality operator and equals methods are used to compare two identical objects:
MonthDay date1 = MonthDay.of(2, 2);
MonthDay date2 = MonthDay.of(2, 2);
System.out.println(date1 == date2);
System.out.println(date1.equals(date2));
The equality operator will return false while the equals method returns true;
Conclusion
The new Java 8 date and time API is large and complex. It uses a variety of classes to support local dates and times along with time zone based needs. The API uses a consistent and easy to understand method naming scheme. This supports a fluent style of programming which improves productivity and the code’s readability.
The API supports a machine timeline through the Clock class. Human timelines are supported through classes like LocalDate and ZonedTimeDate. In addition, time intervals are created using the Duration class for hours, minutes, and seconds and the Period class for units such as months and days.
There are several classes that include time zone related information. The developer can work with zone offsets and zone rules as needed. Dates and time can be created through several methods including parsing strings. Dates and times can be displayed using a variety of formatting techniques.
* * * * *