java.time for the extremely impatient
This is a super quick history and primer for those familiar with java, but unfamiliar with Joda-Time.
Java’s original date and time handling had some issues that could make it cumbersome and error-prone to work with. Then came the Joda-Time library, which is a fantastic replacement for pretty much everything date and time related. It’s intuitive, clean, fully-featured, and performant.
Starting with Java 8, the library was folded into the core JDK, with very few modifications to the Joda API.
In short, if you’re not on Java 8 yet, you should probably be using the joda-time library. If you are on Java 8, you should use the java.time classes.
There’s much more to the package than what I’ll show, and you’ll eventually want to dive deeper, but this article shows you some of the most frequently used techniques I’ve experienced on the last few projects. I’m using groovy for the code snippets, but they’re just java without the semicolons.
91.83% of the time, you’ll work with LocalDate or ZonedDateTime.
import java.time.LocalDate import java.time.ZonedDateTime import java.time.ZoneId import java.time.format.DateTimeFormatter import java.time.temporal.TemporalAdjusters // If you have a date without time information, use a LocalDate. e.g. someone's birthday LocalDate localDate = new LocalDate(2016, 4, 12) println localDate.toString() // 2016-04-12 // If you need to include time in the date, use ZonedDateTime ZonedDateTime zdt = ZonedDateTime.now() // when formatting a ZonedDateTime for API communication, // you'll typically use the DateTimeFormatter.ISO_INSTANT format println zdt.format(DateTimeFormatter.ISO_INSTANT) // 2016-04-12T19:20:45.539Z // some more examples of formatters println zdt.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME) // 2016-04-12T14:20:45.539 println zdt.format(DateTimeFormatter.RFC_1123_DATE_TIME) // Tue, 12 Apr 2016 14:20:45 -0500 println zdt.format(DateTimeFormatter.ISO_ZONED_DATE_TIME) // 2016-04-12T14:20:45.539-05:00[America/Chicago] // you can also create a custom formatter DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MMM dd yyyy GG") println zdt.format(formatter) // Apr 12 2016 AD
Date manipulation is fluent and intuitive.
import java.time.LocalDate import java.time.temporal.TemporalAdjusters LocalDate localDate = LocalDate.now() println localDate // 2016-04-12 println localDate.plusMonths(1).withDayOfMonth(1) // 2016-05-01 println localDate.minusMonths(1).with(TemporalAdjusters.lastDayOfMonth()) // 2016-03-31
Prefer to work in UTC if you can - logs, database timestamps…consistency helps avoid mistakes.
in a startup class:
or as a JVM flag:
When working with a point in time, always be aware of timezone!!!!
Even when working with objects like LocalDate, you must be timezone aware if you’re using a point-in-time operation. And by point-in-time, 99.273% of the time I’m referring to the
For example, given the following scenario:
- The default JVM timezone is
2016-04-12 3:10 PM in America/Chicago (which is GMT-6), a call to
println LocalDate.now() will result in
2016-04-12 6:10 PM in America/Chicago, the same call will result in
If you’re in Chicago, that looks like tomorrow, and might be a bug for what you’re trying to do! The code is doing precisely what it’s told though; the date in London is April 13th at the time the LocalDate is created for the UTC timezone.
So to repeat, when working with specific points in time, be aware of the timezone you’re working with. For example, if you want to set a date 30 days out from now according to your business location, you could say
The java.time javadocs are actually really good, so read them for more detailed information. Wrapping up the whirlwind tour, here’s a quick table to help get you started with a few objects and their example uses:
|Object||Example Usage and Notes|
|LocalDate||Birthday, Contract date where no time or timezone is needed|
|ZonedDateTime||Most points in time, like some startDateTime, endDateTime|
|YearMonth||When you only want to work with a year/month combination. Less commonly used, but handy for date comparisons if you don’t want granularity to the day. e.g. Credit Card Expiration Month|
|LocalTime||e.g. Chris’s hardware store opens at 8 am. This date is irrespective of timezone (i.e. you wouldn’t change the opening time when daylight savings rolls around.)|