Date背景
Date存在的问题不支持除用户本地时间以外的时区
不支持开发人员通过API来切换时区信息。
解析器行为不可靠以至于无法使用newDate();newDate(value);newDate(dateString);newDate(year,monthIndex[,day[,hours[,minutes[,seconds[,milliseconds]]]]]);
开发人员常常因为输入的参数格式问题,引发时间错误,导致程序崩溃。比如输入(‘2022-02-22′)和(2022,02,22)得到的结果却不同,
计算 API 缺失
涉及时间的运算逻辑通常都需要开发人员自己去写,比如比较两个时间的长短,时间之间的加减运算,没有自己的计算API
不支持非公历
除了全球通用的公历外,无法使用各国的自己的历法。比如中国的农历
Temporal的诞生
感兴趣的同学可以去博客[1]阅读更多细节,该网页的控制台已经支持Temporal对象或者在本地运行,安装Temporal的 polyfill
$npminstall@js-temporal/polyfillimport{Temporal}from’@js-temporal/polyfill’;
Temporal是一个全局对象,像Math、Promise一样位于顶级命名空间中,为Javascript语言带来了现代化的日期、时间接口。
WechatIMG258.jpeg
如图所示,一个全面的Temporal包含三个部分:
绿色区域为ISO 8601格式的日期和时间;
黄色区域为时区(日本东京);
红色区域为日历(日本历法);
ISO 8601格式:国际通用时间格式,T用来分割日期(2020-08-05)和时间(20:06:13),“ ”或者“-”分别代表东时区和西时区。 09:00,代表东九区。
对比 Date
newDate()//FriJan28202217:03:11GMT 0800(中国标准时间)
Date采用 GMT格式(旧的时间表示格式) 的时间,使用方面不如 ISO 8601 通用,同时不包含时区和历法。
Temporal各种类型介绍
推翻重新设计的Temporal,包含5种主要类型,每个类型负责不同的功能,类型之间还可以相互进行转换。
学会了这5种类型的功能以及类型的之间的关系,就基本掌握了Temporal。
下面是Temporal各种类型的功能与转换关系图,非常重要,十分有利于我们全面理解和使用Temporal,下文将逐步讲解。
exxqq.png
ZonedDateTime
定义:最全面的Temporal类型,与时区和日历都有关联。表示从地球上特定区域的角度来看,在特定时刻发生的事。
使用场景:在北京时间的2008年5月12日14时28分4秒发生汶川大地震,或者 在纽约时间的2008年5月12日01时28分4秒发生了汶川大地震。
如何获得一个 ZonedDateTime 类型?
不仅是获得一个 ZonedDateTime 类型,其实所有的 Temporal类型都是一样的获取途径。通常有两种方法获得,分别是new 构造函数(),和from方法。
new 构造函数()方式
参数:(纳秒数,时区,日历),不同类型要求的参数不同。
纳秒数:从Unix 纪元(1970 年 1 月 1 日午夜 UTC)计算,所经过的纳秒数,单位为bigint
时区,日期:可以是字符串,也可以是Temporal类型。
newTemporal.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
调用toString()用字符串来表达,方便阅读。
newTemporal.ZonedDateTime(0n,’Asia/Shanghai’,’chinese’).toString();//1970-01-01T08:00:00 08:00[Asia/Shanghai][u-ca=chinese]
这个ZonedDateTime的类型含义为,从北京时间看,unix纪元起始时间为1970-01-01T08:00:00 08:00,而非1970-01-01T00:00:00 00:00
from()形式获得一个Temporal类型
from()的参数更为多样,同时支持溢出处理(下文有讲解),所以通常作为首选方法。通常作为获得一个Temporal类型的首选方法。
接受字符串
Temporal.ZonedDateTime.from(‘2022-02-28T00:00:00 08:00[Asia/Shanghai]’).toString();//2022-02-28T00:00:00 08:00[Asia/Shanghai]
or
接受对象,({时区,日期,日历},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:valueoutofrange:1<=31<=28
Instant
定义:负责单个时间点(称为“精确时间”),精度以纳秒为单位。不存在时区和日历信息。
使用场景:2020-01-23T17:04:36.491865121-08:00,只用来表达一个瞬间的时间,没有其他意义。
获得一个Instant类型
new Temporal.Instant( bigint )
bigint:纳秒数,从Unix 纪元(1970 年 1 月 1 日午夜 UTC)计算,所经过的纳秒数,单位为bigint
newTemporal.Instant(1553906700000000000n);//2019-03-30T00:45:00ZnewTemporal.Instant(0n);//1970-01-01T00:00:00ZnewTemporal.Instant(-2208988800000000000n);//1900-01-01T00:00:00Z
Z在ISO 8601时间格式表示,没有时区关联。
Temporal.Instant.from(thing: any)
from方法在生成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’);
虽然前两个携带了时区信息,但获取到的Instant 时间值相同,三个都是2019-03-30T00:45Z
Plain XX系列
负责Temporal的日历日期(xx年xx月xx日) 和钟表时间(xx点xx分xx秒)表达,不涉及时区,
使用场景:日历日期:小红的生日是农历每年3月25。钟表时间:现在是下午2:00
对比Instant,双方的使用场景不同,内部的属性也不同,Instant不包含时区和日期,Plain XX系列则包含日历。
Plain XX系列包含 5种 类型,覆盖最广的PlainDateTime包含日期和时间,还有只包含日期的Plaindate和只包含时间的Plaintime。日期类型里,还有分类更精细的PlainYearMonth(年月)和PlainMonthDay(月天)
以PlainDateTime举例,其他的类同。
获取一个PlainDateTime
new Temporal.PlainDateTime(year,month,day…)
参数以年 -> 纳秒顺序排列,其中 年 月 日,为必填项,其余选填。
newTemporal.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
定义:负责Temporal时区的相关信息。
例子:北京时区,东八区,不单独使用,通常结合其他类型搭配。
获取一个TimeZone类型
new Temporal.TimeZone(string)
string:对一个时区的描述
//东八区,即北京时间newTemporal.TimeZone(‘8:00’);//直接字符串描述,前提是Temporal内部有定义newTemporal.TimeZone(‘Asia/Shanghai’);//Asia/Shanghai
from同理
Temporal.TimeZone.from(‘Asia/Shanghai’);//Asia/Shanghai
在和其他类型搭配时,可以直接使用字符串(“Asia/Shanghai”),或者 Temporal.TimeZone对象
例子:
获取一个ZonedDateTime类型,设置时区时,使用Temporal.TimeZone对象。
newTemporal.ZonedDateTime(0n,Temporal.TimeZone.from(‘Asia/Shanghai’));//1970-01-01T08:00:00 08:00[Asia/Shanghai]等价于newTemporal.ZonedDateTime(0n,’Asia/Shanghai’));
Calendar
定义:负责Temporal的日历系统。
例子:中国农历。不单独使用,结合其他类型搭配。
获取一个Calendar类型
同TimeZone类同,new Calendar(string)或者Temporal.Calendar.from(string)
newTemporal.Calendar(‘chinese’).toString();//chineseTemporal.Calendar.from(‘chinese’).toString();//chinese
Calendar类型不会单独使用,要配合其他带有日历属性的类型使用。
如上所述,在 Temporal 里,包含日历属性的有plainXX系列和ZonedDateTime
这两种类型的原型上有一个withCalendar的方法,用来设置该日期的日历属性。
例子:
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
定义:表示一段持续时间,并且这段时间可以用来进行算术。
使用场景:两段时间,一小时一分钟和一小时十分钟,可以转换Duration类型,再进行时间的长度比较,从而得知前者的时长 小于 后者。
Duration并非像Date的时间戳形式那样表达一段时间,而是根据ISO 8601表示法生成一个字符串来表达一段时间。
简而言之,ISO 8601表示法的首字母必须由P开头,后跟日期,年、月、周和日 再由T字母进行分割,后跟时间,小时、分钟、 秒。
一个Duration字符串可以缺失年 / 月 / 周 / 日 / 小时 / 分 / 秒中的任意一个,但必须包含首字母P,如果同时有小时 / 分 / 秒,则必须包含字母T.
举例:
一年:P1Y,必须保留P,没有时间信息,不用加T来分割。
一分钟:PT1M,必须保留P,有时间信息,则加T来分割日期和时间
一些Duration的字符串表达练习
JUEJIN.png获得一个Duration类型
new Temporal.Duration()
参数:年 => 纳秒,全部可选,非必填。需要按照顺序输入,某单位空缺则输入undefined或者0。
newTemporal.Duration(1,2,3,4,5,6,7,987,654,321);//P1Y2M3W4DT5H6M7.987654321S//中文翻译=>1年2月3周4天5小时6分钟7秒987毫秒654微秒321纳秒newTemporal.Duration(0,0,0,40);//P40D中文翻译=>40天Temporal.Duration.from(undefined,undefined,undefined,40);//P40DnewTemporal.Duration();//PT0S
了解了Duration的字符串含义以及怎么生成一个Duration后,可以用其进行一些日期与时间的计算与运算。
比对日期或时间的长度大小
调用Duration原型上的compare方法。返回值:-1, 0, 1
one=Temporal.Duration.from({hours:79,minutes:10});//PT1H10Mtwo=Temporal.Duration.from({days:3,hours:7,seconds:630});//P3DT7H630STemporal.Duration.compare(one,two)//-1
返回-1,则one比two的时间短
返回0,则one比two的时间一样
返回1,则one比two的时间长
事实上,除了Timezone和Calendar类型外,所有具备日期和时间属性的类型都可以进行算术
如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’);//PT1Hhour.add({minutes:30});//=>PT1H30M
减法:
hourAndAHalf=Temporal.Duration.from(‘PT1H30M’);//PT1H30MhourAndAHalf.subtract({hours:1});//=>PT30M
同样,这些算术除了除了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.000004Temporal类型之间的转换
Temporal的各种类型,除了完成自身的功能外,还可以类型转换。
exxqq.png
再次回看这个类型关系图,左侧黄色区域的Instant类型,用来表达某个瞬间的时间,不包含时区和日历的信息。右侧黄色区域的PlainXX系列(5个),用来表达日历日期或者钟表时间,包含日历信息,而中间的ZonedDateTime则横跨左右两个区域,包含时区和日历信息,可以作为一个通道,连接左侧的Instant和右侧的Plain系列,负责类型之间转换的桥梁,同时中间的Timezone时区类型Calendar日历类型,不单独使用,配合上方的ZonedDateTime类型来辅助转换。最下面的Duration与所有类型没有直接关系,不参与类型转换,表示一段持续时间,并且这段时间可以用来进行算术。
Instant => ZonedTimeDate
转换前Temporal.Instant.from(‘2020-08-05T20:06:13 0900’).toString()//2020-08-05T11:06:13Z转换后Temporal.Instant.from(‘2020-08-05T20:06:13 0900’).toZonedDateTimeISO(‘Asia/Tokyo’).toString();//2020-08-05T20:06:13 09:00[Asia/Tokyo]
ZonedTimeDate => Instant
转换前Temporal.ZonedDateTime.from(‘2020-11-01T01:45-07:00[America/Los_Angeles]’).toString();//2020-11-01T01:45:00-07:00[America/Los_Angeles]转换后Temporal.ZonedDateTime.from(‘2020-11-01T01:45-07:00[America/Los_Angeles]’).toInstant().toString();//2020-11-01T08:45:00Z
ZonedTimeDate => PlainDateTime
转换前Temporal.ZonedDateTime.from(‘2020-11-01T01:45-07:00[America/Los_Angeles]’).toString()//2020-11-01T01:45:00-07:00[America/Los_Angeles]转换后Temporal.ZonedDateTime.from(‘2020-11-01T01:45-07:00[America/Los_Angeles]’).toPlainDateTime().toString();//2020-11-01T01:45:00
PlainDateTime => ZonedTimeDate
转换前Temporal.PlainDateTime.from(‘2020-08-05T20:06:13’).toString()//2020-08-05T20:06:13转换后Temporal.PlainDateTime.from(‘2020-08-05T20:06:13’).toZonedDateTime(‘Asia/Tokyo’).toString();//2020-08-05T20:06:13 09:00[Asia/Tokyo]总结
回到最开始Date的问题。
1.不支持除用户本地时间以外的时区。Temporal支持开发人员通过TimeZone来设置本地时间以外的时区。
2.计算 API 缺失。除了时区和日历类型外,其他类型都可以进行算术运算,即时间的比较,增加,减少等。
3.不支持非公历,Calendar类型支持Temporal选择日历。
4.解析器行为不可靠以至于无法使用,在Temporal里,new 构造函数()或者From方法,对参数的要求都更加规范,同时From方法支持日期溢出后的逻辑处理,可以防止系统崩溃。
最后附一张Temporal各种类型的功能对照图。每个类型负责Temporal哪些功能已经标注清楚。
功能图.png参考资料[1]
博客:https://tc39.es/proposal-temporal/docs/
— EOF —
推荐↓↓↓