0%

dayjs源码解析(一):概念、locale、constant、utils

插播一个新系列:时间库 dayjs 的源码解析。

用官方的描述 “Day.jsMoment.js 的 2kB 轻量化方案,拥有同样强大的 API”。优点是如下三个:

  • 简易:Day.js 是一个轻量的处理时间和日期的 JavaScript 库,和 Moment.jsAPI 设计保持完全一样。
  • 不可变:所有的 API 操作都将返回一个新的 Dayjs 实例。这种设计能避免 bug 产生,节约调试时间。
  • 国际化:Day.js 对国际化支持良好。但除非手动加载,多国语言默认是不会被打包到工程里的。

总的来说,dayjs 的优点就是 pluginlocale 手动按需加载,减少打包体积。

dayjs 是饿了么的大佬 iamkun 开发维护的,大佬同时也是 ElementUI 的开发者。解析之前先从dayjs 源代码仓库 fork 了一份:https://github.com/MageeLin/dayjs-source-code-analysis

时间是 2020 年 12 月 7 日,commitIDeb5fbc4c。解析时从 master 分支拉了一个新分支 analysis

打算分五章完成,目录如下:

  1. dayjs 源码解析(一):概念、locale、constant、utils
  2. dayjs 源码解析(二):Dayjs 类
  3. dayjs 源码解析(三):插件(上)
  4. dayjs 源码解析(四):插件(中)
  5. dayjs 源码解析(五):插件(下)

代码结构

目录结构

源代码的目录结构如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
dayjs
│ .editorconfig // 编辑器配置
│ .eslintrc.json // ESLint配置
│ .gitignore // git忽略配置
│ .npmignore // npm发布忽略配置
│ .travis.yml // 持续集成配置
│ babel.config.js // babel配置
│ CHANGELOG.md // 更新日志
│ CONTRIBUTING.md // 共建指南
│ karma.sauce.conf.js // karma测试配置
│ LICENSE // 许可声明
│ package.json
│ prettier.config.js // prettier 格式化配置
│ README.md

├─.github // github的一些配置
├─build // 构建打包
├─docs // 各语言的说明文档
├─src
│ │ constant.js // 常量
│ │ index.js // 主入口,定义Dayjs类
│ │ utils.js // 工具函数
│ │
│ ├─locale // 国际化
│ └─plugin // 插件
├─test // 测试
└─types // TypeScript

依赖结构

入口 src/index.js 的依赖如下所示:

依赖结构图

可以发现依赖链特别简单,没有依赖到 localeplugin 目录下的语言包和插件。这也就是 dayjs 的核心优点。

基础概念

在分析源码之前,先理解下一些相关的基础概念。

时间标准

几种时间标准的解释来自维基百科。

GMT

格林尼治平均时间(Greenwich Mean Time,GMT)是指位于英国伦敦郊区的皇家格林尼治天文台当地的平太阳时,因为本初子午线被定义为通过那里的经线。

自 1924 年 2 月 5 日开始,格林尼治天文台负责每隔一小时向全世界发放调时信息。

格林尼治标准时间的正午是指当平太阳横穿格林尼治子午线时(也就是在格林尼治上空最高点时)的时间。由于地球每天的自转是有些不规则的,而且正在缓慢减速,因此格林尼治平时基于天文观测本身的缺陷,已经被原子钟报时的协调世界时(UTC)所取代。

UTC

协调世界时(英语:Coordinated Universal Time,法语:Temps Universel Coordonné,简称 UTC)是最主要的世界时间标准,其以原子时秒长为基础,在时刻上尽量接近于格林威治标准时间。

协调世界时是世界上调节时钟和时间的主要时间标准,它与 0 度经线的平太阳时相差不超过 1 秒,并不遵守夏令时。

现行的协调世界时根据国际电信联盟的建议《Standard-frequency and time-signal emissions》(ITU-R TF.460-6)所确定。UTC 基于国际原子时,并在必要时通过不规则的加入闰秒来抵消地球自转变慢的影响。

如果本地时间比 UTC 时间快,例如中国、蒙古、菲律宾、新加坡、马来西亚、澳大利亚西部的时间比 UTC8 小时,就会写作 UTC+8,俗称东八区。相反,如果本地时间比 UTC 时间慢,例如夏威夷的时间比 UTC 时间慢 10 小时,就会写作 UTC-10,俗称西十区

ISO

国际标准 ISO 8601,是国际标准化组织的日期和时间的表示方法,全称为《数据存储和交换形式·信息交换·日期和时间的表示方法》。目前是 2004 年 12 月 1 日发行的第三版“ISO8601:2004”

在 Javascript 中的 Date.prototype.toISOString() 中,返回的是 YYYY-MM-DDTHH:mm:ss.sssZ格式的字符串,时区总是 UTC(协调世界时),加一个后缀“Z”标识。

Date 对象输出时间的格式

Javascript 的 Date.prototype 上有很多种方式可以输出时间,以时间戳 1607561462990 为例,在 Chrome87 中返回值如下表:

方法 格式 输出
valueOf 时间戳 1607561462990
getTime GMT 时间戳 1607561462990
toString 英语格式的本地时间字符串 Thu Dec 10 2020 08:51:02 GMT+0800 (中国标准时间)
toUTCString 英语格式的 UTC 时间字符串 Thu, 10 Dec 2020 00:51:02 GMT
toGMTString(标准已废弃) 英语格式的 GMT 时间字符串 Thu, 10 Dec 2020 00:51:02 GMT
toISOString ISO 格式的 UTC 时间字符串 2020-12-10T00:51:02.990Z
toLocaleString 字符串格式因不同语言而不同 2020/12/10 上午 8:51:02
toJSON toISOString 相同 2020-12-10T00:51:02.990Z

语言(文化)代码

不同语言对事物的描述方式肯定不同,即使同一种语言由于文化地区差异,对相同事物的描述也有区别,所以国际上就形成了一套标准来识别各种语言。

先放一篇 Hax 的回答BCP47 规范,对于汉语代码来说,按照标准应该使用 zh-cmn-Hans-CNzh-cmn-Hant-HKzh-cmn-Hans-SGzh-cmn-Hant-TW。但是由于历史的原因,广泛应用的是zh-CNzh-HKzh-SGzh-TW

引用一个通用的语言列表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
语言代码
国家|地区
"" (空字符串) 无变化的文化
af 公用荷兰语
af-ZA 公用荷兰语 - 南非
sq 阿尔巴尼亚
sq-AL 阿尔巴尼亚 -阿尔巴尼亚
ar 阿拉伯语
ar-DZ 阿拉伯语 -阿尔及利亚
ar-BH 阿拉伯语 -巴林
ar-EG 阿拉伯语 -埃及
ar-IQ 阿拉伯语 -伊拉克
ar-JO 阿拉伯语 -约旦
ar-KW 阿拉伯语 -科威特
ar-LB 阿拉伯语 -黎巴嫩
ar-LY 阿拉伯语 -利比亚
ar-MA 阿拉伯语 -摩洛哥
ar-OM 阿拉伯语 -阿曼
ar-QA 阿拉伯语 -卡塔尔
ar-SA 阿拉伯语 - 沙特阿拉伯
ar-SY 阿拉伯语 -叙利亚共和国
ar-TN 阿拉伯语 -北非的共和国
ar-AE 阿拉伯语 - 阿拉伯联合酋长国
ar-YE 阿拉伯语 -也门
hy 亚美尼亚
hy-AM 亚美尼亚的 -亚美尼亚
az Azeri
az-AZ-Cyrl Azeri-(西里尔字母的) 阿塞拜疆
az-AZ-Latn Azeri(拉丁文)- 阿塞拜疆
eu 巴斯克
eu-ES 巴斯克 -巴斯克
be Belarusian
be-BY Belarusian-白俄罗斯
bg 保加利亚
bg-BG 保加利亚 -保加利亚
ca 嘉泰罗尼亚
ca-ES 嘉泰罗尼亚 -嘉泰罗尼亚
zh-HK 中国 -香港
zh-MO 中国 -澳门
zh-CN 中国 -中国
zh-CHS 中国 (单一化)
zh-SG 中国 -新加坡
zh-TW 中国 -台湾
zh-CHT 中国 (传统的)
hr 克罗埃西亚
hr-HR 克罗埃西亚 -克罗埃西亚
cs 捷克
cs-CZ 捷克 - 捷克
da 丹麦文
da-DK 丹麦文 -丹麦
div Dhivehi
div-MV Dhivehi-马尔代夫
nl 荷兰
nl-BE 荷兰 -比利时
nl-NL 荷兰 - 荷兰
en 英国
en-AU 英国 -澳洲
en-BZ 英国 -伯利兹
en-CA 英国 -加拿大
en-CB 英国 -加勒比海
en-IE 英国 -爱尔兰
en-JM 英国 -牙买加
en-NZ 英国 - 新西兰
en-PH 英国 -菲律宾共和国
en-ZA 英国 - 南非
en-TT 英国 - 千里达托贝哥共和国
en-GB 英国 - 英国
en-US 英国 - 美国
en-ZW 英国 -津巴布韦
et 爱沙尼亚
et-EE 爱沙尼亚的 -爱沙尼亚
fo Faroese
fo-FO Faroese- 法罗群岛
fa 波斯语
fa-IR 波斯语 -伊朗王国
fi 芬兰语
fi-FI 芬兰语 -芬兰
fr 法国
fr-BE 法国 -比利时
fr-CA 法国 -加拿大
fr-FR 法国 -法国
fr-LU 法国 -卢森堡
fr-MC 法国 -摩纳哥
fr-CH 法国 -瑞士
gl 加利西亚
gl-ES 加利西亚 -加利西亚
ka 格鲁吉亚州
ka-GE 格鲁吉亚州 -格鲁吉亚州
de 德国
de-AT 德国 -奥地利
de-DE 德国 -德国
de-LI 德国 -列支敦士登
de-LU 德国 -卢森堡
de-CH 德国 -瑞士
el 希腊
el-GR 希腊 -希腊
gu Gujarati
gu-IN Gujarati-印度
he 希伯来
he-IL 希伯来 -以色列
hi 北印度语
hi-IN 北印度的 -印度
hu 匈牙利
hu-HU 匈牙利的 -匈牙利
is 冰岛语
is-IS 冰岛的 -冰岛
id 印尼
id-ID 印尼 -印尼
it 意大利
it-IT 意大利 -意大利
it-CH 意大利 -瑞士
ja 日本
ja-JP 日本 -日本
kn 卡纳达语
kn-IN 卡纳达语 -印度
kk Kazakh
kk-KZ Kazakh-哈萨克
kok Konkani
kok-IN Konkani-印度
ko 韩国
ko-KR 韩国 -韩国
ky Kyrgyz
ky-KZ Kyrgyz-哈萨克
lv 拉脱维亚
lv-LV 拉脱维亚的 -拉脱维亚
lt 立陶宛
lt-LT 立陶宛 -立陶宛
mk 马其顿
mk-MK 马其顿 -FYROM
ms 马来
ms-BN 马来 -汶莱
ms-MY 马来 -马来西亚
mr 马拉地语
mr-IN 马拉地语 -印度
mn 蒙古
mn-MN 蒙古 -蒙古
no 挪威
nb-NO 挪威 (Bokm?l) - 挪威
nn-NO 挪威 (Nynorsk)- 挪威
pl 波兰
pl-PL 波兰 -波兰
pt 葡萄牙
pt-BR 葡萄牙 -巴西
pt-PT 葡萄牙 -葡萄牙
pa Punjab 语
pa-IN Punjab 语 -印度
ro 罗马尼亚语
ro-RO 罗马尼亚语 -罗马尼亚
ru 俄国
ru-RU 俄国 -俄国
sa 梵文
sa-IN 梵文 -印度
sr-SP-Cyrl 塞尔维亚 -(西里尔字母的) 塞尔
sr-SP-Latn 塞尔维亚 (拉丁文)- 塞尔维亚共
sk 斯洛伐克
sk-SK 斯洛伐克 -斯洛伐克
sl 斯洛文尼亚
sl-SI 斯洛文尼亚 -斯洛文尼亚
es 西班牙
es-AR 西班牙 -阿根廷
es-BO 西班牙 -玻利维亚
es-CL 西班牙 -智利
es-CO 西班牙 -哥伦比亚
es-CR 西班牙 - 哥斯达黎加
es-DO 西班牙 - 多米尼加共和国
es-EC 西班牙 -厄瓜多尔
es-SV 西班牙 - 萨尔瓦多
es-GT 西班牙 -危地马拉
es-HN 西班牙 -洪都拉斯
es-MX 西班牙 -墨西哥
es-NI 西班牙 -尼加拉瓜
es-PA 西班牙 -巴拿马
es-PY 西班牙 -巴拉圭
es-PE 西班牙 -秘鲁
es-PR 西班牙 - 波多黎各
es-ES 西班牙 -西班牙
es-UY 西班牙 -乌拉圭
es-VE 西班牙 -委内瑞拉
sw Swahili
sw-KE Swahili-肯尼亚
sv 瑞典
sv-FI 瑞典 -芬兰
sv-SE 瑞典 -瑞典
syr Syriac
syr-SY Syriac-叙利亚共和国
ta 坦米尔
ta-IN 坦米尔 -印度
tt Tatar
tt-RU Tatar-俄国
te Telugu
te-IN Telugu-印度
th 泰国
th-TH 泰国 -泰国
tr 土耳其语
tr-TR 土耳其语 -土耳其
uk 乌克兰
uk-UA 乌克兰 -乌克兰
ur Urdu
ur-PK Urdu-巴基斯坦
uz Uzbek
uz-UZ-Cyrl Uzbek-(西里尔字母的) 乌兹别克
uz-UZ-Latn Uzbek(拉丁文)- 乌兹别克斯坦
vi 越南
vi-VN 越南 -越南

locale

对于 day.js 来说,同样也是实现了很多种语言的国际化,都放置在 src/locale 目录下,跟语言代码稍微有点不同的就是命名全部小写。

由于 day.js 是按需加载的,所以在使用某种语言前需要提前引入:

1
2
3
4
import 'dayjs/locale/zh-cn';

dayjs.locale('zh-cn'); // 全局使用
dayjs().locale('zh-cn').format(); // 当前实例使用

其实 dayjs/locale/xxx.js 种保存的是对应语言的各种模板和配置,以 zh-cn.js 为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
// Chinese [zh]
import dayjs from 'dayjs';

// locale 对象
const locale = {
name: 'zh', // 对象的名,关键
// 数组都是用 split 实现
// weekdays 数组
weekdays: '星期日_星期一_星期二_星期三_星期四_星期五_星期六'.split('_'),
// 可选,简写 weekdays 数组,没有就用前 3 个字符
weekdaysShort: '周日_周一_周二_周三_周四_周五_周六'.split('_'),
// 可选,最简写 weekdays 数组,没有就用前 2 个字符
weekdaysMin: '日_一_二_三_四_五_六'.split('_'),
// months 数组
months: '一月_二月_三月_四月_五月_六月_七月_八月_九月_十月_十一月_十二月'.split(
'_'
),
// 可选,简写 months 数组,没有就用前 3 个字符
monthsShort: '1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月'.split('_'),
/**
* @description: 返回例如3周,2日
* @param {Number} number 第几个
* @param {String} period 单位标志
* @return {String}
*/
ordinal: (number, period) => {
switch (period) {
case 'W':
return `${number}周`;
default:
return `${number}日`;
}
},
// 可选,设置一周的开始,默认周日,1 代表周一
weekStart: 1,
// 可选,设置一年的开始周,包含1月4日的那一周作为第一周
yearStart: 4,
// 格式化模板
formats: {
LT: 'HH:mm',
LTS: 'HH:mm:ss',
L: 'YYYY/MM/DD',
LL: 'YYYY年M月D日',
LLL: 'YYYY年M月D日Ah点mm分',
LLLL: 'YYYY年M月D日ddddAh点mm分',
// 小写或者简写
l: 'YYYY/M/D',
ll: 'YYYY年M月D日',
lll: 'YYYY年M月D日 HH:mm',
llll: 'YYYY年M月D日dddd HH:mm',
},
// 相对时间的格式化模板,保正 %s %d 相同
relativeTime: {
future: '%s后',
past: '%s前',
s: '几秒',
m: '1 分钟',
mm: '%d 分钟',
h: '1 小时',
hh: '%d 小时',
d: '1 天',
dd: '%d 天',
M: '1 个月',
MM: '%d 个月',
y: '1 年',
yy: '%d 年',
},
/**
* @description: 根据时和分返回当前的时间阶段
* @param {Number} hour 时
* @param {Number} minute 分
* @return {String} 时间阶段
*/
meridiem: (hour, minute) => {
const hm = hour * 100 + minute;
if (hm < 600) {
return '凌晨';
} else if (hm < 900) {
return '早上';
} else if (hm < 1130) {
return '上午';
} else if (hm < 1230) {
return '中午';
} else if (hm < 1800) {
return '下午';
}
return '晚上';
},
};

// 把 locale 对象加载到locale的 Ls 中
dayjs.locale(locale, null, true);

export default locale;

除了配置以外,可以发现,最后两步中首先把 locale 对象加载并保存,然后把 locale 对象默认导出。所以虽然官方没明说,但是也可以如下导入:

1
2
3
4
5
import dayjs from 'dayjs';
import zhCN from 'dayjs/locale/zh-cn';

dayjs.locale(zhCN); // 全局使用
dayjs().locale(zhCN).format(); // 当前实例使用

constant

src/constant.js 中存放的是一些常量和正则表达式。包括不同单位包含的秒数和毫秒数、标准的时间单位表达、默认格式化模板、无效时间和两个正则表达式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// 计算几个常量
// 包含的秒数
export const SECONDS_A_MINUTE = 60;
export const SECONDS_A_HOUR = SECONDS_A_MINUTE * 60;
export const SECONDS_A_DAY = SECONDS_A_HOUR * 24;
export const SECONDS_A_WEEK = SECONDS_A_DAY * 7;

// 包含的毫秒数
export const MILLISECONDS_A_SECOND = 1e3;
export const MILLISECONDS_A_MINUTE = SECONDS_A_MINUTE * MILLISECONDS_A_SECOND;
export const MILLISECONDS_A_HOUR = SECONDS_A_HOUR * MILLISECONDS_A_SECOND;
export const MILLISECONDS_A_DAY = SECONDS_A_DAY * MILLISECONDS_A_SECOND;
export const MILLISECONDS_A_WEEK = SECONDS_A_WEEK * MILLISECONDS_A_SECOND;

// 标准的 unit 写法
export const MS = 'millisecond';
export const S = 'second';
export const MIN = 'minute';
export const H = 'hour';
export const D = 'day';
export const W = 'week';
export const M = 'month';
export const Q = 'quarter';
export const Y = 'year';
export const DATE = 'date';

// 默认时间格式是ISO 2020-12-06T20:11:43Z
export const FORMAT_DEFAULT = 'YYYY-MM-DDTHH:mm:ssZ';

// 无效时间
export const INVALID_DATE_STRING = 'Invalid Date';

// 正则表达式
export const REGEX_PARSE = /^(\d{4})[-/]?(\d{1,2})?[-/]?(\d{0,2})[^0-9]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?.?(\d+)?$/;
export const REGEX_FORMAT = /\[([^\]]+)]|Y{1,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g;

这两个正则表达式比较有意思,第一个正则表达式 REGEX_PARSE 是用来解析字符串格式的时间,便于生成 Dayjs 实例关联的 Date 对象;第二个正则表达式 REGEX_FORMAT 用于解析 format 参数,返回想要的时间格式。

utils

src/utils.js 中存放的是一些工具函数。其实在 index.js 中也放置了很多工具函数,只不过那些工具函数需要用到一些 index.js 的全局变量,所以不能定义在 utils.js 中。但是在 index.js 中最后还是把它们放在了一个 Utils 对象里共同管理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
// C 是定义的常量 constant
import * as C from './constant.js';

/**
* @description: 在 string 的开头补充 pad,直到长度为 length,相当于`string.padStart(length, pad)`
* @param {String} string 被补充的字符串
* @param {Number} length 最后的长度
* @param {String} pad 填充的内容
* @return {String} 补充后的字符串
*/
const padStart = (string, length, pad) => {
const s = String(string);
if (!s || s.length >= length) return string;
// 前面的数组join更简单的表示就是 `pad.repeat(length - string.length)`
return `${Array(length + 1 - s.length).join(pad)}${string}`;
};

/**
* @description: 返回实例的UTC偏移量(分钟)转化成的 [+|-]HH:mm的格式
* @param {Dayjs} instance Dayjs的实例
* @return {String} UTC偏移量 格式:[+|-]HH:mm
*/
const padZoneStr = (instance) => {
// 这里感觉用Number(instance.utcOffset())会更易读
const negMinutes = -instance.utcOffset();
const minutes = Math.abs(negMinutes);
const hourOffset = Math.floor(minutes / 60);
const minuteOffset = minutes % 60;
return `${negMinutes <= 0 ? '+' : '-'}${padStart(
hourOffset,
2,
'0'
)}:${padStart(minuteOffset, 2, '0')}`;
};

/**
* @description: 求两个实例的月份差
* @param {Dayjs} a Dayjs的实例
* @param {Dayjs} b Dayjs的实例
* @return {Number} 返回两个实例的月份差
*/
const monthDiff = (a, b) => {
// 来自moment.js的函数,保证能返回相同的结果
if (a.date() < b.date()) return -monthDiff(b, a);
const wholeMonthDiff = (b.year() - a.year()) * 12 + (b.month() - a.month());
const anchor = a.clone().add(wholeMonthDiff, C.M);
const c = b - anchor < 0;
const anchor2 = a.clone().add(wholeMonthDiff + (c ? -1 : 1), C.M);
return +(
-(
wholeMonthDiff +
(b - anchor) / (c ? anchor - anchor2 : anchor2 - anchor)
) || 0
);
};

/**
* @description: 向 0 取整
* @param {Number} n 要取整的数字
* @return {Number} 取整后的数字
*/
const absFloor = (n) => (n < 0 ? Math.ceil(n) || 0 : Math.floor(n));

/**
* @description: 返回 u 对应的单位,能自动适配标准格式和缩写格式
* @param {String} u M(month) y(year) w(week) d(day) D(date) h(hour) m(minute) s(second) ms(millisecond) Q(quarter) 或 其他字符串
* @return {String} u 对应的单位
*/
const prettyUnit = (u) => {
const special = {
M: C.M,
y: C.Y,
w: C.W,
d: C.D,
D: C.DATE,
h: C.H,
m: C.MIN,
s: C.S,
ms: C.MS,
Q: C.Q,
};
return (
// 返回 u 对应的单位
special[u] ||
// 或者是把 u 结尾的 字符s 删除,当作单位
String(u || '')
.toLowerCase()
.replace(/s$/, '')
);
};

/**
* @description: 判断是否为 undefined
* @param {Any} s
* @return {Boolean} true: 是, false: 否
*/
const isUndefined = (s) => s === undefined;

export default {
s: padStart,
z: padZoneStr,
m: monthDiff,
a: absFloor,
p: prettyUnit,
u: isUndefined,
};

本篇内容完成,下一篇文章来分析 day.js 的核心 src/index.js 文件,学习 Dayjs 类的实现。


前端记事本,不定期更新,欢迎关注!


👆 全文结束,棒槌时间到 👇