JSR 310: A New Java Date/Time API Blog

Version 2


    JSR 310 is an API for time- and calendar-related calculations that has been proposed for Java SE 7. The API aims to replace the two existing classes that form Java's current date and time API,java.util.Date and java.util.Calendar, while still providing backwards-compatible access to these older APIs. The JSR is currently in development, and a tentative Javadoc is available for the API.

    Improvements on the Java 6 Date/Time API

    The JSR 310 date/time API attempts to improve upon Java's current date/time API by providing increased performance and ease of use. For example, the Java Calendar class stores a date simultaneously as a millisecond offset from a standard epoch, and as a set of calendar fields (e.g., day of week, day of month, and month of year). This double representation causes the calendar fields to be recomputed at unexpected times, resulting in unpredictable performance characteristics. In contrast, JSR 310 classes store date/time representations only as a nanosecond offset from the same standard epoch used by Date andCalendar; calendar fields such as day of month are only computed when needed and are not used for internal representation of dates.

    JSR 310 also improves on the current Java date/time model. The Java 6 API contains no classes that represent local times (times without an associated time zone), durations, or intervals. This forces programmers to use confusing design practices, such as using an int to represent a duration of time. JSR 310 contains classes that represent each of these concepts, allowing for cleaner program design.

    Finally, the JSR 310 API strives for thread safety through the use of immutable classes. Java's current date/time classes,Date and Calendar, are both mutable and therefore not thread-safe.

    JSR 310 Date/Time Concepts

    The JSR 310 API draws on experience gained from several third-party Java date/time APIs. JSR 310 is based mainly on the Joda Time API; other influences include ICU, Time and Money, and Calendar Date. JSR 310's API is built around the same five basic date/time concepts used in Joda Time:

    The Discrete Timeline

    Like Joda Time, JSR 310 uses a discretized timeline: time is modeled as a sequence of consecutive instants separated by small, fixed durations. The discrete timeline of JSR 310 has nanosecond resolution, so it is possible to express the time "One nanosecond after midnight on 01/01/08," but not "One picosecond after midnight on 01/01/08." Each discrete nanosecond on the timeline is considered an instant, described below.


    An instant is a specific point on a discretized timeline. An example of an instant is "January 7, 2008, at time 23:00:00.0 UTC." An instant can also be defined as a nanosecond offset from a standard epoch, such as "20,000,000 nanoseconds after January 1, 1970 at midnight UTC." Both of these descriptions define a single, unique point on the discrete timeline. Instants are different frompartials, which define a set of moments on the timeline, rather than one unique moment.

    The JSR 310 API provides several classes that represent instants: Instant, OffsetDateTime, andZonedDateTime, all of which implement theReadableInstant interface. TheOffsetDateTime class represents a date, time of day, and offset from coordinated universal time (UTC), such as+1:00. The similar ZonedDateTime class incorporates a time zone ID, such as America/New_York, instead of an offset. A given time zone may use several different offsets depending on the time of year; for example, theAmerica/New_York time zone's offset is -4:00 during daylight savings time and -5:00 at other times. Thus, theZonedDateTime class should be used when locale-specific time rules, such as daylight savings time, must be taken into account.

    The ZonedDateTime class provides several categories of methods for creating, accessing, and modifying instants. To create a new instance of ZonedDateTime representing the current system time in your computer's default time zone, you can use the Clock.currentZonedDateTime() factory method, as shown by the following example.

    Clock systemClock = Clock.system(); ZonedDateTime currentTime = systemClock.currentZonedDateTime(); 

    To create a ZonedDateTime instance that represents a specific, predetermined date, you can use one of severalZonedDateTime.dateTime factory methods. The following example demonstrates the creation of a ZonedDateTimerepresenting midnight on January 1, 2000, in your computer's default time zone.

    Clock systemClock = Clock.system(); TimeZone defaultTimeZone = systemClock.timeZone(); int year = 2000; int month = 1; int day = 1; int hour = 0; int minute = 0; ZonedDateTime theDate = ZonedDateTime.dateTime(year, month, day, hour, minute, defaultTimeZone); 

    Instances of OffsetDateTime can be created in a manner similar to that of ZonedDateTime, except that aZoneOffset rather than a TimeZone is passed into the OffsetDateTime.dateTime factory method. To get an instance of ZoneOffset, you can use the static ZoneOffset.zoneOffset(int hours) method, where hours is the hour offset from UTC.


    A partial is an indication of date or time that is not sufficient to specify a specific, unique point on the timeline. For example, "June 7" is a partial, because it specifies a month and day, but it does not specify a year or time of day. Thus, the above partial is not sufficient to identify a unique point on the timeline. Because partials do not identify specific times, they cannot be expressed as nanosecond offsets from a standard epoch. Their definition is necessarily field-based, using calendar fields such as year, month, day of month, and time of day.

    Partials can be categorized based on the set of calendar fields that they define. For example, a partial that represents a yearly holiday might contain month and day fields, whereas a partial that represents a store's opening time might contain hour and minute fields. JSR 310 provides classes for commonly used partials, such as MonthDay (the aforementioned "holiday" partial),LocalDate (a date with no time or time zone), andLocalTime (a time with no time zone). To create an instance of MonthDay or one of the other ready-made partial classes, you can use one of the provided static factory methods. The example below is specific to MonthDay, but it can easily be adapted for one of the other partial classes.

    //Create a MonthDay representing December 25 (Christmas) int theMonth = 12; int theDay = 25; MonthDay christmas = MonthDay.monthDay(theMonth, theDay); 

    A duration represents a length of elapsed time, defined to the nanosecond level; for example, "100,000 nanoseconds" is a duration. Durations are somewhat similar to periods, which also define a length of elapsed time; however, unlike periods, durations represent a precise number of elapsed nanoseconds.

    A duration can be added to an instant to return a new instant that is offset from the original instant by the number of nanoseconds in the duration. For example, if the duration "86,400,000,000,000 nanoseconds" (24 hours) is added to the instant "March 1, 2008, at midnight UTC," the resulting instant is "March 2, 2008, at midnight UTC."

    Durations may be created in two ways: by supplying a second, millisecond, or nanosecond timespan for the duration, or by providing starting and ending instants. The first method is appropriate for creating a duration of a predetermined length:

    System.out.println("Enter a duration length in hours:"); Scanner s = new Scanner(System.in); int durationSeconds = 3600 * s.nextInt(); Duration d = Duration.duration(durationSeconds); 

    Alternatively, you may wish to determine the duration between two predetermined instants. The staticDuration.durationBetween(ReadableInstant startInclusive, ReadableInstant endExclusive) factory method is useful in this case.

    Clock systemClock = Clock.system(); ZonedDateTime instant1 = systemClock.currentZonedDateTime(); try{Thread.sleep(1000)} //Use up some time catch(InterruptedException e){System.out.println("Error")} ZonedDateTime instant2 = systemClock.currentZonedDateTime(); Duration d = Duration.durationBetween(instant1, instant2); 

    Like durations, periods represent a length of elapsed time. Examples of periods are "4 years, 8 days," and "1 hour." As shown by these examples, periods are defined using calendar fields (years, days, hours, etc.), rather than by an exact number of nanoseconds.

    At first, periods and durations may seem to be different ways of expressing the same concept; however, a given period cannot be converted to an exact number of nanoseconds, as the following example of instant/period addition demonstrates. Instant/period addition adds the value of each of the period's calendar fields to the corresponding field of the instant. This is in contrast to instant/duration addition, which adds the duration's length in nanoseconds to the instant. At first glance, it may seem that adding an 86,400,000,000,000-nanosecond (24-hour) duration to a given instant should produce the same result as adding a period of "1 day," but this is not always the case, because the calendar field "day" does not have a fixed nanosecond length. Most days are 24 hours long, but some are longer or shorter due to daylight savings time. Adding a 24-hour duration to a instant will always advance the instant by exactly 24 hours, whereas adding a one-day period will advance the day by one, while leaving the time of day unchanged.

    For example, if the period "1 day" is added to the instant "March 9, 2008, at midnight EST," the resulting field-based addition produces "March 10, 2008, at midnight EST." However, if a 24-hour duration is added to the instant "March 9, 2008, at midnight EST," the result is "March 10, 2008, at 01:00:00.0 EST." The discrepancy stems from the fact that daylight savings time begins at 02:00:00 EST on March 9, 2008; thus, this day is only 23 hours long.

    JSR 310 provides period functionality with several classes, the most important of which are Period,Periods.Builder, and the subclasses ofPeriods.Builder: Periods.SecondsBuilder,Periods.MinutesBuilder (which is a subclass ofSecondsBuilder), and so on, up toPeriods.YearsBuilder.

    To create an instance of Period, you must first obtain an instance of Periods.Builder using the staticPeriods.periodBuilder() method. This method returns aPeriods.YearsBuilder object, which is an indirect subclass of Periods.Builder. ThePeriods.YearsBuilder class provides a method,years(int numYears), that adds numYearsyears to the period being built. Methods provided by the direct and indirect superclasses of YearsBuilder(MonthsBuilder, DaysBuilder,HoursBuilder, MinutesBuilder, andSecondsBuilder) allow other calendar fields to be added to the period. For example, theMinutesBuilder.minutes(int numMinutes) addsnumMinutes minutes to the period being built. All of these methods return this, allowing periods with multiple calendar fields to be built with a single statement. Note that it is possible to subtract from a period by passing a negative integer into the appropriate builder method. Once all desired fields have been added to the period, thePeriods.PeriodBuilder.build() method is called to return the completed instance of Period.

    //Build a period representing "8 years, 3 months." Period thePeriod = Periods.periodBuilder().years(8).months(3).build(); 

    An interval represents a period of time between two instants on the timeline. Thus, "midnight UTC on January 1, 2007 (inclusive) to midnight UTC on January 1, 2008 (exclusive)" is an interval. The interval can be inclusive or exclusive with regard to the starting and ending instants.

    JSR 310 provides the InstantInterval class to represent intervals. To create an instance ofInstantInterval, you can use one of several factory methods:

    //Create some intervals. ZonedDateTime time1 = systemClock.currentZonedDateTime(); try{Thread.sleep(1000)} //Use up some time catch(InterruptedException e){System.out.println("Error")} ZonedDateTime time2 = systemClock.currentZonedDateTime(); //Create an interval with inclusive start and exclusive end. InstantInterval interval1 = InstantInterval.intervalBetween( time1.toInstant(), time2.toInstant()); //Create an interval with exclusive start and inclusive end. boolean startInclusive = false; boolean endInclusive = true; InstantInterval interval2 = InstantInterval.intervalBetween( time1.toInstant(), startInclusive, time2.toInstant(), endInclusive); 

    Using JSR 310 Date/Time Classes

    The previous sections describe the five basic JSR 310 date/time concepts and explain how to instantiate the classes representing those concepts. This section illustrates some calendrical computations that are possible with the JSR 310 API.

    For illustrative purposes, suppose that you are developing a program that takes as input several events that occur at different times and recur at particular intervals. The program outputs all times at which two or more events occur simultaneously. You can use a ZonedDateTime and a Duration to represent an instantaneous event that recurs after a fixed length of time has passed; to generate the next occurrence of the event, you add the Duration to the ZonedDateTimerepresenting the previous occurrence:

    //ZonedDateTime firstTime is the time the event first occurs //Duration repeatTime is the duration after which event recurs //TimeZone defaultZone is your computer's default time zone ZonedDateTime secondTime = ZonedDateTime.dateTime( firstTime.toInstant().plus(repeatTime), defaultZone); 

    If you have an event that repeats after a certain number of days, months, or years, you can generate the next occurrence by adding a Period to the ZonedDateTimerepresenting the previous occurrence:

    //ZonedDateTime firstTime is the time the event first occurs //Period repeatPeriod is the period after which event recurs ZonedDateTime secondTime = firstTime.plus(repeatPeriod); 

    To test whether or not two instantaneous events occur simultaneously, you can use the ZonedDateTime.equals(Object other) method:

    //time1 and time2 are ZonedDateTimes representing events if (time1.equals(time2)){ System.out.println("The two events occur simultaneously."); } 

    If you need to represent a non-instantaneous event, using aZonedDateTime is not the best choice. Partials are more appropriate for representing non-instantaneous events such as yearly holidays. To determine whether an event represented by aZonedDateTime and one represented by a partial (e.g.,MonthDay) occur simultaneously, you can use theMonthDay.matchesDate(LocalDate date) method. This method returns true if the month and day fields in theLocalDate, which is a date without a time, match the month and day fields in the MonthDay.

    //Monthday christmas is a partial representing December 25. //ZonedDateTime time1 represents an instantaneous event if (christmas.matchesDate(time1.localDate())){ System.out.println("The event occurs on Christmas."); } 

    Finally, events that occur over a specific timespan may best be represented by an InstantInterval. To test whether or not an InstantInterval overlaps aZonedDateTime, you can use theInstantInterval.contains(InstantProvider instant)method. (InstantProvider is an interface implemented by the three JSR 310 instant classes.)

    //InstantInterval event1 is an event that occurs over a timespan //ZonedDateTime event2 is an instantaneous event if (event1.contains(event2)){ System.out.println("Event 2 occurs during event 1."); } 


    This article has illustrated some features of JSR 310, a new Java date/time API that is proposed for Java 7. In particular, it has illustrated the use of classes representing the five JSR 310 date/time concepts: instants, partials, durations, periods, and intervals. There is much additional functionality in JSR 310, such as advanced date/time matching, that this article does not describe. Furthermore, because this article was based mainly on a snapshot of the current JSR 310 API, additional functionality may be added after the publication of this article. See the Resources section to learn more about the features of JSR 310.