服务粉丝

我们一直在努力
当前位置:首页 > 财经 >

主流时间库横向对比

日期: 来源:字节前端 ByteFE收集编辑:俞泽霖

1 背景

State of Frontend 2022:https://tsh.io/state-of-frontend/

根据《State of Frontend 2022》问卷调查, 最受欢迎的前五个工具库中,时间处理相关的库占据了两席。时间处理工具为什么如受前端工程师青睐?JS Date 为什么无法满足开发需求?不同的时间库之间又存在哪些差异?

2 时间

2.1 计量标准

时间包含了时刻和时段两个概念,常规意义上的时段是可以通过两个时刻计算得到的,但地球自转的不稳定,这种方式得到的时段并不是恒定的,因而诞生了两种时间计量系统:

  1. 世界时(GMT):根据地球自转的天文测量得到,并不稳定。
  2. 原子时(IAT):根据原子的单次振动时间得到,可以认为是恒定不变的。

这两种时间尺度速率上的不同每一至二年会带来会差大约 1 秒的差异,因此每隔一定时间会在原子时的基础上增加或减少 1 秒,即闰秒,得到接近于世界时的时间,称为协调世界时(UTC)。

2.2 时区

全球被划分为 24 个时区。规定英国的格林威治天文台旧址所在经线为基准线,即本初子午线,所在时区为零时区,零时区以东为东 1-12 区,以西为西 1-12 区(东 12 区和西 12 区是重合的)。每差一个时区,区时相差一小时,越往东区时越早。为了让世界各地都有一个统一的参照时间,规定零时区的 UTC 时间作为标准时间,简称 UTC 时间。

3 new Date()

3.1 YYYY-MM-DD 的时区问题

  • 使用 new Date('YYYY-MM-DD') 实例化 Date 对象时,由于没有指定具体时刻,系统会自动设置一个时刻为 '00:00:00' 的 UTC 时间,并在用户访问时,返回系统时区的对应时间。如,位于东八区的开发者访问 Date 显示的时间是 UTC+8。位于西七区的开发者访问 Date 显示的时间是 UTC-7。也就是说,如果直接使用 new Date('YYYY-MM-DD') 设置时间,位于不同时区的用户会获取不同的结果,某些情况下会导致意想不到的 bug。
  • 使用 new Date('YYYY-MM-DD HH-MM-SS') 或其他官方推荐的日期格式实例化对象,如new Date(DD, MM YYYY),可以避免上述时区问题,创建当前时区下 00:00:00 的 Date 对象。
// 时区为中国
var date1 = new Date('2022-10-24')
// Mon Oct 24 2022 08:00:00 GMT+0800 (中国标准时间)

// 时区为美国
var date2 = new Date('2022-10-24')
// Sun Oct 23 2022 17:00:00 GMT-0700 (北美太平洋夏令时间)

// 使用 new Date('YYYY-MM-DD HH-MM-SS') 可以创建当前时区的 Date 对象
var date3 = new Date('2022-10-24 00:00:00')
// Mon Oct 24 2022 00:00:00 GMT+0800 (中国标准时间)

// 传入格式为'DD, MM YYYY'的日期
var date4 = new Date('10, 24 2022')
// Mon Oct 24 2022 00:00:00 GMT+0800 (中国标准时间)

3.2 Safari 的兼容性问题

Safari 只支持 YYYY/MM/DDMM/DD/YYYYMMMM DD, YYYY 格式的日期,使用 new Date('YYYY-MM-DD') 会报错。

3.3 无法解析或展示特定格式的日期

特定格式日期的解析需要借助正则表达式来完成。

// 通过正则表达式解析
const datePattern = /^(\d{2})-(\d{2})-(\d{4})$/;
const [, month, day, year] = datePattern.exec('10-24-2022');
new Date(`${month}, ${day} ${year}`);

4 主流时间库

4.1 MomentJS

  • 彻底解决解析问题和格式化问题

    const date1 = moment('2022-10-24');
    console.log(date1.format())   // 2022-10-24T00:00:00+08:00
    console.log(date1.toArray())  // [2022, 9, 24, 0, 0, 0, 0],注:月份的起始数为0
    console.log(date1.toJSON())   // 2022-10-23T16:00:00.000Z
  • 适配多种甚至自定义的格式写法

    const date2 = moment('10/24/2022', 'MM/DD/YYYY');
    const date3 = moment('2022-10-24-4-30', 'YYYY-MM-DD-HH-mm');
  • 包体积大:MomentJS 包体积十分庞大,接近 300kb,且基于 OOP(Object Oriented Programming)的设计需要先引入 moment 对象,再使用对象中的方法,导致无法通过 tree-shaking 压缩体积,引用后会打包所有方法,容易引发首屏加载的性能问题。

  • 时间对象是可变的(mutable):对时间对象的计算操作会改变对象本身,通常需要拷贝后操作。

    const startDate = moment(); // Sun Oct 23 2022 23:11:34 GMT+0800
    const endDate = startDate.add(1, 'year'); // Mon Oct 23 2023 23:11:34 GMT+0800
    console.log(startDate === endDate);   // true

目前 moment.js 由于历史包袱升级困难, 加上更好的替代品出现,已经停止维护。对于深度使用 moment.js 但希望更换时间库的项目,可以安装 eslint-plugin-you-dont-need-momentjs 来帮助升级。配置方式如下:

// package.json
"extends" : ["plugin:you-dont-need-momentjs/recommended"]

4.2 DayJS

是 Moment.js 的轻量化方案,拥有同样强大的 API,但包体积只有 6.5KB。

  • 不可变(Immutable)

  • 体积小。为了减小体积,day.js 将一些复杂功能抽离到插件中,使用时需额外引入

  • 拥有和 MomentJS 相同的 API,迁移成本低。但迁移时需注意:

    注 1:涉及到更改时间对象的操作,不能简单地替换。

    import moment from "moment";
    const timeEntity = moment();
    timeEntity.add(1, "d"); // 天数加1

    import dayjs from "dayjs";
    const timeEntity = moment();
    timeEntity = timeEntity.add(1, "d"); // 天数加1

    如果项目中大量依赖此类逻辑的话,Day.js 插件提供了适配方案用,虽然官方并不推荐。

    var badMutable = require('dayjs/plugin/badMutable')
    dayjs.extend(badMutable) // with BadMutable plugin
    const today = dayjs()
    today.add(1, 'day') // immutable

    注 2:一些特殊功能需要通过插件额外引入,并进行配置。

    import dayjs from "dayjs";
    import dayOfYear from "dayjs/plugin/dayOfYear";
    import objectSupport from "dayjs/plugin/objectSupport";

    // 配置插件
    dayjs.extend(dayOfYear);
    dayjs.extend(objectSupport);

    export default dayjs;

4.3 Date-fns

Date-fns 的 API 是基于 FP(Functional Programming)的,可以按需导入函数。

  • 不可变(Immutable)

  • 函数导入,但相比对象导入,调用不够灵活,每个工具函数都要从指定路径引入。

    export addDays from 'date-fns/addDays/index.js'
    const newDate = addDays(new Date(), 7);

    优化:将需要用到的工具函数引入到模块文件中,再对外暴露方法。

    // custom-date-fns.js
    export { default as add } from 'date-fns/add/index.js'
    export { default as addBusinessDays } from 'date-fns/addBusinessDays/index.js'
    export { default as addDays } from 'date-fns/addDays/index.js'
    export { default as addHours } from 'date-fns/addHours/index.js'
  • 支持 tree-shaking,某些情况下具有更小的引入体积
    add 操作,2KB

    format + add 操作,24.1KB

    parse 解析日期字符串,98.5KB

4.4 横向对比

5 总结

  • Native Date 无法直接解析自定义格式的时间字符串,且容易引入时区问题。不推荐
  • Moment.js 包体积过大,且时间对象存在 mutable 问题,源代码也早已停止维护。不推荐
  • Day.js 克服了 moment.js 的缺陷,且 api 与 moment.js 高度吻合,从 moment.js 迁移成本低。但是部分功能需要通过插件引入。推荐在不涉及 UTC 时间的情况下使用
  • Date-fns 同样克服了 moment.js 的缺陷,并支持 tree-shaking,单独使用某些功时,引入的包体积甚至小于 day.js。但需要从目标目录导入所需的工具函数,上手难度大。在引入了多种工具函数或涉及解析时间字符串时,还会导致包体积过大。推荐存在轻度需求时使用。


点击上方关注

我们下期再见

相关阅读

  • How to build your own React: Didact 解析

  • 背景作为前端开发工程师,学习自己平时使用的前端框架的源码是非常有帮助的,但是深入一个框架源码的学习,往往会遇到如下等诸多问题:源码的包和代码量太多,无从下手;源码太复杂,学习
  • rescript 学习笔记

  • rescript 介绍rescript 跟 typescript 类似,也是一门 js 方言。在现在 typescript 大流行的背景下,为什么会写这篇文档去介绍 rescript 呢?最初接触 rescript 的原因是惊讶于作
  • 技术周报·State of JS 2022 发布

  • 编辑推荐State of JS 2022 发布2023 年 1 月 11 日,2022 年度 StateOfJS 调查结果正式公布!此次问卷于 2022 年 11 月 21 日至 12 月 22 日进行,共收集了 39472 份问卷。此调查
  • 隧道行车大有文章!这些知识点必须牢记!

  • 文明守法 平安回家隧道行车,安全为上隧道是高速公路和城市道路上的特殊路段,由于其特殊性和封闭性,稍不留意一些安全细节,便可能酿成事故造成危险。因此,行车遇到隧道时,一定要慎
  • 当血液病患者遇上这个病毒感染……

  • 血液病患者免疫功能低下一旦在病程中遇上无论何种病毒都有可能会是“致命打击”尤其在遇到“狡猾”的新冠病毒后除了戴好口罩、做好手卫生外血液病患者又该如何做好自身防护

热门文章

  • “复活”半年后 京东拍拍二手杀入公益事业

  • 京东拍拍二手“复活”半年后,杀入公益事业,试图让企业捐的赠品、家庭闲置品变成实实在在的“爱心”。 把“闲置品”变爱心 6月12日,“益心一益·守护梦想每一步”2018年四

最新文章

  • 主流时间库横向对比

  • 1 背景State of Frontend 2022:https://tsh.io/state-of-frontend/根据《State of Frontend 2022》问卷调查, 最受欢迎的前五个工具库中,时间处理相关的库占据了两席。时间处理
  • How to build your own React: Didact 解析

  • 背景作为前端开发工程师,学习自己平时使用的前端框架的源码是非常有帮助的,但是深入一个框架源码的学习,往往会遇到如下等诸多问题:源码的包和代码量太多,无从下手;源码太复杂,学习
  • rescript 学习笔记

  • rescript 介绍rescript 跟 typescript 类似,也是一门 js 方言。在现在 typescript 大流行的背景下,为什么会写这篇文档去介绍 rescript 呢?最初接触 rescript 的原因是惊讶于作
  • 技术周报·State of JS 2022 发布

  • 编辑推荐State of JS 2022 发布2023 年 1 月 11 日,2022 年度 StateOfJS 调查结果正式公布!此次问卷于 2022 年 11 月 21 日至 12 月 22 日进行,共收集了 39472 份问卷。此调查
  • 2023,新春祝福语音

  • Hi,我是晓书童,明天就过大年了,新年好啊。年味淡了吗?或许是吧。小时候,新衣裳,年夜饭,压岁钱,爆竹声声,真叫人满心期待。如今,生活越来越好了,新装美味早已不再是过年的特权,心里自然也