Java 8 Date Time API

24 November 2014
By Gonçalo Marques
In this article we will cover the Java 8 Date Time API.

Introduction

Java 8 brought a completely redefined Date-Time API. One important feature - similarly to other already existing 3rd party API's - is that the great majority of the API objects that represent date and/or time information are immutable, and therefore thread-safe.

Until now, one had to use Calendar objects in order to do simple operations like date arithmetic or even to extract fields from a given date/time representation. Implementing such operations with the new API has become quite simple and intuitive.

The API is designed as what is called a fluent API, meaning that one may chain consecutive method calls over date/time instances in order to produce a final immutable result.

In this article we will go through an overview of the Java 8 Date Time API.

Enums

A couple of interesting enums are DayOfWeek and Month:

DayOfWeek

DayOfWeek monday = DayOfWeek.MONDAY;

// Will print MONDAY
System.out.println(monday);

// Will print Mon
System.out.println(monday.getDisplayName(TextStyle.SHORT, Locale.getDefault()));

// Will print Monday
System.out.println(monday.getDisplayName(TextStyle.FULL, Locale.getDefault()));

// Will print SATURDAY
System.out.println(monday.plus(5));

Month

Month april = Month.APRIL;

// Will print APRIL
System.out.println(april);

// Will print Apr
System.out.println(april.getDisplayName(TextStyle.SHORT, Locale.getDefault()));

// Will print April
System.out.println(april.getDisplayName(TextStyle.FULL, Locale.getDefault()));

// Will print JULY
System.out.println(april.plus(3));

// Will print 29
System.out.println(Month.FEBRUARY.maxLength());

Date

The API provides some classes that contain only date related information, without considering time or timezones.

The LocalDate class represents a date without time information:

LocalDate

// Current date from the system clock
LocalDate now = LocalDate.now();

// November 16th 2014
LocalDate date = LocalDate.of(2014, Month.NOVEMBER, 16);

// November 22nd 2014
LocalDate nextSaturday = date.with(TemporalAdjusters.next(DayOfWeek.SATURDAY));

// SUNDAY
DayOfWeek dayOfWeek = date.getDayOfWeek();

The YearMonth class represents, as the name states, a month + year pair:

YearMonth

// Current year/month from the system clock
YearMonth now = YearMonth.now();

// February 2014
YearMonth february2014 = YearMonth.of(2014, Month.FEBRUARY);

// Will print 28
System.out.println(february2014.lengthOfMonth());

// February 2016
YearMonth february2016 = YearMonth.of(2016, Month.FEBRUARY);

// Will print 29 (leap year)
System.out.println(february2016.lengthOfMonth());

The MonthDay class represents a day + month pair:

MonthDay

// Current month/day from the system clock
MonthDay now = MonthDay.now();

// February 29th
MonthDay monthDay = MonthDay.of(Month.FEBRUARY, 29);

// true
boolean isValidYear = monthDay.isValidYear(2016);

// false
isValidYear = monthDay.isValidYear(2014);

// February 29th 2016
LocalDate date = monthDay.atYear(2016);

// Will adjust to February 28th 2014
date = monthDay.atYear(2014);

The Year class represents an arbitrary year:

Year

// Current year from the system clock
Year now = Year.now();

// 2015
Year year = Year.of(2015);

// April 15th 2015
LocalDate date = year.atMonth(Month.APRIL).atDay(15);

// false
boolean leapYear = year.isLeap();

// true
leapYear = Year.of(2016).isLeap();

// July 21st 2015
date = year.atMonthDay(MonthDay.of(Month.JULY, 21));

Time

The LocalTime class deals with time information only:

LocalTime

// Current time from the system clock
LocalTime now = LocalTime.now();

// Midnight
LocalTime midnight = LocalTime.MIDNIGHT;

// 10:23:45
LocalTime time = LocalTime.of(10, 23, 45);

// 10
int hour = time.get(ChronoField.HOUR_OF_DAY);

// 37425000000000
long nanos = time.toNanoOfDay();

Date and Time

The LocalDateTime class represents date together with time information:

LocalDateTime

// Current date-time from the system clock
LocalDateTime now = LocalDateTime.now();

// November 22nd 2014, 10:13:34
LocalDateTime dateTime = LocalDateTime.of(2014, Month.NOVEMBER, 22, 10,
		13, 34);

// November 28th 2014, 15:13:34
LocalDateTime sixDaysAndFiveHoursAfter = dateTime.plusDays(6).plusHours(5);

// 10
int hours = dateTime.get(ChronoField.HOUR_OF_DAY);

LocalDate date = LocalDate.of(2014, Month.NOVEMBER, 22);
LocalTime time = LocalTime.of(10, 14, 56);

// November 22nd 2014, 10:14:56
LocalDateTime other = LocalDateTime.of(date, time);

// 1
int compare = other.compareTo(dateTime);

// 82
long secondsElapsed = Duration.between(dateTime, other).getSeconds();

Time Zone and Offset

The API also provides classes to deal with time zones and offsets.

The ZonedDateTime class represents a LocalDateTime with a time zone:

ZonedDateTime

// November 20th 2014, 14:30
LocalDateTime dateTime = LocalDateTime.of(2014, Month.NOVEMBER, 20, 14, 30);

ZoneId USEastZone = ZoneId.of("US/Eastern");
// November 20th 2014, 14:30 (-05:00)
ZonedDateTime USEastDateTime = ZonedDateTime.of(dateTime, USEastZone);

ZoneId USPacificZone = ZoneId.of("US/Pacific");
// November 20th 2014, 11:30 (-08:00)
ZonedDateTime USPacificDateTime = USEastDateTime
  .withZoneSameInstant(USPacificZone);

The OffsetDateTime is used in order to represent a LocalDateTime with a specific offset:

OffsetDateTime

// November 20th 2014, 14:30
LocalDateTime dateTime = LocalDateTime.of(2014, Month.NOVEMBER, 20, 14, 30);

ZoneOffset offset = ZoneOffset.of("-05:00");
// November 20th 2014, 14:30 (-05:00)
OffsetDateTime offsetDate = OffsetDateTime.of(dateTime, offset);

The OffsetTime class is similar to OffsetDateTime but deals with time only, ie. represents a LocalTime with a specific offset.

Instant

An Instant represents the number of nanoseconds since the Epoch (January 1st 1970). Instants which are previous to the Epoch are represented with negative values.

Instant

Instant instant = Instant.now();

Instant twoDaysLater = instant.plus(2, ChronoUnit.DAYS);

long hoursElapsed = instant.until(twoDaysLater, ChronoUnit.HOURS);

LocalDateTime dateTime = LocalDateTime.ofInstant(instant,
		ZoneId.systemDefault());

ZonedDateTime USEastDateTime = ZonedDateTime.ofInstant(instant,
		ZoneId.of("US/Eastern"));

ZonedDateTime USPacificDateTime = ZonedDateTime.ofInstant(instant,
		ZoneId.of("US/Pacific"));

Converting from an Instant to a LocalDateTime or ZonedDateTime obviously needs a time zone, since it is the only way to transform an instant of the time line since the Epoch into a meaningful date (the same Epoch instant represents distinct date-time depending on the time zone).

Parsing and Formatting

Parsing and formatting makes use of the usual format string:

Parsing

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime dateTime = LocalDateTime.parse("2014-11-21 10:23:45", formatter);

Formatting

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime dateTime = LocalDateTime.of(2014, Month.NOVEMBER, 20, 14, 30);
String formattedDate = dateTime.format(formatter);

Temporal Adjuster

Temporal adjusters allows one to adjust any of the temporal types we have seen previously in this article.

TemporalAdjuster

// November 16th 2014
LocalDate date = LocalDate.of(2014, Month.NOVEMBER, 16);

// November 1st 2014
LocalDate adjusted = date.with(TemporalAdjusters.firstDayOfMonth());

// January 1st 2014
adjusted = date.with(TemporalAdjusters.firstDayOfYear());

// November 5th 2014
adjusted = date.with(TemporalAdjusters.firstInMonth(DayOfWeek.WEDNESDAY));

One may also define custom temporal adjusters. Since the TemporalAdjuster interface is a functional interface (an interface with a single method), one may define a custom adjuster using a lambda expression (more information in Java 8 Lambda expressions and Java 8 Method References):

Custom TemporalAdjuster

TemporalAdjuster adjuster = (temporal) -> temporal
  .plus(10, ChronoUnit.MONTHS)
  .minus(Period.ofDays(23));

// November 16th 2014
LocalDate date = LocalDate.of(2014, Month.NOVEMBER, 16);

// August 24th 2015
LocalDate adjusted = date.with(adjuster);

Temporal Query

Temporal queries allows one to execute queries against temporal representations:

TemporalQuery

TemporalQuery<Boolean> leapYearQuery = (temporal) -> 
  Year.of(temporal.get(ChronoField.YEAR)).isLeap();

TemporalQuery<Long> epochSecondsQuery = (temporal) -> 
  LocalDateTime.of(
    temporal.get(ChronoField.YEAR),
    temporal.get(ChronoField.MONTH_OF_YEAR),
    temporal.get(ChronoField.DAY_OF_MONTH),
    temporal.get(ChronoField.HOUR_OF_DAY),
    temporal.get(ChronoField.MINUTE_OF_HOUR),
    temporal.get(ChronoField.SECOND_OF_MINUTE))
    .atZone(ZoneId.of("US/Eastern")).toEpochSecond();

// November 16th 2014, 10:23:45
LocalDateTime date = LocalDateTime.of(2014, Month.NOVEMBER, 16, 10, 23, 45);

// false
boolean leapYear = date.query(leapYearQuery);

// 1416151425
Long epochSeconds = date.query(epochSecondsQuery);

// November 16th 2016, 10:23:45
date = LocalDateTime.of(2016, Month.NOVEMBER, 16, 10, 23, 45);

// true
leapYear = date.query(leapYearQuery);

// 1479309825
epochSeconds = date.query(epochSecondsQuery);

Period, Duration and ChronoUnit

The following classes may be used to measure intervals of time.

The Duration class is best suitable to measure machine based time, ie. a duration that is represented by seconds or nanoseconds:

Duration

LocalDateTime dateTime = LocalDateTime.of(2014, Month.NOVEMBER, 22, 10, 13, 34);
LocalDateTime other = LocalDateTime.of(2014, Month.NOVEMBER, 22, 11, 24, 12);

// 4238
long secondsElapsed = Duration.between(dateTime, other).getSeconds();

The Period class is best suitable to measure intervals that are represented by date constructs, like years, months and days:

Period

LocalDate date = LocalDate.of(2014, Month.NOVEMBER, 22);
LocalDate other = LocalDate.of(2017, Month.APRIL, 17);

Period period = Period.between(date, other);

// 2
int years = period.getYears();

// 4
int months = period.getMonths();

// 26
int days = period.getDays();

The ChronoUnit class allows one to define the unit under which the interval of time should be calculated:

ChronoUnit

LocalDate date = LocalDate.of(2014, Month.NOVEMBER, 22);
LocalDate other = LocalDate.of(2017, Month.APRIL, 17);

// 2
long years = ChronoUnit.YEARS.between(date, other);

// 28
long months = ChronoUnit.MONTHS.between(date, other);

// 877
long days = ChronoUnit.DAYS.between(date, other);

Reference

Trail: Date Time (The Java Tutorials)

Related Articles

Comments

About the author
Gonçalo Marques is a Software Engineer with several years of experience in software development and architecture definition. During this period his main focus was delivering software solutions in banking, telecommunications and governmental areas. He created the Bytes Lounge website with one ultimate goal: share his knowledge with the software development community. His main area of expertise is Java and open source.

GitHub profile: https://github.com/gonmarques

He is also the author of the WiFi File Browser Android application: