当前位置: 首页 > 科技观察

反而!以后不再有newDate()了

时间:2023-03-14 15:49:48 科技观察

日期背景众所周知,1995年,Brendan(JavaScript之父)被Netscape分派了一项艰巨而紧迫的任务,要在10天内编写出JavaScript语言。日期处理是几乎所有编程语言的基本组成部分,因此JavaScript也必须具备它。这是一个非常复杂的领域,但是作者却很少有时间去写它。最终,Brendan选择借鉴当时流行的java语言,从java.Util.Date日期实现中复制了Javascript日期对象。坦率地说,这个实现很糟糕。事实上,Java在两年后的1.1版本中弃用并替换了这个实现。然而20年后,我们仍然在JavaScript编程语言中使用这个API。日期问题不支持用户本地时间以外的时区,不支持开发者通过API切换时区信息。解析器行为是如此不可靠以至于newDate();新日期(值);新日期(日期字符串);newDate(year,monthIndex[,day[,hours[,minutes[,seconds[,milliseconds]]]]]]);开发者经常会因为输入参数格式问题导致时间错误,导致程序崩溃。比如输入('2022-02-22')和(2022,02,22)得到的结果是不一样的,与缺少计算API相关的计算逻辑通常需要开发者自己写,比如如比较两个时间的长度,时间之间的加减法,没有自己的计算API,不支持非公历除了世界通用的公历外,不能使用自己的各国日历.比如在中国农历诞生时,为了弥补Date的不足,很多程序员开始开发一些开源库来绕过Date的直接使用。date.js、moment.js等优秀的npm库,但Date的问题始终困扰着Javascript的进一步发展,TC39组织开始对Date进行升级改造。他们找到了moment.js库的作者Maggie,她是新功能Temporal的主要设计者。有兴趣的同学可以去博客[1]阅读更多细节。该网页的控制台已经支持Temporal对象或在本地运行。安装Temporalpolyfill$npminstall@js-temporal/polyfillimport{Temporal}from'@js-temporal/polyfill';Temporal是一个全局对象,与Math和Promise一样位于顶级命名空间中,为Javascript语言带来了现代日期和时间接口。如图所示,一个综合的Temporal包含三个部分:绿色区域是ISO8601格式的日期和时间;黄色区域为时区(日本东京);红色区域是日历(日本日历);ISO8601格式:国际通用时间格式,T用于分隔日期(2020-08-05)和时间(20:06:13),“+”或“-”分别代表东部时区和西部时区.+09:00,代表东九区。CompareDatenewDate()//FriJan28202217:03:11GMT+0800(ChinaStandardTime)Date使用GMT格式(旧时间表示格式)的时间,在时间方面不像ISO8601那样通用用法,并且不包括时区和日历。各种类型的Temporal介绍颠覆和重新设计的Temporal,它包含5个主要类型,每个类型负责不同的功能,类型之间也可以相互转换。学习完这5种类型的作用和类型之间的关系,你就基本掌握了Temporal。下面是各种类型的Temporal的功能和转换图,对于我们全面理解和使用Temporal非常重要,非常有帮助,下面会一步步讲解。ZonedDateTime定义:最全面的Temporal类型,与时区和日历相关联。从地球上特定区域的角度指示在特定时刻发生的事情。情景:汶川地震发生在北京时间2008年5月12日14:28:4,或者汶川地震发生在纽约时间2008年5月12日01:28:4。如何获得ZonedDateTime类型?不仅仅是获取一个ZonedDateTime类型,其实所有的Temporal类型都是用同样的方式获取的。通常有两种获取方式,分别是newconstructor(),和from方法。newconstructor()方法参数:(纳秒,时区,日历),不同的类型需要不同的参数。Nanoseconds:从Unix纪元(UTC时间1970年1月1日午夜)开始计算,经过的纳秒数,单位是bigint时区,date:可以是字符串或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]thisZonedDateTime的类型含义是,从北京时间开始,Unix时代开始时间为1970-01-01T08:00:00+08:00,不是1970-01-01T00:00:00+00:00from()一个Temporal类型的from()有更多不同的参数并且支持溢出处理(下面解释),所以它通常是首选方法。通常用作获取时间类型的首选方法。接受字符串Temporal.ZonedDateTime.from('2022-02-28T00:00:00+08:00[亚洲/上海]').toString();//2022-02-28T00:00:00+08:00[Asia/Shanghai]oracceptsobjects,({timezone,date,calendar},options)options表示容错机制配置,即可以处理输入日期溢出问题,有两个配置选项,{overflow:'constrain'}:automaticHandleoverflow。{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]选择拒绝配置,超过日期会报错。Temporal.ZonedDateTime.from({timeZone:'Asia/Shanghai',year:2022,month:2,day:31},{overflow:'reject'}).toString();//RangeError:valueoutofrange:1<=31<=28Instant的定义:负责单个时间点(称为“精确时间”),精度为纳秒。缺少时区和日历信息。使用场景:2020-01-23T17:04:36.491865121-08:00,仅用于表示瞬间时间,无其他含义。getanInstanttypenewTemporal.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(-2208988800000000000n00-1/0-0-001-01T00:00:00ZZ表示ISO8601格式的时间,没有时区关联。Temporal.Instant.from(thing:any)from方法在生成Instant时会考虑时区的偏差。Temporal.Instant.from('2019-03-30T01:45:00+01:00[欧洲/柏林]');Temporal.Instant.from('2019-03-30T01:45+01:00');时间.Instant.from('2019-03-30T00:45Z');前两个虽然携带了时区信息,但是得到的Instant时间值是一样的,三个都是2019-03-30T00:45Z。PlainXX系列负责Temporalcalendardate(xx年??xx月xx日)和clocktime(xx点xx分xx秒)的表达,不分时区,使用场景:calendardate:小红生日三月每年农历二十五。时钟时间:现在是下午2:00与Instant相比,双方的使用场景不同,内部属性也不同。Instant不包括时区和日期,而PlainXX系列包括日历。PlainXX系列包括5种类型,覆盖范围最广的PlainDateTime包括日期和时间,还有只有日期的Plaindate和只有时间的Plaintime。在日期类型中,还有更细分类的PlainYearMonth(年月)和PlainMonthDay(月日)。以PlainDateTime为例,其他类似。获取一个PlainDateTimenewTemporal.PlainDateTime(year,month,day...)参数按照年->纳秒的顺序排列,其中年月日为必填项,其余可选。newTemporal.PlainDateTime(2020,3,14,13,37)//2020-03-14T13:37:00Temporal.PlainDateTime.from()Temporal.PlainDateTime.from({年:2001,月:1,日:1,hour:25,calendar:'chinese'},{overflow:'constrain'}).toString()//2001-01-24T23:00:00[u-ca=chinese]TimeZone定义:负责Temporal时区信息。示例:北京时区,东八区,不单独使用,通常与其他类型组合使用。获取一个TimeZone类型newTemporal.TimeZone(string)string:一个时区的描述//东八区,即北京时间newTemporal.TimeZone('8:00');//直接字符串描述,前提是Temporalinternal有一个定义newTemporal.TimeZone('Asia/Shanghai');//Asia/Shanghaifrom和Temporal.TimeZone.from('Asia/Shanghai');//Asia/Shanghai时可以直接使用字符匹配其他类型的String("Asia/Shanghai"),或Temporal.TimeZone对象例子:得到一个ZonedDateTime类型,在设置时区时,使用Temporal.TimeZone对象。newTemporal.ZonedDateTime(0n,Temporal.TimeZone.from('亚洲/上海'));//1970-01-01T08:00:00+08:00[Asia/Shanghai]相当于newTemporal.ZonedDateTime(0n,'Asia/Shanghai'));Calendar定义:负责Temporal的日历系统。示例:中国农历。不能单独使用,要与其他类型结合使用。获取类似TimeZone的Calendar类型,newCalendar(string)或Temporal.Calendar.from(string)newTemporal.Calendar('chinese').toString();//chineseTemporal.Calendar.from('chinese')。toString();//chineseCalendar类型不会单独使用,应该和其他带有日历属性的类型一起使用。上面提到,在Temporal中,plainXX系列和ZonedDateTime这两个包含calendar属性的原型上都有一个withCalendar方法,用于设置日期的calendar属性。示例:plainXX系列添加日历属性不添加日历属性Temporal.PlainDate.from('2019-02-06');//添加日历属性后2019-02-06Temporal.PlainDate.from('2019-02-06').withCalendar('chinese');//2019-02-06[u-ca=chinese]ZonedDateTimeTemporal.ZonedDateTime.from('2022-02-28T00:00:00+08:00[亚洲/上海]')//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的时间戳形式那样表达一个时间段,而是按照ISO8601表示法生成一个字符串来表达一个时间段。简而言之,ISO8601表示法的首字母必须以P开头,后面是日期、年、月、星期和日,中间用T字母分隔,然后是时间、小时、分钟、秒。Duration字符串可以缺少年/月/周/日/时/分/秒中的任意一个,但必须包含首字母P,如果同时有小时/分/秒,则必须包含字母T.例子:一年:P1Y,P必须预留,没有时间信息,不用加T分。一分钟:PT1M,必须保留P,如果有时间信息,则添加T拆分日期和时间一些Duration字符串表达式练习得到一个Duration类型newTemporal.Duration()参数:year=>nanoseconds,都是可选的,不需要。需要按顺序输入,如果一个单元空着,就输入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原型上的比较方法来比较Date或Time的长度。返回值:-1,0,1one=Temporal.Duration.from({hours:79,minutes:10});//PT1H10Mtwo=Temporal.Duration.from({days:3,hours:7,seconds:630});//P3DT7H630STemporal.Duration.compare(one,two)//-1返回-1,然后一比二短返回0,然后一等于二返回-1,然后一比二长事实上,除了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({分钟:30});//=>PT1H30M减法:hourAndAHalf=Temporal.Duration.from('PT1H30M');//PT1H30MhourAndAHalf。减去({小时:1});//=>PT30M同样,这些算法适用于除Timezone和Calendar之外的所有类型。如PlainDateTime类型:dt=Temporal.PlainDateTime.from('1995-12-07T03:24:30.000003500');dt.add({年:20,月:4,纳秒:500});//=>2016-04-07T03:24:30.000004Temporal类型之间的转换各种类型的Temporal除了完成自己的功能外,还可以进行类型转换。再回头看这张类型图,左边黄色区域的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('亚洲/东京').toString();//2020-08-05T20:06:13+09:00[亚洲/东京]ZonedTimeDate=>Temporal.ZonedDateTime.from('2020-11-01T01:45-07:00[美国/洛杉矶]').toString();//2020-11-01T01:45:00-07:00[美国/洛杉矶]Temporal.ZonedDateTime.from('2020-11-01T01:45-07:00[America/Los_Angeles]').toInstant().toString();//2020-11-01T08:45:00ZZonedTimeDate=>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:00PlainDateTime=>转换前的ZonedTimeDateTemporal.PlainDateTime。from('2020-08-05T20:06:13').toString()//转换后的2020-08-05T20:06:13Temporal.PlainDateTime.from('2020-08-05T20:06:13').toZonedDateTime('Asia/Tokyo').toString();//2020-08-05T20:06:13+09:00[亚洲/Tokyo]总结回到原来的Date问题1.不支持用户本地时间以外的时区。Temparal支持开发者通过TimeZone设置本地时间以外的时区。2.缺少计算API。除了时区和日历类型,其他类型都可以进行算术运算,即时间的比较、增加、减少等。3、不支持非公历,Calendar类型支持Temparal选择日历。4.解析器的行为太不可靠,无法使用。在Temporal中,newconstructor()或者From方法对参数的要求更加规范。同时From方法支持日期溢出后的逻辑处理,可以防止系统崩溃。最后附上各款Temparal的功能对比图。每种类型负责Temporal的哪些功能,都有明确标示。