JS’s new date-time API, Temporal, which is really useful compared to Date (Temporal tutorial)

Wang Ming, the author of this article, is a front-end development engineer of 360 Navigation front-end team

Date, the most troublesome object in JS history, is going to be eliminated. The substitute Temporal is really useful.

Original address: juejin.cn/post/706485…

The Date the background


As we all know, in 1995 Brendan (the father of JavaScript) was given a huge and urgent assignment at Netscape to write the JavaScript language in 10 days. Date handling is a fundamental part of almost any programming language, so JavaScript must have it too. This is a very complex area, but the time left for authors to write it is short. In the end, Brendan took a page out of the then-hot Java language and copied the Javascript Date object from the java.util.Date implementation. Frankly, this implementation sucks. In fact, Java deprecated and replaced this implementation two years later in version 1.1. Twenty years later, however, we still use the API in the JavaScript programming language.

Problems with Date


  • Time zones other than the user’s local time are not supported

Developers are not supported to switch time zone information through the API.

  • The parser behaves so unreliable that it cannot be used
new Date(a);new Date(value); 
new Date(dateString); 
new Date(year, monthIndex [, day [, hours [, minutes [, seconds [, milliseconds]]]]]);
Copy the code

Developers often crash programs due to timing errors caused by input parameter formatting problems. For example, typing (‘2022-02-22’) and (2022, 2,02,22) gives different results,

  • Missing computing API

Computing logic involving time is usually written by developers themselves, such as comparing the length of two times, adding and subtracting between times, without their own computing apis

  • Non – Gregorian calendar is not supported

It is not possible to use each country’s own calendar, except the Gregorian calendar, which is widely used worldwide. Like the Chinese lunar calendar

The birth of the Temporal


In order to make up for the defects of Date, many programmers started to develop some open source libraries to bypass the direct use of Date, such as excellent NPM libraries such as date.js and moment.js. However, the problem of Date has always troubled the further development of Javascript. So TC39 started to upgrade Date. They found Maggie, the author of the moment.js library, to be the main designer of the new feature Temporal.

Those of you who are interested can goblogRead more details about the console page already supportedTemporalObject or run locally, installedTemporalthepolyfill

$ npm install @js-temporal/polyfill
import { Temporal} from '@js-temporal/polyfill';
Copy the code

Temporal is a global object that, like Math and Promise, resides in a top-level namespace and brings a modern date and time interface to the Javascript language.

As shown in the figure, a comprehensive Temporal includes three parts:

The green area is the date and time in ISO 8601 format;

The yellow area is the time zone (Tokyo, Japan);

The red area is calendar (Japanese calendar);

ISO 8601: An international time format. T is used to separate the date (2020-08-05) from the time (20:06:13). + or – indicates the Eastern and Western time zones. Plus 09:00 for east Ninth ward.

Compared with the Date

new Date(a)//Fri Jan 28 2022 17:03:11 GMT+0800
Copy the code

Date Indicates the time in GMT format, which is less common than ISO 8601 and does not include time zones and calendars.

The various types of Temporal are introduced


The redesign of Temporal was overturned, which included 5 main types, each of which was responsible for different functions and could be converted between types.

After learning the functions of these five types and the relationship between them, you can basically master Temporal.

The following is the diagram of functions and transformations of various types of Temporal, which is very important and conducive to our comprehensive understanding and use of Temporal. It will be explained step by step in the following.

ZonedDateTime

Definition: The most comprehensive Temporal type, associated with both time zone and calendar. Represents what is happening at a particular time from the point of view of a particular region of the earth.

Use scenario: the Wenchuan earthquake occurred at 14:28:4 on May 12, 2008, Beijing time, or the Wenchuan earthquake occurred at 01:28:4 on May 12, 2008, New York time.

How do I get a ZonedDateTime type?

Not only get a ZonedDateTime type, actually all Temporal types are obtained in the same way. There are usually two ways to get this: the new constructor (), and the FROM method.

  • The new constructor () method

Parameters :(nanoseconds, time zone, calendar). Different types require different parameters.

Nanoseconds: The number of nanoseconds elapsed since the Unix era (midnight UTC, January 1, 1970), in bigint

Time zone, date: can be a string ora Temporal type.

new Temporal.ZonedDateTime(0n.'Asia/Shanghai'.'chinese'); 
//Temporal.ZonedDateTime <1970-01-01T08:00:00+08:00[Asia/Shanghai][u-ca=chinese]>
Copy the code

Usually each Temporal type has the toString () method, covering the Object. The prototype, the toString () method, is used by a string representation of Temporal

The toString() call is expressed as a string to make it easy to read.

new Temporal.ZonedDateTime(0n.'Asia/Shanghai'.'chinese').toString(); 
//1970-01-01T08:00:00+08:00[Asia/Shanghai][u-ca=chinese]
Copy the code

The ZonedDateTime type means that the Unix era starts at 1970-01-0t08:00:00 +08:00 from Beijing time, not 1970-01-0t00:00:00 +00:00

  • Get one from () Temporal type

From () takes more diverse parameters and supports overflow handling (explained below), so it is usually the preferred method. Usually used as the preferred way to get a Temporal type.

Accept string

Temporal.ZonedDateTime.from('2022-02-28T00:00:00+08:00[Asia/Shanghai]').toString(); 
//2022-02-28T00:00:00+08:00[Asia/Shanghai]
Copy the code

or

Accept object, ({time zone, date, calendar}, options)

Options is a fault tolerance configuration that handles input date overflow. {overflow: ‘constrain’} : automatic overflow. {overflow: ‘reject’}, error if date overflow.

In the example below, February 2022 is 28 days. If you select constrain, an overflow will be performed if the date is longer than the date that matches the closest existence value.

// Enter 31 days to get 28 days
Temporal.ZonedDateTime.from({ timeZone: 'Asia/Shanghai'.year: 2022.month: 2.day: 31}, { overflow: 'constrain' }).toString(); 
//2022-02-28T00:00:00+08:00[Asia/Shanghai]
Copy the code

Select reject configuration and an error will be reported if the date is exceeded.

Temporal.ZonedDateTime.from({ timeZone: 'Asia/Shanghai'.year: 2022.month: 2.day: 31}, { overflow: 'reject' }).toString(); 
//RangeError: value out of range: 1 <= 31 <= 28
Copy the code

Instant

Definition: Responsible for a single point in time (called “exact time”), with accuracy in nanoseconds. There is no time zone or calendar information.

Use scenario: 2020-01-23T17:04:36.491865121-08:00, only used to express a moment of time, there is no other meaning.

To obtain aInstanttype

new Temporal.Instant( bigint )

Bigint: Nanoseconds, the number of nanoseconds elapsed in Unix era (midnight UTC, January 1, 1970), in bigint

new Temporal.Instant(1553906700000000000n);
//2019-03-30T00:45:00Z
new Temporal.Instant(0n);
//1970-01-01T00:00:00Z
new Temporal.Instant(-2208988800000000000n);
//1900-01-01T00:00:00Z
Copy the code

Z is represented in ISO 8601 time format without time zone association.

Temporal.Instant.from(thing: any)

The FROM method takes time zone bias into account when generating Instant.

 Temporal.Instant.from('2019-03-30T01:45:00+01:00[Europe/Berlin]');  
 Temporal.Instant.from('2019-03-30T01:45+01:00');
 Temporal.Instant.from('2019-03-30T00:45Z');
Copy the code

Although the first two carry time zone information, the obtained Instant time value is the same, the three are 2019-03-30T00:45z

Plain XX series

Responsible for the expression of Temporal calendar date (xx year, XX month, XX day) and clock time (XX point, XX minute, and XX second), not involving time zone,

Calendar Date: Xiao Hong’s birthday is on March 25 of the lunar calendar every year. Clock time: It’s 2:00 p.m

Compared with Instant, the two parties use different scenarios and have different internal attributes. Instant does not contain time zone and date, while Plain XX series contains a calendar.


The Plain XX family contains five types, the broadest PlainDateTime containing date and time, as well as date-only Plaindate and time-only Plaintime. Other date types are PlainYearMonth (month) and PlainMonthDay (day), which are classified more precisely.

Take PlainDateTime for example, the others are similar.

To get aPlainDateTime

New Temporal.PlainDateTime (year,month,day…)

The parameters are listed in year -> nanosecond order. Year, month and day are mandatory, and other parameters are optional.

new Temporal.PlainDateTime(2020.3.14.13.37)
//2020-03-14T13:37:00
Copy the code

Temporal.PlainDateTime.from()

Temporal.PlainDateTime.from({ year: 2001.month: 1.day: 1.hour: 25 ,calendar:'chinese'}, { overflow: 'constrain' }).toString()
//2001-01-24T23:00:00[u-ca=chinese]
Copy the code

TimeZone

Definition: Responsible for Temporal time zone information.

Example: Beijing time zone, east 8 zone, not used alone, usually combined with other types.

To get aTimeZonetype

New Temporal.TimeZone (string)

String: Description of a time zone

// East 8th District, namely Beijing time
new Temporal.TimeZone('8');
// Direct string description, provided that Temporal is defined internally
new Temporal.TimeZone('Asia/Shanghai');
//Asia/Shanghai
Copy the code

From the same

Temporal.TimeZone.from('Asia/Shanghai');
//Asia/Shanghai
Copy the code

When paired with other types, you can use a string directly (” Asia/Shanghai “), ora Temporal.TimeZone object

Example:

Get a ZonedDateTime type and use the Temporal.TimeZone object when setting the TimeZone.

new Temporal.ZonedDateTime(0n, Temporal.TimeZone.from('Asia/Shanghai')); 
//1970-01-01T08:00:00+08:00[Asia/Shanghai]Is equivalent tonew Temporal.ZonedDateTime(0n.'Asia/Shanghai')); 
Copy the code

Calendar

Definition: the calendar system responsible for Temporal.

Example: Chinese lunar calendar. Not used alone, combined with other types.

To get aCalendartype

Similar to TimeZone, new Calendar (string) or grey.calendar.from (string)

new Temporal.Calendar('chinese').toString();
//chinese
Temporal.Calendar.from('chinese').toString();
//chinese
Copy the code

The Calendar type is not used alone, but in conjunction with other types with Calendar attributes.

As mentioned above, in Temporal, the ones that contain the calendar attribute are plainXX series and ZonedDateTime

Both types of prototypes have a withCalendar method that sets the calendar property for that date.

Example:

PlainXX series adds calendar attributes

Without adding the calendar property, Temporal. Plaindate. from('2019-02-06');
/ / 2019-02-06Add the calendar attribute to Temporal.plaindate. from('2019-02-06').withCalendar('chinese');
//2019-02-06[u-ca=chinese]
Copy the code

ZonedDateTime adds calendar attributes

No Temporal before add calendar properties. ZonedDateTime. The from ('2022-02-28T00:00:00+08:00[Asia/Shanghai]')
//2022-02-28T00:00:00+08:00[Asia/Shanghai]Add a calendar properties after Temporal. ZonedDateTime. The from ('2022-02-28T00:00:00+08:00[Asia/Shanghai]').withCalendar('chinese')
//2022-02-28T00:00:00+08:00[Asia/Shanghai][u-ca=chinese]
Copy the code

Duration

Definition: represents a period of time that can be used for arithmetic.

Duration (1 hour, 1 minute, 1 hour, 10 minutes) Duration (1 hour, 1 minute, 1 hour, 10 minutes)

Duration does not represent a period of time like Date, but rather generates a string representing a period of time according to ISO 8601 notation.

In short, the ISO 8601 notation must begin with a P, followed by the date, year, month, week, and day separated by T, followed by time, hour, minute, and second.

A Duration string may be missing either year/month/week/day/hour/minute/second, but must contain the initial letter P, or the letter T if both hours/minutes/seconds are present.

For example:

Year: P1Y, P must be kept, no time information, do not add T to divide.

One minute: PT1M, P must be retained, if there is time information, add T to split the date and time

Some string expression exercises for Duration

To obtain aDurationtype

New Temporal Duration ()

Parameters: Year => nanosecond (optional). If a unit is empty, enter undefined or 0.

new Temporal.Duration(1.2.3.4.5.6.7.987.654.321); 
/ / P1Y2M3W4DT5H6M7. 987654321 s
1 year 2 months 3 weeks 4 days 5 hours 6 minutes 7 seconds 987 ms 654 microseconds 321 nanoseconds
new Temporal.Duration(0.0.0.40); 
// P40D 中文 translation => 40 days
Temporal.Duration.from(undefined.undefined.undefined.40); 
// P40D 
new Temporal.Duration(); 
// PT0S 
Copy the code

Now that you know what a Duration is and how to generate a Duration, you can use it for some date and time calculations.

The length of a date or time

Call the compare method on the Duration prototype. Return values: -1, 0, 1

one = Temporal.Duration.from({ hours: 79.minutes: 10 });//PT1H10M
two = Temporal.Duration.from({ days: 3.hours: 7.seconds: 630 });//P3DT7H630S
Temporal.Duration.compare(one,two)
/ / 1
Copy the code

Return -1, then one is shorter than two

Return 0, then one takes the same time as two

Return -1, then one takes longer than two

In fact, with the exception of the Timezone and Calendar types, all types with date and time attributes can do arithmetic

Such as PlainDateTime type:

one = Temporal.PlainDateTime.from('1995-12-07T03:24');
two = Temporal.PlainDateTime.from('1995-12-07T01:24');
Temporal.PlainDateTime.compare(two,two)
/ / 1
Copy the code

The addition and subtraction of a date or time.

Add:

Temporal.Duration.from('PT1H'); //PT1H
hour.add({ minutes: 30 }); 
// => PT1H30M
Copy the code

Subtraction:

hourAndAHalf = Temporal.Duration.from('PT1H30M'); //PT1H30M
hourAndAHalf.subtract({ hours: 1 }); // => PT30M
Copy the code

Again, the arithmetic applies to all but the Timezone and Calendar types.

Such as PlainDateTime type:

dt = Temporal.PlainDateTime.from('the 1995-12-07 T03:13. 000003500'); 
dt.add({ years: 20.months: 4.nanoseconds: 500 }); 
/ / = > 2016-04-07 T03:13. 000004
Copy the code

Conversions between Temporal types

The various types of Temporal can be converted in addition to completing their own functions.

Looking back at the type diagram again, the Instant type in the yellow area on the left is used to express the time of an Instant, without time zone and calendar information. The PlainXX series (five in yellow on the right), used to express calendar dates or clock times, contains calendar information, while the ZonedDateTime in the middle, spanning the left and right fields, contains time zone and calendar information, and can serve as a channel connecting the Instant on the left and the Plain series on the right. The bridge is responsible for the conversion between the types, and the middle Timezone Timezone type Calendar type, not used alone, with the above ZonedDateTime type to assist the conversion. The bottom Duration has no direct relationship to any type, does not participate in type conversions, and represents a Duration that can be used for arithmetic.

Instant => ZonedTimeDate

Temporal before conversion. Instant. The from ('2020-08-05T20:06:13+0900').toString()
//2020-08-05T11:06:13ZThe converted Temporal. Instant. The from ('2020-08-05T20:06:13+0900').toZonedDateTimeISO('Asia/Tokyo').toString();
//2020-08-05T20:06:13+09:00[Asia/Tokyo]
Copy the code

ZonedTimeDate => Instant

Temporal before conversion. ZonedDateTime. The from ('2020-11-01T01:45-07:00[America/Los_Angeles]').toString();
//2020-11-01T01:45:00-07:00[America/Los_Angeles]The converted Temporal. ZonedDateTime. The from ('2020-11-01T01:45-07:00[America/Los_Angeles]').toInstant().toString();
//2020-11-01T08:45:00Z
Copy the code

ZonedTimeDate => PlainDateTime

Temporal before conversion. ZonedDateTime. The from ('2020-11-01T01:45-07:00[America/Los_Angeles]').toString()
//2020-11-01T01:45:00-07:00[America/Los_Angeles]The converted Temporal. ZonedDateTime. The from ('2020-11-01T01:45-07:00[America/Los_Angeles]').toPlainDateTime().toString();
//2020-11-01T01:45:00
Copy the code

PlainDateTime => ZonedTimeDate

Temporal before conversion. PlainDateTime. The from ('2020-08-05T20:06:13').toString()
//2020-08-05T20:06:13The converted Temporal. PlainDateTime. The from ('2020-08-05T20:06:13').toZonedDateTime('Asia/Tokyo').toString();
//2020-08-05T20:06:13+09:00[Asia/Tokyo]
Copy the code

conclusion


Go back to the beginningDateThe problem.

1. Time zones other than the user’s local time are not supported. Temparal allows developers to use TimeZone to set time zones other than local time.

2. The computing API is missing. Except for the time zone and calendar types, all types can perform arithmetic operations, i.e. time comparison, increase, decrease, and so on.

3. Non-gregorian Calendar is not supported. Temparal Calendar is supported.

4. Parser behavior is not reliable so that it can not be used. In Temporal, new constructor () or From method have more standard requirements for parameters, and From method supports logical processing after date overflow, which can prevent system crash.


Finally, a comparison diagram of the various types of Temparal functions is attached. Each type is responsible for Temparal which functions have been clearly marked.