咱们来自字节跳动飞书商业应用研发部(Lark Business Applications),现在咱们在北京、深圳、上海、武汉、杭州、成都、广州、三亚都设立了工作区域。咱们重视的产品范畴主要在企业经验管理软件上,包含飞书 OKR、飞书绩效、飞书招聘、飞书人事等 HCM 范畴体系,也包含飞书审批、OA、法务、财政、收购、差旅与报销等体系。欢迎各位参加咱们。
本文作者:飞书商业应用研发部 俞泽霖
欢迎大家重视飞书技能,每周定期更新飞书技能团队技能干货内容,想看什么内容,欢迎大家评论区留言~
1 布景
依据《State of Frontend 2022》问卷调查, 最受欢迎的前五个东西库中,时刻处理相关的库占有了两席。时刻处理东西为什么如受前端工程师青睐?JS Date 为什么无法满意开发需求?不同的时刻库之间又存在哪些差异?
2 时刻
2.1 计量规范
时刻包含了时刻和时段两个概念,惯例意义上的时段是能够经过两个时刻核算得到的,但地球自转的不稳定,这种方式得到的时段并不是恒定的,因此诞生了两种时刻计量体系:
- 世界时(GMT):依据地球自转的天文丈量得到,并不稳定。
- 原子时(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/DD
或 MM/DD/YYYY
或 MMMM 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 搬迁成本低。可是部分功用需求经过插件引进。引荐。
- Date-fns 相同克服了 moment.js 的缺点,并支撑 tree-shaking,独自运用某些功时,引进的包体积甚至小于 day.js。但需求从目标目录导入所需的东西函数,上手难度大。在引进了多种东西函数或涉及解析时刻字符串时,还会导致包体积过大。引荐存在轻度需求时运用。
参加咱们
扫码发现职位 & 投递简历:
官网投递:job.toutiao.com/s/FyL7DRg