Java 8 Date – LocalDate, LocalDateTime, Instant

Filed Under: Java

Date Time API is one of the biggest features of Java 8 release. Java was missing a consistent approach for Date and Time from start and Java 8 Date Time API is a welcome addition to the core Java APIs.

Why do we need new Java Date Time API?

Before we start looking at the Java 8 Date Time API, let’s see why do we need a new API for this. There have been several problems with the existing date and time related classes in java, some of them are:

  • Java Date Time classes are not defined consistently, we have Date Class in both java.util as well as java.sql packages. Again formatting and parsing classes are defined in java.text package.
  • java.util.Date contains both date and time values whereas java.sql.Date contains only date value. Having this in java.sql package doesn’t make any sense. Also, both the classes have the same name, which is a very bad design itself.
  • There are no clearly defined classes for time, timestamp, formatting, and parsing. We have java.text.DateFormat abstract class for parsing and formatting need. Usually, the SimpleDateFormat class is used for parsing and formatting.
  • All the Date classes are mutable, so they are not thread-safe. It’s one of the biggest problems with Java Date and Calendar classes.
  • Date class doesn’t provide internationalization, there is no timezone support. So java.util.Calendar and java.util.TimeZone classes were introduced, but they also have all the problems listed above.

There are some other issues with the methods defined in Date and Calendar classes but above problems make it clear that a robust Date Time API was needed in Java. That’s why Joda Time played a key role as a quality replacement for Java Date Time requirements.

Java 8 Date Time Design Principles

Java 8 Date Time API is JSR-310 implementation. It is designed to overcome all the flaws in the legacy date time implementations. Some of the design principles of new Date Time API are:

  1. Immutability: All the classes in the new Date-Time API are immutable and good for multithreaded environments.
  2. Separation of Concerns: The new API separates clearly between human-readable date time and machine time (Unix timestamp). It defines separate classes for Date, Time, DateTime, Timestamp, Timezone, etc.
  3. Clarity: The methods are clearly defined and perform the same action in all the classes. For example, to get the current instance we have now() method. There are format() and parse() methods defined in all these classes rather than having a separate class for them.

    All the classes use Factory Pattern and Strategy Pattern for better handling. Once you have used the methods in one of the classes, working with other classes won’t be hard.
  4. Utility operations: All the new Date-Time API classes come with methods to perform common tasks, such as plus, minus, format, parsing, getting the separate part in date/time, etc.
  5. Extendable: The new Date Time API works on the ISO-8601 calendar system but we can use it with other non-ISO calendars as well.

Date Time API Packages

Java 8 Date Time API consists of following packages.

  1. java.time: This is the base package of the new Java Date Time API. All the major base classes are part of this package, such as LocalDate, LocalTime, LocalDateTime, Instant, Period, Duration, etc. All of these classes are immutable and thread-safe. Most of the time, these classes will be sufficient for handling common requirements.
  2. java.time.chrono: This package defines generic APIs for non-ISO calendar systems. We can extend AbstractChronology class to create our own calendar system.
  3. java.time.format: This package contains classes used for formatting and parsing date-time objects. Most of the time we would not be directly using them because of principle classes in java.time package provides formatting and parsing methods.
  4. java.time.temporal: This package contains temporal objects and we can use it to find out the specific dates or times related to the date/time objects. For example, we can use these to find out the first or last day of the month. You can identify these methods easily because they always have the format “withXXX”.
  5. java.time.zone Package: This package contains classes for supporting different time zones and their rules.

Java 8 Date Time API Classes Examples

We have looked into most of the important parts of Java Date Time API. It’s time now to look into most important classes of Date Time API with examples.

1. LocalDate

LocalDate is an immutable class that represents Date with default format of yyyy-MM-dd. We can use now() method to get the current date. We can also provide input arguments for year, month and date to create LocalDate instance.

This class provides an overloaded method for now() where we can pass ZoneId for getting dates in a specific time zone. This class provides the same functionality as java.sql.Date.

package com.journaldev.java8.time;

import java.time.LocalDate;
import java.time.Month;
import java.time.ZoneId;

/**
 * LocalDate Examples
 * @author pankaj
 *
 */
public class LocalDateExample {

	public static void main(String[] args) {
		
		//Current Date
		LocalDate today = LocalDate.now();
		System.out.println("Current Date="+today);
		
		//Creating LocalDate by providing input arguments
		LocalDate firstDay_2014 = LocalDate.of(2014, Month.JANUARY, 1);
		System.out.println("Specific Date="+firstDay_2014);
		
		
		//Try creating date by providing invalid inputs
		//LocalDate feb29_2014 = LocalDate.of(2014, Month.FEBRUARY, 29);
		//Exception in thread "main" java.time.DateTimeException: 
		//Invalid date 'February 29' as '2014' is not a leap year
		
		//Current date in "Asia/Kolkata", you can get it from ZoneId javadoc
		LocalDate todayKolkata = LocalDate.now(ZoneId.of("Asia/Kolkata"));
		System.out.println("Current Date in IST="+todayKolkata);

		//java.time.zone.ZoneRulesException: Unknown time-zone ID: IST
		//LocalDate todayIST = LocalDate.now(ZoneId.of("IST"));
		
		//Getting date from the base date i.e 01/01/1970
		LocalDate dateFromBase = LocalDate.ofEpochDay(365);
		System.out.println("365th day from base date= "+dateFromBase);
		
		LocalDate hundredDay2014 = LocalDate.ofYearDay(2014, 100);
		System.out.println("100th day of 2014="+hundredDay2014);
	}

}

LocalDate methods explanation is provided in the comments. When we run this program, we get the following output.

Current Date=2014-04-28
Specific Date=2014-01-01
Current Date in IST=2014-04-29
365th day from base date= 1971-01-01
100th day of 2014=2014-04-10

2. LocalTime

LocalTime is an immutable class whose instance represents a time in the human readable format. It’s default format is hh:mm:ss.zzz. Just like LocalDate, this class provides time zone support and creating instance by passing hour, minute and second as input arguments.

package com.journaldev.java8.time;

import java.time.LocalTime;
import java.time.ZoneId;

/**
 * LocalTime Examples
 * @author pankaj
 *
 */
public class LocalTimeExample {

	public static void main(String[] args) {
		
		//Current Time
		LocalTime time = LocalTime.now();
		System.out.println("Current Time="+time);
		
		//Creating LocalTime by providing input arguments
		LocalTime specificTime = LocalTime.of(12,20,25,40);
		System.out.println("Specific Time of Day="+specificTime);
		
		
		//Try creating time by providing invalid inputs
		//LocalTime invalidTime = LocalTime.of(25,20);
		//Exception in thread "main" java.time.DateTimeException: 
		//Invalid value for HourOfDay (valid values 0 - 23): 25
		
		//Current date in "Asia/Kolkata", you can get it from ZoneId javadoc
		LocalTime timeKolkata = LocalTime.now(ZoneId.of("Asia/Kolkata"));
		System.out.println("Current Time in IST="+timeKolkata);

		//java.time.zone.ZoneRulesException: Unknown time-zone ID: IST
		//LocalTime todayIST = LocalTime.now(ZoneId.of("IST"));
		
		//Getting date from the base date i.e 01/01/1970
		LocalTime specificSecondTime = LocalTime.ofSecondOfDay(10000);
		System.out.println("10000th second time= "+specificSecondTime);

	}

}

Output:

Current Time=15:51:45.240
Specific Time of Day=12:20:25.000000040
Current Time in IST=04:21:45.276
10000th second time= 02:46:40

3. LocalDateTime

LocalDateTime is an immutable date-time object that represents a date-time with default format as yyyy-MM-dd-HH-mm-ss.zzz. It provides a factory method that takes LocalDate and LocalTime input arguments to create LocalDateTime instance.

package com.journaldev.java8.time;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Month;
import java.time.ZoneId;
import java.time.ZoneOffset;

public class LocalDateTimeExample {

	public static void main(String[] args) {
		
		//Current Date
		LocalDateTime today = LocalDateTime.now();
		System.out.println("Current DateTime="+today);
		
		//Current Date using LocalDate and LocalTime
		today = LocalDateTime.of(LocalDate.now(), LocalTime.now());
		System.out.println("Current DateTime="+today);
		
		//Creating LocalDateTime by providing input arguments
		LocalDateTime specificDate = LocalDateTime.of(2014, Month.JANUARY, 1, 10, 10, 30);
		System.out.println("Specific Date="+specificDate);
		
		
		//Try creating date by providing invalid inputs
		//LocalDateTime feb29_2014 = LocalDateTime.of(2014, Month.FEBRUARY, 28, 25,1,1);
		//Exception in thread "main" java.time.DateTimeException: 
		//Invalid value for HourOfDay (valid values 0 - 23): 25

		
		//Current date in "Asia/Kolkata", you can get it from ZoneId javadoc
		LocalDateTime todayKolkata = LocalDateTime.now(ZoneId.of("Asia/Kolkata"));
		System.out.println("Current Date in IST="+todayKolkata);

		//java.time.zone.ZoneRulesException: Unknown time-zone ID: IST
		//LocalDateTime todayIST = LocalDateTime.now(ZoneId.of("IST"));
		
		//Getting date from the base date i.e 01/01/1970
		LocalDateTime dateFromBase = LocalDateTime.ofEpochSecond(10000, 0, ZoneOffset.UTC);
		System.out.println("10000th second time from 01/01/1970= "+dateFromBase);

	}

}

In all the three examples, we have seen that if we provide invalid arguments for creating Date/Time, then it throws java.time.DateTimeException, which is a RuntimeException so we don’t need to explicitly catch it.

We have also seen that we can get Date/Time data by passing ZoneId, you can get the list of supported ZoneId values from its JavaDoc. When we run the above class, we get the following output.

Current DateTime=2014-04-28T16:00:49.455
Current DateTime=2014-04-28T16:00:49.493
Specific Date=2014-01-01T10:10:30
Current Date in IST=2014-04-29T04:30:49.493
10000th second time from 01/01/1970= 1970-01-01T02:46:40

4. Instant

Instant class is used to work with machine readable time format. Instant stores date time in unix timestamp.

package com.journaldev.java8.time;

import java.time.Duration;
import java.time.Instant;

public class InstantExample {

	public static void main(String[] args) {
		//Current timestamp
		Instant timestamp = Instant.now();
		System.out.println("Current Timestamp = "+timestamp);
		
		//Instant from timestamp
		Instant specificTime = Instant.ofEpochMilli(timestamp.toEpochMilli());
		System.out.println("Specific Time = "+specificTime);
		
		//Duration example
		Duration thirtyDay = Duration.ofDays(30);
		System.out.println(thirtyDay);
	}

}

Output:

Current Timestamp = 2014-04-28T23:20:08.489Z
Specific Time = 2014-04-28T23:20:08.489Z
PT720H

Java 8 Date API Utilities

Most of the Date Time principle classes provide various utility methods such as plus/minus days, weeks, months etc. There are some other utility methods for adjusting the date using TemporalAdjuster and to calculate the period between two dates.

package com.journaldev.java8.time;

import java.time.LocalDate;
import java.time.LocalTime;
import java.time.Period;
import java.time.temporal.TemporalAdjusters;

public class DateAPIUtilities {

	public static void main(String[] args) {
		
		LocalDate today = LocalDate.now();
		
		//Get the Year, check if it's leap year
		System.out.println("Year "+today.getYear()+" is Leap Year? "+today.isLeapYear());
		
		//Compare two LocalDate for before and after
		System.out.println("Today is before 01/01/2015? "+today.isBefore(LocalDate.of(2015,1,1)));
		
		//Create LocalDateTime from LocalDate
		System.out.println("Current Time="+today.atTime(LocalTime.now()));
		
		//plus and minus operations
		System.out.println("10 days after today will be "+today.plusDays(10));
		System.out.println("3 weeks after today will be "+today.plusWeeks(3));
		System.out.println("20 months after today will be "+today.plusMonths(20));

		System.out.println("10 days before today will be "+today.minusDays(10));
		System.out.println("3 weeks before today will be "+today.minusWeeks(3));
		System.out.println("20 months before today will be "+today.minusMonths(20));
		
		//Temporal adjusters for adjusting the dates
		System.out.println("First date of this month= "+today.with(TemporalAdjusters.firstDayOfMonth()));
		LocalDate lastDayOfYear = today.with(TemporalAdjusters.lastDayOfYear());
		System.out.println("Last date of this year= "+lastDayOfYear);
		
		Period period = today.until(lastDayOfYear);
		System.out.println("Period Format= "+period);
		System.out.println("Months remaining in the year= "+period.getMonths());		
	}
}

Output:

Year 2014 is Leap Year? false
Today is before 01/01/2015? true
Current Time=2014-04-28T16:23:53.154
10 days after today will be 2014-05-08
3 weeks after today will be 2014-05-19
20 months after today will be 2015-12-28
10 days before today will be 2014-04-18
3 weeks before today will be 2014-04-07
20 months before today will be 2012-08-28
First date of this month= 2014-04-01
Last date of this year= 2014-12-31
Period Format= P8M3D
Months remaining in the year= 8

Java 8 Date Parsing and Formatting

It’s very common to format date into different formats and then parse a String to get the Date Time objects.

package com.journaldev.java8.time;

import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class DateParseFormatExample {

	public static void main(String[] args) {
		
		//Format examples
		LocalDate date = LocalDate.now();
		//default format
		System.out.println("Default format of LocalDate="+date);
		//specific format
		System.out.println(date.format(DateTimeFormatter.ofPattern("d::MMM::uuuu")));
		System.out.println(date.format(DateTimeFormatter.BASIC_ISO_DATE));
		
		
		LocalDateTime dateTime = LocalDateTime.now();
		//default format
		System.out.println("Default format of LocalDateTime="+dateTime);
		//specific format
		System.out.println(dateTime.format(DateTimeFormatter.ofPattern("d::MMM::uuuu HH::mm::ss")));
		System.out.println(dateTime.format(DateTimeFormatter.BASIC_ISO_DATE));
		
		Instant timestamp = Instant.now();
		//default format
		System.out.println("Default format of Instant="+timestamp);
		
		//Parse examples
		LocalDateTime dt = LocalDateTime.parse("27::Apr::2014 21::39::48",
				DateTimeFormatter.ofPattern("d::MMM::uuuu HH::mm::ss"));
		System.out.println("Default format after parsing = "+dt);
	}

}

Output:

Default format of LocalDate=2014-04-28
28::Apr::2014
20140428
Default format of LocalDateTime=2014-04-28T16:25:49.341
28::Apr::2014 16::25::49
20140428
Default format of Instant=2014-04-28T23:25:49.342Z
Default format after parsing = 2014-04-27T21:39:48

Java Date API Legacy Date Time Support

Legacy Date/Time classes are used in almost all the applications, so having backward compatibility is a must. That’s why there are several utility methods through which we can convert Legacy classes to new classes and vice versa.

package com.journaldev.java8.time;

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.TimeZone;

public class DateAPILegacySupport {

	public static void main(String[] args) {
		
		//Date to Instant
		Instant timestamp = new Date().toInstant();
		//Now we can convert Instant to LocalDateTime or other similar classes
		LocalDateTime date = LocalDateTime.ofInstant(timestamp, 
						ZoneId.of(ZoneId.SHORT_IDS.get("PST")));
		System.out.println("Date = "+date);
		
		//Calendar to Instant
		Instant time = Calendar.getInstance().toInstant();
		System.out.println(time);
		//TimeZone to ZoneId
		ZoneId defaultZone = TimeZone.getDefault().toZoneId();
		System.out.println(defaultZone);
		
		//ZonedDateTime from specific Calendar
		ZonedDateTime gregorianCalendarDateTime = new GregorianCalendar().toZonedDateTime();
		System.out.println(gregorianCalendarDateTime);
		
		//Date API to Legacy classes
		Date dt = Date.from(Instant.now());
		System.out.println(dt);
		
		TimeZone tz = TimeZone.getTimeZone(defaultZone);
		System.out.println(tz);
		
		GregorianCalendar gc = GregorianCalendar.from(gregorianCalendarDateTime);
		System.out.println(gc);
		
	}

}

Output:

Date = 2014-04-28T16:28:54.340
2014-04-28T23:28:54.395Z
America/Los_Angeles
2014-04-28T16:28:54.404-07:00[America/Los_Angeles]
Mon Apr 28 16:28:54 PDT 2014
sun.util.calendar.ZoneInfo[id="America/Los_Angeles",offset=-28800000,dstSavings=3600000,useDaylight=true,transitions=185,lastRule=java.util.SimpleTimeZone[id=America/Los_Angeles,offset=-28800000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=3,startMonth=2,startDay=8,startDayOfWeek=1,startTime=7200000,startTimeMode=0,endMode=3,endMonth=10,endDay=1,endDayOfWeek=1,endTime=7200000,endTimeMode=0]]
java.util.GregorianCalendar[time=1398727734404,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="America/Los_Angeles",offset=-28800000,dstSavings=3600000,useDaylight=true,transitions=185,lastRule=java.util.SimpleTimeZone[id=America/Los_Angeles,offset=-28800000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=3,startMonth=2,startDay=8,startDayOfWeek=1,startTime=7200000,startTimeMode=0,endMode=3,endMonth=10,endDay=1,endDayOfWeek=1,endTime=7200000,endTimeMode=0]],firstDayOfWeek=2,minimalDaysInFirstWeek=4,ERA=1,YEAR=2014,MONTH=3,WEEK_OF_YEAR=18,WEEK_OF_MONTH=5,DAY_OF_MONTH=28,DAY_OF_YEAR=118,DAY_OF_WEEK=2,DAY_OF_WEEK_IN_MONTH=4,AM_PM=1,HOUR=4,HOUR_OF_DAY=16,MINUTE=28,SECOND=54,MILLISECOND=404,ZONE_OFFSET=-28800000,DST_OFFSET=3600000]

As you can see that legacy TimeZone and GregorianCalendar classes toString() methods are too verbose and not user friendly.

Conclusion

I like this new Date Time API a lot. Some of the most used classes will be LocalDate and LocalDateTime. It’s very easy to work with the new classes. And, having similar methods that does a particular job makes it easy to find. It will take some time from moving legacy classes to new Date Time classes, but I believe it will be worth the time and effort.

Comments

  1. Ram says:

    Very clear explanation Pankaj. Great Job 👏

  2. johan974 says:

    Thank you for your overview!
    Maybe you can elaborate more on ZonedDateTime to complete the overview

  3. Sandeep Katabathuni says:

    very nice article Pankaj, thanks

  4. Lokesh Sharma says:

    How parse a date in IST and then print it in UTC?

  5. Manish Shokeen says:

    Really nice article.. keep up the good work.

  6. sunil says:

    Great explanation. very useful. Many thanks.

  7. Neha Jain says:

    Very nice article. Thank you.

  8. alam says:

    Hi pankage, thanks for such a nice article, as much as i read and try to learn by heart them, I forget and can’t remember them when interviewer ask. By the way here I have prepared your github profile summary at – https://alamnr.github.io/profile.html?user=journaldev.
    May you live long.

  9. Hrishav says:

    hii… pankaj can u provide me all the details of this article in a pdf……….

    some programs are not prnting fully when i print them ….

    please provide this article in pdf format

  10. Tara says:

    Thank u for ur explanation.Its useful for me

  11. Utpal Nandi says:

    Many thanks for the clear and to the point explanation! That’s exactly what I was looking for.

  12. yellappa says:

    Nice article

  13. Binh Thanh Nguyen says:

    Thanks, nice post

  14. fred says:

    thanks for this good sharing ! It was in deed a great article !

  15. kangddanddan says:

    Hello, I’m a java developer in South Korea.

    I would like to share this article for my friends.
    Could I repost this on my website after translating?

    if it is possible, please send me email.

    Email : goodmorning0726@gmail.com

    thanks

  16. Shuba says:

    Great Job Pankanj ! It was in deed a great article !

  17. Rafael says:

    How to use LocalDate with JSF Form ? I have a field (inputText or inputMask (dd/mm/yyyy)) that will be filled with a date by User. What i have to do for store this value inserted in an attribute type LocalDate. ?

  18. Jothimanickam M says:

    great explanation

  19. Leonardo says:

    Hello,

    Hello I would like to get a fixed schedule ( another class ) and subtract your local schedule , how can I do this ?

    Thank you!

  20. Pradeep Tiwari says:

    Very clear explanation – Thanks for sharing.

Leave a Reply

Your email address will not be published. Required fields are marked *

close
Generic selectors
Exact matches only
Search in title
Search in content