新聞中心
Date背景
眾所周知,在 1995 年,Brendan(JavaScript之父) 被網(wǎng)景公司安排了一個巨大而緊急的工作任務(wù),用 10 天的時間來編寫 JavaScript 語言。而 日期處理 是幾乎所有編程語言的基本部分,所以JavaScript 也必須擁有它。

創(chuàng)新互聯(lián)專業(yè)做網(wǎng)站、成都做網(wǎng)站,集網(wǎng)站策劃、網(wǎng)站設(shè)計、網(wǎng)站制作于一體,網(wǎng)站seo、網(wǎng)站優(yōu)化、網(wǎng)站營銷、軟文推廣等專業(yè)人才根據(jù)搜索規(guī)律編程設(shè)計,讓網(wǎng)站在運行后,在搜索中有好的表現(xiàn),專業(yè)設(shè)計制作為您帶來效益的網(wǎng)站!讓網(wǎng)站建設(shè)為您創(chuàng)造效益。
這是一個非常復(fù)雜的領(lǐng)域,但留給作者編寫它的時間卻很短。最終 Brendan 選擇了借鑒當(dāng)時紅極一時的 java 語言,從java.Util.Date日期實現(xiàn)中復(fù)制了 Javascript 的日期對象。坦率地說,這個實現(xiàn)很糟糕。事實上 Java 在兩年后的 1.1 版本中就棄用和替換這種實現(xiàn)。然而 20 年后,我們?nèi)匀辉?JavaScript 編程語言中使用這個 API。
Date存在的問題
- 不支持除用戶本地時間以外的時區(qū)
不支持開發(fā)人員通過 API 來切換時區(qū)信息。
- 解析器行為不可靠以至于無法使用
new Date();
new Date(value);
new Date(dateString);
new Date(year, monthIndex [, day [, hours [, minutes [, seconds [, milliseconds]]]]]);
開發(fā)人員常常因為輸入的參數(shù)格式問題,引發(fā)時間錯誤,導(dǎo)致程序崩潰。比如輸入 ('2022-02-22') 和 (2022,02,22) 得到的結(jié)果卻不同,
- 計算 API 缺失
涉及時間的運算邏輯通常都需要開發(fā)人員自己去寫,比如比較兩個時間的長短,時間之間的加減運算,沒有自己的計算API
- 不支持非公歷
除了全球通用的公歷外,無法使用各國的自己的歷法。比如中國的農(nóng)歷
Temporal的誕生
為了彌補 Date 的缺陷,很多程序員著手開發(fā)一些開源的庫來繞過對 Date 的直接使用,比較優(yōu)秀的npm庫如 date.js 、moment.js,但 Date 的問題始終困擾著 Javascript 這門語言的進一步發(fā)展,于是 TC39 組織開始了對 Date 的升級改造,他們找到了 moment.js 庫的作者,Maggie ,由她來擔(dān)任新特性 Temporal的主力設(shè)計。
感興趣的同學(xué)可以去博客[1]閱讀更多細(xì)節(jié),該網(wǎng)頁的控制臺已經(jīng)支持Temporal 對象或者在本地運行,安裝Temporal的 polyfill
$ npm install @js-temporal/polyfill
import { Temporal} from '@js-temporal/polyfill';
Temporal是一個全局對象,像 Math 、Promise 一樣位于頂級命名空間中,為 Javascript 語言帶來了現(xiàn)代化的日期、時間接口。
如圖所示,一個全面的Temporal包含三個部分:
綠色區(qū)域為 ISO 8601 格式的 日期和時間;
黃色區(qū)域為時區(qū)(日本東京);
紅色區(qū)域為日歷(日本歷法);
ISO 8601格式:國際通用時間格式,T用來分割日期(2020-08-05)和時間(20:06:13),“+”或者“-”分別代表東時區(qū)和西時區(qū)。+09:00,代表東九區(qū)。
對比 Date
new Date()
//Fri Jan 28 2022 17:03:11 GMT+0800 (中國標(biāo)準(zhǔn)時間)
Date 采用 GMT格式(舊的時間表示格式) 的時間,使用方面不如 ISO 8601 通用,同時不包含 時區(qū)和歷法。
Temporal各種類型介紹
推翻重新設(shè)計的Temporal,包含5種主要 類型,每個類型負(fù)責(zé)不同的功能,類型之間還可以相互進行轉(zhuǎn)換。
學(xué)會了這5種類型的 功能 以及類型的之間的 關(guān)系 ,就基本掌握了Temporal。
下面是Temporal各種類型的功能與轉(zhuǎn)換關(guān)系圖,非常重要,十分有利于我們?nèi)胬斫夂褪褂肨emporal,下文將逐步講解。
ZonedDateTime
定義:最全面的Temporal類型,與時區(qū)和日歷都有關(guān)聯(lián)。表示從地球上特定區(qū)域的角度來看,在特定時刻發(fā)生的事。
使用場景:在北京時間的 2008年5月12日14時28分4秒發(fā)生汶川大地震,或者 在 紐約時間的 2008年5月12日01時28分4秒發(fā)生了汶川大地震。
如何獲得一個 ZonedDateTime 類型?
不僅是獲得一個 ZonedDateTime 類型,其實所有的 Temporal 類型都是一樣的獲取途徑。通常有兩種方法獲得,分別是 new 構(gòu)造函數(shù)(),和from方法。
- new 構(gòu)造函數(shù)()方式
參數(shù):(納秒數(shù),時區(qū),日歷),不同類型要求的參數(shù)不同。
納秒數(shù):從 Unix 紀(jì)元(1970 年 1 月 1 日午夜 UTC)計算,所經(jīng)過的納秒數(shù),單位為bigint
時區(qū),日期:可以是字符串,也可以是 Temporal 類型。
new Temporal.ZonedDateTime(0n, 'Asia/Shanghai', 'chinese');
//Temporal.ZonedDateTime <1970-01-01T08:00:00+08:00[Asia/Shanghai][u-ca=chinese]>
通常每個Temporal類型的都有toString()方法,覆蓋了Object.prototype.toString()方法,作用是通過一個字符串表示Temporal。
調(diào)用 toString() 用字符串來表達,方便閱讀。
new Temporal.ZonedDateTime(0n, 'Asia/Shanghai', 'chinese').toString();
//1970-01-01T08:00:00+08:00[Asia/Shanghai][u-ca=chinese]
這個 ZonedDateTime 的類型含義為,從北京時間看,unix紀(jì)元 起始時間為 1970-01-01T08:00:00+08:00,而非1970-01-01T00:00:00+00:00
- from()形式獲得一個 Temporal 類型
from()的參數(shù)更為多樣,同時支持 溢出處理 (下文有講解),所以通常作為首選方法。通常作為獲得一個 Temporal 類型的首選方法。
接受字符串
Temporal.ZonedDateTime.from('2022-02-28T00:00:00+08:00[Asia/Shanghai]').toString();
//2022-02-28T00:00:00+08:00[Asia/Shanghai]
or
接受對象,({時區(qū),日期,日歷},options)
options 代表 容錯機制 配置,即可以處理輸入的日期溢出問題,有兩種配置選項,{ overflow: 'constrain' }:自動處理溢出。{ overflow: 'reject' },日期溢出則報錯。
比如下面例子中,2022年2月一共28天,如果選擇了 constrain 配置,輸入日期超過了會進行溢出處理,即匹配最接近的存在值。
//輸入 31 天,得到 28 天
Temporal.ZonedDateTime.from({ timeZone: 'Asia/Shanghai', year: 2022, month: 2, day: 31}, { overflow: 'constrain' }).toString();
//2022-02-28T00:00:00+08:00[Asia/Shanghai]
選擇 reject 配置,日期超出則報錯。
Temporal.ZonedDateTime.from({ timeZone: 'Asia/Shanghai', year: 2022, month: 2, day: 31}, { overflow: 'reject' }).toString();
//RangeError: value out of range: 1 <= 31 <= 28
Instant
定義:負(fù)責(zé)單個時間點(稱為 “精確時間” ),精度以納秒為單位。不存在時區(qū)和日歷信息。
使用場景:2020-01-23T17:04:36.491865121-08:00,只用來表達一個瞬間的時間,沒有其他意義。
獲得一個 Instant 類型
new Temporal.Instant( bigint )
bigint:納秒數(shù),從 Unix 紀(jì)元(1970 年 1 月 1 日午夜 UTC)計算,所經(jīng)過的納秒數(shù),單位為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
Z 在 ISO 8601 時間格式表示,沒有時區(qū)關(guān)聯(lián)。
Temporal.Instant.from(thing: any)
from方法在生成 Instant 時,會考慮時區(qū)的偏差。
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');
雖然前兩個攜帶了時區(qū)信息,但獲取到的Instant 時間值相同,三個都是 2019-03-30T00:45Z。
Plain XX系列
負(fù)責(zé) Temporal 的 日歷日期 (xx年xx月xx日) 和 鐘表時間 (xx點xx分xx秒)表達,不涉及時區(qū),
使用場景:日歷日期:小紅的生日是農(nóng)歷 每年3月25。鐘表時間:現(xiàn)在是下午2:00
對比 Instant,雙方的 使用場景 不同, 內(nèi)部的屬性 也不同,Instant 不包含時區(qū)和日期,Plain XX 系列則包含日歷。
Plain XX系列包含 5種 類型,覆蓋最廣的 PlainDateTime 包含日期和時間,還有只包含日期的 Plaindate 和只包含時間的 Plaintime。日期類型里,還有分類更精細(xì)的PlainYearMonth(年月)和 PlainMonthDay(月天)
以 PlainDateTime 舉例,其他的類同。
獲取一個 PlainDateTime
new Temporal.PlainDateTime(year,month,day...)
參數(shù)以 年 -> 納秒 順序排列,其中 年 月 日,為必填項,其余選填。
new Temporal.PlainDateTime(2020, 3, 14, 13, 37)
//2020-03-14T13:37:00
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]
TimeZone
定義:負(fù)責(zé) Temporal 時區(qū)的相關(guān)信息。
例子:北京時區(qū),東八區(qū),不單獨使用,通常結(jié)合其他類型搭配。
獲取一個 TimeZone 類型
new Temporal.TimeZone(string)
string:對一個時區(qū)的描述
//東八區(qū),即北京時間
new Temporal.TimeZone('8:00');
//直接字符串描述,前提是 Temporal 內(nèi)部有定義
new Temporal.TimeZone('Asia/Shanghai');
//Asia/Shanghai
from同理
Temporal.TimeZone.from('Asia/Shanghai');
//Asia/Shanghai
在和其他類型搭配時,可以直接使用字符串(“Asia/Shanghai”),或者 Temporal.TimeZone對象
例子:
獲取一個 ZonedDateTime 類型,設(shè)置時區(qū)時,使用 Temporal.TimeZone 對象。
new Temporal.ZonedDateTime(0n, Temporal.TimeZone.from('Asia/Shanghai'));
//1970-01-01T08:00:00+08:00[Asia/Shanghai]
等價于
new Temporal.ZonedDateTime(0n, 'Asia/Shanghai'));
Calendar
定義:負(fù)責(zé) Temporal 的日歷系統(tǒng)。
例子:中國農(nóng)歷。不單獨使用,結(jié)合其他類型搭配。
獲取一個 Calendar 類型
同 TimeZone 類同,new Calendar(string) 或者 Temporal.Calendar.from(string)
new Temporal.Calendar('chinese').toString();
//chinese
Temporal.Calendar.from('chinese').toString();
//chinese
Calendar 類型不會單獨使用,要配合其他帶有 日歷屬性 的類型使用。
如上所述,在 Temporal 里,包含 日歷屬性 的有 plainXX系列 和 ZonedDateTime
這兩種類型的原型上有一個 withCalendar的方法,用來設(shè)置該日期的日歷屬性。
例子:
plainXX系列 添加日歷屬性
沒有添加日歷屬性前
Temporal.PlainDate.from('2019-02-06');
//2019-02-06
添加日歷屬性后
Temporal.PlainDate.from('2019-02-06').withCalendar('chinese');
//2019-02-06[u-ca=chinese]
ZonedDateTime 添加日歷屬性
沒有添加日歷屬性前
Temporal.ZonedDateTime.from('2022-02-28T00:00:00+08:00[Asia/Shanghai]')
//2022-02-28T00:00:00+08:00[Asia/Shanghai]
添加日歷屬性后
Temporal.ZonedDateTime.from('2022-02-28T00:00:00+08:00[Asia/Shanghai]').withCalendar('chinese')
//2022-02-28T00:00:00+08:00[Asia/Shanghai][u-ca=chinese]
Duration
定義:表示一段持續(xù)時間,并且這段時間可以用來進行 算術(shù)。
使用場景:兩段時間,一小時一分鐘 和 一小時十分鐘 ,可以轉(zhuǎn)換 Duration 類型,再進行時間的長度比較,從而得知前者的時長 小于 后者。
Duration 并非像 Date 的 時間戳 形式那樣表達一段時間,而是根據(jù) ISO 8601 表示法生成一個 字符串 來表達一段時間。
簡而言之,ISO 8601 表示法的首字母 必須 由 P 開頭,后跟日期,年、月、周和日 再由 T 字母進行分割,后跟時間,小時、分鐘、 秒。
一個 Duration 字符串可以缺失 年 / 月 / 周 / 日 / 小時 / 分 / 秒 中的任意一個,但必須包含首字母 P,如果同時有 小時 / 分 / 秒,則必須包含字母 T。
舉例:
一年: P1Y,必須保留 P ,沒有時間信息,不用加 T來分割。
一分鐘: PT1M,必須保留 P ,有時間信息,則加 T來分割日期和時間
一些Duration 的字符串表達練習(xí)
獲得一個 Duration 類型
new Temporal.Duration()
參數(shù):年 => 納秒,全部可選,非必填。需要按照順序輸入,某單位空缺則輸入 undefined 或者 0。
new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
// P1Y2M3W4DT5H6M7.987654321S
//中文翻譯 => 1年2月3周4天5小時6分鐘7秒987毫秒654微秒321納秒
new Temporal.Duration(0, 0, 0, 40);
// P40D 中文翻譯 => 40天
Temporal.Duration.from(undefined, undefined, undefined, 40);
// P40D
new Temporal.Duration();
// PT0S
了解了 Duration的字符串含義以及怎么生成一個 Duration后,可以用其進行一些 日期與時間 的計算與運算。
比對 日期或時間 的長度大小
調(diào)用 Duration 原型上的 compare 方法。返回值:-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
返回-1,則 one 比 two 的時間短
返回0,則 one 比 two 的時間一樣
返回-1,則 one 比 two 的時間長
事實上,除了 Timezone 和 Calendar 類型外,所有具備日期和時間屬性的類型都可以進行算術(shù)
如 PlainDateTime 類型:
one = Temporal.PlainDateTime.from('1995-12-07T03:24');
two = Temporal.PlainDateTime.from('1995-12-07T01:24');
Temporal.PlainDateTime.compare(two,two)
//1
日期或時間的 加減運算。
加法:
Temporal.Duration.from('PT1H'); //PT1H
hour.add({ minutes: 30 });
// => PT1H30M
減法:
hourAndAHalf = Temporal.Duration.from('PT1H30M'); //PT1H30M
hourAndAHalf.subtract({ hours: 1 }); // => PT30M
同樣,這些算術(shù)除了除了 Timezone 和 Calendar 類型外,其他類型都適用。
如 PlainDateTime 類型:
dt = Temporal.PlainDateTime.from('1995-12-07T03:24:30.000003500');
dt.add({ years: 20, months: 4, nanoseconds: 500 });
// => 2016-04-07T03:24:30.000004
Temporal類型之間的轉(zhuǎn)換
Temporal的各種類型,除了完成自身的功能外,還可以 類型轉(zhuǎn)換。
再次回看這個類型關(guān)系圖,左側(cè)黃色區(qū)域的 Instant 類型,用來表達某個瞬間的時間,不包含時區(qū)和日歷的信息。
右側(cè)黃色區(qū)域的 PlainXX系列(5個),用來表達日歷日期或者鐘表時間,包含日歷信息,而中間的 ZonedDateTime 則橫跨左右兩個區(qū)域,包含時區(qū)和日歷信息,可以作為一個通道,連接左側(cè)的 Instant 和右側(cè)的 Plain系列,負(fù)責(zé)類型之間轉(zhuǎn)換的橋梁,同時中間的 Timezone 時區(qū)類型 Calendar 日歷類型,不單獨使用,配合上方的 ZonedDateTime 類型來輔助轉(zhuǎn)換。
最下面的 Duration 與所有類型沒有直接關(guān)系,不參與類型轉(zhuǎn)換,表示一段持續(xù)時間,并且這段時間可以用來進行算術(shù)。
Instant => ZonedTimeDate
轉(zhuǎn)換前
Temporal.Instant.from('2020-08-05T20:06:13+0900').toString()
//2020-08-05T11:06:13Z
轉(zhuǎn)換后
Temporal.Instant.from('2020-08-05T20:06:13+0900').toZonedDateTimeISO('Asia/Tokyo').toString();
//2020-08-05T20:06:13+09:00[Asia/Tokyo]
ZonedTimeDate => Instant
轉(zhuǎn)換前
Temporal.ZonedDateTime.from('2020-11-01T01:45-07:00[America/Los_Angeles]').toString();
//2020-11-01T01:45:00-07:00[America/Los_Angeles]
轉(zhuǎn)換后
Temporal.ZonedDateTime.from('2020-11-01T01:45-07:00[America/Los_Angeles]').toInstant().toString();
//2020-11-01T08:45:00Z
ZonedTimeDate => PlainDateTime
轉(zhuǎn)換前
Temporal.ZonedDateTime.from('2020-11-01T01:45-07:00[America/Los_Angeles]').toString()
//2020-11-01T01:45:00-07:00[America/Los_Angeles]
轉(zhuǎn)換后
Temporal.ZonedDateTime.from('2020-11-01T01:45-07:00[America/Los_Angeles]').toPlainDateTime().toString();
//2020-11-01T01:45:00
PlainDateTime => ZonedTimeDate
轉(zhuǎn)換前
Temporal.PlainDateTime.from('2020-08-05T20:06:13').toString()
//2020-08-05T20:06:13
轉(zhuǎn)換后
Temporal.PlainDateTime.from('2020-08-05T20:06:13').toZonedDateTime('Asia/Tokyo').toString();
//2020-08-05T20:06:13+09:00[Asia/Tokyo]
總結(jié)
回到最開始 Date 的問題。
1.不支持除用戶本地時間以外的時區(qū)。Temparal 支持開發(fā)人員通過 TimeZone 來設(shè)置本地時間以外的時區(qū)。
2.計算 API 缺失。除了時區(qū)和日歷類型外,其他類型都可以進行 算術(shù)運算,即時間的比較,增加,減少等。
3.不支持非公歷,Calendar 類型支持 Temparal 選擇日歷。
4.解析器行為不可靠以至于無法使用,在 Temporal 里,new 構(gòu)造函數(shù)() 或者From 方法,對參數(shù)的要求都更加規(guī)范,同時From 方法支持 日期溢出 后的邏輯處理,可以防止系統(tǒng)崩潰。
最后附一張 Temparal 各種類型的功能對照圖。每個類型負(fù)責(zé) Temparal 哪些功能已經(jīng)標(biāo)注清楚。
網(wǎng)頁標(biāo)題:取而代之!以后不用再newDate()了
網(wǎng)站路徑:http://m.5511xx.com/article/djposje.html


咨詢
建站咨詢
