Java 8 Date Time API Usage

Introduction

Every effort we put in to showcase the extent of Java 8 Date Time API usage leaves us only scratching the surface of it. This post walks through a couple of examples and some standard methods that can be helpful in daily use. Timezone aspects are not covered in this post. Refer to this post to for an introduction to Java 8 Date Time API.

Old And The New

Let us see how date formatting code changes with the new Java 8 Date Time API. If you landed directly on this post, we recommend reading this introductory post first.

Goodbye ThreadLocals and complicated code. Welcome meaningful and fluent code. The DateTimeFormatterBuilder is a very powerful tool and we will look at the possibilities soon.

Date Time API Usage

Let us look at a problem statement.

A busy parking lot in the middle of the city wants to automate and charge its regulars, online. A parking entry ticket costs $2. The rates are as follows:
1) First half-hour in part or full costs $2 (excluding entry ticket cost)
2) Second hour and above the rate is $4 for each part or full hour
Given the entry and exit time in HH:MM format, calculate the total amount to be charged to the customer

Parsing HH:MM to a LocalTime instance is easy, but other formats can also be parsed easily with a formatter of your choice using  LocalTime.parse(CharSequence text, DateTimeFormatter formatter)

Utility methods like  isBefore()  and  isAfter() are used, in this case, to check for boundary conditions.  LocalTime.of() shows the fluent API and is also available in LocalDate and other concerns.

We now need to find the duration of hours that elapsed while the car was parked in the lot and rightly so, the class to be used is called  Duration . Again, an immutable and thread-safe class, it allows modelling a quantity or amount of time in terms of seconds or nanoseconds. E.g. 120 seconds, 320.4 seconds. Similarly, date based modelling is allowed by the  Period class. We use the  between(Temporal startInclusive, Temporal endExclusive) method to obtain the duration between the car entering the lot and exiting it.

Temporal (meaning: relating to time) is an object such as date, time, offset or some combination of these. For our charge calculation we need the total number of hours (part or full). So if you use the method  toHours() you will get 12 based on our first sample (10:20 to 23:00). But we need the 40 mins remainder to be considered as an hour too. So we use the  toMinutes() to get the total minutes in the duration. Although we said that Duration is time-based modelling, it allows to fetch number of days using  toDays() . However, this assumes a 24 hr day thus ignoring day light savings effects. If you want to factor in day light savings then use the Period class.

Fun Fact: A physical duration could be of infinite length. For practicality, the duration is stored with constraints similar to Instant. The duration uses nanosecond resolution with a maximum value of the seconds that can be held in a long. This is greater than the current estimated age of the universe.

Quick Reference

EPOCH constant referring to 1970 Jan 1st

now() always returns the current time based on the System clock

Get Instant using traditional millis from epoch

Get Instant using seconds from Epoch too

Parse standard date formats to get an Instant

Unparseable date gives a DateTimeParseException

now() always returns the current time when invoked

Get Instant from a custom clock

As you can see, Instant.now() can also take a Clock reference. When invoked without parameters, Instant return current date/time based on the system clock in UTC zone. But when you pass a clock instance to it, it returns time based on that clock. In this example, we have initialized a fixed clock that always points to the birth-date of my daughter. Calling Instant.now() using this clock, an hour or a day later, will still return the same date and time my daughter was born.

A fixed clock is not a true clock in the sense but is useful for testing purposes. You can use dependency injection to put in a fixed clock for testing. E.g. raise an alarm 10 secs after the test is started by initializing a clock with an instant set to 10 secs later than the current time.

Playing with Date Time and Zones

Adding time component to LocalDate is as easy as

Consider this usage in a punch-in/punch-out time keeping application for a small store that does not reward an employee for being before time but punishes if they are late.

The  with() method returns an adjusted copy of the date-time. If the employee punches-in before start of day (e.g. 09:30 AM), his punch-in time is reset to 9:30 AM but not so if he comes in late. It is much clearer to use  with(TemporalAdjuster) rather than  adjustInto() as can be seen from the above example. Both do exactly the same thing, in fact with() calls adjustInto() internally.

Conclusion

It is very evident that a lot of effort has been put in, so that the API usage results in clear and concise code. The API offers tons of manipulation options between temporal objects. The API is well written and very extensive, please comment if there is a use-case that you think is not easily achievable. We would like to hear about it.