后端突然告诉我返回给我的时间用了一个新的规范,我展示的时候突然发现这个规范蛮有意思,算是一个新的规范,展示到页面的时候也思考了很多,记录一下子~(注:此 blog 主要目的仅是供自己记录,所以写的不会太细致!假如博友有幸看到并有疑问,欢迎留言交流~~)
--------~--------
因为我工作时用到的就是时间范围的展示,所以详细记录一下怎么展示范围。首先后端返回的格式为类似 P1YT2D2M30S
的格式,先要转换成一个 duration 对象,解析推荐使用:Temporal Polyfill
.
import { Temporal } from '@js-temporal/polyfill';
const duration = Temporal.Duration.from(data)
转换后的对象:
发现后端返回的时间不可以直接展示成时间,毕竟展示 3600s 挺奇怪的哈哈,所以我还单纯考虑了一下,怎么把秒自动转换成小时或分钟,比如 3600s 转换成 1h,62s 转换成 1min2s,还要考虑 42 天转换成 1个月2天等等,发现会很复杂,这才想到了应该会提供 API 转换,就看文档,果不其然找到了!(这里就体现了敲代码时间久了就会有的感觉,也是经验吧,很显然我没有~ 😭)。
官方文档特意提到了一个概念:Duration balancing
就是针对这个情况提出的,并给出了解决办法:duration.round()
方法,可以任意转换时间范围的单位,在后端返回的字符串或相加减之后出现时间范围不符合我们的常规标准时,最后一步使用 round()
来规范对象的展示,同样,因为涉及到年份、月份等的差别,也可以使用 relativeTo
来设置起始日期:
const transformDuration = date =>
Temporal.Duration.from(date).round({ largestUnit: 'year' });
const tripDurationDate = transformDuration(data);
这时,我们的 duration
对象就变成了:
接下来就是展示了,使用的 if
判断法,因为 duration.toLocaleString
还不能用,原因如下~(这里又涉及到了之前没听过的一个对象:Intl
,有时间我去看看。)
还想着找一下源码,但是源码也没找到,因为这个方法是基于 Intl.DurationFormat
这个方法实现的,但MDN 还没收录 Intl.DurationFormat
这个方法。
顺便也写一下 Temporal.Duration
的其他方法,参考内容全部来自官方文档~
一、先说 Temporal 是什么
Temporal 是一个全局对象,是 tc39 组织针对日期 / 时间 API 新提出的一个提议,旨在解决一些之前 Date 对象的痛点:
- 提供一些更便捷的日期时间计算的 API;
- 支持所有时区,包括 DST- 安全算法;
- 只处理表示固定日期和时间的对象;
- 严格解析指定的字符串格式;
- 支持非公立
Temporal 为纯日期、时间和其他限定作用域提供了单独的 ECMAScript 类。这样可以提高代码的可读性,并防止由于错误地为实际上是未知的值但假定 0、UTC 或本地时区而导致的 bug。
二、一张图解释清楚 Temporal 的构成
这张图清晰的展示了这些 API 的用法,可以转换成自己想要的形式。
三、Temporal.Duration
:时间范围
因为这次工作中只用到了 Temporal.Duration
,其实还有很多日期和时间的方法,但我就不说了,只说时间范围。
学习一个新东西,老套路:看看他的参数以及返回值是什么,就大致明白是什么了。(直接截屏官方文档,因为我懒)
以上所有的参数都是可选的,任何丢失或未定义的参数都为 0,所有非整数数值的参数都四舍五入为整数。
除了上述属性,duration
还有两个属性:sign
和 blank
:
sign
:只有三个值:-1
、0
、1
,代表duration
的正负或 0;blank
:代表该duration
是否有值,duration.blank === (duration.sign === 0)
知道是什么之后,再去看看他有哪些函数方法,就知道他能干什么事了。
-
duration.with(durationLike: Obgect): Temporal.Duration
参数:Temporal.Duration
的参数,可以是全部,也可以是部分
返回值:一个新的Temporal.Duration
对象
这个方法创建一个新的 duration 副本的Temporal.Duration
对象,(就是复制一份~ )with
的参数可以覆盖原本duration
上已经存在的属性。感觉这个方法用处就是修改对象的值(因为duration
对象是 readonly~)。duration = Temporal.Duration.from({ months: 50, days: 50, hours: 50, minutes: 100 }); let { years, months } = duration; years += Math.floor(months / 12); months %= 12; duration = duration.with({ years, months }); // => P4Y2M50DT50H100M
-
duration.add()、duration.subtract()
这两个个函数就不用多介绍,顾名思义就是两个计算两个日期范围相加减,官方文档描述:是在
duration
上加减另外一个时间段,得到一个更长(短)的时间范围。另一个时间范围可以是{ hours: 5, minutes: 30 }
这样的Temporal.Duration
部分属性,也可以是符合规范的字符串:PT5H30M
,亦或是Temporal.Duration
对象 。要注意的是,在进行years、months、weeks
相加减时,要使用relativeTo
来提供一个起始时间,因为有的年份是 365 天,有的是 366 天,月份天数也不相同。
相加减负数的duration
时,就相当于相减加duration
的绝对值。hourAndAHalf = Temporal.Duration.from('PT1H30M'); hourAndAHalf.subtract({ hours: 1 }); // => PT30M one = Temporal.Duration.from({ minutes: 180 }); two = Temporal.Duration.from({ seconds: 30 }); one.subtract(two); // => PT179M30S one.subtract(two).round({ largestUnit: 'hour' }); // => PT2H59M30S // Example of converting ambiguous units relative to a start date threeMonths = Temporal.Duration.from({ months: 3 }); oneAndAHalfMonth = Temporal.Duration.from({ months: 1, days: 15 }); /* WRONG */ threeMonths.subtract(oneAndAHalfMonth); // => throws threeMonths.subtract(oneAndAHalfMonth, { relativeTo: '2000-02-01' }); // => P1M16D threeMonths.subtract(oneAndAHalfMonth, { relativeTo: '2000-03-01' }); // => P1M15D hourAndAHalf = Temporal.Duration.from('PT1H30M'); hourAndAHalf.subtract({ hours: 1 }); // => PT30M one = Temporal.Duration.from({ minutes: 180 }); two = Temporal.Duration.from({ seconds: 30 }); one.subtract(two); // => PT179M30S one.subtract(two).round({ largestUnit: 'hour' }); // => PT2H59M30S // Example of converting ambiguous units relative to a start date threeMonths = Temporal.Duration.from({ months: 3 }); oneAndAHalfMonth = Temporal.Duration.from({ months: 1, days: 15 }); /* WRONG */ threeMonths.subtract(oneAndAHalfMonth); // => throws threeMonths.subtract(oneAndAHalfMonth, { relativeTo: '2000-02-01' }); // => P1M16D threeMonths.subtract(oneAndAHalfMonth, { relativeTo: '2000-03-01' }); // => P1M15D
-
duration.negated() : Temporal.Duration
取反~duration.sign
的值也会跟着变d = Temporal.Duration.from('P1Y2M3DT4H5M6.987654321S'); d.sign; // 1 d.negated(); // -P1Y2M3DT4H5M6.987654321S d.negated().sign; // -1
-
duration.abs() : Temporal.Duration
取绝对值~ -
duration.total(totalOf: string | object) : number
用来算规定单位的总时间~ 例如:130 小时是多少秒// How many seconds in 130 hours and 20 minutes? d = Temporal.Duration.from({ hours: 130, minutes: 20 }); d.total({ unit: 'second' }); // => 469200 // How many 24-hour days is 123456789 seconds? d = Temporal.Duration.from('PT123456789S'); d.total({ unit: 'day' }); // 1428.8980208333332 // Find totals in months, with and without taking DST into account d = Temporal.Duration.from({ hours: 2756 }); d.total({ relativeTo: '2020-01-01T00:00+01:00[Europe/Rome]', unit: 'month' }); // => 3.7958333333333334 d.total({ unit: 'month', relativeTo: '2020-01-01' }); // => 3.7944444444444443
-
duration.toString(options?: object) : string
转换成字符串,可以规定转换后的最小单位(duration.toString({ smallestUnit: 'second' })
)或是保留几位小数(duration.toString({ fractionalSecondDigits: 4 })
)d = Temporal.Duration.from({ years: 1, days: 1 }); d.toString(); // => P1Y1D d = Temporal.Duration.from({ years: -1, days: -1 }); d.toString(); // => -P1Y1D d = Temporal.Duration.from({ milliseconds: 1000 }); d.toString(); // => PT1S // The output format always balances units under 1 s, even if the // underlying Temporal.Duration object doesn't. nobal = Temporal.Duration.from({ milliseconds: 3500 }); console.log(`${nobal}`, nobal.seconds, nobal.milliseconds); // => 'PT3.5S 0 3500' bal = nobal.round({ largestUnit: 'year' }); // balance through round console.log(`${bal}`, bal.seconds, bal.milliseconds); // => 'PT3.5S 3 500' d = Temporal.Duration.from('PT59.999999999S'); d.toString({ smallestUnit: 'second' }); // => PT59S // 保留几位小数 d.toString({ fractionalSecondDigits: 0 }); // => PT59S d.toString({ fractionalSecondDigits: 4 }); // => PT59.9999S d.toString({ fractionalSecondDigits: 8, roundingMode: 'halfExpand' }); // => PT60.00000000S
-
duration.toJSON() : string
和toString()
方法差不多,不多赘述。const ban = { reason: 'cooldown', banDuration: Temporal.Duration.from({ hours: 48 }) }; const str = JSON.stringify(ban, null, 2); console.log(str); // => // { // "reason": "cooldown", // "banDuration": "PT48H" // } // To rebuild from the string: function reviver(key, value) { if (key.endsWith('Duration')) return Temporal.Duration.from(value); return value; } JSON.parse(str, reviver);
-
duration.toLocaleString(locales?: string | array<string>, options?: object) : string
可以说是我最喜欢的一个方法了!很可惜现在还不能用,必须要求 js 环境支持Intl.DurationFormat
,很可惜,今年 1 月份才到了 stage3 草稿阶段。这个方法可以把对象转换成我们页面可以展示的字符串。d = Temporal.Duration.from('P1DT6H30M'); d.toLocaleString(); // example output: '1 day 6 hours 30 minutes' d.toLocaleString('de-DE'); // example output: '1 Tag 6 Stunden 30 Minuten' d.toLocaleString('en-US', { day: 'numeric', hour: 'numeric' }); // example output: '1 day 6 hours'
over~