JavaScripts Temporal API: New era for date and time calculations
Page 2: Formatting of date and time values
For formatting date and time values, the Temporal API relies on the Intl interface and its DateTimeFormat class. This allows developers to specify the various aspects of date formatting, for example that the month and day should be formatted with two digits. A locale containing the formatting of a date can also be selected. All formatting specifications that are omitted during the call are also omitted in the output. Listing 2 ensures that JavaScript outputs the passed object PlainDateTime in the format "day.month.year, hour:minute".
const dateTime = Temporal.PlainDateTime.from('1999-08-11T12:35');
const formatter = new Intl.DateTimeFormat('de-DE', {
day: '2-digit',
month: '2-digit',
year: 'numeric',
hour: '2-digit',
minute: '2-digit',
});
const formattedDateTime = formatter.format(dateTime);
console.log(formattedDateTime); // 11.08.1999, 12:35
Listing 2: Formatting a PlainDateTime object with Intl.DateTimeFormat
Calculation and comparisons
The Temporal API makes it easy to calculate with dates and times. A simple use case is the planning of three dates four weeks apart. The code in Listing 3 first defines the first date and then uses the add method to add four weeks in each case.
const startDate = Temporal.PlainDateTime.from('2025-02-23T10:00:00');
const numberOfAppointments = 3;
const appointments = [];
for (let i = 0; i < numberOfAppointments; i++) {
const appointment = startDate.add({ weeks: -4 * i });
appointments.push(appointment);
}
appointments.forEach((appointment, index) => {
console.log(`Appointment ${index + 1}: ${appointment.toString()}`);
});
Listing 3: Calculation of date and time values
Both the add and subtract methods create new objects and accept either a string, an object or a Duration instance that describes the value to be added or subtracted. In the example, the second variant, an object with the weeks property, is used. With the add and substract methods, other date values such as years, months, days or time values such as hours or minutes are also permitted.
The equals method compares two temporal objects with each other. This works not only within a class, but also across classes. It is therefore possible to check whether a PlainDateTime object matches a ZonedDateTime object or a PlainTime. The available information must be the same for different classes.
Rounding with the round method
There are use cases in which the details of a date or time specification are not relevant. For such cases, the temporal classes offer a round method. It rounds a temporal instance to different units such as days, hours or minutes and uses different rounding strategies for this. An example could be an application for time bookings that records the project effort with the specification to round the times to quarter hours (Listing 4).
const currentTime = Temporal.Now.plainDateTimeISO();
// 2025-02-23T13:30:25.425
const roundedTime = currentTime.round({
smallestUnit: 'minute',
roundingIncrement: 15,
roundingMode: 'ceil',
});
console.log('Rounded time:', roundedTime.toString());
// Rounded time: 2025-02-23T13:45:00
Listing 4: Rounding date and time values with round
The only mandatory value of this method is the smallest unit that the rounded object should contain. All values that are less than or equal to day are possible. Months and years cannot be rounded. With roundingIncrement you can specify an increment for the rounding, in Listing 4 for example it is a quarter of an hour.
The default value for the increment is 1. The roundingMode specifies how rounding is performed. The value ceil stands for rounding up, the default value halfExpand rounds commercially and floor rounds down. There are also a number of other rounding strategies that the Temporal API takes from the NumberFormat class of the Intl API.
Duration and time spans
The Temporal API provides the Duration class for the representation of time spans. As with the other classes, its objects can either be created using a constructor or, better still, using the from method. In addition, the since and until methods of the date and time classes also produce Duration instances to represent the time span between two time specifications. The code in Listing 5 shows two use cases of the Duration class. The script first outputs how many weeks lie between 1.1.25 and 1.6.25 and adds 5 weeks to 1.6.25 in the second part.
const start = Temporal.PlainDate.from('2025-01-01');
const end = Temporal.PlainDate.from('2025-06-01');
const duration = start.until(end);
const weeks = duration.total({ unit: 'week', relativeTo: start });
console.log(weeks); // 21.571428571428573
const newEnd = end.add({ weeks: 5 });
console.log(newEnd.toString()); // 2025-07-06
Listing 5: Working with time spans
A special feature is the total method of the Duration object. It returns the value of a time span. In the example, this is the weeks between the start and end date. If a Duration object is about the time span between two calendar values, the method requires the start date, otherwise it returns a RangeException. The rest of the code behaves very similarly to the other Temporal interfaces.
Dealing with time zones
A major advantage of the new time API is that it fully supports time zones. This addresses one of the biggest problems of the previous Date API. A typical use case is the conversion of date and time values into different time zones.
One example could be the Super Bowl. In 2025, the game took place on 09.02.2025 at 17:30 in New Orleans. The ZonedDateTime class of the Temporal API is used for the conversion to Central European Standard Time. Listing 6 shows the source code for this example.
const start = Temporal.PlainDateTime.from('2025-02-09T17:30');
const startZoned = start.toZonedDateTime('America/Chicago');
console.log(startZoned.toString()); // 2025-02-09T17:30:00-06:00[America/Chicago]
const germanStart = startZoned.withTimeZone('Europe/Berlin');
console.log(germanStart.toString()); // 2025-02-10T00:30:00+01:00[Europe/Berlin]
Listing 6: Time conversion between time zones
The basis is a PlainDateTime object that provides the toZonedDateTime with the specification "America/Chicago" of the North American central time zone. This does not adjust the time and date value. The start of the game remains at 17:30 on 09.02.2025. A call to withTimeZone for "Europe/Berlin" produces a new object with the value 00:30 on 10.02.2025.
A web application often saves such time and date values in the neutral UTC time zone and converts the values based on the desired time zone. This minimizes the potential for errors caused by the wrong time zone.
One weakness remains
Formatting remains a weakness. Unfortunately, a format method is not available in the Temporal API. With its format function, date-fns offers a convenient and flexible solution for formatting a value with a format string. But solutions are also emerging here.
The established libraries will probably support the Temporal API as soon as it is widespread enough in common browsers. Until then, there is unfortunately only a somewhat cumbersome detour as in Listing 7.
import { format } from 'date-fns';
const start = Temporal.PlainDateTime.from('2025-02-25T10:00');
const startZoned = start.toZonedDateTime('UTC');
const startInstant = startZoned.toInstant();
const date = new Date(startInstant.epochMilliseconds);
const formattedDateTime = format(date, 'yyyy-MM-dd HH:mm:ss');
console.log(formattedDateTime); // 2025-02-25 11:00:00
Listing 7: Using date-fns with Temporal objects