java.time

When did an event happen?

long

long timestamp = System.currentTimeMillis();

long

long

long

java.util.Date

Date now = new Date();

Date

It does not represent... date. Seriously, officially date is "[...] the day of the month or year as specified by a number [...]" [1] whereas in Java it represents point in time without any specific calendar (day/month/year). Its toString() is misleading, displaying calendar date and time in system timezone. Not only it misled thousands of developers to think that Date has a timezone attached. Moreover it shows time, but date should only represent day, not hour. It has 20+ deprecated methods, including getYear() , parse(String) and many constructors. These methods are deprecated for a reason, because they lead you to believe Date represents, you know, date. java.sql.Date extends java.util.Date and is actually much more accurate because it indeed represents calendar date ( DATE in SQL). However this narrows the functionality of base class Date , thus violating Liskov substitution principle. Don't believe me? java.util.Date.toInstant() works as expected but java.sql.Date.toInstant() fails unconditionally with UnsupportedOperationException ... Worst of them all, Date is mutable.

Date

Date addOneMinute(Date in) { in.setTime(in.getTime() + 1_000 * 60); return in; }

Date now = new Date(); System.out.println(now); System.out.println(addOneMinute(now)); System.out.println(now);

Tue Jul 26 22:59:22 CEST 2016 Tue Jul 26 23:00:22 CEST 2016 Tue Jul 26 23:00:22 CEST 2016

now

Date

Date

x

y

x

java.lang.Integer

String

BigDecimal

ScheduledTask

class ScheduledTask { Date getNextRunTime(); }

ScheduledTask task = //... task.getNextRunTime().setTime(new Date());

Date

ScheduledTask

ScheduledTask

Date

java.util.Date

Date

toString()

Date

java.time.Instant

Instant

toString()

Instant

Instant now = Instant.now(); Instant later = now.plusSeconds(60);

Instant

plusMinutes()

plusHours()

Instant

Instant

...must happen during office hours...

...up to one day...

...two business days...

...valid for up to one year...

...

java.time.ZonedDateTime

java.util.Calendar

java.util.Date

Calendar

ZonedDateTime

Instant

Instant

ZonedDateTime

ZoneId

Instant now = Instant.now(); System.out.println(now); ZonedDateTime dateTime = ZonedDateTime.ofInstant( now, ZoneId.of("Europe/Warsaw") ); System.out.println(dateTime);

2016-08-05T07:00:44.057Z 2016-08-05T09:00:44.057+02:00[Europe/Warsaw]

Instant

ZonedDateTime

ZoneId

Calendar misconceptions

LocalDate localDate = LocalDate.of(2016, Month.AUGUST, 5); LocalTime localTime = LocalTime.of(10, 21); LocalDateTime local = LocalDateTime.of(localDate, localTime); ZonedDateTime warsaw = ZonedDateTime.of(local, ZoneId.of("Europe/Warsaw")); ZonedDateTime sydney = warsaw.withZoneSameInstant(ZoneId.of("Australia/Sydney")); System.out.println(warsaw); System.out.println(sydney);

2016-08-05T10:21+02:00[Europe/Warsaw] 2016-08-05T18:21+10:00[Australia/Sydney]

2016-02-05T10:21+01:00[Europe/Warsaw] 2016-02-05T20:21+11:00[Australia/Sydney]

2016-10-05T10:21+02:00[Europe/Warsaw] 2016-10-05T19:21+11:00[Australia/Sydney]

LocalDate localDate = LocalDate.of(2014, Month.FEBRUARY, 5); LocalTime localTime = LocalTime.of(10, 21); LocalDateTime local = LocalDateTime.of(localDate, localTime); ZonedDateTime warsaw = ZonedDateTime.of(local, ZoneId.of("Europe/Warsaw")); ZonedDateTime moscow = warsaw.withZoneSameInstant(ZoneId.of("Europe/Moscow")); System.out.println(warsaw); System.out.println(moscow);

2014-02-05T10:21+01:00[Europe/Warsaw] 2014-02-05T13:21+04:00[Europe/Moscow]

2015-02-05T10:21+01:00[Europe/Warsaw] 2015-02-05T12:21+03:00[Europe/Moscow]

LocalDate localDate = LocalDate.of(2017, Month.MARCH, 26); LocalTime localTime = LocalTime.of(1, 0); ZonedDateTime warsaw = ZonedDateTime.of(localDate, localTime, ZoneId.of("Europe/Warsaw")); ZonedDateTime oneDayLater = warsaw.plusDays(1); Duration duration = Duration.between(warsaw, oneDayLater); System.out.println(duration);

PT23H

Australia/Sydney

LocalDate localDate = LocalDate.of(2017, Month.APRIL, 2); LocalTime localTime = LocalTime.of(1, 0); ZonedDateTime warsaw = ZonedDateTime.of(localDate, localTime, ZoneId.of("Australia/Sydney"));

"Australia/Brisbane"

Storing and transmitting time

long

Instant.toString()

long

long

//1985-12-25 LocalDate.of(1985, Month.DECEMBER, 25)

//20:00 LocalTime.of(20, 0, 0)

//2016-12-25T20:00 LocalDateTime party = LocalDateTime.of( LocalDate.of(2016, Month.DECEMBER, 25), LocalTime.of(20, 0, 0) );

LocalDateTime

Instant

ZonedDateTime

Testing

import spock.lang.Specification import spock.lang.Unroll import java.time.* class PlusMinusMonthSpec extends Specification { static final LocalDate START_DATE = LocalDate.of(2016, Month.JANUARY, 1) @Unroll def '#date +/- 1 month gives back the same date'() { expect: date == date.plusMonths(1).minusMonths(1) where: date << (0..365).collect { day -> START_DATE.plusDays(day) } } }

date == date.plusMonths(1).minusMonths(1) | | | | | | | | 2016-02-29 2016-01-29 | | 2016-01-30 | false 2016-01-30 date == date.plusMonths(1).minusMonths(1) | | | | | | | | 2016-02-29 2016-01-29 | | 2016-01-31 | false 2016-01-31 date == date.plusMonths(1).minusMonths(1) | | | | | | | | 2016-04-30 2016-03-30 | | 2016-03-31 | false 2016-03-31 ...

Summary