Temporal.Duration 规范用法

后端突然告诉我返回给我的时间用了一个新的规范,我展示的时候突然发现这个规范蛮有意思,算是一个新的规范,展示到页面的时候也思考了很多,记录一下子~(注:此 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 还有两个属性:signblank

  • sign:只有三个值:-101,代表 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~

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值