Time

Goals

Concepts

Library

Lesson

Time is a concept that is easy to experience but hard to define. Even more difficult is describing time in a computer program. On the face of it describing the passage of time should be simple for a computer, which can keep track of the endless procession of seconds and microseconds without being encumbered with philosophical explanations or human descriptions of time. But this is exactly what causes problems with time when programming: the computer must eventually interact with humans, and the disparate human ways of conceptualizing and describing time can easily cause confusion and mistakes.

Modeling Time

There are several aspects of referring to time, and each has two opposing approaches:

human time / computer time
Humans refer to time differently than computers.
local time / absolute time
Humans can refer to a local time relative to their time zone, or an absolute time that is same for everyone in the world. Computers by default represent absolute times, but in their own way.
point in time / passage of time
Sometimes a program needs to indicate the time when an event occurred or when it will occur; in other circumstances a program may need to describe how long an event lasted.

Human Time

Calendar with today indicated.Clock.
Common ways humans represent local time.

You will likely be most familiar with how humans describe local time by referring to the clock on the wall or the days marked on a calendar. We refer to this as local time because it is only valid in your time zone, one of the 24 divisions of time around the world. Take the date for the New Year as an example, which is January 1. The year begins on January 1 in New York as well as in San Francisco. But because these cities are separated by several time zones, some people in San Francisco will still be having dinner when they see people on New York celebrating the new year on television.

A typical clock only tells local time as well. The New Year parade in New York may begin at noon, or 12:00 p.m. But people in San Francisco will see the parade on their televisions when clocks in California indicate only 9:00 a.m. To remove ambiguity, we can indicate a time zone to indicate an absolute time.

Time Zones.
Time zones of the world showing offsets from UTC. (Wikimedia Commons)

Time zones are politically-defined geographic regions that determine the time around the world. The time within any particular time zone is the same throughout the zone, and can be measured by an offset from Coordinated Universal Time (UTC), an international standard based on the time at 0° longitude. Because 0° longitude goes through Greenwich, a predecessor to UTC was named Greenwich Mean Time (GMT). UTC is sometimes represented by a Z for zero. You may even here UTC referred to as Zulu Time, as Zulu is the international phonetic spelling of Z.

Even with a time zone designation, however, the absolute time in human terms is complicated by daylight saving time (DST), in which a country or an area of a country changes the time forward or backwards during different seasons. (UTC is independent of DST.) To identify the time zones independent of their offsets from UTC, the Internet Assigned Numbers Authority (IANA) has assigned a unique name for each region in its Time Zone Database. For example the IANA TZ identifizer for the region commonly known as Pacific Time is America/Los_Angeles; during the winter it has the offset -08:00, while during the summer it has the offset -07:00.

Calculating how much time has passed between two absolute times can become quite difficult, as some designated times may never have existed within a time zone. If a state advances an hour for DST at midnight, for example, the clock completely skips 12:30 a.m.! The IANA Time Zone Database maintains historical DST and other changes, allowing the the time in each time zone to be calculated in the past.

Computer Time

The computer avoids many of the problems imposed by the strange customs of humans by conceptualizing time as a series of seconds endlessly marching on from some starting point. The starting point or epoch used by most computer systems is midnight on January 1, 1970 in UTC. Thus every event can be identified as a number that is independent of any time zone. Many computers track the number of milliseconds that have passed since the epoch.

TODO provide some Instant examples in human time and computer time

ISO 8601

The standard that governs time representation around the world is ISO 8601: Data elements and interchange formats — Information interchange — Representation of dates and times. This document is put out by the International Organization for Standardization (ISO), and prescribes various format for representing dates and times. One of the most most common is the representation for a date and time of day, with an included offset from UTC, such as 1985-04-12T10:15:30+04:00.

Beyond the specific syntax, however, ISO 8601 is based on a certain conception of dates and times. Dates are based on the Gregorian calendar, and rules are introduced for representing dates before the institution of the Gregorian calendar in 1875. Week numbers and day ordinals are fixed, and definitions are put into place regarding time scales and leap years. Thus many time library implementations rely not only on ISO 8601 for how time should be represented as text, but also for its basic model of time representation.

Representing Time

The latest versions of Java provide built-in libraries for tracking time, located in the java.time package. Lower-level time processing classes may be found in the java.time.temporal subpackage including the java.time.temporal.Temporal interface that underlies most of the Java time-related types, and an even lower-level interface java.time.temporal.TemporalAccessor which allows access to individual fields of time classes. The time classes represent value objects, which you have studied. You can therefore expect the time classes to be immutable to be comparable with equals(…), and to include static factory methods typically named of(…).

Local and Zoned Time

Representing a general date date or time, not tied to any time zone, is accomplished using the java.time.LocalDate and java.time.LocalTime classes. These types correspond to the date marked on a calendar or the time displayed on a clock, respectively. These classes represent immutable value types, so it is no surprise that they provide many static factory methods including LocalDate.of(int year, int month, int dayOfMonth) and LocalTime.of(int hour, int minute, int second). The local date and time classes provide a variety of static factory methods, including some that use the java.time.Month enum.

java.time.LocalDate
A general year, month, and day not fixed to any time zone.
java.time.MonthDay
A month and a day not fixed to any time zone or even associated with a particular year.
java.time.Month
A single month of the year.
java.time.Year
A year without regard to any time zone.
java.time.LocalTime
A general time not associated with any particular date nor fixed to any time zone.
java.time.LocalDateTime
A combined date and time not fixed to any time zone.
Examples of local time classes.
//someone's date of birth: May 30, 1970
final LocalDate dateOfBirth = LocalDate.of(1980, Month.MAY, 30);
//someone's birthday: May 30
final MonthDay birthday = MonthDay.of(Month.MAY, 30);

//a typical end of a workday: 5:00 p.m.
final LocalTime endOfWorkday = LocalTime.of(17, 0);

//current date and time
final LocalDateTime = LocalDateTime.now();

Local dates and times are relative or floating; they do not provide sufficient information to determine precisely when an event occurred or will occur. Recording the date and time of a birth, for instance, must include the time zone. A journal entry as well would normally indicate the time zone in order to fix the exact moment in an absolute way worldwide: December 11, 2016 10:53 a.m. America/Los Angeles.

The zoned time classes keep track of a time zone in addition to their other time components, and thus are able to represent absolute times in history. To represent the time zone itself Java provides the class java.time.ZoneId. You can create a ZoneId using its static factory method ZoneId.of(String zoneId) with the identifier from the IANA Time Zone Database, such as ZoneId.of("Europe/Paris"). The current time zone of the system can be found using ZoneId.systemDefault().

The main zoned time class is java.time.ZonedDateTime, which is essentially a LocalDateTime combined with a ZoneId. It can be created using all its components using the static factory method ZonedDateTime.of(int year, int month, int dayOfMonth, int hour, int minute, int second, int nanoOfSecond, ZoneId zone). There exist various other static factory methods, including one from an existing LocalDate, and LocalTime, with the addition of a time zone using ZonedDateTime.of(LocalDate date, LocalTime time, ZoneId zone).

java.time.ZoneId
Identifies a time zone from the IANA Time Zone Database.
java.time.ZonedDateTime
Represents an absolute date and time in a particular time zone.
Examples of absolute time.
//the exact moment of someone's birth: May 30, 1970 9:42:12 p.m. in New York
final ZoneId easternZone = ZoneId.of("America/New_York");
final ZonedDateTime born = ZonedDateTime.of(1980, Month.MAY.getValue(), 30,
    21, 42, 12, 0, easternZone); //no nanoseconds recorded

//current date
final ZoneDateTime hereAndNow = ZoneDateTime.now();

Instances

The number of ticks on a computer that marks some single, absolute, instantaneous point in time is referred to as an instant. In applications an instance is The java.time.Instant class keeps track of time down to the nanosecond, although it is more typical to create an Instant from the number of milliseconds past the epoch using Instant.ofEpochMilli(long epochMilli), as milliseconds past the epoch is the traditional record of Java time. You can also create an instant from one of the human time classes with time zone information such as ZonedDateTime.toInstant(). You can produce a human time representation by providing the Instant with a time zone using Instant.atZone(ZoneId zone).

Examples of computer time.
//the exact instance of someone's birth in computer terms
final ZoneId easternZone = ZoneId.of("America/New_York");
final ZonedDateTime born = ZonedDateTime.of(1980, Month.MAY.getValue(), 30,
    21, 42, 12, 0, easternZone); //no nanoseconds recorded
final Instant bornInstant = born.toInstant();

//the current time for all computers around the world (roughly)
final Instant now = Instant.now();

Periods and Durations

Calculating the number of days from January 1 to July 1 for an arbitrary year might seem simple until you recall that leap years must be taken into account. Measuring the number of hours between Friday and Tuesday for some arbitrary dates might seem an easy thing to do until you remember that, depending on time zone, daylight savings might have come into effect depending on the date, robbing an hour from the result. Just as there is a human approach and a computer approach to representing points in time, Java provides two separate approaches for measuring the passage of time: a period for the passage of human calendar days, and a duration for calculating the time difference between two instants.

java.time.Period
Measures a difference based on human dates: calendar days, months, and years.
java.time.Duration
Measures the amount of of computer time that passes.

Period can be created using the static factory method Period.between(LocalDate startDateInclusive, LocalDate endDateExclusive). You can also create periods from a certain number of days, months, weeks, or years, using for example Period.of(int years, int months, int days) or Period.ofDays(int days). After getting a Period, you can get the number of days or months that have passed, such as with Period.getDays().

A Duration on the other hand is a more general, computer-oriented designation of time passing, and can be created from various types of points in time using Duration.between(Temporal startInclusive, Temporal endExclusive). A Duration can also be created from a number of seconds/milliseconds/nanoseconds, minutes, hours, or days, using for example Duration.ofDays(long days). Once you have a Duration, you can calculate the passage of time using for example Duration.toMinutes() or Duration.toDays(). The days and other times given by a Duration are standardized times and will not take into account leap seconds or calendar-time concepts such as DST.

Time Formats

Java provides the java.time.format.DateTimeFormatter class both for creating and for parsing text-based representations of time. A DateTimeFormatter is created from a pattern; once created, it is immutable and can be as often as needed, even from multiple threads of execution.

Predefined Formats

There are several predefined formatters that you use, most of them for creating ISO 8601 based representations (hence the word ISO in the names). One of the most useful of these is DateDimeFormatter.ISO_INSTANT, which provides an ISO 8601 compliant way to represent a timestamp for storage and later retrieval. Some of these formatters are ISO-like, and include elements that are not part of ISO 8601; check the API documentation before using them.

Predefined DateTimeFormatters (DateTimeFormatter API)
Formatter Description Example
DateTimeFormatter.BASIC_ISO_DATE Basic ISO date "20111203"
DateTimeFormatter.ISO_LOCAL_DATE ISO Local Date "2011-12-03"
DateTimeFormatter.ISO_OFFSET_DATE ISO Date with offset "2011-12-03+01:00"
DateTimeFormatter.ISO_DATE ISO Date with or without offset "2011-12-03+01:00", "2011-12-03"
DateTimeFormatter.ISO_LOCAL_TIME Time without offset "10:15:30"
DateTimeFormatter.ISO_OFFSET_TIME Time with offset "10:15:30+01:00"
DateTimeFormatter.ISO_TIME Time with or without offset "10:15:30+01:00", "10:15:30"
DateTimeFormatter.ISO_LOCAL_DATE_TIME ISO Local Date and Time "2011-12-03T10:15:30"
DateTimeFormatter.ISO_OFFSET_DATE_TIME Date Time with Offset "2011-12-03T10:15:30+01:00"
DateTimeFormatter.ISO_ZONED_DATE_TIME Zoned Date Time "2011-12-03T10:15:30+01:00[Europe/Paris]"
DateTimeFormatter.ISO_DATE_TIME Date and time with ZoneId "2011-12-03T10:15:30+01:00[Europe/Paris]"
DateTimeFormatter.ISO_ORDINAL_DATE Year and day of year "2012-337"
DateTimeFormatter.ISO_WEEK_DATE Year and Week "2012-W48-6"
DateTimeFormatter.ISO_INSTANT Date and Time of an Instant "2011-12-03T10:15:30Z"
DateTimeFormatter.RFC_1123_DATE_TIME RFC 1123 / RFC 822 "Tue, 3 Jun 2008 11:05:30 GMT"

Formatting

Formatting is performed using DateTimeFormatter.format(TemporalAccessor temporal), as shown in this example using DateTimeFormatter.ISO_LOCAL_DATE:

Formatting a date of birth in ISO 8601 format.
final LocalDate dateOfBirth = LocalDate.of(1980, Month.MAY, 30);
System.out.println(DateTimeFormatter.ISO_LOCAL_DATE.format(dateOfBirth)); //1980-05-30

If you wish to show the time in a different time zone (for those formats that support time zones), you can create a new DateTimeFormatter using the fluent method DateTimeFormatter.withZone(ZoneId zone). The new DateTimeFormatter will show the time in the requested zone, but the time value being formatted not be changed. The following example shows how to determine the date in Paris at the exact time a person was born in New York, using the DateTimeFormatter.ISO_LOCAL_DATE formatter and DateTimeFormatter.withZone(…). Note that although the birth occurred on May 30 in the America/New_York time zone, because of the time difference it was already May 31 in the Europe/Paris time zone.

Formatting a time in a different time zone.
final ZoneId easternZone = ZoneId.of("America/New_York");
final ZonedDateTime born = ZonedDateTime.of(1980, Month.MAY.getValue(), 30,
    21, 42, 12, 0, easternZone); //May 30, 1980
final ZoneId parisZone = ZoneId.of("Europe/Paris");
final DateTimeFormatter parisFormatter = DateTimeFormatter.ISO_LOCAL_DATE.withZone(parisZone);
System.out.println(parisFormatter.format(born)); //1980-05-31

Parsing

Parsing is most easily done through a static factory parse(CharSequence text, DateTimeFormatter formatter) method of the type you want to create for producing an instance of the time class based upon a text-based representation. For example to parse an ISO 8601 representation of a date back into a LocalDate instance, you could use LocalDate.parse(CharSequence text, DateTimeFormatter formatter).

Parsing an ISO 8601 text representation of a date of birth.
final LocalDate dateOfBirth = LocalDate.parse("1980-05-30", DateTimeFormatter.ISO_LOCAL_DATE);

Localized Formats

When displaying dates to the user, or parsing dates entered by the user, instead of using one of the ISO 8601 representations you will likely prefer to show the date in the format expected for the user's region. For example a user in the United States might be accustomed to writing the date as 5/30/80 or May 30, 1980, while a user in Brazil might expect to use 30/05/80 or 30 de Maio de 1980. Java provides several static factory methods to create a localized DateTimeFormatter appropriate for the user expectations of your system's region.

Localized DateTimeFormatters (DateTimeFormatter API)
Formatter Description Example
DateTimeFormatter.ofLocalizedDate(dateStyle) Formatter with date style from the locale "2011-12-03"
DateTimeFormatter.ofLocalizedTime(timeStyle) Formatter with time style from the locale "10:15:30"
DateTimeFormatter.ofLocalizedDateTime(dateTimeStyle) Formatter with a style for date and time from the locale "3 Jun 2008 11:05:30"
DateTimeFormatter.ofLocalizedDateTime(dateStyle,timeStyle) Formatter with date and time styles from the locale "3 Jun 2008 11:05"

To create one of these localized formats, provide a java.time.format.FormatStyle enum value specifying the style of the date and/or time portion of the string. You can use the FormatStyle values as you would any other value object constants.

Localized DateTimeFormatters (FormatStyle API)
Format Style Description Example
FormatStyle.FULL Representation including the most detail. "Wednesday, December 14, 2016 8:10:19 AM PST"
FormatStyle.LONG Representation including much detail. "December 14, 2016 8:10:19 AM PST"
FormatStyle.MEDIUM Representation including fewer details. "Dec 14, 2016 8:10:19 AM"
FormatStyle.SHORT Mostly numeric representation. "12/14/16 8:10 AM"

Legacy

The date and time classes presented in this lesson are reasonably elegant and reasonably complete. But they weren't always part of Java; they were only added relatively recently after development under JSR 310: Date and Time API. In fact most Java code you come in contact with will likely be using one of the legacy approaches to timekeeping, so it is valuable to have an idea of the alternatives and how they interact with the new Java approach to time.

System.currentTimeMillis()

Java's most primitive representation of time is that shared by many computer systems: a record of the number of milliseconds that has passed since the epoch on midnight, January 1, 1970 UTC. This long value marks passage of computer time, and can be retrieved using java.lang.System.currentTimeMillis().

Date

The java.util.Date class is conceptually little more than a wrapper around some value produced by System.currentTimeMillis(); a frozen system time, analogous to an Instant. Calling the no-parameter constructor Date() in fact will create a date with the current value of System.currentTimeMillis(), and Date.getTime() allows you to retrieve this value. But Date has several very large drawbacks:

For these reasons you should not use Date except as necessary for interacting with legacy code.

Calendar

To address some of the shortcomings in Date, Java introduced the java.util.Calendar abstract class. Calendar, like Date, is also a wrapper around a system time in milliseconds. The difference is that Calendar allows more robust manipulation of human time concepts such as dates and time zones. A Calendar is not that much different from your computer's operating system: internally it represents some absolute system time in milliseconds, yet its interface allows access of human time components based upon its current time zone.

The specific rules for working with calendar dates and times depend on which Calendar subclass is used. The most common subclass java.util.GregorianCalendar, which represents the calendar in use in ares around the world, and more or less agrees with ISO 8601. You can create an instance of the appropriate subclass of Calendar for your region, representing your current time zone, using Calendar.getInstance().

You can get individual components of human time using Calendar.get(int field), providing a field constant such as Calendar.HOUR, Calendar.YEAR, or even Calendar.DAY_OF_WEEK_IN_MONTH. You can also update the calendar time using Calendar.set(int field, int value) with one of the same fields. You can set several fields at the same time using one of the convenience setters that take certain predefined fields such as Calendar.set(int year, int month, int date, int hourOfDay, int minute).

Time zones are represented by the legacy class java.util.TimeZone. You can get a Calendar for a particular time zone using Calendar.getInstance(TimeZone zone). The complementary method Calendar.setTimeZone(TimeZone value) will change the Calendar's time zone.

Joda-Time

The Jode-Time library was created to address the deficiencies of Java's Date and Calendar classes. For many years before JSR 310 was available Java developers needing more sophisticated and/or easier date and time handling increasingly opted to abandon the legacy classes and instead use Jode-Time. With the introduction of JSR 310 there is no longer any need for Joda Time, and any programs using that library should migrate to the new Java time handling API.

Review

Summary

TODO

Gotchas

In the Real World

Think About It

Self Evaluation

Task

In your value object for a publication's range of years for first published date, you likely represented the year as an int or an Integer. Upgrade your range value class to use the java.time.Year class to represent each of the years in the range. Do not allow either of the years to be null; if the range consists a single year, simply use the same year for ends of the range (assuming your range end is inclusive).

When Booker starts, display the current time and date in an easily readable format using the user's current time zone.

Once Booker has completed performing its functionality, before exiting print a user-friendly message indicating how long the operations took in total, measured from the time Booker started executing.

The CURRENT_YEAR constant makes Booker brittle. It requires the program to be recompiled every year or it will become inaccurate. Rather than use a CURRENT_YEAR constant, use the Java time library to determine the current year dynamically each time you do the comparison.

See Also

References

Resources

Acknowledgments