November 25, 2014
Hot Topics:

Java SE 8 and the New Date and Time Library

  • May 21, 2014
  • By Alin Marian
  • Send Email »
  • More Articles »

Introduction (Business Case)

With the release of the new Java SE 8, a new Date and Time API has been introduced to fix common problems that developers have encountered among time. The previous API had mutable classes (when you have a reference to an instance of an object, the contents of that instance can be altered), not thread-safe and used zero-based indexes for months; facts that led developers to other date/time libraries like Joda-Time in order to get a better API.

The new API, JSR-310, is designed to replace the existing Date and Calendar classes, incorporating ideas from Joda-Time. Each of the new core Java classes for date, time, date and time combined, time zones, instants, duration, and clocks, has a specific purpose and has explicitly defined behavior. It also provides support for the international ISO-8601 time standard and supports the frequently used Japanese, Minguo, Hijrah and Thai Buddhist calendars. This article is intended to give a brief overview over the new Date and Time API and provides simple and ready to use examples.

Creating Objects

First, let’s start by creating objects. The java.time package has separate classes to represent a date without time, a time-of-day without date, a year, a month, a day-of-week, a year and month, a month and day-of-month, a date and time without time-zone, a full date and time complete with time-zone and an instantaneous point in time. The following examples use the Instant, LocalDate, LocalTime and LocalDateTime classes in order to get an instance of time, date or an instance of date and time.

The Instant class is one of the core new classes introduced in the date/time package. An Instant instance represents the start of a nanosecond on the timeline and it’s useful for working with time viewed from a machine perspective:

// Current timestamp
Instant timestamp = Instant.now();
System.out.println("Current timestamp: " + timestamp);
 
// Create Instant from specified clock
Instant fromClock = Instant.now(Clock.systemUTC());
System.out.println("Instant from specified clock: " + fromClock);
 
// Create Instant from a String
Instant fromString = Instant.parse("1995-10-23T10:12:35Z");
System.out.println("Instant from a String: " + fromString);
 
// java.util.Date into an Instant
Instant instant = Instant.ofEpochMilli(new Date().getTime());
System.out.println("java.util.Date into an Instant: " + instant);

The output will be:

Current timestamp: 2014-05-09T10:58:27.657Z
Instant from specified clock: 2014-05-09T10:58:27.757Z
Instant from a String: 1995-10-23T10:12:35Z
java.util.Date into an Instant: 2014-05-09T10:58:27.764Z

An instance of the LocalDate class represents a year-month-day in the ISO-8601 calendar system and is useful for representing a date without a time zone:

// Current date
LocalDate date = LocalDate.now();
System.out.println("Current date: " + date);
 
// Create LocalDate from a String
LocalDate dateFromString = LocalDate.parse("2014-05-05");
System.out.println("LocalDate from a String: " + dateFromString);
 
// Create LocalDate from default time-zone clock
LocalDate date1 = LocalDate.now(Clock.systemDefaultZone());
System.out.println("LocalDate from default time-zone clock: " + date1);
 
// Create LocalDate by providing input arguments
LocalDate birthday = LocalDate.of(2014, Month.JULY, 16);
System.out.println("LocalDate by providing input arguments: " + birthday);
 
// Current date in "Asia/Tokyo"
LocalDate todayTokyo = LocalDate.now(ZoneId.of("Asia/Tokyo"));
System.out.println("Current date in 'Asia/Tokyo': " + todayTokyo);
 
// Get an instance of LocalDate from a year and day-of-year
LocalDate get99DayOfYear = LocalDate.ofYearDay(2000, 99);
System.out.println("LocalDate from a year and day-of-year: " + get99DayOfYear);

The output will be:

Current date: 2014-05-09
LocalDate from a String: 2014-05-05
LocalDate from default time-zone clock: 2014-05-09
LocalDate by providing input arguments: 2014-07-16
Current date in 'Asia/Tokyo': 2014-05-09
LocalDate from a year and day-of-year: 2000-04-08

The LocalTime class is similar to LocalDate and represents a human-based time of day without time zone in the ISO-8601 calendar system:

// Current time
LocalTime time = LocalTime.now();
System.out.println("Current time: " + time);
 
// Create LocalTime from a String
LocalTime time1 = LocalTime.parse("12:00:53");
System.out.println("LocalTime from a String: " + time1);
 
// Create LocalTime from default time-zone clock
LocalTime time2 = LocalTime.now(Clock.systemDefaultZone());
System.out.println("LocalTime from default time-zone clock: " + time2);
 
// Create LocalTime by providing input arguments
LocalTime time3 = LocalTime.of(12, 26, 53);
System.out.println("LocalTime by providing input arguments: " + time3);
 
// Current time in "Asia/Tokyo"
LocalTime timeTokyo = LocalTime.now(ZoneId.of("Asia/Tokyo"));
System.out.println("Current time in 'Asia/Tokyo': " + timeTokyo);
 
// Create LocalTime from LocalDateTime
LocalTime fromLocalDateTime = LocalTime.from(LocalDateTime.now(ZoneId.of("Asia/Tokyo")));
System.out.println("LocalTime from LocalDateTime: " + fromLocalDateTime);
 
// Get an instance of LocalTime from a second-of-day value
LocalTime localTime = LocalTime.ofSecondOfDay(50000);
System.out.println("LocalTime from a second-of-day value: " + localTime);

The output will be:

Current time: 13:58:27.797
LocalTime from a String: 12:00:53
LocalTime from default time-zone clock: 13:58:27.797
LocalTime by providing input arguments: 12:26:53
Current time in 'Asia/Tokyo': 19:58:27.797
LocalTime from LocalDateTime: 19:58:27.798
LocalTime from a second-of-day value: 13:53:20

       

If we want to work both with date and time, then we can use another core class from the date/time package, the LocalDateTime class. It’s a class used to represent date (month-day-year) together with time (hour-minute-second-nanosecond):

// Current date
LocalDateTime localDateTime = LocalDateTime.now();
System.out.println("Current date: " + localDateTime);
 
// Current date using LocalDate and LocalTime
LocalDateTime localDateTime1 = LocalDateTime.of(LocalDate.now(), LocalTime.now());
System.out.println("Current date using LocalDate and LocalTime: " + localDateTime1);
 
// Create LocalDateTime from a String
LocalDateTime localDateTime2 = LocalDateTime.parse("2014-05-05T12:07:45.915");
System.out.println("LocalDateTime from a String: " + localDateTime2);
 
// Create LocalDateTime by providing input arguments
LocalDateTime birthday1 = LocalDateTime.of(2014, Month.JULY, 16, 13, 15, 30);
System.out.println("LocalDateTime by providing input arguments: " + birthday1);
 
// Current date in "Asia/Tokyo"
LocalDateTime localDateTime3 = LocalDateTime.now(ZoneId.of("Asia/Tokyo"));
System.out.println("Current date in 'Asia/Tokyo': " + localDateTime3);
 
// Create LocalDateTime from Instant
LocalDateTime localDateTime4 = LocalDateTime.ofInstant(Instant.now(), ZoneId.systemDefault());
System.out.println("LocalDateTime from Instant: " + localDateTime4);

The output will be:

Current date: 2014-05-09T13:58:27.798
Current date using LocalDate and LocalTime: 2014-05-09T13:58:27.798
LocalDateTime from a String: 2014-05-05T12:07:45.915
LocalDateTime by providing input arguments: 2014-07-16T13:15:30
Current date in 'Asia/Tokyo': 2014-05-09T19:58:27.800
LocalDateTime from Instant: 2014-05-09T13:58:27.800

Basic Usage Examples

Going further, we can use the plus and minus methods for adding or subtracting hours, minutes, days, weeks, and months. Note that these methods always return a new instance since Java 8 date/time classes are immutable. The following example shows a few of these methods:

LocalDate localDate = LocalDate.now();
System.out.println("Today is: " + localDate);

        
// Plus and minus operations to an LocalDate
System.out.println("Adding 3 weeks to date: " + localDate.plusWeeks(3));
System.out.println("Adding 4 years and 6 months to date: "

+ localDate.plusYears(4).plusMonths(6));
System.out.println("3 weeks and 9 days before today: "

+ localDate.minusWeeks(3).minusDays(9));
System.out.println("6 months before today: "

+ localDate.minusMonths(6));

 
// Combines this date with a time to create a LocalDateTime
LocalDateTime localDateTime = localDate.atTime(LocalTime.now());

        
// Plus and minus operations to an LocalDateTime
System.out.println("Adding 43 minutes and 14 seconds: "

+ localDateTime.toLocalTime().plusMinutes(43).plusSeconds(14));
System.out.println("Adding the same amount of time using Duration: "

+ localDateTime.toLocalTime().plus(Duration.ofMinutes(43).plusSeconds(14)));
System.out.println("Adding 5 weeks and 3 days using Period: "

+ localDateTime.toLocalDate().plus(Period.ofWeeks(5).plusDays(3)));

        
Instant timestamp = Instant.now();
System.out.println("Current timestamp: " + timestamp);

        
// Plus and minus operations to an Instant
System.out.println("Subtracts 2 hours from an Instant: " 

+ timestamp.minus(Duration.ofHours(2)));
System.out.println("Subtracts 1 day from an Instant: " 

+ timestamp.minus(1, ChronoUnit.DAYS));
System.out.println("Adding 2 hours and 30 minutes to an Instant: " 

+ timestamp.plus(Duration.ofHours(2).plusMinutes(30)));
System.out.println("Adding 2 days to an Instant: " 

+ timestamp.plus(2, ChronoUnit.DAYS));

The output will be:

Today is: 2014-05-07
Adding 3 weeks to date: 2014-05-28
Adding 4 years and 6 months to date: 2018-11-07
3 weeks and 9 days before today: 2014-04-07
6 months before today: 2013-11-07
Adding 43 minutes and 14 seconds: 17:44:31.506
Adding the same amount of time using Duration: 17:44:31.506
Adding 5 weeks and 3 days using Period: 2014-06-14
Current timestamp: 2014-05-07T14:01:17.507Z
Subtracts 2 hours from an Instant: 2014-05-07T12:01:17.507Z
Subtracts 1 day from an Instant: 2014-05-06T14:01:17.507Z
Adding 2 hours and 30 minutes to an Instant: 2014-05-07T16:31:17.507Z
Adding 2 days to an Instant: 2014-05-09T14:01:17.507Z

Working with TemporalAdjusters

The java.time.temporal package is another way of date and time manipulation. A good example is to use the TemporalAdjuster interface that provides methods that can take a Temporal value and return an adjusted value. If we want to find the first day of a month, the last day of the year, the next Sunday or the last Sunday of the year, and other examples like these, the TemporalAdjusters class provides a set of predefined adjusters. The following examples use some of these predefined adjusters:

LocalDate localDate = LocalDate.now();
Locale locale = Locale.getDefault();

 
Month month = localDate.getMonth();
DayOfWeek dayOfWeek = localDate.getDayOfWeek();

        
// Working with TemporalAdjusters
System.out.println("Today is: "

+ dayOfWeek.getDisplayName(TextStyle.FULL, locale));
System.out.println("This month is: "

+ month.getDisplayName(TextStyle.FULL, locale));
System.out.println("First day of the month was: "

+ localDate.with(TemporalAdjusters.firstDayOfMonth()).getDayOfWeek().name());
System.out.println("Last day of the month will be: "

+ localDate.with(TemporalAdjusters.lastDayOfMonth()).getDayOfWeek().name());
System.out.println("Past Friday was: "

+ localDate.with(TemporalAdjusters.previous(DayOfWeek.FRIDAY)));
System.out.println("Next Friday is: "

+ localDate.with(TemporalAdjusters.next(DayOfWeek.FRIDAY)));
System.out.println("First Friday this month was: "

+ localDate.with(TemporalAdjusters.firstInMonth(DayOfWeek.FRIDAY)));
System.out.println("Last Friday this month will be: "

+ localDate.with(TemporalAdjusters.lastInMonth(DayOfWeek.FRIDAY)));
System.out.println("First day of next month will be: "

+ localDate.with(TemporalAdjusters.firstDayOfNextMonth()).getDayOfWeek().name());
System.out.println("First day of the year was: "

+ localDate.with(TemporalAdjusters.firstDayOfYear()).getDayOfWeek().name());
System.out.println("Last day of the year will be: "

+ localDate.with(TemporalAdjusters.lastDayOfYear()).getDayOfWeek().name());

The output will be:

Today is: Friday
This month is: May
First day of the month was: THURSDAY
Last day of the month will be: SATURDAY
Past Friday was: 2014-05-02
Next Friday is: 2014-05-16
First Friday this month was: 2014-05-02
Last Friday this month will be: 2014-05-30
First day of next month will be: SUNDAY
First day of the year was: WEDNESDAY
Last day of the year will be: WEDNESDAY

Working with ZoneId, ZoneOffset and ZonedDateTime

As we all know, time zones are regions described by an identifier and has the format region/city (Europe/London) and an offset from Greenwich/UTC time (+01:00). The new API has five main classes to help developers in dealing with time zones:

  • ZoneId is a class that represents a time zone identifier and it also provides rules for converting between an Instant and a LocalDateTime time.
  • ZoneOffset is a class that represents a time zone offset from Greenwich/UTC time .
  • ZonedDateTime is a class that represents a full date (year, month, day) and time (hour, minute, second, nanosecond) with a time zone (region/city).
  • OffsetDateTime is a class that represents a full date (year, month, day) and time (hour, minute, second, nanosecond) with an offset from Greenwich/UTC time (+/-hours:minutes).
  • OffsetTime is a class that represents time (hour, minute, second, nanosecond) with an offset from Greenwich/UTC time (+/-hours:minutes).

Going further, to make use of these classes, the next example passes a region and in return, receives a java.util.Map with the available time zones from the specified region:

public static Map<String, String> getZoneDateTime(String continent) {
 
	LocalDateTime dateTime = LocalDateTime.now();
	Map<String, String> zoneList = new HashMap<>();
	for (String s : ZoneId.getAvailableZoneIds()) {
		ZoneId zone = ZoneId.of(s);
		if (zone.getId().contains(continent)) {
			zoneList.put(zone.getId(), dateTime.atZone(zone).getOffset().getId());
		}
	}
	return zoneList;
}
 
public static void main(String[] args) {
 
	Map<String, String> zoneList = getZoneDateTime("Europe");
	 
	for (Map.Entry<String, String> entry : zoneList.entrySet()) {
		String zoneId = entry.getKey();
		String offset = entry.getValue();
		System.out.println(zoneId + "   " + offset);
	}
}

The output will be:

Europe/Ljubljana   +02:00
Europe/Kaliningrad   +03:00
Europe/Berlin   +02:00
Europe/Lisbon   +01:00
Europe/Moscow   +04:00
Europe/Oslo   +02:00
Europe/Chisinau   +03:00
Europe/London   +01:00
. . . . . . . . . . . . .

In the next example, we suppose that we are in Chicago and we need to get to Tokyo. At least two sub-scenarios can result from this scenario. The first one, we know the departure time for our flight and the duration of the flight, and we want to find out when we will land in Tokyo. In the second one, we know the departure and the arriving time, and we want to find out the duration of the flight:

public static LocalTime getArrivalHour(ZoneId leaving, LocalTime leavingAt, ZoneId arriving, Duration flightDuration) {
 
			ZonedDateTime departure = ZonedDateTime.of(LocalDateTime.of(LocalDate.now(), leavingAt), leaving);
			ZonedDateTime arrival = departure.withZoneSameInstant(arriving).plus(flightDuration);
			return arrival.toLocalTime();
}
 
public static Duration getFlightDuration(ZoneId leaving, LocalTime leavingAt, ZoneId arriving, LocalTime arrivingAt) {
 
			ZonedDateTime departure = ZonedDateTime.of(LocalDateTime.of(LocalDate.now(), leavingAt), leaving);
			ZonedDateTime arrival = ZonedDateTime.of(LocalDateTime.of(LocalDate.now(), arrivingAt), arriving);
			return Duration.between(departure, arrival).abs();
			}
			 
			public static void main(String[] args) {
			 
			ZoneId leaving = ZoneId.of("America/Chicago");
			ZoneId arriving = ZoneId.of("Asia/Tokyo");
			LocalTime leavingAt = LocalTime.of(12, 00, 00);
			Duration flightDuration = Duration.ofHours(12).plusMinutes(21);
			 
			LocalTime arrivingAt = getArrivalHour(leaving, leavingAt, arriving, flightDuration);
			System.out.println("Flight arrival hour: " + arrivingAt);
			System.out.println("Flight duration: " 
				+ getFlightDuration(leaving, leavingAt, arriving, arrivingAt));
}

The output will be:

Flight arrival hour: 14:21
Flight duration: PT11H39M

Parsing and Formatting Dates

Parsing and formatting strings is important when you need to work with dates and times. The new date/time API has simplified these things and provides methods like parse() and format(). A DateTimeParseException exception can occur at parsing a string and a DateTimeException exception can occur at formatting a string:

public static String parseDate(String date) {
 
			String formattedDate = "";
			try {
			LocalDateTime localDate = LocalDateTime.parse(date);
			formattedDate = formatDate(localDate);
			} catch (DateTimeParseException e) {
			return date + " is not parsable!";
			}
			return formattedDate;
}
 
public static String formatDate(LocalDateTime date) {
 
			String formattedDate = "";
			try {
				DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MMM d yyyy - hh:mm a");
				formattedDate = date.format(formatter);
			} catch (DateTimeException e) {
				return date + " can't be formatted!";
			}
			return formattedDate;
}
 
public static void main(String[] args) {
 
			String date = "2014-05-05T12:07:45.915";
			System.out.println("Parse and format date: " + parseDate(date));
}

The output will be:

       Parse and format date: Mai 5 2014 - 12:07 PM

Date and Time Conversions

Of course, we can’t end this article without talking about possible conversions between dates and times. The following examples convert a java.util.Date to a LocalDate, LocalTime and LocalDateTime and backwards, and also, using the java.time.chrono package, we can convert a ISO-based date to non-ISO-based dates:

// Java SE 7 Date
Date date = new Date();
 
// Convert java.util.Date to LocalDate, LocalTime, LocalDateTime
LocalDate localDate = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
LocalTime localTime = date.toInstant().atZone(ZoneId.systemDefault()).toLocalTime();
LocalDateTime localDateTime = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
 
System.out.println(" java.util.Date: " + date
		+ "\n java.util.Date to LocalDate: " + localDate
		+ "\n java.util.Date to LocalTime: " + localTime
		+ "\n java.util.Date to LocalDateTime: " + localDateTime);
 
// Convert LocalDate, LocalTime, LocalDateTime to java.util.Date
LocalDate newLocalDate = LocalDate.now();
LocalTime newLocalTime = LocalTime.now();
LocalDateTime newLocalDateTime = LocalDateTime.now();
 
Date fromLocalDate = Date.from(LocalDateTime.of(
		newLocalDate, LocalTime.now()).toInstant(ZoneOffset.UTC));
Date fromLocalTime = Date.from(LocalDateTime.of(
		LocalDate.now(), newLocalTime).toInstant(ZoneOffset.UTC));
Date fromLocalDateTime = Date.from(newLocalDateTime.toInstant(ZoneOffset.UTC));
 
System.out.println(" LocalDate to java.util.Date: " + fromLocalDate
		+ "\n LocalTime to java.util.Date: " + fromLocalTime
		+ "\n LocalDateTime to java.util.Date: " + fromLocalDateTime);
 
// Converting LocalDateTime to a Non-ISO-Based Date
LocalDateTime ldt = LocalDateTime.now();
HijrahDate hdate = HijrahDate.from(ldt);
JapaneseDate jdate = JapaneseDate.from(ldt);
MinguoDate mdate = MinguoDate.from(ldt);
ThaiBuddhistDate tdate = ThaiBuddhistDate.from(ldt);
 
System.out.println(" Today: " + date + "\n Hijrah date: " + hdate 
		+ "\n Japanese date: " + jdate + "\n Minguo date: " + mdate 
		+ "\n ThaiBuddhist date: " + tdate);

The output will be:

Convert java.util.Date to LocalDate, LocalTime, LocalDateTime
java.util.Date: Fri May 09 12:41:07 EEST 2014
java.util.Date to LocalDate: 2014-05-09
java.util.Date to LocalTime: 12:41:07.657
java.util.Date to LocalDateTime: 2014-05-09T12:41:07.657
 
Convert LocalDate, LocalTime, LocalDateTime to java.util.Date
LocalDate to java.util.Date: Fri May 09 15:41:07 EEST 2014
LocalTime to java.util.Date: Fri May 09 15:41:07 EEST 2014
LocalDateTime to java.util.Date: Fri May 09 15:41:07 EEST 2014
 
Converting LocalDateTime to a Non-ISO-Based Date
Today: Fri May 09 12:41:07 EEST 2014
Hijrah date: Hijrah-umalqura AH 1435-07-10
Japanese date: Japanese Heisei 26-05-09
Minguo date: Minguo ROC 103-05-09
ThaiBuddhist date: ThaiBuddhist BE 2557-05-09

Conclusion

In this article, we make use of the new Date and Time API released in Java 8 and as we can see, even if it is significantly inspired from Joda-Time, which has been the preferred Java Date and Time API for quite some time, it seems to be a rich API, intuitive and easy to work with.


Download Java-SE-8-examples.zip.


Tags: Java SE, API, DATE, Time, datetime function




Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.

Sitemap | Contact Us

Rocket Fuel