An Introduction to the JavaScript Temporal API

    Craig Buckler

    Whatever your opinion of JavaScript, everyone thinks date handling is a mess. The Date() object implementation was copied directly from Java in 1995. Java scrapped it two years later but it remained in JavaScript for backward compatibility.

    These are the problems we face with Date():

    • it only supports UTC and the user’s local time
    • there’s no support for non-Gregorian calendars
    • daylight-saving behavior is unpredictable
    • parsing dates from strings is unreliable
    • the API is clunky
    • Date objects are mutable: a date will change as methods are applied

    The most popular alternative used to be the Moment.js date library . However, this has been placed in maintenance mode and should not really be used in new projects. They recommend a couple of alternatives, but each of these have their own pros and cons.

    The Date() object can’t be removed from JavaScript, but a new Temporal option is at stage 2 in the TC39 standards approval process. The properties and methods discussed here are subject to change, but the API should arrive in browsers and runtimes some time in 2021.

    Temporal Time

    Temporal is a top-level static global object (like Math).

    Its main objectives are:

    • a predictable cross-browser/runtime behavior
    • easier APIs for date and time computations
    • support for non-Gregorian calendars
    • support for all time zones, including daylight-saving arithmetic
    • parsing of strictly specified ISO-8601 strings
    • making all objects immutable

    The API is comprehensive and may change, but you can look forward to the following key features at some point in the near future.

    Current Date and Time returns the current date/time, which can be passed to further methods to provide additional information. For example:

    // exact time since the Unix epoch on 1 Janary, 1970 UTC;;;
    // current time zone;
    // time in current location, e.g.
    // 2021-09-18T04:17:48.435068431-04:00[America/New_York];
    // time in another time zone, e.g.
    // 2021-09-18T09:17:48.438068435+01:00[Europe/London]'Europe/London');

    Creating Instant Date/Times

    The Temporal.Instant object represents a single point in time to the nearest nanosecond. It can be created from ISO 8601 formatted strings, or a number of seconds, milliseconds, or microseconds:

      t1 = Temporal.Instant.from('2021-03-30T01:45:00+01:00[Europe/Berlin]'),
      t2 = Temporal.Instant.from('2021-04-01T02:00+01:00'),
      t3 = Temporal.Instant.fromEpochSeconds(1.0e8),
      t4 = Temporal.Instant.fromEpochMilliseconds(1.0e10),
      t5 = Temporal.Instant.epochNanoseconds(1.0e12);

    Creating Zone-aware Date/Times

    The Temporal.ZonedDateTime object represents a timezone and calendar-aware date/time at the instant it occurred (or will occur) in a particular region. A variety of different constructors can be used:

    new Temporal.ZonedDateTime(
      1234567890000, // epoch nanoseconds
      Temporal.TimeZone.from('America/Los_Angeles'), // timezone
      Temporal.Calendar.from('iso8601') // default calendar
      timeZone: 'America/New_York'
      year: 2025,
      month: 1,
      day: 7,
      hour: 9,
      minute: 30,
      second: 1,
      millisecond: 2,
      microsecond: 3,
      nanosecond: 4

    Creating Plain Dates and Times

    It’s not always necessary to use exact moments, so the Temporal API provides objects which are independent of time zones. These could be used for simpler events, such as a meeting at 2pm today.

    All have similar constructors:

    // create a new PlainDateTime
    // both are 4 May 2021 at 1:14pm and 15 seconds
    new Temporal.PlainDateTime(2021, 5, 4, 13, 14, 15);
    // create a new PlainDate
    // both are 4 May, 2021
    new Temporal.PlainDate(2021, 5, 4);
    // create a new PlainTime
    // both are 1:14pm and 15 seconds
    new Temporal.PlainTime(13, 14, 15);
    // create a new year PlainYearMonth
    // both are April 2021
    new Temporal.PlainYearMonth(2021, 4);
    // create a new PlainMonthDay
    // both are 14 March
    new Temporal.PlainMonthDay(3, 14);

    Extracting Values

    All Temporal objects can return discrete values about a specific date/time. For example, using a ZonedDateTime:

    const t1 = Temporal.ZonedDateTime.from('2025-12-07T03:24:30+02:00[Africa/Cairo]');
    t1.year;        // 2025
    t1.month;       // 12;         // 7
    t1.hour;        // 3
    t1.minute;      // 24
    t1.second;      // 30
    t1.millisecond; // 0
    t1.microsecond; // 0
    t1.nanosecond;  // 0

    Other useful properties include:

    • dayOfWeek (1 for Monday to 7 for Sunday)
    • dayOfYear (1 to 365 or 366)
    • weekOfYear (1 to 52, or sometimes 53)
    • daysInMonth (28, 29, 30, or 31)
    • daysInYear (365 or 366)
    • inLeapYear (true or false)

    Comparing and Sorting Dates

    All Temporal objects can be compared using a compare() function, which returns an integer. For example, to compare two ZonedDateTime objects:

    // returns:
    //  -1 if t1 is before t2
    //   0 if t1 and t2 are the same
    //   1 is t1 is after t2, t2);

    compare() can be used as an Array sort() function to arrange date/times into ascending chronological order (earliest to latest):

    const t = [
      ].map( d => Temporal.ZonedDateTime.from(d) )
      .sort( );

    Date Calculations

    Several methods are provided to perform date calculations on any Temporal object. All return a new Temporal of the same type when passed a Temporal.Duration object, which defines a period in years, months, days, hours, and so on.

    const t1 = Temporal.ZonedDateTime.from('2022-01-01T00:00:00+00:00[Europe/London]');
    // add 8 hours 30 minutes
    t1.add({ hours: 8, minutes: 30 });
    // subtract 5 days
    t1.subtract({ days: 5 });
    // round to nearest month
    t1.round({ smallestUnit: 'month' });

    Note that Plain dates and times can wrap. For example, adding 24 hours to any PlainTime will return an object holding an identical value.

    The until() and since() methods return a Temporal.Duration object describing the time until or since a specific date/time:

    // mounths until t1
    // weeks since t1

    Finally, the equals() method can determine whether two date/time values are equivalent:

      d1 = Temporal.PlainDate.from('2022-01-31');
      d2 = Temporal.PlainDate.from('2021-01-31');
    d1.equals(d2); // false

    Formatting Dates With the Internationalization API

    While not part of the Temporal API, the JavaScript Intl (Internationalization) API provides a DateTimeFormat() constructor which can be used to format Temporal or Date objects:

    const d = new Temporal.PlainDate(2021, 3, 14);
    // 3/14/2021
    new Intl.DateTimeFormat('en-US').format(d);
    // 14/3/2021
    new Intl.DateTimeFormat('en-GB').format(d);
    // miércoles, 14 de abril de 2021
    new Intl.DateTimeFormat('es-ES', { dateStyle: 'full' }).format(d);

    About Time?

    The Date() object has been a cause of developer frustration for a quarter of a century. While there’s no guarantee Temporal will become a standard feature quickly, the future for JavaScript dates looks a little brighter.

    You can try out Temporal today, via an experimental polyfill, but don’t use it in production (yet)!

    Further information: