Date and Time in Java 8 (1)


One of the exciting new features coming up in Java 8 is a new date and time library, which is defined by JSR 310. This is the first of a 5 part series on it.

Fri 14 December 2012

What is JSR-310

One of the exciting new features coming up in Java 8 is a new date and time library, which is defined by Jodatime - the most popular third party library in this space - but it differs in a few key ways. This is the first of a series of introductory articles on JSR-310, describing a few core concepts and classes. In the future I plan to cover Timezones, Non-ISO Calendaring Systems and how to use JSR-310 inside your application.

Core ideas

  • Immutable Value Classes - one of the serious weaknesses of the existing formatters in Java is that they aren't threadsafe. This puts the burden on the developer to use them in a threadsafe manner, and think about concurrency problems in their day to day development. JSR-310 avoids this issue by ensuring that all its core classes are Immutable and represent well defined Values.
  • Domain Driven Design - JSR-310 models its domain very precisely, with classes that represent different use cases for Date and Time closely. This differs from previous Java libraries that were quite poor in that regard. For example java.util.Date represents an instant on the timeline - a wrapper around the milliseconds since unix epoch - but if you call 'toString()' the result suggests that it has a Timezone, causing confusion amongst developers.
  • ISO vs Chrono - JSR-310 has an API that allows people to work over different calendaring systems in order to support the needs of users in some areas of the world, such as Japan or Thailand, that don't necessarily follow ISO-8601 everywhere. It puts more emphasis, however, on the needs of most people who want to work with only the ISO model, allowing people to write code that doesn't need to deal with corner cases such as two birthday in one year! The java.util.Calendar model of Date and Time uses a single parent class, with an entirely unified API to represent these needs. This has resulted in many developers using Calendar in their code without understanding the full implications of their choice.

LocalDate and LocalTime

The first classes that you may encounter when using Threeten are LocalDate and Local time - these model the core values of a date and a time within the ISO Calendaring System in the local timezone. They are local in the sense that they represent date and time from the context of the observer. Concepts like Timezones which disambiguate between the contexts of different observers are put to one side here. The Local* classes are appropriate for times when you don't need to disambiguate between such contexts. A desktop application might be one of those times - or if one was trying to provide a human readable time amount since an event. These classes can even be used for representing time on a globally distributed system, providing all the servers are in the same timezone.

There is also a composite class called LocalDateTime, which is a pair of LocalDate and LocalTime. You may be wondering why you have three classes to support this, and not a single class that reporsents all three local values. Martin Fowler explains about the concept of a time point here, and gives a very in depth explanation of this topic.

Factories

All the core classes in Threeten are constructed by fluent factory methods. When constructing a value by its constituent fields the factory is called of, when converting from another type, the factory is called from. There are also parse methods which take in strings. Here are some examples:

        // The current date and time
        LocalDateTime.now();
        
        // construct from values
        LocalDate.of(2012, 12, 12);
        LocalDate.of(2012, Month.DECEMBER, 12);
        
        // Somewhere in the middle of 1970
        LocalDate.ofEpochDay(150);
        
        // the train I took home today
        LocalTime.of(17, 18);
        
        // From a String
        LocalTime.parse("10:15:30");
        

Field Accessors

Standard Java getter conventions are used in order to obtain values from JSR-310 Classes.

        LocalDateTime timePoint = ...
        
        LocalDate theDate = timePoint.getDate();
        
        int monthAsInt = timePoint.getMonthValue();
        Month month = timePoint.getMonth();
        
        int day = timePoint.getDayOfMonth();
            day = timePoint.getDayOfYear();
        
        timePoint.getSecond();
        timePoint.getNano();
        

Adjustment

You can also alter the object values in order to perform calculations. Since all core classes are immutable in JSR-310 these methods are called 'with' and return new objects, rather than using setters. There are also methods for calculations based on the different fields.

        LocalDateTime timePoint = ...
        
        // Set the value, returning a new object
        LocalDateTime another = timePoint.withDayOfMonth(10).withYear(2010);
        
        // You can use direct manipulation methods, or pass a value and field pair
        LocalDateTime yetAnother = another.plusWeeks(3).plus(3, WEEKS);
        

JSR-310 also has the concept of an adjuster, which is a block of code that can be used in order to wrap up common processing logic. You can either write a 'WithAdjuster' - which is used to set one or more fields, or a 'PlusAdjuster' which is used to add/subtract some fields. Value classes can also act as adjusters, in which case they update the values of the fields that they represent. There are built in adjusters defined by JSR-310, but you can write your own adjusters if you have specific business logic that you wish to reuse.

        import static javax.time.calendrical.DateTimeAdjusters.*;
        
        LocalDateTime timePoint = ...
        
        // Statically imported (see above)
        foo = timePoint.with(lastDayOfMonth());
        bar = timePoint.with(firstDayOfYear());
        
        // Adjusters can also be parameterised
        timePoint.with(lastInMonth(TUESDAY));
        timePoint.with(previousOrSame(WEDNESDAY));
        
        // Using value classes as adjusters
        timePoint.with(LocalTime.now());
        

Truncation

JSR-310 tries to support different precision time-points by offering types to represent a Date, a Time and Date with Time, but obviously there are notions of precision that is more fine grained than this. The truncateTo method exists in order to support such use cases, and allows you to truncate a value to a field.

        LocalDate date = ...
        date.truncatedTo(DAYS);
        
        LocalTime time = ...
        time.truncatedTo(MICROS);
        time.truncatedTo(SECONDS);
        

Databases

ANSI SQL defines a number of types for Dates and Times. The SQL DATE type corresponds to a LocalDate, TIME corresponds to a LocalTime, and TIMESTAMP corresponds to a LocalDateTime.

Conclusion

Hopefully this has been an easy to follow introduction, and in Part 2 I plan to be cover Timezones and Periods.