Day.js常用方法集合

Day.js常用方法集合

一、背景与问题

在现代前端开发中,日期时间处理是一个不可避免的痛点。传统做法往往需要手动处理年月日时分秒的计算,容易引入大量边界条件判断。Moment.js曾是JavaScript日期处理的黄金标准,但其体积过大(约200KB)和内存泄漏问题导致其逐渐被取代。

Day.js作为Moment.js的轻量级替代方案,通过以下特性解决了这些问题:

  • 仅18KB(压缩后)
  • 不维护时区数据库
  • 支持ISO 8601标准格式
  • 可扩展性(通过插件支持时区、本地化等)

然而,开发者在使用过程中常遇到如下问题:

  1. 对内部实现机制不熟悉,导致格式化结果不一致
  2. 时区处理不当引发显示错误
  3. 性能瓶颈(尤其在处理大量日期数据时)
  4. 对API的误用(如混用静态方法和实例方法)

二、基本原理

Day.js的底层实现基于JavaScript的Date对象,但通过封装和优化提升了使用体验。其核心原理包含三个层面:

1. 日期表示

Day.js内部使用一个简单的数字数组表示日期:

[year, month, date, hour, minute, second, millisecond]

这个结构避免了Date对象的复杂性,同时支持直接操作各个时间单位。

2. 时区处理

Day.js默认使用本地时区,但通过插件(如dayjs-tz)可以实现时区转换。其核心逻辑是:

// 基础时区转换
function convertToUTC(date) {
  return date.getTime() + (date.getTimezoneOffset() * 60 * 1000);
}

通过调整毫秒数实现时区转换。

3. 格式化机制

Day.js的格式化引擎采用预定义的格式字符串解析规则,核心逻辑如下:

function format(date, format) {
  const tokens = parseFormat(format);
  let result = '';
  for (const token of tokens) {
    result += formatToken(token, date);
  }
  return result;
}

其中parseFormat函数会将格式字符串拆分为各个格式符号,如YYYY、MM、DD等。

三、环境准备

npm install dayjs

四、核心实现

1. 日期解析(Parsing)

Day.js支持多种日期格式的解析,包括ISO字符串、数组、对象等。

// 基础用法
const date1 = dayjs('2023-04-05');
console.log(date1.format()); // 2023-04-05T00:00:00

// 自定义格式
const date2 = dayjs('2023-04-05T14:30:00');
console.log(date2.format('YYYY-MM-DD HH:mm:ss')); // 2023-04-05 14:30:00

// 从数组创建
const date3 = dayjs([2023, 3, 5, 14, 30, 0]);
console.log(date3.format()); // 2023-04-05T14:30:00

// 处理不完整日期
const date4 = dayjs('2023-04');
console.log(date4.format('YYYY-MM-DD')); // 2023-04-01

关键点说明:

  • ISO格式支持完整的YYYY-MM-DDTHH:mm:ss格式
  • 当解析不完整日期时,会自动填充默认值(日为1,时分秒为0)
  • 数组格式必须包含完整的日期字段

2. 日期格式化(Formatting)

格式化是处理日期显示的核心,支持丰富的格式符:

const date = dayjs();
console.log(date.format('YYYY-MM-DD HH:mm:ss')); // 2023-04-05 14:30:00
console.log(date.format('dddd, MMMM D, YYYY')); // Wednesday, April 5, 2023
console.log(date.format('HH:mm:ss')); // 14:30:00

格式符说明:

格式符说明示例
YYYY四位年份2023
MM两位月份04
DD两位日期05
HH24小时制小时14
mm分钟30
ss秒00
dddd星期几Wednesday
MMMM全称月份April

3. 时间差计算(Time Difference)

const now = dayjs();
const future = dayjs().add(3, 'day');

console.log(future.diff(now, 'day')); // 3
console.log(future.diff(now, 'hour')); // 72
console.log(future.diff(now, 'minute')); // 4320

底层实现:

function diff(date1, date2, unit) {
  const diffMs = date1.diff(date2);
  switch (unit) {
    case 'day': return Math.round(diffMs / (1000 * 60 * 60 * 24));
    case 'hour': return Math.round(diffMs / (1000 * 60 * 60));
    case 'minute': return Math.round(diffMs / (1000 * 60));
    default: return diffMs;
  }
}

五、完整案例

1. 日历组件实现

// calendar.js
import dayjs from 'dayjs';

function getCalendar(date = dayjs()) {
  const year = date.year();
  const month = date.month();
  const firstDay = dayjs([year, month]).startOf('month');
  const lastDay = dayjs([year, month]).endOf('month');
  
  const days = [];
  const daysInMonth = lastDay.date();
  
  // 计算前一个月的天数
  const prevMonth = dayjs([year, month - 1]).endOf('month');
  const prevDays = prevMonth.date();
  
  // 填充前一个月的空白天数
  for (let i = 0; i < firstDay.day(); i++) {
    days.push({ date: prevDays - i, isPrevMonth: true });
  }
  
  // 填充当前月的日期
  for (let i = 1; i <= daysInMonth; i++) {
    days.push({ date: i, isPrevMonth: false });
  }
  
  // 填充下个月的空白天数
  const nextMonth = dayjs([year, month + 1]).startOf('month');
  for (let i = 1; i < (nextMonth.daysInMonth() - nextMonth.date()); i++) {
    days.push({ date: i, isPrevMonth: true });
  }
  
  return days;
}
<!-- calendar.html -->
<div id="calendar">
  <div class="days">
    <div>Sun</div>
    <div>Mon</div>
    <div>Tue</div>
    <div>Wed</div>
    <div>Thu</div>
    <div>Fri</div>
    <div>Sat</div>
  </div>
  <div id="days"></div>
</div>
// calendar.js (继续)
function renderCalendar() {
  const calendar = getCalendar();
  const container = document.getElementById('days');
  
  let html = '';
  let row = 0;
  
  calendar.forEach((day, index) => {
    if (index % 7 === 0) {
      html += '<div class="row">';
    }
    
    html += `<div class="${day.isPrevMonth ? 'prev-month' : ''}">
              ${day.date}
            </div>`;
    
    if (index % 7 === 6) {
      html += '</div>';
    }
  });
  
  container.innerHTML = html;
}

renderCalendar();

关键点说明:

  • 使用startOf和endOf方法精确计算月份范围
  • 通过date()方法获取当前日期
  • 使用daysInMonth()获取月份天数
  • 填充前/后月份的空白天数

六、源码解析

Day.js的核心源码包含以下几个关键部分:

1. 日期对象封装

function Dayjs(value, isUTC) {
  this._isUTC = isUTC;
  this._d = dayjs.moment(value, isUTC);
}

2. 格式化函数

Dayjs.prototype.format = function (format) {
  const tokens = parseFormat(format);
  let result = '';
  
  for (const token of tokens) {
    result += formatToken(token, this._d);
  }
  
  return result;
};

3. 时间差计算

Dayjs.prototype.diff = function (input, unit) {
  const diffMs = this._d - input._d;
  
  switch (unit) {
    case 'day': return Math.round(diffMs / (1000 * 60 * 60 * 24));
    case 'hour': return Math.round(diffMs / (1000 * 60 * 60));
    case 'minute': return Math.round(diffMs / (1000 * 60));
    default: return diffMs;
  }
};

七、进阶使用

1. 时区处理

通过dayjs-tz插件处理时区转换:

import dayjs from 'dayjs';
import utc from 'dayjs/plugin/utc';
import timezone from 'dayjs/plugin/timezone';

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

const now = dayjs().tz('Asia/Shanghai');
console.log(now.format()); // 2023-04-05T14:30:00+08:00

2. 自定义格式化

dayjs.extend({
  parseFormat: function (format) {
    // 自定义格式解析逻辑
  },
  formatToken: function (token, date) {
    // 自定义格式化逻辑
  }
});

3. 日期计算

const date = dayjs();
console.log(date.add(1, 'day').format()); // 2023-04-06T00:00:00
console.log(date.subtract(2, 'hour').format()); // 2023-04-04T22:00:00

八、性能与工程实践

1. 性能优化

  • 避免频繁创建实例:使用dayjs()直接获取当前时间
  • 缓存常用日期对象:对于固定日期(如生日)可预先创建
  • 避免不必要的格式化:仅在需要显示时进行格式化

2. 异常处理

try {
  dayjs('Invalid date');
} catch (e) {
  console.error('Invalid date format');
}

3. 安全考虑

  • 验证用户输入的日期格式
  • 对特殊字符进行转义处理
  • 避免直接将用户输入作为格式字符串

九、常见问题与踩坑

1. 时区误解

// 错误示例
const date = dayjs('2023-04-05T14:30:00');
console.log(date.format()); // 2023-04-05T14:30:00+00:00

问题:未考虑时区,可能与用户本地时间不一致

2. 格式符错误

// 错误示例
dayjs().format('YYYY-MM-DD HH:mm:ss'); // 正确
dayjs().format('YYYY-MM-DD HH:mm:SS'); // 错误(SS不是标准格式符)

3. 性能瓶颈

// 错误示例(处理大量数据)
const dates = Array(10000).fill().map(() => dayjs());

改进方法:使用dayjs的静态方法处理批量数据

十、最佳实践

1. 推荐使用场景

  • 需要轻量级日期处理的前端项目
  • 需要高性能的日期计算场景
  • 需要支持本地时区的显示
  • 需要快速开发的日期格式化需求

2. 不推荐使用场景

  • 需要复杂时区转换(建议使用dayjs-tz)
  • 需要处理历史日期(Day.js不维护时区数据库)
  • 需要精确到毫秒级的计算(建议使用dayjs-milliseconds插件)

3. 推荐实践

  • 使用dayjs()直接获取当前时间
  • 使用format方法处理显示需求
  • 对于复杂场景使用插件扩展功能
  • 避免直接操作Date对象

十一、总结

Day.js作为现代JavaScript日期处理的首选方案,通过其轻量级设计、高性能特性和丰富的API,解决了传统日期处理的诸多痛点。本文深入解析了其核心原理,提供了多个实际应用场景的代码示例,并分析了常见错误和性能优化方法。

在开发过程中,建议根据具体需求选择合适的实现方式:对于简单场景可直接使用基础方法,复杂时区需求可结合插件使用,高性能场景可采用批量处理策略。同时要特别注意格式符的正确使用,避免时区处理的常见错误。通过合理使用Day.js,可以显著提升日期处理的开发效率和代码质量。

最后修改于:2026年09月19日 06:08

评论已关闭

推荐阅读

AIGC实战——Transformer模型
2024年12月01日
Socket TCP 和 UDP 编程基础(Python)
2024年11月30日
python , tcp , udp
如何使用 ChatGPT 进行学术润色?你需要这些指令
2024年12月01日
AI
最新 Python 调用 OpenAi 详细教程实现问答、图像合成、图像理解、语音合成、语音识别(详细教程)
2024年11月24日
ChatGPT 和 DALL·E 2 配合生成故事绘本
2024年12月01日
omegaconf,一个超强的 Python 库!
2024年11月24日
【视觉AIGC识别】误差特征、人脸伪造检测、其他类型假图检测
2024年12月01日
[超级详细]如何在深度学习训练模型过程中使用 GPU 加速
2024年11月29日
Python 物理引擎pymunk最完整教程
2024年11月27日
MediaPipe 人体姿态与手指关键点检测教程
2024年11月27日
深入了解 Taipy:Python 打造 Web 应用的全面教程
2024年11月26日
基于Transformer的时间序列预测模型
2024年11月25日
Python在金融大数据分析中的AI应用(股价分析、量化交易)实战
2024年11月25日
AIGC Gradio系列学习教程之Components
2024年12月01日
Python3 `asyncio` — 异步 I/O,事件循环和并发工具
2024年11月30日
llama-factory SFT系列教程:大模型在自定义数据集 LoRA 训练与部署
2024年12月01日
Python 多线程和多进程用法
2024年11月24日
Python socket详解,全网最全教程
2024年11月27日
python之plot()和subplot()画图
2024年11月26日
理解 DALL·E 2、Stable Diffusion 和 Midjourney 工作原理
2024年12月01日