-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.js
316 lines (267 loc) · 7.56 KB
/
index.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
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
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
/* eslint-disable no-param-reassign */
/**
* 统一处理日期或格式化输出
* Author: Tyler.Chao
* github: https://github.com/cz848/dateio
*/
// 位数不够前补0,为了更好的兼容,用slice替代padStart
const zeroFill = (number, targetLength) => `00${number}`.slice(-targetLength || -2);
// 值是否定义
const isDefined = value => [null, undefined].indexOf(value) < 0;
// 匹配不同方法的正则
const formatsRegExp = /MS|ms|[YMDWHISAUymdwhisau]/g;
const getUnitRegExp = /^(?:MS|ms|[YMDWHISAUymdwhisau])$/;
const setUnitRegExp = /^(?:ms|[Uymdwhisu])$/;
const addUnitRegExp = /^([+-]?(?:\d*\.)?\d+)(ms|[ymdwhis])?$/;
// 每个时间单位对应的毫秒数或月数
const unitStep = {
ms: 1,
s: 1e3,
i: 6e4,
h: 36e5,
d: 864e5,
w: 864e5 * 7,
m: 1,
y: 12,
};
// 语言包
let I18N = {
weekdays: ['日', '一', '二', '三', '四', '五', '六'],
// 默认四个时段,可根据需要增减
meridiem: ['凌晨', '上午', '下午', '晚上'],
};
// 设置语言包
const locale = config => {
I18N = { ...I18N, ...config };
return I18N;
};
// from moment.js in order to keep the same result
const monthDiff = (a, b) => {
const wholeMonthDiff = (b.y() - a.y()) * 12 + (b.m() - a.m());
const anchor = a.clone().add(wholeMonthDiff, 'm');
const anchor2 = a.clone().add(wholeMonthDiff + (b > anchor ? 1 : -1), 'm');
return -(wholeMonthDiff + (b - anchor) / Math.abs(anchor2 - anchor)) || 0;
};
// 转换为可识别的日期格式
const toDate = input => {
if (!isDefined(input)) return new Date();
if (typeof input === 'string' && !/T.+(?:Z$)?/i.test(input)) input = input.replace(/-/g, '/');
else if (Array.isArray(input)) input = new Date(input.splice(0, 3).join('/')).setHours(...input.concat(0));
return new Date(input);
};
// 调用Date内部的get/set方法
const get = (that, type) => that.$date[`get${type}`]() + Number(type === 'Month');
const set = (that, type, input) => {
// 输入为非数字直接返回此对象
// eslint-disable-next-line no-restricted-globals
if (input.some(isNaN)) return that;
// 处理原生月份的偏移量
if (type === 'FullYear' && input.length > 1) input[1] -= 1;
else if (type === 'Month') input[0] -= 1;
// 得到最终结果
if (type === 'Day') that.add(input[0] - get(that, type), 'd');
else that.$date[`set${type}`](...input);
// 修正计算月份的误差,同步moment.js
if (type === 'Month' && input.length === 1 && get(that, type) > (input[0] % 12) + (input[0] < 0 ? 13 : 1)) {
that.add(`-${that.d()}d`);
}
return that;
};
const gs = (that, type, input) => {
const value = [].concat(input).filter(isDefined);
return value.length ? set(that, type, value) : get(that, type);
};
class DateIO {
constructor(input) {
this.I18N = locale();
this.init(input);
}
init(input) {
this.$date = toDate(input);
return this;
}
// 年
// 100...2020
y(...input) {
return gs(this, 'FullYear', input);
}
// 年 (4位)
// 0100...2020
Y() {
return zeroFill(this.y(), 4);
}
// 加偏移后的月
// 1...12
m(...input) {
return gs(this, 'Month', input);
}
// 月 (前导0)
// 01...12
M() {
return zeroFill(this.m());
}
// 日
// 1...31
d(input) {
return gs(this, 'Date', input);
}
// 日 (前导0)
// 01...31
D() {
return zeroFill(this.d());
}
// 周几
// 0...6
w(input) {
return gs(this, 'Day', input);
}
// 周几
// 本地化后的星期x
W() {
return this.I18N.weekdays[this.w()];
}
// 24小时制
// 0...23
h(...input) {
return gs(this, 'Hours', input);
}
// 24小时制 (前导0)
// 00...23
H() {
return zeroFill(this.h());
}
// 分
// 0...59
i(...input) {
return gs(this, 'Minutes', input);
}
// 分 (前导0)
// 00...59
I() {
return zeroFill(this.i());
}
// 秒
// 0...59
s(...input) {
return gs(this, 'Seconds', input);
}
// 秒 (前导0)
// 00...59
S() {
return zeroFill(this.s());
}
// 毫秒数
// 0...999
ms(input) {
return gs(this, 'Milliseconds', input);
}
MS() {
return zeroFill(this.ms(), 3);
}
// 时间段
a() {
const { meridiem } = this.I18N;
if (typeof meridiem === 'function') return meridiem(this.h(), this.i());
return meridiem[Math.floor((this.h() / 24) * meridiem.length)];
}
// 时间段
A() {
return this.a().toUpperCase();
}
// unix 偏移量 (毫秒)
// 0...1571136267050
u(input) {
return isDefined(input) ? this.init(input) : +this;
}
// Unix 时间戳 (秒)
// 0...1542759768
U(input) {
return isDefined(input) ? this.init(input * 1000) : Math.round(this / 1000);
}
// 获取以上格式的日期,每个unit对应其中一种格式
get(unit) {
return getUnitRegExp.test(unit) ? this[unit]() : null;
}
// 设置以上格式的日期
set(unit, ...input) {
return setUnitRegExp.test(unit) ? this[unit](...input) : this;
}
toDate() {
return this.$date;
}
toString() {
return this.$date.toString();
}
valueOf() {
return this.$date.valueOf();
}
clone() {
return new DateIO(+this);
}
// 利用格式化串格式化日期
format(formats) {
return String(formats || 'Y-M-D H:I:S').replace(formatsRegExp, unit => this[unit]());
}
// 开始于,默认ms
startOf(unit, isEndOf) {
let formats = 'y m d h i s';
formats = formats.slice(0, formats.indexOf(unit === 'w' ? 'd' : unit) + 1);
if (!formats) return this;
if (unit === 'w') this.w(isEndOf ? 6 : 0);
const dates = this.format(formats).split(' ');
// 分别对应年/月/日/时/分/秒/毫秒
const starts = [1, 1];
const ends = [0, 12, 31, 23, 59, 59, 999];
if (unit === 'm') ends[2] = this.daysInMonth();
const input = isEndOf ? ends : starts;
return this.init(dates.concat(input.slice(dates.length)));
}
// 结束于,默认ms
endOf(unit) {
return this.startOf(unit, true);
}
// 返回两个日期的差值,精确到毫秒
// unit: ms: milliseconds(default)|s: seconds|i: minutes|h: hours|d: days|w: weeks|m: months|y: years
// isFloat: 是否返回小数
diff(input, unit, isFloat = false) {
const that = new DateIO(input);
let diff = this - that;
if (/^[ym]$/.test(unit)) diff = monthDiff(this, that) / unitStep[unit];
else diff /= unitStep[unit] || 1;
return isFloat ? diff : Math.trunc(diff);
}
// 对日期进行+-运算,默认精确到毫秒,可传小数
// input: '7d', '-1m', '10y', '5.5h'等或数字。
// unit: 'y', 'm', 'd', 'w', 'h', 'i', 's', 'ms'。
add(input, unit) {
const pattern = String(input).match(addUnitRegExp);
if (!pattern) return this;
const [, addend, u = unit || 'ms'] = pattern;
// 年月转化为月,并四舍五入
if (/^[ym]$/.test(u)) return this.m(this.m() + Number((addend * unitStep[u]).toFixed(0)));
return this.init(addend * (unitStep[u] || 0) + this.valueOf());
}
subtract(input, unit) {
return this.add(`-${input}`, unit);
}
// 是否为闰年
isLeapYear() {
const y = this.y();
return y % 100 ? y % 4 === 0 : y % 400 === 0;
}
// 获取某月有多少天
daysInMonth() {
return this.m(this.m() + 1, 0).d();
}
// 比较两个日期是否具有相同的年/月/日/周/时/分/秒,默认精确比较到毫秒
isSame(input, unit) {
return +this.clone().startOf(unit) === +new DateIO(input).startOf(unit);
}
}
const dateio = input => new DateIO(input);
dateio.prototype = DateIO.prototype;
// 方便设置语言包
dateio.locale = locale;
// 用Unix秒时间戳设置时间
dateio.U = input => dateio(input * 1e3);
export default dateio;