Appearance
Day.js 课程计划
课程概述
本课程将深入讲解 Day.js 的原理和实现,帮助你掌握轻量级日期处理库的使用和最佳实践。
课程安排
第 1 节:Day.js 简介与环境搭建(20 分钟)
学习目标
- 了解 Day.js 的历史和设计目标
- 理解 Day.js 与 Moment.js 的区别
- 搭建开发环境
课程内容
- Day.js 的历史背景
- Day.js 的设计目标
- Day.js 与 Moment.js 的对比
- 环境搭建
实践任务
bash
# 创建项目
mkdir dayjs-core
cd dayjs-core
npm init -y
# 安装依赖
npm install --save-dev jest
# 创建目录结构
mkdir -p src test测试命令
bash
npm test预期输出
PASS test/index.test.js第 2 节:日期解析实现(30 分钟)
学习目标
- 理解日期解析的原理
- 实现日期解析功能
- 支持多种日期格式
课程内容
- 日期解析原理
- 支持 Date 对象
- 支持时间戳
- 支持 ISO 8601 格式
- 支持自定义格式
代码实现
javascript
// src/parse.js
export function parseDate(input) {
if (input === null || input === undefined) {
return new Date()
}
if (input instanceof Date) {
return input
}
if (typeof input === 'number') {
return new Date(input)
}
if (typeof input === 'string') {
if (/^\d+$/.test(input)) {
return new Date(parseInt(input, 10))
}
return new Date(input)
}
return new Date()
}测试用例
javascript
// test/parse.test.js
import { parseDate } from '../src/parse'
test('parse Date object', () => {
const date = new Date('2023-01-01')
const result = parseDate(date)
expect(result.getTime()).toBe(date.getTime())
})
test('parse timestamp', () => {
const timestamp = 1672531200000
const result = parseDate(timestamp)
expect(result.getTime()).toBe(timestamp)
})
test('parse ISO string', () => {
const result = parseDate('2023-01-01')
expect(result.getFullYear()).toBe(2023)
expect(result.getMonth()).toBe(0)
expect(result.getDate()).toBe(1)
})测试命令
bash
npm test预期输出
PASS test/parse.test.js
✓ parse Date object (2 ms)
✓ parse timestamp
✓ parse ISO string第 3 节:日期格式化实现(30 分钟)
学习目标
- 理解日期格式化的原理
- 实现日期格式化功能
- 支持自定义格式字符串
课程内容
- 日期格式化原理
- 支持常用格式标记
- 实现格式化函数
- 补零处理
代码实现
javascript
// src/format.js
export function formatDate(date, formatString) {
const year = date.getFullYear()
const month = date.getMonth() + 1
const day = date.getDate()
const hours = date.getHours()
const minutes = date.getMinutes()
const seconds = date.getSeconds()
const tokens = {
YYYY: year,
YY: String(year).slice(-2),
M: month,
MM: padZero(month),
D: day,
DD: padZero(day),
H: hours,
HH: padZero(hours),
m: minutes,
mm: padZero(minutes),
s: seconds,
ss: padZero(seconds)
}
return formatString.replace(
/YYYY|YY|M|MM|D|DD|H|HH|m|mm|s|ss/g,
(match) => tokens[match]
)
}
function padZero(num) {
return num < 10 ? `0${num}` : num
}测试用例
javascript
// test/format.test.js
import { formatDate } from '../src/format'
test('format date with YYYY-MM-DD', () => {
const date = new Date('2023-01-01')
const result = formatDate(date, 'YYYY-MM-DD')
expect(result).toBe('2023-01-01')
})
test('format date with HH:mm:ss', () => {
const date = new Date('2023-01-01T12:30:45')
const result = formatDate(date, 'HH:mm:ss')
expect(result).toBe('12:30:45')
})
test('pad zero', () => {
const date = new Date('2023-01-01T01:02:03')
const result = formatDate(date, 'YYYY-MM-DD HH:mm:ss')
expect(result).toBe('2023-01-01 01:02:03')
})测试命令
bash
npm test预期输出
PASS test/format.test.js
✓ format date with YYYY-MM-DD
✓ format date with HH:mm:ss
✓ pad zero第 4 节:日期操作实现(30 分钟)
学习目标
- 理解日期操作的原理
- 实现日期加减功能
- 支持多种时间单位
课程内容
- 日期操作原理
- 实现加法功能
- 实现减法功能
- 支持多种时间单位
代码实现
javascript
// src/manipulate.js
export function addDate(date, amount, unit) {
const result = new Date(date)
switch (unit) {
case 'year':
case 'years':
result.setFullYear(result.getFullYear() + amount)
break
case 'month':
case 'months':
result.setMonth(result.getMonth() + amount)
break
case 'day':
case 'days':
result.setDate(result.getDate() + amount)
break
case 'hour':
case 'hours':
result.setHours(result.getHours() + amount)
break
case 'minute':
case 'minutes':
result.setMinutes(result.getMinutes() + amount)
break
case 'second':
case 'seconds':
result.setSeconds(result.getSeconds() + amount)
break
}
return result
}
export function subtractDate(date, amount, unit) {
return addDate(date, -amount, unit)
}测试用例
javascript
// test/manipulate.test.js
import { addDate, subtractDate } from '../src/manipulate'
test('add 1 day', () => {
const date = new Date('2023-01-01')
const result = addDate(date, 1, 'day')
expect(result.getDate()).toBe(2)
})
test('add 1 month', () => {
const date = new Date('2023-01-01')
const result = addDate(date, 1, 'month')
expect(result.getMonth()).toBe(1)
})
test('subtract 1 day', () => {
const date = new Date('2023-01-02')
const result = subtractDate(date, 1, 'day')
expect(result.getDate()).toBe(1)
})
test('add 1 year', () => {
const date = new Date('2023-01-01')
const result = addDate(date, 1, 'year')
expect(result.getFullYear()).toBe(2024)
})测试命令
bash
npm test预期输出
PASS test/manipulate.test.js
✓ add 1 day
✓ add 1 month
✓ subtract 1 day
✓ add 1 year第 5 节:日期比较实现(20 分钟)
学习目标
- 理解日期比较的原理
- 实现日期比较功能
- 支持多种比较方式
课程内容
- 日期比较原理
- 实现比较函数
- 支持不同粒度的比较
代码实现
javascript
// src/compare.js
export function isBefore(date1, date2, unit) {
const time1 = unit ? getUnitTime(date1, unit) : date1.getTime()
const time2 = unit ? getUnitTime(date2, unit) : date2.getTime()
return time1 < time2
}
export function isAfter(date1, date2, unit) {
const time1 = unit ? getUnitTime(date1, unit) : date1.getTime()
const time2 = unit ? getUnitTime(date2, unit) : date2.getTime()
return time1 > time2
}
export function isSame(date1, date2, unit) {
const time1 = unit ? getUnitTime(date1, unit) : date1.getTime()
const time2 = unit ? getUnitTime(date2, unit) : date2.getTime()
return time1 === time2
}
function getUnitTime(date, unit) {
switch (unit) {
case 'year':
return date.getFullYear()
case 'month':
return date.getFullYear() * 100 + date.getMonth()
case 'day':
return date.getFullYear() * 10000 + (date.getMonth() + 1) * 100 + date.getDate()
default:
return date.getTime()
}
}测试用例
javascript
// test/compare.test.js
import { isBefore, isAfter, isSame } from '../src/compare'
test('isBefore', () => {
const date1 = new Date('2023-01-01')
const date2 = new Date('2023-01-02')
expect(isBefore(date1, date2)).toBe(true)
expect(isBefore(date2, date1)).toBe(false)
})
test('isAfter', () => {
const date1 = new Date('2023-01-01')
const date2 = new Date('2023-01-02')
expect(isAfter(date2, date1)).toBe(true)
expect(isAfter(date1, date2)).toBe(false)
})
test('isSame', () => {
const date1 = new Date('2023-01-01')
const date2 = new Date('2023-01-01')
expect(isSame(date1, date2)).toBe(true)
})
test('isSame with unit', () => {
const date1 = new Date('2023-01-01T12:00:00')
const date2 = new Date('2023-01-01T18:00:00')
expect(isSame(date1, date2, 'day')).toBe(true)
expect(isSame(date1, date2, 'hour')).toBe(false)
})测试命令
bash
npm test预期输出
PASS test/compare.test.js
✓ isBefore
✓ isAfter
✓ isSame
✓ isSame with unit第 6 节:获取和设置实现(20 分钟)
学习目标
- 理解获取和设置的原理
- 实现日期属性获取和设置
- 支持链式调用
课程内容
- 获取日期属性
- 设置日期属性
- 链式调用实现
- 不可变性保证
代码实现
javascript
// src/get-set.js
export function getDateProperty(date, property) {
switch (property) {
case 'year':
return date.getFullYear()
case 'month':
return date.getMonth()
case 'date':
return date.getDate()
case 'day':
return date.getDay()
case 'hour':
return date.getHours()
case 'minute':
return date.getMinutes()
case 'second':
return date.getSeconds()
case 'millisecond':
return date.getMilliseconds()
default:
throw new Error(`Invalid property: ${property}`)
}
}
export function setDateProperty(date, property, value) {
const result = new Date(date)
switch (property) {
case 'year':
result.setFullYear(value)
break
case 'month':
result.setMonth(value)
break
case 'date':
result.setDate(value)
break
case 'hour':
result.setHours(value)
break
case 'minute':
result.setMinutes(value)
break
case 'second':
result.setSeconds(value)
break
case 'millisecond':
result.setMilliseconds(value)
break
default:
throw new Error(`Invalid property: ${property}`)
}
return result
}测试用例
javascript
// test/get-set.test.js
import { getDateProperty, setDateProperty } from '../src/get-set'
test('get year', () => {
const date = new Date('2023-01-01')
expect(getDateProperty(date, 'year')).toBe(2023)
})
test('get month', () => {
const date = new Date('2023-01-01')
expect(getDateProperty(date, 'month')).toBe(0)
})
test('set year', () => {
const date = new Date('2023-01-01')
const result = setDateProperty(date, 'year', 2024)
expect(result.getFullYear()).toBe(2024)
expect(date.getFullYear()).toBe(2023) // 原对象不变
})
test('set month', () => {
const date = new Date('2023-01-01')
const result = setDateProperty(date, 'month', 5)
expect(result.getMonth()).toBe(5)
expect(date.getMonth()).toBe(0) // 原对象不变
})测试命令
bash
npm test预期输出
PASS test/get-set.test.js
✓ get year
✓ get month
✓ set year
✓ set month第 7 节:Day.js 类实现(30 分钟)
学习目标
- 理解 Day.js 类的设计
- 实现 Day.js 类
- 集成所有功能
课程内容
- Day.js 类设计
- 构造函数实现
- 方法集成
- 链式调用支持
代码实现
javascript
// src/dayjs.js
import { parseDate } from './parse'
import { formatDate } from './format'
import { addDate, subtractDate } from './manipulate'
import { isBefore, isAfter, isSame } from './compare'
import { getDateProperty, setDateProperty } from './get-set'
export class Dayjs {
constructor(input) {
this.$d = parseDate(input)
}
format(formatString) {
return formatDate(this.$d, formatString)
}
add(amount, unit) {
const newDate = addDate(this.$d, amount, unit)
return new Dayjs(newDate)
}
subtract(amount, unit) {
const newDate = subtractDate(this.$d, amount, unit)
return new Dayjs(newDate)
}
isBefore(date, unit) {
const otherDate = date instanceof Dayjs ? date.$d : parseDate(date)
return isBefore(this.$d, otherDate, unit)
}
isAfter(date, unit) {
const otherDate = date instanceof Dayjs ? date.$d : parseDate(date)
return isAfter(this.$d, otherDate, unit)
}
isSame(date, unit) {
const otherDate = date instanceof Dayjs ? date.$d : parseDate(date)
return isSame(this.$d, otherDate, unit)
}
get(property) {
return getDateProperty(this.$d, property)
}
set(property, value) {
const newDate = setDateProperty(this.$d, property, value)
return new Dayjs(newDate)
}
year() {
return this.get('year')
}
month() {
return this.get('month')
}
date() {
return this.get('date')
}
hour() {
return this.get('hour')
}
minute() {
return this.get('minute')
}
second() {
return this.get('second')
}
millisecond() {
return this.get('millisecond')
}
}
export default function dayjs(input) {
return new Dayjs(input)
}测试用例
javascript
// test/dayjs.test.js
import dayjs, { Dayjs } from '../src/dayjs'
test('create dayjs instance', () => {
const d = dayjs('2023-01-01')
expect(d).toBeInstanceOf(Dayjs)
})
test('format date', () => {
const d = dayjs('2023-01-01')
expect(d.format('YYYY-MM-DD')).toBe('2023-01-01')
})
test('add 1 day', () => {
const d = dayjs('2023-01-01')
const result = d.add(1, 'day')
expect(result.format('YYYY-MM-DD')).toBe('2023-01-02')
expect(d.format('YYYY-MM-DD')).toBe('2023-01-01') // 原对象不变
})
test('chain calls', () => {
const result = dayjs('2023-01-01')
.add(1, 'day')
.subtract(1, 'month')
.format('YYYY-MM-DD')
expect(result).toBe('2022-12-02')
})
test('isBefore', () => {
const d1 = dayjs('2023-01-01')
const d2 = dayjs('2023-01-02')
expect(d1.isBefore(d2)).toBe(true)
})
test('get and set', () => {
const d = dayjs('2023-01-01')
expect(d.year()).toBe(2023)
const d2 = d.set('year', 2024)
expect(d2.year()).toBe(2024)
expect(d.year()).toBe(2023)
})测试命令
bash
npm test预期输出
PASS test/dayjs.test.js
✓ create dayjs instance
✓ format date
✓ add 1 day
✓ chain calls
✓ isBefore
✓ get and set第 8 节:综合实践与总结(30 分钟)
学习目标
- 综合运用所有功能
- 实现实际应用场景
- 总结学习要点
课程内容
- 综合实践
- 实际应用场景
- 最佳实践
- 学习总结
综合实践
javascript
// 实践 1:计算两个日期之间的天数
function daysBetween(date1, date2) {
const d1 = dayjs(date1)
const d2 = dayjs(date2)
const diff = d2.subtract(d1, 'millisecond')
return Math.floor(diff.get('millisecond') / (1000 * 60 * 60 * 24))
}
// 实践 2:判断是否是工作日
function isWeekday(date) {
const d = dayjs(date)
const day = d.day()
return day !== 0 && day !== 6
}
// 实践 3:获取本月的最后一天
function getLastDayOfMonth(date) {
const d = dayjs(date)
return d.set('date', 1).add(1, 'month').subtract(1, 'day').date()
}
// 实践 4:格式化相对时间
function formatRelativeTime(date) {
const now = dayjs()
const d = dayjs(date)
const diff = now.subtract(d, 'millisecond').get('millisecond')
const minutes = Math.floor(diff / (1000 * 60))
const hours = Math.floor(diff / (1000 * 60 * 60))
const days = Math.floor(diff / (1000 * 60 * 60 * 24))
if (minutes < 1) return '刚刚'
if (minutes < 60) return `${minutes} 分钟前`
if (hours < 24) return `${hours} 小时前`
if (days < 7) return `${days} 天前`
return d.format('YYYY-MM-DD')
}测试用例
javascript
// test/integration.test.js
import dayjs from '../src/dayjs'
test('days between', () => {
const days = daysBetween('2023-01-01', '2023-01-10')
expect(days).toBe(9)
})
test('is weekday', () => {
expect(isWeekday('2023-01-02')).toBe(true) // 周一
expect(isWeekday('2023-01-01')).toBe(false) // 周日
})
test('get last day of month', () => {
expect(getLastDayOfMonth('2023-01-15')).toBe(31)
expect(getLastDayOfMonth('2023-02-15')).toBe(28)
})
test('format relative time', () => {
const now = dayjs()
const fiveMinutesAgo = now.subtract(5, 'minute').format()
const twoHoursAgo = now.subtract(2, 'hour').format()
const threeDaysAgo = now.subtract(3, 'day').format()
expect(formatRelativeTime(fiveMinutesAgo)).toBe('5 分钟前')
expect(formatRelativeTime(twoHoursAgo)).toBe('2 小时前')
expect(formatRelativeTime(threeDaysAgo)).toBe('3 天前')
})测试命令
bash
npm test预期输出
PASS test/integration.test.js
✓ days between
✓ is weekday
✓ get last day of month
✓ format relative time总结
通过本课程的学习,你已经掌握了:
- Day.js 的设计理念和架构
- 日期解析、格式化、操作的实现
- 日期比较和获取设置
- Day.js 类的实现和链式调用
- 实际应用场景的综合实践
下一步学习
完成本课程后,建议继续学习:
- Moment.js 课程 - 学习 Moment.js 的实现
- 插件系统 - 学习 Day.js 插件开发
- 其他工具库 - 学习更多前端工具库
