Core Java

Java 8 Date/Time API Tutorial

In this article we are going to explain the main features of the new Date/Time API coming with Java 8. We are going to briefly explain why a new Date/Time API is necessary in Java and what benefits it has in comparison with the “old” world.

All examples listed in this article have been done using Eclipse Luna version 4.4 and Java version 8 update 5.
 
 
 
 
 
 
 

Why do we need a new Date/Time API in Java

In the “old” Java world there were basically two main possibilities when dealing with dates and times: java.util.Date and all the related classes and interfaces; and the Joda library.

The usage of the java.util.Date related classes had several problems:

  • Concurrency problems: non Thread safe and statefull. This is a poor design.
  • Horrible naming: Date is not a date, but a timestamp, Calendar is mix of dates and times…
  • Difficulties to support different time zones.
  • SimpleDateFormat and Calendar have problems while working together.
  • Months start with 0, days with 1 and years with 1900!
  • Several things are just not possible: dates without years, 1 hour durations, periods of 3 months, dates without seconds…
  • The Joda library is a very good approach and solves some of these issues but has some performance and design problems that the new API solves. The Java 8 Date/Time API is based in the Joda library and has been implemented by the Joda library team.

    The new API has solutions for all the problems mentioned at the beggining of this chapter. The main design principles are that the used classes are inmmutable, that dates and times are separated and that it supports global calendars (although it is based on the ISO calendar). The new Date/Time API was developed under the JSR 310.

    In the following chapters we are going to show how to use the new API by going through a bunch of examples listing its main features:

    LocalDateTime, LocalDate, LocalTime, Instant and others

    There are many classes that we have to know before we start to use the API productively. In this chapter we are going to show several snippets with code using these classes. Among them we have LocalDateTime, LocalDate, LocalTime or Instant.

    It is possible to create a local date time directly with the method now():

    		LocalDateTime localDateTime = LocalDateTime.now();
    

    or by using a clock to pass to the now() method:

            Clock clock = Clock.systemDefaultZone();
            localDateTime = LocalDateTime.now( clock );
    

    or using a zoneId. In this article we are going to see how to use zone ids more in deep:

    		ZoneId zoneId = ZoneId.systemDefault();
            localDateTime = LocalDateTime.now( zoneId );
    

    It is also possible to create a local date time by passing arguments with predefined values:

    		localDateTime = LocalDateTime.of( Year.now().getValue(), Month.FEBRUARY, DayOfWeek.SATURDAY.getValue(), 1, 1, 1 );
    

    Until here, we just saw how to create LocalDateTime that contains date and time. For sure the new API offers the possiblity to work just with dates (LocalDate) and just with times (LocalTime), we are going to see this in the next chapters.
    Mentioned that, it is also possible to create a date time by combining a date and a time:

    		LocalTime time = LocalTime.NOON;
            LocalDate date = LocalDate.now();
            localDateTime = LocalDateTime.of( date, time );
    

    it is also possible to create a date (or a time) using epoch values (days or seconds since 1970):

            LocalDate localDate = LocalDate.ofEpochDay( 150 );
    

    Here only the date is created, the time part is “ignored”. This would be the output of printint this localDate in the console:

    		1970-05-31
    

    So, 150 days after 1970-01-01, ignoring the time.

    There are many other possiblities to create dates and times (and other structures like Instants, periods or durations) and to combine them. In this article we are going to see some of them:

    Stateless

    One of the best things that the new API offers is that it is stateless. This means that variables created using the Date/Time API are thread safe, so it is much easier to implement thread safe applications using this API than before.

    We are going to show this with some examples:

            LocalDateTime timeInThePast = LocalDateTime.now().withDayOfMonth( 5 ).withYear( 2005 );
    		System.out.println( "timeInThePast: " + timeInThePast );
            LocalDateTime moreInThePast = timeInThePast.minusWeeks( 2 ).plus( 3, ChronoUnit.DAYS );
    		System.out.println( "timeInThePast: " + timeInThePast );
            System.out.println( "moreInThePast: " + moreInThePast );
    

    In the code above we create a date time based on the current moment, we change the month to May and the year to 2005, then we print it out. After that we create a new date time by subtracting 2 weeks and adding 3 days to the date time created before. At the end we print both out to the console. This is the output:

    		timeInThePast: 2005-07-05T22:35:53.874
    		timeInThePast: 2005-07-05T22:35:53.874
    		moreInThePast: 2005-06-24T22:35:53.874
    

    As we can see, the first variable is not modified, although some operations have been made to it. So we can rely on that and use this API in our concurrent applications. This is a big advantage in comparison with the “old” API.

    Temporal Adjusters

    Adjusters are classes and interfaces with methods that “adjust” any kind of temporal value preserving its state, i.e. the state and values of the used temporal value does not change after applying the adjuster operations.

    Here is an piece of code showing how to use a temporal adjuster (in the project attached at the end of the article you can find more examples):

    		LocalDate now = LocalDate.now();
    		LocalDate adjusted = now.with( TemporalAdjusters.lastDayOfMonth() );
            System.out.println( "now with last day of month " + adjusted );
            System.out.println( "now " + now );
    

    and the output would be something similar to:

    		now with last day of month 2014-07-31
    		now 2014-07-24
    

    We can see that the value of the variable now did not change.

    Adjusters can be used in combination with a zone id (or with a ZonedDateTime) and its computations take into consideration the proper zone.

    You can also create your own custom adjuster. To do this, you create a class that implements the TemporalAdjuster interface with a adjustInto(Temporal) method

    It is good to mention the interface TemporalQuery that can be used to retrieve information from a temporal-based object.

    Time Zones

    All the classes in the API can be used in combination with a different time zone. There are aprox. 40 time zones available in the API. They can be retrieved via its key or via the long name:

    		ZoneId zoneIdParis = ZoneId.of( "Europe/Paris" );
            ZoneId zoneIdAGT = ZoneId.of( ZoneId.SHORT_IDS.get( "AGT" ) );
    

    and a time (or date) can be created using these time zones:

    	    LocalDateTime dateTime = LocalDateTime.now( zoneIdAGT );
    

    There is a class called ZonedDateTime that contains information about a zone and about a concrete date and time:

    		ZonedDateTime zonedDateTimeAGT = ZonedDateTime.of( dateTime, zoneIdAGT );
    		System.out.println( "Zoned Date Time AGT " + zonedDateTimeAGT );
    

    the variable zonedDateTimeAGT holds informatoin about the zone AGT and about the LocalDateTime passed as parameter. The output would be something like:

    		2014-07-23T17:55:51.612-03:00[America/Argentina/Buenos_Aires]
    

    If we are interested in knowing the current time in all the available time zones we can write a Lambda expression as follows:

    		 ZoneId.SHORT_IDS.keySet().
    		 stream().forEach( 
    		 zoneKey ->System.out.println( ZoneId.of( ZoneId.SHORT_IDS.get( zoneKey ) ) +":"+ LocalDateTime.now(ZoneId.of(ZoneId.SHORT_IDS.get( zoneKey ) ) ) ) );
    

    This expression iterates using an stream through all the available zones (ZoneId.SHORT_IDS is a map that contains all the zones) and prints them out. Maybe it does not look that nice…but I like Lambdas! so the output would be something like:

    		Asia/Shanghai : 2014-07-25T05:14:37.206
    		Africa/Cairo : 2014-07-24T23:14:37.207
    		America/St_Johns : 2014-07-24T18:44:37.209
    		America/Puerto_Rico : 2014-07-24T17:14:37.210
    		America/Phoenix : 2014-07-24T14:14:37.210
    		Asia/Karachi : 2014-07-25T02:14:37.210
    		America/Anchorage : 2014-07-24T13:14:37.210
    		Asia/Dhaka : 2014-07-25T03:14:37.211
    		America/Chicago : 2014-07-24T16:14:37.212
    		-05:00 : 2014-07-24T16:14:37.212
    		-10:00 : 2014-07-24T11:14:37.212
    		Asia/Tokyo : 2014-07-25T06:14:37.212
    		Asia/Kolkata : 2014-07-25T02:44:37.213
    		America/Argentina/Buenos_Aires : 2014-07-24T18:14:37.213
    		Pacific/Auckland : 2014-07-25T09:14:37.213
    		-07:00 : 2014-07-24T14:14:37.213
    		Australia/Sydney : 2014-07-25T07:14:37.214
    		America/Sao_Paulo : 2014-07-24T18:14:37.215
    		America/Los_Angeles : 2014-07-24T14:14:37.215
    		Australia/Darwin : 2014-07-25T06:44:37.216
    		Pacific/Guadalcanal : 2014-07-25T08:14:37.216
    		Asia/Ho_Chi_Minh : 2014-07-25T04:14:37.216
    		Africa/Harare : 2014-07-24T23:14:37.216
    		Europe/Paris : 2014-07-24T23:14:37.216
    		Africa/Addis_Ababa : 2014-07-25T00:14:37.216
    		America/Indiana/Indianapolis : 2014-07-24T17:14:37.217
    		Pacific/Apia : 2014-07-25T10:14:37.217
    

    Powerful, isn’t it?

    Instants and timestamps

    An instant is a point of time counting from the first second of 1.1.1970, also known as epoch. This timestamps are very useful and used in several applications and operating systems. The Instant class is the API answer for this machine view of the time.

    An Instant can be created in a similar way as a date or a time:

    		Instant now = Instant.now();
    

    Or using an epoch directly (in seconds or days):

    		Instant epochNow = Instant.ofEpochSecond( 60 * 60 * 24 * 30 );
    

    Instants support several getter operations:

    		System.out.println( "epoch seconds " + now.getEpochSecond() );
            System.out.println( "nano seconds " + now.getNano() );
    

    and operations like plus and minus in order to modify them:

    	    Instant tenSecondsAfter = now.plusSeconds( 10 );
    

    Instant values can be negative as well.

    Periods

    A period is a distance on the timeline. Its precision is in years, months and days. This is a very important innovation to all the classes seen until this point, that were basically points of time (instants, dates, times). It is possible to create a period using an amount of years, months and days or a set of those:

    		Period period = Period.of( 3, 2, 1 );
    		Period period4Months = Period.ofMonths( 4 );
    

    And also by specifying an start and an end dates:

     	
    		period = Period.between( LocalDate.now(), LocalDate.of( 2015, Month.JANUARY, 1 ) );
    

    Periods support different operations.

    		period4Weeks.get( ChronoUnit.DAYS )
    

    It is possible to modify a date using a period by applying operations like plus or minus:

    		LocalDate newDate = LocalDate.now().plus( period4Months );
    

    Durations

    A Duration is similar to a period but its precision is based on hours, minutes, seconds, miliseconds…It is also a distance on the timeline. A Duration can be created using an amount of seconds (or minutes, hours…) or by specifying an start and an end times:

    		Duration duration = Duration.ofSeconds( 59 );
    		duration = Duration.between( LocalTime.now(), LocalTime.MIDNIGHT );
    		duration = Duration.between( LocalTime.now( ZoneId.of( ZoneId.SHORT_IDS.get( "AGT" ) ) ), LocalTime.MIDNIGHT );
    

    As we can see in the code above, zones can be used as well.

    Durations support operations like plus, minus, gets and others.

    		duration59Mins.get( ChronoUnit.SECONDS )
    

    By using these operations it is possible to modify a date and a time using a desired duration.

    		LocalTime timeNow = LocalTime.now().plus( duration59Mins );
    

    Formatting and parsing

    We can’t finish the article without showing the several options available while parsing and formatting dates and times.

    It is possible to parse a given date using the desired pattern (can be predefined or customized):

    		LocalDateTime dateTime = LocalDateTime.of( 2014, Month.DECEMBER, 15, 15, 0, 30 );
            System.out.println( "without formatting " + dateTime );
    
            String isoDateTime = dateTime.format( DateTimeFormatter.ISO_DATE_TIME );
            System.out.println( "iso date time " + isoDateTime );
    
            String isoDate = dateTime.format( DateTimeFormatter.ISO_DATE );
            System.out.println( "iso date  " + isoDate );
    
            String isoTime = dateTime.format( DateTimeFormatter.ISO_TIME );
            System.out.println( "iso time  " + isoTime );
    
            String patternDateTime = dateTime.format( DateTimeFormatter.o
            System.out.println( "using pattern  " + patternDateTime );
    

    and the output would be:

    		without formatting 2014-12-15T15:00:30
    		iso date time 2014-12-15T15:00:30
    		iso date  2014-12-15
    		iso time  15:00:30
    		using pattern  2014.12.15 03:00:30
    

    It is also possible to parse a string into a date (or a time or both):

    		LocalDate fromString = LocalDate.parse( "2014-01-20" );
            System.out.println( "parsed from an string  " + fromString );
    		
    		LocalDate parsedFromPatern = LocalDate.parse( "2014/03/03", DateTimeFormatter.ofPattern( "yyyy/MM/dd" ) );
            System.out.println( "using pattern  " + parsedFromPatern );
    

    As we can see in the code above we can also force the application to parse only with the desired pattern, throwing a DateTimeParseException otherwise.

    The class DateTimeFormatter has several features that are not explained in this article. For more information about these please visit the official page: http://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html

    So that’s it!

    Summary

    In this article we explained many of the features and possibilities offered by the new Date/Time API like differentiation between dates and times; definition and usages of instants, durations and periods. We made some example to show that the new API is stateless, we explained the temporal adjusters briefly and we saw how to parse and format dates.

    Apart of all the new date/time related functions and operations, it offers safety and functionality for developers while handling time related information. And it is thread safe!

    We mentioned several times other features that came out with the Java 8 update. If you want to read more about all these features please visit the page: http://www.javacodegeeks.com/2014/05/java-8-features-tutorial.html.

    If you want to have more information about the implementation details of the Date/Time API please visit following links:

    – Oracle tutorial: http://docs.oracle.com/javase/tutorial/datetime/TOC.html.

    – API summary: http://docs.oracle.com/javase/8/docs/api/java/time/package-summary.html.

    – API temporal package also mentioned in this article: http://docs.oracle.com/javase/8/docs/api/java/time/temporal/package-summary.html.

    – JSR 310: https://jcp.org/en/jsr/detail?id=310.

    Download the examples

    Download
    All examples from this article (and some more) can be downloaded in the following link: datetimeapi.zip

    Dani Buiza

    Daniel Gutierrez Diez holds a Master in Computer Science Engineering from the University of Oviedo (Spain) and a Post Grade as Specialist in Foreign Trade from the UNED (Spain). Daniel has been working for different clients and companies in several Java projects as programmer, designer, trainer, consultant and technical lead.
    Subscribe
    Notify of
    guest

    This site uses Akismet to reduce spam. Learn how your comment data is processed.

    0 Comments
    Inline Feedbacks
    View all comments
    Back to top button