Java 8 Date and Time API is one of the most sought after change for developers. Java has been missing a consistent approach for Date and Time from start and Date Time API is a welcome addition to the core Java APIs.
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.util
as well asjava.sql
packages. Again formatting and parsing classes are defined in java.text
package.java.util.Date
contains both date and time, whereas java.sql.Date
contains only date. Having this injava.sql
package doesn’t make sense. Also both the classes have same name, that is a very bad design itself.java.text.DateFormat
abstract class for parsing and formatting need. Usually SimpleDateFormat
class is used for parsing and formatting.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 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:
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 class, working with other classes won’t be hard.
Java Date Time API consists of following packages.
LocalDate
, LocalTime
, LocalDateTime
, Instant
, Period
, Duration
etc. All of these classes are immutable and thread safe. Most of the times, these classes will be sufficient for handling common requirements.AbstractChronology
class to create our own calendar system.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.
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 overloaded method for now() where we can pass ZoneId for getting date in specific time zone. This class provides the same functionality as java.sql.Date
. Let’s look at a simple example for it’s usage. 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); } }
The example methods explanation is provided in comments, when we run this program, we get 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
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); } }
When we run above program, we get following 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
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 takesLocalDate
and LocalTime
input arguments to create LocalDateTime
instance. Let’s look it’s usage with a simple example. 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
that 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 it’s javadoc. When we run above class, we get 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
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 of above program is:
Current Timestamp = 2014-04-28T23:20:08.489Z Specific Time = 2014-04-28T23:20:08.489Z PT720H
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 of above program is:
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
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); } }
When we run above program, we get following 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
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); } }
When we run above application, we get following 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.
That’s all for Java 8 Date Time API, I like this new API a lot. It’s very easy to work with 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 worthy of the time.