0%

Day.JS 使用详解——轻量级日期处理库实战指南

moment.JS 包体积太大,果断换成了 dayjs。这个轻量级的日期处理库几乎支持 moment.JS 的所有功能,但包体积只有2KB 左右,性能也更优。

Day.JS 简介

  Day.JS 是一个轻量级的 Javascript 日期处理库,API 设计完全模仿 Moment.JS,但包体积只有2KB 左右。它提供了链式调用、不可变实例、国际化支持等特性,是 Moment.JS 的绝佳替代品。

  与 Moment.JS 相比,Day.JS 的主要优势:

  1. 体积小: 只有2KB,Moment.JS 的1/50
  2. 性能好: 比 Moment.JS 快2-3倍
  3. API 兼容: 99%的 API 与 Moment.JS 相同
  4. 不可变性: 日期操作不会修改原实例
  5. 插件化: 功能按需加载

安装和基本使用

1
2
3
4
5
6
# npm 安装 npm install dayjs

# yarn 安装 yarn add dayjs

# CDN 引入
# <script src="https://cdn.jsdelivr.net/npm/dayjs@1/dayjs.min.JS"></script>
1
2
3
4
5
// ES6导入 import dayjs from 'dayjs';

// CommonJS 导入 const dayjs = require('dayjs');

// 基本使用 const now = dayjs(); // 当前时间 console.log(now.format()); // ISO 8601格式 console.log(now.format('YYYY-MM-DD HH:mm:ss')); // 自定义格式

核心功能详解

1. 日期解析

1
2
3
4
5
6
7
8
9
// 解析不同格式的日期 const date1 = dayjs('2024-01-01'); // 字符串日期 const date2 = dayjs(1704067200000); // 时间戳 const date3 = dayjs(new Date()); // Date 对象 const date4 = dayjs([2024, 0, 1]); // 数组格式 [年, 月, 日]

// 指定格式解析 const customFormat = dayjs('01-01-2024', 'MM-DD-YYYY');
console.log(customFormat.format('YYYY-MM-DD')); // 2024-01-01

// 解析 ISO 8601格式 const isoDate = dayjs('2024-01-01T12:00:00.000Z');
console.log(isoDate.format()); // 2024-01-01T12:00:00.000Z

// 解析相对时间 const relativeDate = dayjs().subtract(7, 'days'); // 7天前 console.log(relativeDate.format('YYYY-MM-DD'));

2. 日期格式化

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
import dayjs from 'dayjs';

// 常用格式化选项 const date = dayjs('2024-03-15T14:30:00');

console.log(date.format('YYYY')); // 年份: 2024
console.log(date.format('MM')); // 月份: 03
console.log(date.format('DD')); // 日期: 15
console.log(date.format('HH')); // 小时: 14
console.log(date.format('mm')); // 分钟: 30
console.log(date.format('ss')); // 秒: 00

// 组合格式 console.log(date.format('YYYY-MM-DD')); // 2024-03-15
console.log(date.format('YYYY-MM-DD HH:mm:ss')); // 2024-03-15 14:30:00
console.log(date.format('MMM DD, YYYY')); // Mar 15, 2024
console.log(date.format('dddd, MMMM D, YYYY')); // Friday, March 15, 2024

// 自定义格式化函数 function customFormat(date) {
const now = dayjs();
const diff = now.diff(date, 'day');

if (diff === 0) {
return '今天 ' + date.format('HH:mm');
} else if (diff === 1) {
return '昨天 ' + date.format('HH:mm');
} else if (diff < 7) {
return diff + '天前';
} else {
return date.format('MM-DD');
}
}

console.log(customFormat(dayjs().subtract(1, 'hour'))); // 今天 13:30 (假设现在是14:30)
console.log(customFormat(dayjs().subtract(2, 'day'))); // 2天前

3. 日期计算和比较

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 日期加减运算 const baseDate = dayjs('2024-03-15');

console.log(baseDate.add(1, 'day').format('YYYY-MM-DD')); // 2024-03-16
console.log(baseDate.subtract(1, 'month').format('YYYY-MM-DD')); // 2024-02-15
console.log(baseDate.add(2, 'years').format('YYYY-MM-DD')); // 2026-03-15
console.log(baseDate.add(24, 'hours').format('YYYY-MM-DD HH:mm')); // 2024-03-16 00:00

// 支持多种单位 const now = dayjs();
console.log(now.add(1, 'week').format('YYYY-MM-DD')); // 一周后 console.log(now.subtract(30, 'minute').format('YYYY-MM-DD HH:mm')); // 30分钟前 console.log(now.add(1, 'quarter').format('YYYY-MM-DD')); // 一季度后 console.log(now.add(100, 'day').format('YYYY-MM-DD')); // 100天后

// 日期比较 const date1 = dayjs('2024-03-15');
const date2 = dayjs('2024-03-20');

console.log(date1.isBefore(date2)); // true
console.log(date1.isAfter(date2)); // false
console.log(date1.isSame(date2, 'day')); // false
console.log(date1.isSame(date1, 'day')); // true

// 比较到指定精度 console.log(date1.isSame(dayjs('2024-03-15T10:00:00'), 'day')); // true
console.log(date1.isSame(dayjs('2024-03-15T10:00:00'), 'hour')); // false

// 获取日期差值 const diffDays = date2.diff(date1, 'day'); // 5
const diffHours = date2.diff(date1, 'hour'); // 120
const diffMilliseconds = date2.diff(date1, 'millisecond'); // 432000000

插件系统

常用插件安装

1
2
3
4
5
# 安装常用插件 npm install dayjs-plugin-advancedFormat
npm install dayjs-plugin-relativeTime
npm install dayjs-plugin-calendar
npm install dayjs-plugin-weekday
npm install dayjs-plugin-isBetween

相对时间插件 (Relative Time)

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
import dayjs from 'dayjs';
import relativeTime from 'dayjs/plugin/relativeTime';
import 'dayjs/locale/zh-cn'; // 中文本地化 dayjs.extend(relativeTime);

// 设置中文 dayjs.locale('zh-cn');

const now = dayjs();
const past = now.subtract(5, 'minute');
const future = now.add(1, 'hour');

console.log(past.fromNow()); // 5分钟前 console.log(future.fromNow()); // 1小时前 console.log(now.from(past)); // 5分钟

// 自定义相对时间词汇 const customLocale = {
name: 'custom',
weekdays: '星期日_星期一_星期二_星期三_星期四_星期五_星期六'.split('_'),
months: '一月_二月_三月_四月_五月_六月_七月_八月_九月_十月_十一月_十二月'.split('_'),
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 年'
}
};

dayjs.locale('custom', customLocale);

日历时间插件 (Calendar)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import dayjs from 'dayjs';
import calendar from 'dayjs/plugin/calendar';
import 'dayjs/locale/zh-cn';

dayjs.extend(calendar);
dayjs.locale('zh-cn');

const now = dayjs();
const yesterday = now.subtract(1, 'day');
const tomorrow = now.add(1, 'day');
const lastWeek = now.subtract(3, 'day');

console.log(now.calendar()); // 今天 HH:mm
console.log(yesterday.calendar()); // 昨天 HH:mm
console.log(tomorrow.calendar()); // 明天 HH:mm
console.log(lastWeek.calendar()); // 上周三 HH:mm

高级格式化插件 (Advanced Format)

1
2
3
4
5
6
7
8
9
10
11
import dayjs from 'dayjs';
import advancedFormat from 'dayjs/plugin/advancedFormat';

dayjs.extend(advancedFormat);

const date = dayjs();

// 额外的格式化选项 console.log(date.format('Do')); // 15日 (日期序号)
console.log(date.format('wo')); // 第11周 (周序号)
console.log(date.format('Mo')); // 3月 (月序号)
console.log(date.format('Qo')); // 第1季度 (季度序号)

范围比较插件 (Is Between)

1
2
3
4
5
6
7
8
9
10
11
import dayjs from 'dayjs';
import isBetween from 'dayjs/plugin/isBetween';

dayjs.extend(isBetween);

const startDate = dayjs('2024-01-01');
const endDate = dayjs('2024-12-31');
const checkDate = dayjs('2024-06-15');

console.log(checkDate.isBetween(startDate, endDate)); // true
console.log(checkDate.isBetween(startDate, endDate, 'year', '[)')); // 包含开始,不包含结束

实际应用场景

场景1: 表单验证中的日期处理

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
// 表单日期验证示例 class DateValidator {
static isValidDate(dateString) {
const date = dayjs(dateString);
return date.isValid();
}

static isFutureDate(dateString) {
const date = dayjs(dateString);
return date.isAfter(dayjs(), 'day');
}

static isInRange(dateString, startRange, endRange) {
const date = dayjs(dateString);
const start = dayjs(startRange);
const end = dayjs(endRange);

return date.isBetween(start, end, 'day', '[]'); // 包含边界
}

static formatDateForDisplay(dateString) {
const date = dayjs(dateString);
return date.format('YYYY 年 MM 月 DD 日');
}

static calculateAge(birthDateString) {
const birthDate = dayjs(birthDateString);
const today = dayjs();
return today.diff(birthDate, 'year');
}
}

// 使用示例 const formData = {
birthDate: '1990-05-15',
startDate: '2024-03-01',
endDate: '2024-12-31'
};

console.log(DateValidator.isValidDate(formData.birthDate)); // true
console.log(DateValidator.calculateAge(formData.birthDate)); // 34 (截至2024年)
console.log(DateValidator.isInRange('2024-06-15', formData.startDate, formData.endDate)); // true

场景2: 时间轴展示

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
// 时间轴组件 class TimelineGenerator {
constructor(events) {
this.events = events.map(event => ({
...event,
date: 2023-12-10 10:00:00
})).sort((a, b) => a.date.valueOf() - b.date.valueOf());
}

getGroupedEvents() {
const groups = {};

this.events.forEach(event => {
const monthYear = event.date.format('YYYY-MM');

if (!groups[monthYear]) {
groups[monthYear] = {
title: event.date.format('YYYY 年 MM 月'),
events: []
};
}

groups[monthYear].events.push({
...event,
displayDate: event.date.format('MM 月 DD 日 HH:mm'),
isToday: event.date.isSame(dayjs(), 'day'),
isPast: event.date.isBefore(dayjs(), 'day')
});
});

return Object.entries(groups).map(([key, value]) => value);
}

getNextNEvents(n = 5) {
const now = dayjs();
return this.events
.filter(event => event.date.isAfter(now, 'minute'))
.slice(0, n)
.map(event => ({
...event,
daysFromNow: event.date.diff(now, 'day'),
hoursFromNow: event.date.diff(now, 'hour')
}));
}
}

// 使用示例 const events = [
{ id: 1, title: '项目启动', date: 2023-12-10 10:00:00
{ id: 2, title: '需求评审', date: 2023-12-10 10:00:00
{ id: 3, title: '开发完成', date: 2023-12-10 10:00:00
{ id: 4, title: '测试开始', date: 2023-12-10 10:00:00
];

const timeline = new TimelineGenerator(events);
console.log(timeline.getGroupedEvents());
console.log(timeline.getNextNEvents());

场景3: 工作日计算

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
// 工作日计算工具 class BusinessDayCalculator {
constructor(holidays = [], businessStartHour = 9, businessEndHour = 18) {
this.holidays = holidays.map(date => dayjs(date).format('YYYY-MM-DD'));
this.businessStartHour = businessStartHour;
this.businessEndHour = businessEndHour;
}

isBusinessDay(date) {
const dayOfWeek = date.day();
const dateString = date.format('YYYY-MM-DD');

// 周末检查 if (dayOfWeek === 0 || dayOfWeek === 6) {
return false;
}

// 节假日检查 if (this.holidays.includes(dateString)) {
return false;
}

return true;
}

addBusinessDays(startDate, days) {
let currentDate = dayjs(startDate);
let remainingDays = Math.abs(days);
const direction = days >= 0 ? 1 : -1;

while (remainingDays > 0) {
currentDate = currentDate.add(direction, 'day');

if (this.isBusinessDay(currentDate)) {
remainingDays--;
}
}

return currentDate;
}

getBusinessDaysBetween(startDate, endDate) {
const start = dayjs(startDate);
const end = dayjs(endDate);
let current = start.clone();
let count = 0;

while (current.isSameOrBefore(end, 'day')) {
if (this.isBusinessDay(current)) {
count++;
}
current = current.add(1, 'day');
}

return count;
}

// 计算工作时间间隔(小时)
getBusinessHoursBetween(startTime, endTime) {
const start = dayjs(startTime);
const end = dayjs(endTime);

if (end.isBefore(start)) {
return 0;
}

let totalHours = 0;
let current = start.clone();

while (current.isBefore(end, 'minute')) {
const currentDay = current.clone().startOf('day');

if (this.isBusinessDay(currentDay)) {
const dayStart = currentDay.hour(this.businessStartHour);
const dayEnd = currentDay.hour(this.businessEndHour);

const effectiveStart = current.isAfter(dayStart) ? current : dayStart;
const effectiveEnd = end.isBefore(dayEnd) ? end : dayEnd;

if (effectiveStart.isBefore(effectiveEnd)) {
totalHours += effectiveEnd.diff(effectiveStart, 'hour', true);
}
}

current = current.add(1, 'day').startOf('day');
}

return totalHours;
}
}

// 使用示例 const calculator = new BusinessDayCalculator(['2024-04-01', '2024-04-05']);

console.log(calculator.isBusinessDay(dayjs('2024-04-02'))); // true
console.log(calculator.isBusinessDay(dayjs('2024-04-06'))); // false (周六)
console.log(calculator.addBusinessDays('2024-04-01', 5).format('YYYY-MM-DD')); // 2024-04-09
console.log(calculator.getBusinessDaysBetween('2024-04-01', '2024-04-10')); // 7个工作日

场景4: 国际化日期显示

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
// 国际化日期格式化 class InternationalDateFormatter {
constructor() {
this.formats = {
'en-US': 'MM/DD/YYYY',
'zh-CN': 'YYYY 年 MM 月 DD 日',
'ja-JP': 'YYYY 年 MM 月 DD 日',
'ko-KR': 'YYYY.MM.DD',
'fr-FR': 'DD/MM/YYYY',
'de-DE': 'DD.MM.YYYY'
};
}

formatDate(date, locale = 'zh-CN', formatType = 'long') {
const d = dayjs(date);

switch (formatType) {
case 'short':
return d.format('YY/MM/DD');
case 'medium':
return d.format('YYYY/MM/DD');
case 'long':
if (locale in this.formats) {
return d.format(this.formats[locale].replace(/[年月日]/g, '-'));
}
return d.format('YYYY-MM-DD');
case 'relative':
// 使用相对时间插件 return d.fromNow();
default:
return d.format('YYYY-MM-DD');
}
}

getLocalizedTime(date, locale = 'zh-CN') {
const d = dayjs(date);

switch (locale) {
case 'zh-CN':
return d.format('YYYY 年 MM 月 DD 日 HH 时 mm 分 ss 秒');
case 'en-US':
return d.format('MMMM D, YYYY h:mm A');
case 'ja-JP':
return d.format('YYYY 年 MM 月 DD 日 HH 時 mm 分 ss 秒');
default:
return d.format('YYYY-MM-DD HH:mm:ss');
}
}

getWeekInfo(date, locale = 'zh-CN') {
const d = dayjs(date);
const weekStart = d.startOf('week');
const weekEnd = d.endOf('week');

const weekDays = [];
for (let i = 0; i < 7; i++) {
const day = weekStart.add(i, 'day');
weekDays.push({
date: 2023-12-10 10:00:00
dayOfWeek: this.getDayOfWeek(day.day(), locale),
isToday: day.isSame(dayjs(), 'day')
});
}

return {
weekStart: weekStart.format('YYYY-MM-DD'),
weekEnd: weekEnd.format('YYYY-MM-DD'),
days: weekDays
};
}

getDayOfWeek(dayIndex, locale = 'zh-CN') {
const days = {
'zh-CN': ['周日', '周一', '周二', '周三', '周四', '周五', '周六'],
'en-US': ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
'ja-JP': ['日曜日', '月曜日', '火曜日', '水曜日', '木曜日', '金曜日', '土曜日']
};

return days[locale]?.[dayIndex] || days['en-US'][dayIndex];
}
}

// 使用示例 const formatter = new InternationalDateFormatter();
const date = dayjs('2024-04-15T14:30:00');

console.log(formatter.formatDate(date, 'zh-CN', 'long')); // 2024年04月15日 console.log(formatter.formatDate(date, 'en-US', 'long')); // 04/15/2024
console.log(formatter.getLocalizedTime(date, 'zh-CN')); // 2024年04月15日 14时30分00秒 console.log(formatter.getWeekInfo(date, 'zh-CN'));

性能优化建议

1. 避免重复解析

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
// 错误做法:重复解析相同的日期字符串 function badExample(dates) {
return dates.map(dateStr => {
const parsed = dayjs(dateStr); // 每次都解析 return {
original: dateStr,
formatted: parsed.format('YYYY-MM-DD'),
isFuture: parsed.isAfter(dayjs())
};
});
}

// 好的做法:缓存解析结果 class DateParser {
constructor() {
this.cache = new Map();
}

parse(dateStr) {
if (!this.cache.has(dateStr)) {
this.cache.set(dateStr, dayjs(dateStr));
}
return this.cache.get(dateStr);
}

batchProcess(dates) {
return dates.map(dateStr => {
const parsed = this.parse(dateStr);
return {
original: dateStr,
formatted: parsed.format('YYYY-MM-DD'),
isFuture: parsed.isAfter(dayjs())
};
});
}
}

2. 批量操作优化

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
// 批量日期处理优化 class BatchDateProcessor {
constructor() {
this.now = dayjs(); // 缓存当前时间
}

processDates(dates, operations) {
// 预解析所有日期 const parsedDates = dates.map(dateStr => ({
original: dateStr,
parsed: dayjs(dateStr)
}));

// 批量应用操作 return parsedDates.map(({ original, parsed }) => {
const result = { original, parsed };

operations.forEach(op => {
switch (op.type) {
case 'format':
result[op.field || 'formatted'] = parsed.format(op.format || 'YYYY-MM-DD');
break;
case 'compare':
result[op.field || 'isAfter'] = parsed.isAfter(this.now, op.unit || 'day');
break;
case 'calculate':
result[op.field || 'calculated'] = parsed[op.method](op.amount, op.unit);
break;
}
});

return result;
});
}
}

// 使用示例 const processor = new BatchDateProcessor();
const dates = ['2024-01-01', '2024-02-15', '2024-12-31'];

const results = processor.processDates(dates, [
{ type: 'format', format: 'YYYY 年 MM 月 DD 日', field: 'chineseFormat' },
{ type: 'compare', unit: 'day', field: 'isFuture' },
{ type: 'calculate', method: 'add', amount: 1, unit: 'year', field: 'nextYear' }
]);

3. 插件按需加载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 按需加载插件 import dayjs from 'dayjs';

// 只在需要时加载插件 const loadRelativeTimePlugin = async () => {
const relativeTime = await import('dayjs/plugin/relativeTime');
dayjs.extend(relativeTime.default);
};

const loadCalendarPlugin = async () => {
const calendar = await import('dayjs/plugin/calendar');
dayjs.extend(calendar.default);
};

// 条件加载插件 const initializeDayjs = async (features) => {
if (features.includes('relativeTime')) {
const relativeTime = await import('dayjs/plugin/relativeTime');
dayjs.extend(relativeTime.default);
}

if (features.includes('calendar')) {
const calendar = await import('dayjs/plugin/calendar');
dayjs.extend(calendar.default);
}
};

常见问题和解决方案

1. 时区处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 时区处理 import utc from 'dayjs/plugin/utc';
import timezone from 'dayjs/plugin/timezone';

dayjs.extend(utc);
dayjs.extend(timezone);

// 获取不同时区的时间 const beijingTime = dayjs().tz('Asia/Shanghai');
const newYorkTime = dayjs().tz('America/New_York');
const londonTime = dayjs().tz('Europe/London');

console.log('北京时间:', beijingTime.format('YYYY-MM-DD HH:mm:ss'));
console.log('纽约时间:', newYorkTime.format('YYYY-MM-DD HH:mm:ss'));
console.log('伦敦时间:', londonTime.format('YYYY-MM-DD HH:mm:ss'));

// 转换时区 const utcTime = dayjs.utc('2024-01-01T12:00:00Z');
const localTime = utcTime.tz('Asia/Shanghai');
console.log('UTC 转上海时间:', localTime.format('YYYY-MM-DD HH:mm:ss'));

2. 日期边界处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 日期边界处理 const boundaryExample = () => {
const date = dayjs('2024-02-15');

// 获取不同精度的开始和结束 console.log('当天开始:', date.startOf('day').format('YYYY-MM-DD HH:mm:ss')); // 2024-02-15 00:00:00
console.log('当天结束:', date.endOf('day').format('YYYY-MM-DD HH:mm:ss')); // 2024-02-15 23:59:59

console.log('当月开始:', date.startOf('month').format('YYYY-MM-DD')); // 2024-02-01
console.log('当月结束:', date.endOf('month').format('YYYY-MM-DD')); // 2024-02-29 (闰年)

console.log('当年开始:', date.startOf('year').format('YYYY-MM-DD')); // 2024-01-01
console.log('当年结束:', date.endOf('year').format('YYYY-MM-DD')); // 2024-12-31

// 获取周的开始和结束(周一是周开始)
console.log('本周一:', date.startOf('week').format('YYYY-MM-DD')); // 2024-02-12
console.log('本周日:', date.endOf('week').format('YYYY-MM-DD')); // 2024-02-18
};

3. 闰年和平年处理

1
2
3
4
5
6
7
8
9
10
// 闰年检查 const leapYearHelper = {
isLeapYear: (year) => dayjs(year + '-02-29').isValid(),
getDaysInMonth: (year, month) => dayjs(`${year}-${month}-01`).daysInMonth(),
getDaysOfYear: (year) => leapYearHelper.isLeapYear(year) ? 366 : 365
};

console.log(leapYearHelper.isLeapYear(2024)); // true
console.log(leapYearHelper.isLeapYear(2023)); // false
console.log(leapYearHelper.getDaysInMonth(2024, 2)); // 29 (闰年2月)
console.log(leapYearHelper.getDaysInMonth(2023, 2)); // 28 (平年2月)

最佳实践

1. 日期常量定义

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
// 日期常量定义 const DATE_CONSTANTS = {
MIN_DATE: dayjs('1900-01-01'),
MAX_DATE: dayjs('2100-12-31'),
UNIX_EPOCH: dayjs(0),

// 常用时间段 ONE_MINUTE: 60 * 1000,
ONE_HOUR: 60 * 60 * 1000,
ONE_DAY: 24 * 60 * 60 * 1000,
ONE_WEEK: 7 * 24 * 60 * 60 * 1000,

// 业务相关常量 WORK_START_TIME: '09:00',
WORK_END_TIME: '18:00',
WEEKEND_DAYS: [0, 6] // 周日和周六
};

// 日期验证工具 const DateValidator = {
validateRange: (date, minDate = DATE_CONSTANTS.MIN_DATE, maxDate = DATE_CONSTANTS.MAX_DATE) => {
const d = dayjs(date);
return d.isBetween(minDate, maxDate, 'day', '[]');
},

validateBusinessHours: (dateTime) => {
const d = dayjs(dateTime);
const hour = d.hour();
return hour >= 9 && hour < 18;
}
};

2. 日期格式化工具类

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
// 统一的日期格式化工具 class DateFormatter {
static formats = {
// 中国地区格式 CHINESE: {
DATE: 'YYYY 年 MM 月 DD 日',
DATETIME: 'YYYY 年 MM 月 DD 日 HH:mm',
TIME: 'HH:mm'
},
// 国际化格式 INTERNATIONAL: {
DATE: 'YYYY-MM-DD',
DATETIME: 'YYYY-MM-DD HH:mm:ss',
TIME: 'HH:mm'
},
// 美国格式 AMERICAN: {
DATE: 'MM/DD/YYYY',
DATETIME: 'MM/DD/YYYY h:mm A',
TIME: 'h:mm A'
}
};

static format(date, formatType = 'DATE', region = 'INTERNATIONAL') {
const d = dayjs(date);
const format = this.formats[region][formatType];
return d.format(format);
}

static relative(date, referenceDate = dayjs()) {
const d = dayjs(date);
const diffSeconds = Math.abs(referenceDate.diff(d, 'second'));

if (diffSeconds < 60) {
return '刚刚';
} else if (diffSeconds < 3600) {
return `${Math.floor(diffSeconds / 60)}分钟前`;
} else if (diffSeconds < 86400) {
return `${Math.floor(diffSeconds / 3600)}小时前`;
} else if (diffSeconds < 2592000) { // 30天 return `${Math.floor(diffSeconds / 86400)}天前`;
} else {
return d.format('YYYY-MM-DD');
}
}
}

// 使用示例 console.log(DateFormatter.format('2024-04-15', 'DATE', 'CHINESE')); // 2024年04月15日 console.log(DateFormatter.relative(dayjs().subtract(2, 'hour'))); // 2小时前

总结

  • Day.JS 是 Moment.JS 的优秀替代品,具有更小的包体积和更好的性能
  • 通过插件系统可以扩展功能,按需加载保持轻量
  • 日期解析、格式化、计算等核心功能使用简单
  • 国际化支持完善,可满足不同地区的日期格式需求
  • 在处理日期时注意时区、闰年等边界情况
  • 合理使用缓存和批量处理可以提升性能

从 Moment.JS 迁移到 Day.JS 后,项目包体积明显减小,而且功能基本没差别。唯一的区别就是要按需引入插件,但这反而是好事,避免了不必要的功能加载。现在处理日期相关的逻辑变得又快又轻量。

扩展阅读

  • Day.JS Official Documentation
  • Date and Time Handling Best Practices
  • Internationalization Date Formats
  • Javascript Date Performance Tips
  • Time Zone Handling in Javascript

参考资料

  • Day.JS GitHub Repository: https://github.com/iamkun/dayjs
  • Moment.JS Documentation: https://momentjs.com/docs/
  • Date-fns Library: https://date-fns.org/
  • Javascript Date Objects: https://developer.mozilla.org/en-US/docs/Web/Javascript/Reference/Global_Objects/Date
bulb