By Shubham Aggarwal | 1/24/2017 | General |Beginners

Java 8 Date and Time API

Java 8 Date and Time API

This article will guide you on how to use of one of the newest APIs in Java 8 which solved many problems its predecessor APIs introduced. This new API address the shortcomings of the older java.util.Date and java.util.Calendar APIs.

 

To start, we will be looking at some basic code samples to offer its usage, followed by the issues and bugs which were present in older APIs and to end, we will be looking at a short guide on how to migrate from an older API to a new one.

Overview of the new API

Working with Date API in Java used to be very cumbersome. The old date library provided included three classes: java.util.Date, java.util.Calendar and java.util.Timezone.

 

These were suitable for just the most trivial tasks. For anything even nearly tricky, the developers had to either go for third-party libraries or write a lot of custom code.

 

Java 8 introduced a completely new Date Time API (java.util.time.*) that is loosely based on the popular Java library called JodaTime. This new API dramatically simplified date and time processing and fixed many shortcomings of the old date library.

Terminology in Date API

Here, let’s look at some terms in the new API.

 

  • Instant : Represents a point in time. A timestamp.
  • LocalDate – Represents a date (in terms of a year, month, day)
  • LocalDateTime – Similar to LocalDate, but includes time with nanosecond precision
  • OffsetDateTime – Similar to LocalDateTime, but with time zone offset
  • LocalTime – Time with nanosecond precision and with no Date information
  • ZonedDateTime – Similar to OffsetDateTime, but with a Time zone ID
  • OffsetLocalTime – Similar to LocalTime, but with time zone offset
  • MonthDay – With Month and day and without year or time
  • YearMonth – With Month and year and without day or time
  • Duration – Amount of time represented in seconds, minutes and hours. Has nanosecond precision
  • Period – Amount of time represented in days, months and years

Date API in Java 8

The following examples cover the most important parts of this new API.

 

Clock

Clock provides access to the current date and time. Clocks are aware of a timezone and can be used in place of System.currentTimeMillis() to retrieve the current milliseconds. Such an instantaneous point on the time-line is also represented by the class Instant. Instants can be used to create legacy java.util.Date objects.

 

 

Clock clock = Clock.systemDefaultZone();
long millis = clock.millis();



System.out.format("millis : %d \n", millis);
System.out.println("ZoneID : " + clock.getZone());



Instant instant = clock.instant();
Date legacyDate = Date.from(instant);   // legacy java.util.Date



System.out.format("legacyDate millis : %d", legacyDate.getTime());

Timezones

ZoneId represents each Timezone. They can be accessed by static factory methods. Timezones define the offsets which are necessary to convert between instants and local dates and times. Let’s look at a sample code below:

 

 

// prints all available timezone ids

System.out.println(ZoneId.getAvailableZoneIds());



ZoneId zone1 = ZoneId.of("Asia/Kolkata");
ZoneId zone2 = ZoneId.of("Brazil/East");
System.out.println(zone1.getRules());
System.out.println(zone2.getRules());

LocalTime

LocalTime represents a time without a timezone, e.g. 1pm or 23:30:16. The following example creates two local times for the time zones defined above. Then we compare both times and calculate the difference in hours and minutes between both times. Here is the fragment:

 

 

ZoneId zone1 = ZoneId.of("Asia/Kolkata");

ZoneId zone2 = ZoneId.of("Brazil/East");

LocalTime now1 = LocalTime.now(zone1);

LocalTime now2 = LocalTime.now(zone2);



System.out.println(now1.isBefore(now2));  // false



long hoursBetween = ChronoUnit.HOURS.between(now1, now2);

long minutesBetween = ChronoUnit.MINUTES.between(now1, now2);



System.out.println(hoursBetween); //-7

System.out.println(minutesBetween); //-449

LocalTime comes with various factory methods to simplify the creation of new instances, including parsing of time strings.

 

 

LocalTime late = LocalTime.of(23, 59, 59);

System.out.println(late);       // 23:59:59



DateTimeFormatter germanFormatter =

      DateTimeFormatter

              .ofLocalizedTime(FormatStyle.SHORT)

              .withLocale(Locale.GERMAN);



LocalTime localTime = LocalTime.parse("11:37", germanFormatter);

System.out.println(localTime);   // 11:37

LocalDate

LocalDate represents a distinct date, e.g. 2014-03-11. It's immutable and works exactly analog to LocalTime. The sample code below demonstrates how to calculate new dates by adding or subtracting days, months or years. Keep in mind that each manipulation returns a new instance and not just the value.

 

 

LocalDate today = LocalDate.now();

LocalDate tomorrow = today.plus(1, ChronoUnit.DAYS);

LocalDate yesterday = tomorrow.minusDays(2);



LocalDate independenceDay = LocalDate.of(2016, Month.DECEMBER, 25);

DayOfWeek dayOfWeek = independenceDay.getDayOfWeek();

System.out.println("dayOfWeek = " + dayOfWeek); //SUNDAY

Parsing a LocalDate from a String is just as easy as parsing a LocalTime:

 

 

DateTimeFormatter germanFormatter =

      DateTimeFormatter

              .ofLocalizedDate(FormatStyle.MEDIUM)

              .withLocale(Locale.GERMAN);



LocalDate xmas = LocalDate.parse("25.12.2016", germanFormatter);

System.out.println(xmas);   // 2016-12-25

LocalDateTime

LocalDateTime class represents a date-time. It combines date and time as seen in the above sections into one instance. LocalDateTime is immutable and works similar to LocalTime and LocalDate. We can utilize methods for retrieving certain fields from a date-time:

 

 

LocalDateTime sylvester = LocalDateTime.of(2014, Month.DECEMBER, 31, 23, 59, 59);



DayOfWeek dayOfWeek = sylvester.getDayOfWeek();

System.out.println(dayOfWeek);      // WEDNESDAY



Month month = sylvester.getMonth();

System.out.println(month);          // DECEMBER



long minuteOfDay = sylvester.getLong(ChronoField.MINUTE_OF_DAY);

System.out.println(minuteOfDay);    // 1439

With the additional information of a timezone it can be converted to an instant. Instants can easily be converted to legacy dates of type java.util.Date.

 

 

Instant instant = sylvester

      .atZone(ZoneId.systemDefault())

      .toInstant();



Date legacyDate = Date.from(instant);

System.out.println(legacyDate);     // Wed Dec 31 23:59:59 CET 2014

Formatting date-times works just like formatting dates or times. Instead of using pre-defined formats we can create formatters from custom patterns.

 

 

DateTimeFormatter formatter =

      DateTimeFormatter

              .ofPattern("MMM dd, yyyy - HH:mm");



LocalDateTime parsed = LocalDateTime.parse("Nov 03, 2014 - 07:13", formatter);

String string = formatter.format(parsed);

System.out.println(string);     // Nov 03, 2014 - 07:13

Unlike java.text.NumberFormat the new DateTimeFormatter is immutable and thread-safe.

 

Periods

A Period represents a value such as “2 months and 3 days,” which is a distance on the timeline. This is in contrast to the other classes we’ve looked at so far, which have been points on the timeline.

 

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



// You can modify the values of dates using periods

      LocalDate newDate = oldDate.plus(period);

      ZonedDateTime newDateTime = oldDateTime.minus(period);

// Components of a Period are represented by ChronoUnit values

      assertEquals(1, period.get(ChronoUnit.DAYS));

Migrating to the New API

This is one of the most important topics to be discussed here. Let’s look at differences in the new API in comparison to the older API.

Getting current Time

// Old

Date now = new Date();



// New

ZonedDateTime newNow = ZonedDateTime.now();

Representing Specific Time

// Old

Date birthDay = new GregorianCalendar(1990, Calendar.DECEMBER, 15).getTime();



// New

LocalDate newBbirthDay = LocalDate.of(1990, Month.DECEMBER, 15);

Adding and Subtracting Time

 

// Old

GregorianCalendar calendar = new GregorianCalendar();

calendar.add(Calendar.HOUR_OF_DAY, -6);

Date sixHoursBefore = calendar.getTime();



// New

LocalDateTime newSixHoursBefore = LocalDateTime.now().minusHours(6);

Extracting specific fields

// Old

int oldMonth = new GregorianCalendar().get(Calendar.MONTH);



// New

Month newMonth = LocalDateTime.now().getMonth();

Truncating

 

// Old

Calendar oldNow = Calendar.getInstance();

oldNow.set(Calendar.MINUTE, 0);

oldNow.set(Calendar.SECOND, 0);

oldNow.set(Calendar.MILLISECOND, 0);

Date truncatedOld = oldNow.getTime();



// New

LocalTime truncatedNew = LocalTime.now().truncatedTo(ChronoUnit.HOURS);

Features of new Date/Time APIs

New API solved many problems and made life easier already. Let’s look at some core features it brings to the table:

 

  • New API is very clear – the API is very clear, concise and easy to understand. It does not have a lot of inconsistencies found in the old library such as the field numbering (in Calendar months are zero-based, but days of the week are one-based).
    • Calendar.getInstance() is not very well named: it is hard to tell which instance you are getting without reading the Javadoc. LocalTime.now() is quite self-describing: you get a time and it is now.
    • To offset a date, you call an offsetting method (plus) whereas with the Calendar API, you have to manually change the fields of the object (in this example, the hour) which is error prone.
  • Another feature of the library is its flexibility – working with multiple representations of time. The old date library included only a single time representation class – java.util.Date, which despite its name, is actually a timestamp. It only stores the number of milliseconds elapsed since the Unix epoch.
  • Yet another feature is that all time representations in Java 8 Date Time API are immutable and thus thread-safe. All mutating methods return a new copy instead of modifying the state of the original object.
    • The Calendar API is not safe: nothing prevents you from writing cal.set(123, 2) which would throw a not-so-helpful ArrayOutOfBoundsException. The new API uses enums which solves that problem.
    • The new API uses immutable objects, which makes it thread safe.

Better than JodaTime

Joda-Time often uses NULL as a default for system timezone, default locale, current timestamp etc. while JSR-310 almost always rejects NULL values.

 

JSR-310 handles nanosecond precision while Joda-Time is limited to millisecond precision.

 

JSR-310 offers enums like DayOfWeek or Month while Joda-Time does not offer this because it was mainly developed in years 2002-2004 before Java 5.

Conclusion

In this guide, we walked through the new Date Time API available in Java 8. We looked at its features, compared to the older API and pointed out differences using multiple examples.

By Shubham Aggarwal | 1/24/2017 | General

{{CommentsModel.TotalCount}} Comments

Your Comment

{{CommentsModel.Message}}

Recent Stories

Top DiscoverSDK Experts

User photo
3355
Ashton Torrence
Web and Windows developer
GUI | Web and 11 more
View Profile
User photo
3220
Mendy Bennett
Experienced with Ad network & Ad servers.
Mobile | Ad Networks and 1 more
View Profile
User photo
3060
Karen Fitzgerald
7 years in Cross-Platform development.
Mobile | Cross Platform Frameworks
View Profile
Show All
X

Compare Products

Select up to three two products to compare by clicking on the compare icon () of each product.

{{compareToolModel.Error}}

Now comparing:

{{product.ProductName | createSubstring:25}} X
Compare Now