刚接触前端开发时,我被各种时间处理需求搞得焦头烂额。直到发现了Moment.js这个神器,它让时间操作变得像搭积木一样简单。先说说最核心的三个方法:subtract、add和calendar。
Moment.js的核心理念可以用一句话概括:将来用add,过去用subtract,现在用moment()。比如你想获取明天的日期,就是当前时间加1天;想获取上周的日期,就是当前时间减7天。这种直观的表达方式,让代码读起来就像在说人话。
安装Moment.js很简单,通过npm或者直接引入CDN都可以:
javascript复制// npm安装
npm install moment
// 浏览器直接引入
<script src="https://cdn.jsdelivr.net/npm/moment@2.29.1/moment.min.js"></script>
基础用法示例:
javascript复制const now = moment(); // 获取当前时间
const tomorrow = moment().add(1, 'days'); // 明天
const lastWeek = moment().subtract(7, 'days'); // 上周
subtract方法用于从当前时间减去指定的时间量。它的语法非常直观:
javascript复制moment().subtract(数量, 单位);
单位可以是:
实际项目中,我经常用它来处理各种"过去时间"相关的需求。比如电商网站显示"3天前下单":
javascript复制const orderTime = moment("2023-05-20");
const daysAgo = moment().subtract(3, 'days');
if(orderTime.isBefore(daysAgo)) {
console.log("订单已超过3天");
}
链式调用是Moment.js的一大特色。你可以把多个操作串联起来,代码会更加简洁:
javascript复制// 获取3个月前的那天的前一天
const specialDate = moment()
.subtract(3, 'months')
.subtract(1, 'days');
在开发后台管理系统时,我常用subtract来做时间范围查询。比如查询过去30天的数据:
javascript复制const startDate = moment().subtract(30, 'days').format('YYYY-MM-DD');
const endDate = moment().format('YYYY-MM-DD');
// 然后将这两个日期作为参数传给API
一个容易踩的坑是月份的天数不一致。比如1月31日减去1个月,结果是12月31日,而不是12月30日。Moment.js会尽量保持日期的一致性。
add方法与subtract相反,用于向当前时间添加时间量。语法结构完全一致:
javascript复制moment().add(数量, 单位);
我在处理订阅系统时,经常用add来计算会员到期日:
javascript复制const startDate = moment();
const endDate = moment().add(1, 'year'); // 一年会员
定时任务中,add也很有用。比如设置一个15分钟后的提醒:
javascript复制const reminderTime = moment().add(15, 'minutes');
setTimeout(() => {
alert("时间到了!");
}, 15 * 60 * 1000);
处理跨年、跨月时,add方法会自动调整日期。比如12月31日加1个月,会自动变成1月31日。
在开发倒计时功能时,我经常这样用:
javascript复制const endTime = moment().add(2, 'hours').add(30, 'minutes');
// 然后可以用diff方法计算剩余时间
const remaining = endTime.diff(moment(), 'minutes');
一个实用的技巧是结合startOf和add来获取时间段的开始和结束:
javascript复制// 获取本周的开始和结束
const weekStart = moment().startOf('week');
const weekEnd = moment().startOf('week').add(6, 'days');
calendar方法是Moment.js中最人性化的功能之一。它能根据时间距离当前的远近,自动返回"昨天"、"明天"、"下周三"这样更友好的格式。
基本用法:
javascript复制moment().subtract(1, 'days').calendar(); // 返回"昨天 14:30"
moment().add(1, 'days').calendar(); // 返回"明天 14:30"
在社交类应用中,这种展示方式用户体验更好。我曾在评论区实现这样的时间显示:
javascript复制function formatCommentTime(time) {
return moment(time).calendar(null, {
sameDay: '[今天] h:mm A',
nextDay: '[明天] h:mm A',
nextWeek: 'dddd h:mm A',
lastDay: '[昨天] h:mm A',
lastWeek: '[上周] dddd h:mm A',
sameElse: 'YYYY/MM/DD'
});
}
calendar方法支持自定义格式。比如中文环境下,我们可能想要显示"今天"而不是"Today":
javascript复制moment.updateLocale('zh-cn', {
calendar: {
lastDay: '[昨天] LT',
sameDay: '[今天] LT',
nextDay: '[明天] LT',
lastWeek: '[上周] dddd LT',
nextWeek: '[下周] dddd LT',
sameElse: 'YYYY/MM/DD'
}
});
在消息通知系统中,我这样优化时间显示:
javascript复制function smartTimeFormat(timestamp) {
const now = moment();
const target = moment(timestamp);
const diff = now.diff(target, 'days');
if(diff === 0) {
return target.calendar(null, {
sameDay: '[今天] HH:mm'
});
} else if(diff < 7) {
return target.calendar();
} else {
return target.format('YYYY年MM月DD日');
}
}
在开发电商网站时,我整合了这些方法来实现复杂的时间逻辑。比如促销活动倒计时+结束时间智能显示:
javascript复制function getPromotionTime(start, end) {
const now = moment();
const startMoment = moment(start);
const endMoment = moment(end);
if(now.isBefore(startMoment)) {
// 活动未开始
const diff = startMoment.diff(now, 'days');
if(diff < 7) {
return `活动将于${startMoment.calendar()}开始`;
} else {
return `活动将于${startMoment.format('YYYY年MM月DD日')}开始`;
}
} else if(now.isBefore(endMoment)) {
// 活动进行中
const remaining = endMoment.diff(now, 'hours');
return `活动剩余${remaining}小时`;
} else {
// 活动已结束
return `活动已于${endMoment.calendar()}结束`;
}
}
在开发项目管理工具时,需要处理各种时间计算:
javascript复制function calculateTaskTime(startDate, duration, unit) {
const start = moment(startDate);
const end = start.clone().add(duration, unit);
// 考虑工作日(简单版)
let workDays = 0;
let current = start.clone();
while(current.isBefore(end)) {
// 假设周末是周六和周日
if(current.day() !== 0 && current.day() !== 6) {
workDays++;
}
current.add(1, 'days');
}
return {
start: start.format('YYYY-MM-DD'),
end: end.format('YYYY-MM-DD'),
workDays: workDays
};
}
虽然Moment.js非常强大,但在性能敏感的场景下需要注意几点:
javascript复制// 不好的写法
for(let i=0; i<1000; i++) {
console.log(moment().add(i, 'days').format());
}
// 好的写法
const base = moment();
for(let i=0; i<1000; i++) {
console.log(base.clone().add(i, 'days').format());
}
javascript复制const moment = require('moment-timezone');
moment().tz('Asia/Shanghai').format();