Moment.js 教程 - JavaScript 日期处理库

项目概述

Moment.js 是一个传统的 JavaScript 日期处理库,提供了丰富的日期操作功能,但体积较大,现已进入维护模式。

核心功能

  1. 日期解析:解析各种格式的日期字符串
  2. 日期格式化:将日期格式化为各种字符串格式
  3. 日期操作:添加、减去时间单位
  4. 日期比较:比较两个日期的先后
  5. 国际化:支持多语言
  6. 功能丰富:提供大量日期处理功能
  7. API 友好:使用链式调用,API 设计友好
  8. 浏览器支持:支持所有现代浏览器
  9. Node.js 支持:支持 Node.js 环境
  10. TypeScript 支持:良好的 TypeScript 类型定义

安装与设置

基本安装

# 安装 Moment.js
npm install moment

# 安装特定版本
npm install moment@2.29.4

基本设置

// 导入 Moment.js
import moment from 'moment';

// 或者使用 CommonJS 导入
const moment = require('moment');

// 导入语言包
import 'moment/locale/zh-cn'; // 导入中文语言包

// 设置默认语言
moment.locale('zh-cn');

基本使用

创建日期对象

// 当前日期
moment();

// 特定日期
moment('2023-01-01');
moment('2023-01-01 12:00:00');
moment('2023-01-01T12:00:00Z');

// 时间戳
moment(1672531200000); // 毫秒时间戳
moment.unix(1672531200); // 秒时间戳

// 日期对象
moment(new Date());

// 复制日期对象
const date = moment();
const copy = moment(date);

日期格式化

// 格式化日期
moment().format('YYYY-MM-DD'); // 2023-01-01
moment().format('YYYY-MM-DD HH:mm:ss'); // 2023-01-01 12:00:00
moment().format('YYYY年MM月DD日'); // 2023年01月01日
moment().format('HH:mm:ss'); // 12:00:00

// 使用本地化格式
moment().format('L'); // 01/01/2023
moment().format('LL'); // January 1, 2023
moment().format('LLL'); // January 1, 2023 12:00 PM
moment().format('LLLL'); // Sunday, January 1, 2023 12:00 PM

// 使用相对时间
moment().fromNow(); // 2 hours ago
moment().toNow(); // in 2 hours
moment('2023-01-01').fromNow(); // 3 months ago

日期操作

// 添加时间
moment().add(1, 'day'); // 明天
moment().add(1, 'week'); // 下周
moment().add(1, 'month'); // 下个月
moment().add(1, 'year'); // 明年
moment().add(1, 'hour'); // 1小时后
moment().add(1, 'minute'); // 1分钟后
moment().add(1, 'second'); // 1秒后

// 减去时间
moment().subtract(1, 'day'); // 昨天
moment().subtract(1, 'week'); // 上周
moment().subtract(1, 'month'); // 上个月
moment().subtract(1, 'year'); // 去年

// 设置时间
moment().year(2023); // 设置年份为 2023
moment().month(0); // 设置月份为 1 月(0-11)
moment().date(1); // 设置日期为 1 号
moment().hour(12); // 设置小时为 12
moment().minute(0); // 设置分钟为 0
moment().second(0); // 设置秒为 0

日期比较

// 比较两个日期
const date1 = moment('2023-01-01');
const date2 = moment('2023-01-02');

// 是否在另一个日期之前
date1.isBefore(date2); // true

// 是否在另一个日期之后
date1.isAfter(date2); // false

// 是否与另一个日期相同
date1.isSame(date2); // false

// 比较单位
date1.isBefore(date2, 'year'); // false(年份相同)
date1.isBefore(date2, 'month'); // false(月份相同)
date1.isBefore(date2, 'date'); // true(日期不同)

获取日期部分

// 获取年份
moment().year(); // 2023

// 获取月份(0-11)
moment().month(); // 0 表示 1 月

// 获取日期(1-31)
moment().date(); // 1

// 获取星期(0-6,0 表示星期日)
moment().day(); // 0

// 获取小时(0-23)
moment().hour(); // 12

// 获取分钟(0-59)
moment().minute(); // 0

// 获取秒(0-59)
moment().second(); // 0

// 获取毫秒(0-999)
moment().millisecond(); // 0

高级特性

国际化

import moment from 'moment';
import 'moment/locale/zh-cn'; // 导入中文语言包
import 'moment/locale/en'; // 导入英文语言包
import 'moment/locale/ja'; // 导入日文语言包

// 设置全局语言
moment.locale('zh-cn');
console.log(moment().format('LLLL')); // 2023年1月1日星期日 12:00

// 临时使用其他语言
console.log(moment().locale('en').format('LLLL')); // Sunday, January 1, 2023 12:00 PM
console.log(moment().locale('ja').format('LLLL')); // 2023年1月1日日曜日 12:00

日期范围

// 检查日期是否在范围内
const date = moment('2023-01-15');
const start = moment('2023-01-01');
const end = moment('2023-01-31');

const isBetween = date.isAfter(start) && date.isBefore(end);
console.log(isBetween); // true

// 包含边界
const isBetweenInclusive = date.isSameOrAfter(start) && date.isSameOrBefore(end);
console.log(isBetweenInclusive); // true

日期克隆

// 克隆日期对象
const date1 = moment('2023-01-01');
const date2 = date1.clone();

date2.add(1, 'day');
console.log(date1.format('YYYY-MM-DD')); // 2023-01-01
console.log(date2.format('YYYY-MM-DD')); // 2023-01-02

日期验证

// 验证日期是否有效
moment('2023-02-30').isValid(); // false(2月没有30天)
moment('2023-01-01').isValid(); // true

// 获取无效日期的原因
moment('2023-02-30').invalidAt(); // 2(表示日期部分无效)

实用场景

日期格式化

import moment from 'moment';

// 格式化当前日期
console.log(moment().format('YYYY-MM-DD')); // 2023-01-01
console.log(moment().format('YYYY-MM-DD HH:mm:ss')); // 2023-01-01 12:00:00
console.log(moment().format('YYYY年MM月DD日')); // 2023年01月01日
console.log(moment().format('HH:mm:ss')); // 12:00:00

// 格式化特定日期
const date = moment('2023-01-01');
console.log(date.format('YYYY-MM-DD')); // 2023-01-01

相对时间显示

import moment from 'moment';
import 'moment/locale/zh-cn';

// 设置语言
moment.locale('zh-cn');

// 显示相对时间
console.log(moment().fromNow()); // 刚刚
console.log(moment().subtract(1, 'minute').fromNow()); // 1分钟前
console.log(moment().subtract(1, 'hour').fromNow()); // 1小时前
console.log(moment().subtract(1, 'day').fromNow()); // 1天前
console.log(moment().subtract(1, 'week').fromNow()); // 1周前
console.log(moment().subtract(1, 'month').fromNow()); // 1个月前
console.log(moment().subtract(1, 'year').fromNow()); // 1年前

// 显示未来相对时间
console.log(moment().add(1, 'minute').fromNow()); // 1分钟后
console.log(moment().add(1, 'hour').fromNow()); // 1小时后
console.log(moment().add(1, 'day').fromNow()); // 1天后

日期计算

import moment from 'moment';

// 计算两个日期之间的差值
const date1 = moment('2023-01-01');
const date2 = moment('2023-01-02');

// 计算天数差
console.log(date2.diff(date1, 'days')); // 1

// 计算小时差
console.log(date2.diff(date1, 'hours')); // 24

// 计算分钟差
console.log(date2.diff(date1, 'minutes')); // 1440

// 计算秒差
console.log(date2.diff(date1, 'seconds')); // 86400

// 计算月数差
const date3 = moment('2023-02-01');
console.log(date3.diff(date1, 'months')); // 1

// 计算年数差
const date4 = moment('2024-01-01');
console.log(date4.diff(date1, 'years')); // 1

日期范围检查

import moment from 'moment';

// 检查日期是否在范围内
function isDateInRange(date, startDate, endDate) {
  const momentDate = moment(date);
  const momentStart = moment(startDate);
  const momentEnd = moment(endDate);
  return momentDate.isSameOrAfter(momentStart) && momentDate.isSameOrBefore(momentEnd);
}

console.log(isDateInRange('2023-01-15', '2023-01-01', '2023-01-31')); // true
console.log(isDateInRange('2023-02-01', '2023-01-01', '2023-01-31')); // false

日期工具函数

import moment from 'moment';

// 获取当月第一天
function getFirstDayOfMonth(date) {
  return moment(date).startOf('month').format('YYYY-MM-DD');
}

// 获取当月最后一天
function getLastDayOfMonth(date) {
  return moment(date).endOf('month').format('YYYY-MM-DD');
}

// 获取当周第一天(星期日)
function getFirstDayOfWeek(date) {
  return moment(date).startOf('week').format('YYYY-MM-DD');
}

// 获取当周最后一天(星期六)
function getLastDayOfWeek(date) {
  return moment(date).endOf('week').format('YYYY-MM-DD');
}

// 获取今年第一天
function getFirstDayOfYear(date) {
  return moment(date).startOf('year').format('YYYY-MM-DD');
}

// 获取今年最后一天
function getLastDayOfYear(date) {
  return moment(date).endOf('year').format('YYYY-MM-DD');
}

// 测试
console.log(getFirstDayOfMonth('2023-01-15')); // 2023-01-01
console.log(getLastDayOfMonth('2023-01-15')); // 2023-01-31
console.log(getFirstDayOfWeek('2023-01-15')); // 2023-01-15(假设 2023-01-15 是星期日)
console.log(getLastDayOfWeek('2023-01-15')); // 2023-01-21
console.log(getFirstDayOfYear('2023-01-15')); // 2023-01-01
console.log(getLastDayOfYear('2023-01-15')); // 2023-12-31

最佳实践

  1. 注意体积:Moment.js 体积较大,考虑使用 Day.js 等轻量级替代方案
  2. 使用链式调用:Moment.js 支持链式调用,使代码更简洁
  3. 注意日期对象的可变性:Moment.js 的方法会修改原对象,注意使用 clone() 方法
  4. 设置默认语言:根据用户的语言环境设置默认语言
  5. 使用 TypeScript:使用 TypeScript 提供类型安全
  6. 测试:为日期处理逻辑编写测试
  7. 文档:为日期处理逻辑编写文档
  8. 考虑迁移:考虑迁移到 Day.js 等轻量级替代方案

常见问题与解决方案

1. 体积过大

问题:Moment.js 体积较大,影响打包体积

解决方案

  • 考虑使用 Day.js 等轻量级替代方案
  • 使用 webpack 的 Tree Shaking(但 Moment.js 可能不支持)
  • 只导入需要的语言包

2. 日期对象的可变性

问题:Moment.js 的方法会修改原对象

解决方案

  • 使用 clone() 方法创建副本
  • 注意方法调用顺序
// 错误示例
const date = moment('2023-01-01');
date.add(1, 'day'); // 修改了原对象
console.log(date.format('YYYY-MM-DD')); // 2023-01-02

// 正确示例
const date = moment('2023-01-01');
const newDate = date.clone().add(1, 'day'); // 使用 clone()
console.log(date.format('YYYY-MM-DD')); // 2023-01-01
console.log(newDate.format('YYYY-MM-DD')); // 2023-01-02

3. 国际化不工作

问题:设置语言后,日期格式没有变化

解决方案

  • 确保正确导入语言包
  • 确保使用 locale() 方法设置语言
  • 检查语言包名称是否正确
import moment from 'moment';
import 'moment/locale/zh-cn';

// 正确设置语言
moment.locale('zh-cn');
console.log(moment().format('LLLL')); // 现在应该显示中文

4. 日期计算错误

问题:日期计算结果不符合预期

解决方案

  • 检查日期对象是否正确创建
  • 检查使用的时间单位是否正确
  • 注意 Moment.js 的可变性,使用 clone() 方法

与其他日期处理库的比较

Moment.js vs Day.js

  • 体积:Moment.js 体积大(16KB+),Day.js 体积小(2KB)
  • API:两者 API 相似,易于迁移
  • 性能:Day.js 性能更好
  • 不可变性:Moment.js 修改原对象,Day.js 返回新的日期对象
  • 插件系统:Day.js 使用插件扩展功能,Moment.js 内置更多功能
  • 维护状态:Moment.js 已进入维护模式,Day.js 仍在积极开发

Moment.js vs Date-fns

  • API 风格:Moment.js 使用链式调用,Date-fns 使用函数式风格
  • 体积:Date-fns 体积小,支持按需导入
  • 功能:两者功能相似
  • 不可变性:Moment.js 修改原对象,Date-fns 返回新的日期对象
  • 维护状态:Date-fns 仍在积极开发

Moment.js vs 原生 Date 对象

  • API 简洁性:Moment.js API 更简洁易读
  • 功能丰富度:Moment.js 提供更多功能
  • 浏览器兼容性:Moment.js 处理了浏览器兼容性问题
  • 性能:原生 Date 对象性能更好
  • 体积:原生 Date 对象无额外体积

参考资源

  1. 官方文档https://momentjs.com/docs/
  2. GitHub 仓库https://github.com/moment/moment
  3. 语言包列表https://momentjs.com/docs/#/i18n/
  4. 与 Day.js 比较https://day.js.org/docs/en/installation/installation#comparison
  5. TypeScript 支持https://momentjs.com/docs/#/use-it/typescript/
« 上一篇 Day.js 教程 - 轻量级的 JavaScript 日期处理库 下一篇 » RxJS 教程 - 响应式编程库