Important!: This article as been written almost 2 years ago
Temporal
updated their API and is even more awesome now - Part two of the article coming soon!
Every developer at some point in their careers had to play with Date & Time and if you are lucky... You only had to deal with time zones when you are travelling.
The JavaScript community & developers have been trying to solve this in different ways, either by developing their own libraries or by using any popular solution like moment, date-fns or DayJS among others. There is a nice blog post about Fixing JavaScript Date.
"It's 2020... and moment
still in my package.json"
Not having nothing against all these folks and the community that made an awesome job with these libraries, I believe that it's time to have a solution that is part of the standards, solving this long-standing issue at once... as an alternative, we can build moment
into the browser too.
"Yeahhhh! Temporal is here... & What is this ?"
Temporal is a new proposal, already at Stage 2 that brings a modern date/time API to the ECMAScript land.
At the time of writing there is no browser implementation yet, a polyfill is available at npm, please give it a try and provide feedback.
Cool things about Temporal?
- Providing easy-to-use APIs for date and time computations
- Dealing only with immutable objects
- Parsing a strictly specified string format: ISO-8601
- Supporting non-Gregorian calendars, and time zones other than the user's local time and UTC
- Still being an experimental API, but hopefully it will eventually be a standard, built-in to JS
- Is local aware, support time zones and locales by default, no extra plugins or data are required
- It is designed to be used directly, with an easy API, as well as embedding well in other libraries
Overview
This overview covers functionalities that I use most in my projects and I’d like you to know the goals & non-goals that we are trying to accomplish through this overview.
Outline
- Review what Temporal has to offer and highlight some API's
- Compare the ergonomics and functionality with existing libraries
- Learn and understand how it works
- Provide valuable feedback from my usage and experience with
Temporal
This post doesn’t cover all API surface, for that please refer to their documentation that has excellent examples.It also omits any benchmarks--the polyfill may be slow, but that’s because it hasn’t been optimized yet.
Usage
Intentionally no configuration or extra plugins for time zone & locales are used, this is required by some libraries and may affect results that won't be showing accordingly with my time zone(Europe/Madrid) or locale(es-ES).
Date of Month
// Temporal
Temporal.now.date().day;
// moment
moment().date();
// dayjs
dayjs().date();
// date-fns
import { getDate } from 'date-fns';
getDate(new Date());
// => 14 (Current Day)
Day of Week
// Temporal
Temporal.now.date().dayOfWeek;
// moment
moment().day();
// dayjs
dayjs().day();
// date-fns
import { getDay } from 'date-fns';
getDay(new Date());
// => 2 (Current Day of Week)
Add
// Temporal
Temporal.now.absolute().plus({ days: 7 });
// => 2020-07-22T13:03:01.419163174Z
// moment
moment().add(7, 'days');
// => Wed Jul 22 2020 15:03:24 GMT+0200
// dayjs
dayjs().add(7, 'day');
// => Wed, 22 Jul 2020 13:03:52 GMT
// date-fns
import { add } from 'date-fns';
add(new Date(), { days: 7 });
// => 2020-07-22T13:04:37.366Z
Subtract
// Temporal
Temporal.now.absolute().minus({ days: 7 });
// => 2020-07-08T13:07:17.807181419Z
// moment
moment().subtract(7, 'days');
// => Wed Jul 08 2020 15:08:03 GMT+0200
// dayjs
dayjs().subtract(7, 'day');
// => Wed, 08 Jul 2020 13:08:24 GMT
// date-fns
import { sub } from 'date-fns';
sub(new Date(), { days: 7 });
// => 2020-07-08T13:08:54.558Z
Difference
const startDate = new Date('1986-07-1');
const endDate = new Date('2020-07-1');
// Temporal
const temporalStart = Temporal.Absolute.from(startDate.toISOString());
const temporalEnd = Temporal.Absolute.from(endDate.toISOString());
const temporalDiff = temporalEnd.difference(temporalStart, {
largestUnit: 'days',
});
console.log(temporalDiff.toString());
// => P12419D (ISO 8601 notation)
// Or `temporalDiff.days`
// => 12419
// moment & dayjs have similar API
const momentStart = moment(startDate);
const momentEnd = moment(endDate);
const momentDiff = momentEnd.diff(momentStart, 'days');
console.log(momentDiff.toString());
// => 12419
// date-fns
import { differenceInDays } from 'date-fns';
differenceInDays(startDate, endDate); //=> -12419
differenceInDays(endDate, startDate); //=> 12419
Duration
// Temporal
new Temporal.Duration(0, 0, 0, 0, 23, 59, 59);
Temporal.Duration.from({ hours: 23, minutes: 59, seconds: 59 });
// moment
moment.duration('23:59:59');
moment.duration({ hours: 23, minutes: 59, seconds: 59 });
// dayjs
// => This dependent on `Duration` plugin to work
// .toString() output
// => PT23H59M59S
// => PT23H59M59S
// date-fns
import formatISODuration from 'date-fns/formatISODuration'; // ESM export is not working
formatISODuration({ hours: 23, minutes: 59, seconds: 59 });
//=> P0Y0M0DT23H59M59S
Days in Month
// Temporal
new Temporal.YearMonth(2020, 2).daysInMonth;
new Temporal.YearMonth(2021, 2).daysInMonth;
// Moment
moment('2020-02', 'YYYY-MM').daysInMonth();
moment('2021-02').daysInMonth();
// DayJS
dayjs('2020-02').daysInMonth();
dayjs('2021-02', 'YYYY-MM').daysInMonth();
// date-fns
import { getDaysInMonth } from 'date-fns';
// https://date-fns.org/v2.14.0/docs/getDaysInMonth
getDaysInMonth(new Date(2020, 1));
getDaysInMonth(new Date(2021, 1));
// Output
// => 29
// => 28
Compare
// Temporal
const t1 = Temporal.Date.from('2020-02-20');
const t2 = Temporal.Date.from('2020-03-21');
Temporal.Date.compare(t1, t2); //=> `−1` if one comes before two;
Temporal.Date.compare(t2, t1); //=> `1` if one comes after two.
Temporal.Date.compare(t2, t2); //=> `0` if one and two are the same;
// moment & dayjs have similar API
const m1 = '2020-02-20';
const m2 = '2020-03-21';
moment(m1).isBefore(m2); // => true
moment(m1).isAfter(m2); // => false
moment(m1).isSame(m1); // => true
// date-fns
import { compareAsc, compareDesc } from 'date-fns';
const fns1 = new Date('2020-02-20');
const fns2 = new Date('2020-03-21');
compareAsc(fns1, fns2); //=> `-1` if the first date is before the second
compareAsc(fns2, fns1); //=> `1` if the first date is after the second
compareAsc(fns2, fns2); //=> `0` if dates are equal.
compareDesc(fns1, fns2); //=> `1` if the first date is before the second
compareDesc(fns2, fns1); //=> `-1` if the first date is after the second
compareDesc(fns2, fns2); //=> `0` if dates are equal.
i18n
// Temporal
Temporal.now.date().toLocaleString('es-ES', {
weekday: 'long',
}); // => martes
// moment (only works loading locale strings separately - 4.04 KB)
moment().locale('es-ES').format('dddd'); //=> martes
// dayjs (only works loading locale strings separately - 1.01 KB)
dayjs().locale('es-ES').format('dddd'); //=> martes
// date-fns only works import locale strings separately - no size info)
import { format } from 'date-fns';
import { es } from 'date-fns/locale';
format(new Date(), 'cccc', { locale: es }); //=> martes
Summing up
First impressions
As I described before, at the time of writing the Temporal
proposal is Stage 2, but it is already in very good shape. The functionality covers a wide spectrum of use cases, arithmetic with dates and times, work with different calendar systems, time zones and more.
The surface of the API together with all other Intl built-in
objects play an important role in this long-standing dilemma of dealing with date/time in the browser together with the internationalization.
API is accessible and easy to use and includes good documentation.
Adoption
Temporal
is part of the standards
and follows the TC39 process to become part of the ECMAScript language and be implemented in the browsers.
The adoption can start by including the proposal-temporal
polyfill in your projects following the instructions.
Voilaaa! no more extra dependencies, no more loading locale-specific data and full functionality.
Remember that a polyfill "...is code that implements a feature on web browsers that do not support the feature...", if they do support, you won't need it, that’s the big difference from using a library.
note: specification text, documentation and polyfill at time of writing still under development and should be understood non-production ready.
Benefits and opportunities
for Users
- Simple and ergonomic API to handle date/time and time zones in the browser
- Dependency free(no extra data, no libraries)
- Works well with
Intl
, which provides high-quality date and time formatting with many languages and options - Have multiple choices and one that is part of the
standards
for Libraries
- Stable and lean API to build features on top of
- Reduce the need for loading extra data to time zones or locales
- Reduced bundle size
Present
All those libraries moment(12.443.725)
, date-fns(6.499.163)
and dayJS(1.941.696)
sum up to 20M weekly downloads, this validates the need of having a solution directly on the language, in the other hand, these figures are a clear indicator about the future impact that Temporal
will have in the JavaScript landscape.
This new API is also paving the base for future improvements on existing ones you have the example of Intl.DisplayNames
that would benefit from the Temporal
calendar to provide even more options(read more here).
Future
Temporal
, has a marathon ahead of them before is widely accepted, this happened with most of the standards we know _.assign()
vs Object.assign()
etc... The transition process requires a well-defined path and then things will follow naturally.
I don't see the end of the road for those libraries, but more likely a sensation of accomplishing a goal. Future looks promising and different synergies can come up from this new standard
.
Help and Feedback
Never had been so easy to have a voice on the standards, you can start by testing, reading the documentation and contribute by filling the feedback form or by opening Github issues with ideas or feedback that can be helpful to make Temporal
even better.
Overall, I have some concerns with Temporal
, which I’ve been raising on issues in the issue tracker like #770, #772. It remains unclear to me how the transition from JS date libraries to Temporal will work exactly, and so I’ve been participating in the Temporal issue tracker to help find a direction. I hope you’ll share your thoughts as well!
👏 Kudos to all library authors that kept our backs during this time and to the Temporal
team that is working hard to make it part of the language.
Let me know what you think about this post on Twitter and I hope you enjoyed it.
Thanks to all the people who helped me review this article.
Top comments (3)
That's great, thanks for the comparison.
There is another library that's worth mentioning, it's Luxon. The API surface is close to Temporal and is by one of the core contributors of Moment. I'm unsure if the intention is to follow gradually Temporal, but is nice to work with.
Of all the examples the new API is the most verbose. Since that is to be widely adopted it is going to be the dominant code which IMHO looks bad, very bad... Will see..
Yeah, like i said in last portion of the article is time to give feedback !! the typical phrase "Speak now or forever hold your peace" ... if you provide inputs they will take into account and consequently we will get a better API.
You have the case here where they use
minus
andplus
where most libraries useadd
andsubtract
just raised this issue github.com/tc39/proposal-temporal/... and now is being dicussed and is likely to end with the most used terminology