Friday, June 15, 2018

Java SE 8 Date and Time API

Time is a scalar quantity, so it would only need a number to express a time value.
Since you want to relate time to calendars, which are made for humans, things get more complicated.

Java date and time classes' history:

  • the java.util.Date class was introduced in Java 1.0
  • the java.util.Calendar class was introduced in Java 1.1
  • third-party date and time libraries, such as Joda-Time, were used by developer to overcome Java standard API's issues
  • the java.time API was introduced in Java 1.8 to fix the flaws in the previous versions of the platforms

1 The Time Line

The unit of time is the second, which is derived from the Earth's rotation around its axis.

Universal Time

  • Earth rotation is not uniform: in 1967, a more precise definition of second was derived from the property of atoms of caesium-133 and atomic clocks were introduced to keep the official time.
  • Since rotation rate of Earth varies with climate events, official time keepers synchronize absolute time with solar mean time
    • official time keepers add or remove a second to keep the Universal Time close to the mean solar time
  • In UTC, a day has 24 * 60 * 60 = 86400 seconds.
    • the number of seconds in a minute is usually 60, but with an occasional leap second it may be 61.

Universal Time and Computer Systems

  • Computer system keeps 86400 seconds per day and do not respect leap seconds.
  • when a leap second is officially introduced, computer systems slow down or speed up before the leap second.

Java Date and Time API specification for the time scale:

  • A day has 86,400 seconds
  • Time scale matches the official time at noon each day

The Time Line in Java and the Instant class

  • the Instant class represents a point in the time line
  • The static method Instant.now() returns the current instant
  • time is measured on time scale with origin set at midnigth of January 1, 1970 at Greenwich meridian
  • from that origin, time is measured in 86,400 seconds per day
  • Instant.MIN and Instant.MAX are one billion behind and ahead the origin

The Duration class represents the amount of time between two instants

  • the static method Duration.between gives the difference between two instants

Example:

 Instant start = Instant.now();
 callMethod();
 Instant stop = Instant.now();
 Duration timeElapsed = Duration.between(start, stop);
 long millis = timeElapsed.toMillis(); 

2 Human Time

When you refer to absolute time you mean the Time Line, when you refer to human time you mean the UTC Local Time.

Java consider two types of human times: local time/date and zoned time

  • the LocalDate/LocalTime class has a date/time of the day, but no time zone information.
    • use a LocalDate to store a local date,
    • for example: Ayrton Senna was born on 21 March 1960
  • the ZonedDateTime class has a time, day and a time zone: it represents a precise instant of time
    • use a ZonedDateTime to store an instant in the time line
    • for example: launch of SpaceX Falcon 9 is scheduled for May 20, 2018, 4:42:00 p.m. EDT, 20:42 UTC

LocalTime Application Examples

  • Use local dates and times to represent bithdays, holidays and timetables/schedule times

ZonedTime Wrong Application Examples

  • use a time zone only to represent absolute time instances
  • using a time zone to represent a meeting scheduled for every week is a bad choice
    • if you happen to cross the time zone boundaries the meeting will be an hour late or early

3 Local Date

The LocalDate class

  • A LocalDate class instance is a date with a year, month and a day of the month
  • To construct a LocalDate instance use the static methods LocalDate.now() or LocalDate.of().

Example: LocalDate.of() and LocalDate.now()

  LocalDate today = LocalDate.now(); // Today's date
  LocalDate sennaBirthday1 = LocalDate.of(1960, 3, 21);
  LocalDate sennaBirthday2 = LocalDate.of(1960, Month.MARCH, 21);
  // Note that LocalDate's conventions for month parameter differ from those in java.util.Date 

The Period class

  • a Period object represents the difference between two local date objects
  • a Period object expresses a number of elapsed years, months or days
  • note that the difference between two time instants is a Duration, not a Period

Example: compute the next birthday

  birthday.plus(Period.ofYears(1))    // OK
  birthday.plusYears(1)               // OK
  birthday.plus(Duration.ofDays(365)) // KO, may produce a wrong result in a leap year

The until method returns the difference between two local dates

Example: until method

  LocalDate today = LocalDate.now();
  LocalDate christmas = LocalDate.of(2018, Month.DECEMBER, 25);
  Period period1 = today.until(christmas);   // a period in months and days 
  Period period2 = today.until(christmas, ChronoUnit.DAYS);  // a period in days   

The DayOfWeek enumeration

  • The localDate.getDayOfWeek() method returns a value of the DayOfWeek enumeration
  • DayOfWeek.MONDAY has the value 1 and DayOfWeek.SUNDAY has the value 7, while in the java.util.Calendar, Sunday has value 1 and Saturday value 7

The MonthDay, YearMonth and Year classes describe partial dates

  • for example: December 25, without the year, can be represented as a MonthDay

4 Date Adjusters

The TemporalAdjusters class provides static methods for making common change in date calculations

  • can be applied to compute dates such as the first Monday of every month in a scheduling application

Example: use the TemporalAdjusters class to get the first Monday of the current month

  int currentYearValue = Year.now().getValue();
  Month currentMonth = LocalDate.now().getMonth();
  LocalDate firstMonday = LocalDate.of(currentYearValue, currentMonth, 1).with(TemporalAdjusters.nextOrSame(DayOfWeek.MONDAY));  

5 Local Time

The LocalTime class
  • A LocalTime object represents a time of day in the ISO format without a time zone.
    • for example: 22:15:30
  • To create a LocalTime instance use the static methods LocalTime.now() or LocalTime.of().

Example: LocalTime.of() and LocalTime.now()

  LocalTime timeOfClock = LocalTime.now();
  LocalTime timeForBed = LocalTime.of(23, 30); // or LocalTime.of(23, 30, 0)

Example: plus() method

  LocalTime nightShift = LocalTime.of(20, 30);
  LocalTime endWork = nightShift.plusHours(9); // endWork is 05:30:00
  System.out.println("work starts at "+nightShift+" and ends at "+endWork);
  // the plus and minus operations wrap around a 24-hour day

Note LocalTime doesn’t concern itself with AM/PM.

The LocalDateTime class
  • The LocalDateTime class represents a date and a time
  • it is suitable for storing a point in time in a fixed time zone.
  • use a LocalDateTime for schedules of events or classes
  • Note: if you need to deal with users that are in different time zones or time that span over daylight saving time, use a ZonedDateTime not a LocalDateTime

6 Zoned Time

Time zones are messy and daylight saving time make things even more complicated.

Time zone makes it harder to develop calendar applications

  • if you travel from a continent to another, you move from a local time to another local time, but you expect the calendar application to alert you with the new local time

The Internet Assigned Numbers Authority (IANA) maintains a database which contains all known time zones around the world

  • The IANA updates this database when rules for daylight saving time change
  • Java uses the IANA's time zones database

A Time Zone has an ID, which is a String such as "Europe/Berlin" or "Europe/London"

  • the static method ZoneId.getAvailableZoneIds returns a collection of all the available time zone

To construct a ZoneId object use the static method ZoneId.of(id) passing a selected time zone ID as a String value

Create a ZonedDateTime object in two ways:

  • invoke the static method ZonedDateTime.of(year, month, day, hour, minute, second, nano, zoneId)
  • turn a LocalDateTime object into a ZonedDateTime by calling local.atZone(zoneId)

Example: create a ZonedDateTime object

  ZonedDateTime liftOff = ZonedDateTime.of(2018, 5, 20, 16, 42, 0, 0, ZoneId.of("America/New_York"));
  System.out.println("launch of SpaceX Falcon 9 is scheduled for: " + liftOff); 
    // 2018-05-20T16:42-04:00[America/New_York]

A zonedDateTime represents a specific instant on the time line in certain time zone

  • you can get a ZonedDateTime from an Instant and viceversa:
    • zonedDateTime.toInstant() returns an Instant object
    • instant.atZone(ZoneId.of("<ZoneIdString>")) returns the ZonedDateTime object at the UTC time zone argument

ZonedDateTime and LocalDateTime have many methods in common, but daylight savings time introduces some complications

  • When a dayligth saving time is introduced, clocks advance by an hour:
    if you construct a ZonedDateTime that is contained in a skipped hour, you get a ZonedDateTime with an hour ahead
  • When a dayligth saving time ends, clocks are set back by an hour and there are two instants with the same local time:
    if you construct a ZonedDateTime that is contained in this range you get the earlier of the two.

Note: pay attention when changing date across saving time boundaries

  • suppose you are building a application that schedule meetings
  • if you want to set another meeting for the next week, add a period of seven days not a duration
  ZonedDateTime nextMeeting = meeting.plus(Period.ofDays(7)); // OK 

The OffsetDateTime class represents a time with an offset from UTC

  • this class does not use time zone rules
  • this class should be used by specialized applications, which do not require time zone rules

7 Formatting and Parsing

The DateTimeFormatter class contains three types of formatters for date/time values:

  • predefined standard formatters
    • intended for machine-readable timestamps
  • locale-specific formatters
    • intended for presenting date and times to human readers
  • formatters with custom patters

Example: how to use a standard formatter

  String formattedString = DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(liftOff);
  System.out.println(formattedString); // 2018-05-20T16:42:00-04:00

To create a Locale-specific formatter call one of the three static construct methods and pass a formatting style:

  • there are three construct methods: ofLocalizedDate, ofLocalizedTime or ofLocalizedDateTime
  • there are four formatting styles: SHORT, MEDIUM, LONG and FULL

Example: how to use a Locale-specific formatter

  String formattedString = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG).format(liftOff);
  System.out.println(formattedString); // May 20, 2018 4:42:00 PM EDT

the withLocale() method

  • the format() method uses the default locale
  • to change to a different locale use the withLocale() method and then call the format() method

Example: use the Locale-specific formatter setting a specific locale

  String formattedString = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG).withLocale(Locale.ITALIAN).format(liftOff);
  System.out.println(formattedString); // 20 maggio 2018 16.42.00 EDT

Note that the java.time.format.DateTimeFormatter class replaces the java.util.DateFormat class

You can create your own date format by specifying a pattern

Example: a DateTimeFormatter with a pattern defined by the developer

  String formattedString = DateTimeFormatter.ofPattern("EEEE dd/MM/yyyy  HH:mm").format(liftOff);
  System.out.println(formattedString); // Sunday 20/05/2018  16:42
  // the pattern is formed by letters, each of which indicates a different time field  
Parsing a string into a date/time

To parse a date/time value from a string, use a static parse method of the class you want parse into

Example: parsing with standard formatter

  LocalDate sennaBirthday = LocalDate.parse("1960-06-21");      
  liftOff = ZonedDateTime.parse("2018-05-20T16:42:00-04:00");

Example: parsing with a custom formatter

  liftOff = ZonedDateTime.parse("2018/05/20 16:42:00-04:00", DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ssxxx"));

8 Interoperating with Legacy Code

The new Date and Time API have to interoperate with Java 1.0 and Java 1.1 classes

The java.util.Date and the Instant classes are similar. In Java SE 8, the java.util.Date class has two added conversion methods:

  • the toInstant() method converts from Date to Instant
  • the from() static method converts from Instant to Date

The java.util.GregorianCalendar and the ZonedDateTime classes are similar. In Java SE 8, the java.util.GregorianCalendar classes has two added conversion methods:

  • the toZonedDateTime() method converts from GregorianCalendar to a ZonedDateTime
  • the from() static method converts from ZonedDateTime to GregorianCalendar

No comments :

Post a Comment