Skip to content

Day.js 源代码导览

文件结构

Day.js 的源代码结构清晰,易于理解和扩展。

dayjs/
├── index.js           # 主入口
├── constant.js        # 常量定义
├── utils.js           # 工具函数
├── plugin.js          # 插件系统
├── dayjs.js           # Day.js 类
├── parse.js           # 日期解析
├── format.js          # 日期格式化
├── get-set.js         # 获取和设置
└── locale/            # 国际化

核心文件

1. index.js - 主入口

主入口文件,导出 dayjs 函数和 Dayjs 类。

javascript
import dayjs from './dayjs'
import isDayjs from './plugin/isDayjs'

export default dayjs
export { dayjs, isDayjs }

2. dayjs.js - Day.js 类

Day.js 类是核心类,封装了日期对象和操作方法。

javascript
import Utils from './utils'

class Dayjs {
  constructor(config) {
    this.$L = this.$L || config.locale || null
    this.$d = config.date
    this.init(config)
  }

  init(config) {
    this.$y = this.$d.getFullYear()
    this.$M = this.$d.getMonth()
    this.$D = this.$d.getDate()
    this.$W = this.$d.getDay()
    this.$H = this.$d.getHours()
    this.$m = this.$d.getMinutes()
    this.$s = this.$d.getSeconds()
    this.$ms = this.$d.getMilliseconds()
  }

  // ... 其他方法
}

export default (date, c) => {
  c = c || {}
  const arg = c.date
  const locale = c.locale
  const d = arg === undefined
    ? new Date()
    : arg instanceof Date
      ? arg
      : typeof arg === 'string'
        ? arg.match(/^[0-9]*$/) ? new Date(+arg) : new Date(arg)
        : new Date(arg)

  return new Dayjs({ date: d, locale })
}

关键函数

1. parse() - 解析日期

解析日期字符串:

javascript
function parseDate(config) {
  if (config === null || config === undefined) {
    return new Date()
  }

  if (config instanceof Date) {
    return config
  }

  if (typeof config === 'string') {
    return new Date(config)
  }

  if (typeof config === 'number') {
    return new Date(config)
  }

  return new Date()
}

2. format() - 格式化日期

格式化日期输出:

javascript
function format(date, formatString) {
  const tokens = {
    YYYY: date.getFullYear(),
    YY: String(date.getFullYear()).slice(-2),
    M: date.getMonth() + 1,
    MM: padZero(date.getMonth() + 1),
    D: date.getDate(),
    DD: padZero(date.getDate()),
    H: date.getHours(),
    HH: padZero(date.getHours()),
    m: date.getMinutes(),
    mm: padZero(date.getMinutes()),
    s: date.getSeconds(),
    ss: padZero(date.getSeconds())
  }

  return formatString.replace(/YYYY|YY|M|MM|D|DD|H|HH|m|mm|s|ss/g, (match) => {
    return tokens[match]
  })
}

3. add() - 添加时间

添加时间到日期:

javascript
function add(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
}

4. subtract() - 减去时间

减去时间:

javascript
function subtract(date, amount, unit) {
  return add(date, -amount, unit)
}

设计模式

1. 工厂模式

使用工厂模式创建 Day.js 实例:

javascript
export default (date, c) => {
  c = c || {}
  const arg = c.date
  const locale = c.locale
  const d = arg === undefined
    ? new Date()
    : arg instanceof Date
      ? arg
      : typeof arg === 'string'
        ? arg.match(/^[0-9]*$/) ? new Date(+arg) : new Date(arg)
        : new Date(arg)

  return new Dayjs({ date: d, locale })
}

2. 链式调用模式

所有操作返回新的 Day.js 实例,支持链式调用:

javascript
Dayjs.prototype.add = function(number, units) {
  const date = new Date(this.$d)
  date.setTime(date.getTime() + number * Utils.getUnits(units))
  return new Dayjs(date)
}

3. 插件模式

使用插件模式扩展功能:

javascript
const installedPlugins = []

function extend(plugin, option) {
  if (!plugin.$i) {
    plugin(option, Dayjs, dayjs)
    plugin.$i = true
    installedPlugins.push(plugin)
  }
  return dayjs
}

工具函数

1. padZero() - 补零

补零函数:

javascript
function padZero(num) {
  return num < 10 ? `0${num}` : num
}

2. isLeapYear() - 判断闰年

判断闰年:

javascript
function isLeapYear(year) {
  return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0
}

3. getDaysInMonth() - 获取月份天数

获取月份天数:

javascript
function getDaysInMonth(year, month) {
  return new Date(year, month + 1, 0).getDate()
}

插件实现

1. 插件接口

插件需要实现特定的接口:

javascript
function plugin(option, Dayjs, dayjsClass) {
  // 扩展 Dayjs 类
  Dayjs.prototype.methodName = function() {
    // 实现逻辑
  }

  // 扩展 dayjs 函数
  dayjsClass.staticMethod = function() {
    // 实现逻辑
  }
}

2. 示例插件

示例插件实现:

javascript
export default (option, Dayjs, dayjsClass) => {
  dayjsClass.isDayjs = function isDayjs(d) {
    return d instanceof Dayjs
  }

  Dayjs.prototype.isDayjs = function isDayjs() {
    return true
  }
}

国际化

1. 语言包结构

语言包结构:

javascript
export default {
  name: 'zh-cn',
  weekdays: '星期日_星期一_星期二_星期三_星期四_星期五_星期六'.split('_'),
  months: '一月_二月_三月_四月_五月_六月_七月_八月_九月_十月_十一月_十二月'.split('_'),
  ordinal: (number) => `${number}日`,
  formats: {
    LT: 'HH:mm',
    LTS: 'HH:mm:ss',
    L: 'YYYY/MM/DD',
    LL: 'YYYY年M月D日',
    LLL: 'YYYY年M月D日 HH:mm',
    LLLL: 'YYYY年M月D日dddd HH:mm'
  }
}

2. 使用语言包

使用语言包:

javascript
import 'dayjs/locale/zh-cn'
dayjs.locale('zh-cn')
dayjs().format('dddd') // '星期一'

性能优化

1. 缓存机制

缓存解析和格式化结果:

javascript
const cache = new Map()

function format(date, formatString) {
  const key = `${date.getTime()}-${formatString}`
  if (cache.has(key)) {
    return cache.get(key)
  }

  const result = formatInternal(date, formatString)
  cache.set(key, result)
  return result
}

2. 惰性计算

惰性计算日期属性:

javascript
class Dayjs {
  constructor(config) {
    this.$d = config.date
    this.$y = null
    this.$M = null
    this.$D = null
  }

  get year() {
    if (this.$y === null) {
      this.$y = this.$d.getFullYear()
    }
    return this.$y
  }
}

总结

Day.js 的源代码结构清晰,易于理解和扩展。它采用了模块化设计,核心库轻量,通过插件系统扩展功能。通过本节的学习,你将理解 Day.js 的源代码结构,为后续的核心功能实现打下基础。