January 25, 2014

Introduction to JSR 310 Part 3 : Overview of new Java Date and Time API

The Date and Time support is a very important part of any programming language and having a stronger Date and Time support makes a language more powerful. Although, Java is a powerful programming language it was consistently lacking a stronger Date and Time support. Joda Time API is the most famous Java Date and Time API till now, but it also has set of issues with its structure. It was necessary to enhance the Java's own Date and Time support, and the outcome is JSR 310, the new Java Date and Time API.



Java 8 is being enriched with lots of cool and exciting features, which will surely be proved as the game changers of future. Java Date and Time API is also one of those cool Java 8 features. We have already had a look at the issues with Java's current Date and Time API and an overview of the Joda Time API. I recommend you to please go through the first two tutorials of this series, if you haven't yet. Please visit Introduction to JSR 310 Part 1 : Overview of existing Date and Time API and Introduction to JSR 310 Part 2 : Overview of Joda Time API.


JSR 310 - Date and Time API

We have already discussed the short comings of Java's existing Date and Time API and the reason of moving towards third party APIs like Joda Time API. Now, Java has designed a new set of Date and Time API which will address these issues. The new API will be shipped as JSR 310 in Java 8 and will appear under java.time package.


The existing Date related classes in Java are mutable and cause lot of issues related to thread safety. Developers need to focus on concurrency issues and scenarios like external systems altering a date value in the object passed to it. JSR 310 Date and Time API provides with immutable classes which helps in building thread safe system.


Another issue with the existing API is its inability to put a solid demarcation between Machine Date and a Human Date. Machine view of a Date is a single and consistently increasing number, that is millisecond counted since Fri 1970-01-01T00:00Z. While the human representation is totally different than machine representations. Humans represent date in terms of Year, Month and Date format. The existing API is mixes up both of these representations which makes the objects more complex. The JSR 310 - Date and Time API completely separates out the Human and Machine representations. 



Machine Representation:

Machine view of time is represented by Instant and Duration. Instant represents a fixed point in time as an offset from standard java epoch - 1st January, 1970. Duration represents a range between two Instants. Both of these classes are based on nanoseconds duration. The existing API has precision of milliseconds.




System.out.println(Instant.now();
System.out.println(Instant.EPOCH);

Output>>
2014-01-24T17:41:11.078Z
1970-01-01T00:00:00Z



Human Representation:

As discussed, human would discuss date or time instance in terms of Hours, Minutes, Day, Month, Year. For this the JSR 310 provides three major classes LocalDate, LocalTime, LocalDateTime. These three classes are able to represent a Date without Time, Time without Date and Date and Time both.


LocalDate, LocalTime, LocalDateTime:

Many a times there are scenarios when we just want to capture the time instance in terms of plain date (in terms of year, month, date), without dealing with time. In the existing API we usually default the time to midnight of a particular day. But this could be misleading in many of the cases. To avoid this the new API provides separate representations for Date only, Time Only, and Both.


System.out.println(LocalDate.now());    //Only Date
System.out.println(LocalTime.now());    //Only Time
System.out.println(LocalDateTime.now());    //Date and Time both

Output>>
2014-01-25
01:12:49.857
2014-01-25T01:12:49.857

The term Local in these names means local to the observer of the system.
The API comes with very convenient factory methods to create instances of these classes. Let's have a look at these factory methods.

LocalTime.from(LocalDateTime.now()); // Creating LocalTime 'from' LocalDateTime LocalDate.of(2014, Month.JANUARY, 26); //Creating LocalDate 'of' given values LocalDate.parse("2014-01-26"); //Creating LocalTime by 'parse' from given String



There are few easy to use update methods on these classes. These are convenient methods as they allow to update fields by some values or from some other fields. As the Date classes are immutable with the new API, none of these update methods actually update the object. Instead they create a newer one and return it. Original object remains unchanged.

//Today
LocalDate today = LocalDate.now();
        
//Same day last year
LocalDate sameDayLastYear = today.withYear(2012); 
System.out.println(sameDayLastYear);    // ->2012-01-25
        
//Book should be returned within 15 days
LocalDate bookDueDate = today.plusDays(15);
System.out.println(bookDueDate);    // ->2014-02-09   
        
//today at 10.30 AM. It returns LocalDateTime instance
LocalDateTime meetingTime = today.atTime(10, 30);
System.out.println(today.atTime(10, 30));    // ->2014-01-25T10:30


In addition to the above method there is also a concept of adjusters. WithAdjuster and PlusAdjuster wraps some block of code of processing logic. With adjuster helps you to adjust a particular field with the given value while the Plus adjuster helps to increment/decrement a field by passed amount of value.



LocalDateTime rightNow = LocalDateTime.now();
//First Day of next month
rightNow.with(TemporalAdjusters.firstDayOfNextMonth());    // ->2014-02-01T09:20:01.833
//Next Friday
rightNow.with(TemporalAdjusters.next(DayOfWeek.FRIDAY));    // ->2014-01-31T09:20:01.833
//First Sunday of a month
rightNow.with(TemporalAdjusters.firstInMonth(DayOfWeek.SUNDAY));    // ->2014-01-05T09:20:01.833
//Adjusting with a value object
rightNow.with(LocalTime.now());    // ->2014-01-25T09:20:01.842 

There are many useful inbuilt adjusters provided by the API but we are free to write our adjusters as well. This helps largely in designing a domain specific system.


Time Zone Support:

The Local classes only support Date or Time belonging to the local environment. But the JSR 310 - Date and Time API also supports TIme Zones. The ZonedDateTime class holds Date and Time along with the zone information.

ZonedDateTime:

ZonedDateTime is an immutable representation of Date-Time with Time Zone contained within it. For example here is what I get when I create an instance of ZonedDateTime: 2014-01-25T10:11:17.498+05:30[Asia/Kolkata].

ZoneId:

There is a special class called as ZoneId which defines mechanism to identify each time zone uniquely. Time zones are geographical regions who have certain set of rules. Rules are defined by ZoneId and ZoneRules classes. ZoneId maps the specific zone identifier to the specific set of zone rules. It supports all of the various regional time zones as it helps us to uniformly deal with the Zones throughout the system.


Time Offset Support:


We have seen JSR 310 - Date Time API's support for various time zones. The Date and Time API also supports the offset calculated from UTC. Java Date and Time API provides OffsetDate, OffSetTime, and OffsetDateTime classes.


OffsetTime offsetTime = OffsetTime.now();    // ->14:51:48.538+05:30
offsetTime.atDate(LocalDate.now());    // ->2014-01-25T14:51:48.538+05:30


Period and Duration:


Now, we'll look at two more interesting concepts provided by the JSR 310 - Date and Time API. Period is a human scale description of an amount of time. Period represent distance (such as 2 months and 3 days) on the timeline. Other classes we have seen so far represents points/instances on the timeline. In the below timeline we'll calculate the starting date of 2016 Rio de Janeiro Olympic games.



//Defining a Period of 2 Years, 6 Months, and 11 Days.
Period daysLeftForNextOlympics = Period.of(2, 6, 11);
//Adding the time Period to current LocalDate
LocalDate olympicStartDate = LocalDate.now().plus(daysLeftForNextOlympics);
System.out.println(olympicStartDate);    // ->2016-08-05

Duration is a duration between two instances on the timeline. It is similar to Period but with a different precision. Here is the same example of calculating olympics start date but this time with precision till minutes.



//Duration of 923 days
Duration durationForOlympics = Duration.ofDays(923);
//Adding 13 hours
durationForOlympics= durationForOlympics.plusHours(13); 
//Adding 42 hours
durationForOlympics = durationForOlympics.plusMinutes(42);
      
//Adding the duration to the Local Date Time  
LocalDateTime olympicStarteDateTime = LocalDateTime.now().plus(durationForOlympics); 
System.out.println(olympicStarteDateTime);    
// -> 2016-08-06T05:29:58.206  //this is the olympic start time from India.



MonthDay and YearMonth:


In the existing Java Date and Time API, everything was represented by a single Date object, which stores Date, Time, Zone offset and everything. What if we just want to store a birthdate where no year is attached. The JSR 310 Date and Time API has a special class meant for such purpose and that is MonthDay. This  represents the date in terms of Month and Day only and no year, time, or zone is attached with it.


YearMonth yearMonth = YearMonth.now();    // ->2014-01
MonthDay monthDay = MonthDay.now();    // ->--01-25


Similar to this there are cases where we just want to store Year and Month and we don't really care about the day. One of such scenarios is the credit card expiry date. JSR 310 Date and Time API provides a class called as YearMonth which represents the date in terms of Year and Month only.



<<   Part 2 : Overview of Joda Time API