1. 为什么需要理解日期时间处理?
日期时间本质上是人类对时间的记录和表达方式,而计算机需要将这些信息转化为可计算的数据。在 JavaScript 中,Date
对象的核心是将时间表示为从 1970 年 1 月 1 日 00:00:00 UTC 开始的毫秒数(时间戳)。无论你是要显示一个格式化的日期、计算时间间隔,还是处理跨时区问题,理解 Date
对象的工作原理是关键。
不过,Date
对象的设计有些历史包袱,比如月份从 0 开始、时区处理复杂等。为了写出健壮的代码,我们需要掌握它的核心机制,同时学会用现代工具库来简化操作。
2. 创建日期对象:从零开始
在 JavaScript 中,Date
是一个构造函数,通过 new Date()
创建实例。以下是几种常见方式:
a. 获取当前时间
最简单的方式,直接调用 new Date()
:
const now = new Date()
console.log(now) // 输出类似:2025-07-12T02:42:00.000Z
这会返回当前时间的 Date
对象,格式是 ISO 8601(UTC 时间)。输出的具体时间取决于你的设备时区。
b. 指定具体日期
可以通过字符串、数字参数或时间戳来创建特定日期。
- **通过 ISO 8601 字符串(推荐)**使用
YYYY-MM-DDTHH:mm:ss
格式,清晰且跨浏览器兼容性好:
const specificDate = new Date('2025-01-01T08:00:00')
console.log(specificDate) // 2025-01-01T00:00:00.000Z(北京时间 08:00)
注意:非 ISO 格式的字符串(如 '2025/01/01'
)在不同浏览器中可能解析不一致,避免使用。
- 通过数字参数使用
new Date(year, monthIndex, day, hours, minutes, seconds, milliseconds)
:
const loveDay = new Date(2025, 4, 20) // 2025 年 5 月 20 日
console.log(loveDay) // 2025-05-19T16:00:00.000Z(北京时间)
大坑:月份从 0 开始(0 表示 1 月,4 表示 5 月)。这是 Date
对象的历史设计问题,务必小心。
- 通过时间戳时间戳是从 1970-01-01 00:00:00 UTC 开始的毫秒数,适合与服务器交互:
const timestamp = 1735689600000
const dateFromTimestamp = new Date(timestamp)
console.log(dateFromTimestamp) // 2025-01-01T00:00:00.000Z
最佳实践:优先使用 ISO 字符串或时间戳创建日期,避免数字参数的月份陷阱。
3. 获取和设置日期的各个部分
创建 Date
对象后,我们通常需要提取或修改年、月、日等信息。
a. 获取日期信息
Date
提供了 get
系列方法,常用的包括:
const d = new Date('2025-08-08T20:00:00')
console.log(d.getFullYear()) // 2025
console.log(d.getMonth()) // 7(8 月,记得 +1)
console.log(d.getDate()) // 8
console.log(d.getDay()) // 5(周五,0 表示周日)
console.log(d.getHours()) // 20
console.log(d.getMinutes()) // 0
console.log(d.getSeconds()) // 0
console.log(d.getMilliseconds()) // 0
console.log(d.getTime()) // 1752153600000(时间戳)
注意:getMonth()
返回 0-11,需手动加 1 才是实际月份;getDay()
返回 0(周日)到 6(周六)。
b. 设置日期信息
set
系列方法可以修改日期的某个部分,但会直接改变原对象:
const d = new Date()
d.setFullYear(2026) // 设置年份为 2026
d.setMonth(11) // 设置为 12 月
d.setDate(25) // 设置为 25 日
console.log(d) // 2026-12-25T02:42:00.000Z(假设当前时间)
注意:set
方法会修改原对象,可能引发副作用。推荐先复制对象:
const today = new Date()
const nextDay = new Date(today) // 创建副本
nextDay.setDate(today.getDate() + 1)
console.log('今天:', today)
console.log('明天:', nextDay)
4. 格式化日期:让输出更友好
Date
对象的默认输出(如 toString()
)通常不够友好,我们需要自定义格式,比如 2025-08-08
或 2025年8月8日 20:00
。
a. 手动拼接(基础但繁琐)
可以用 get
方法提取信息,拼接成字符串:
const d = new Date()
const year = d.getFullYear()
const month = String(d.getMonth() + 1).padStart(2, '0') // 补零
const day = String(d.getDate()).padStart(2, '0')
const formatted = `${year}-${month}-${day}`
console.log(formatted) // 2025-07-12
缺点是代码冗长,补零逻辑容易出错。
b. 使用Intl.DateTimeFormat
(现代推荐)
Intl.DateTimeFormat
是浏览器内置的国际化 API,支持灵活的格式化和多语言支持:
const d = new Date()
const options = {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
hour12: false // 24 小时制
}
const formatter = new Intl.DateTimeFormat('zh-CN', options)
console.log(formatter.format(d)) // 2025/07/12 10:42
优点:支持多语言,代码简洁,内置补零功能。适合现代浏览器项目。
c. 使用第三方库(项目首选)
手动拼接和 Intl
适合简单场景,但在复杂项目中(如日期计算、时区处理),第三方库更高效。推荐以下库:
- Day.js:轻量(2KB),API 简单,适合大多数项目。
- date-fns:模块化设计,按需引入,函数式风格,适合性能敏感场景。
- Luxon:功能强大,专注于时区和国际化。
以 Day.js 为例:
import dayjs from 'dayjs'
const now = dayjs()
console.log(now.format('YYYY-MM-DD HH:mm:ss')) // 2025-07-12 10:42:00
console.log(now.add(7, 'day').format('YYYY-MM-DD')) // 2025-07-19
为什么用库? 它们简化了格式化、计算和时区处理,代码更可读,减少 bug。
5. 日期计算:从简单到复杂
日期计算是常见需求,比如“明天是星期几”或“两个日期相差多少天”。
a. 增加或减少时间
直接用 set
方法调整日期:
const today = new Date()
const nextWeek = new Date(today)
nextWeek.setDate(today.getDate() + 7)
console.log(nextWeek) // 7 天后的日期
用 Day.js 更简洁:
import dayjs from 'dayjs'
const nextWeek = dayjs().add(7, 'day')
console.log(nextWeek.format('YYYY-MM-DD')) // 2025-07-19
b. 计算日期差
计算两个日期之间的天数:
function daysBetween(date1, date2) {
const oneDay = 24 * 60 * 60 * 1000 // 一天的毫秒数
const diff = Math.abs(date1 - date2)
return Math.round(diff / oneDay)
}
const d1 = new Date('2025-07-01')
const d2 = new Date('2025-07-12')
console.log(daysBetween(d1, d2)) // 11
Day.js 提供更便捷的 API:
import dayjs from 'dayjs'
const d1 = dayjs('2025-07-01')
const d2 = dayjs('2025-07-12')
console.log(d2.diff(d1, 'day')) // 11
6. 时区问题:如何避免混乱
Date
对象默认使用浏览器本地时区,这在跨时区项目中容易引发问题。以下是处理时区的建议:
- 统一使用 UTC:与服务器交互时,优先使用
toISOString()
或时间戳,避免时区差异。
const d = new Date()
console.log(d.toISOString()) // 2025-07-12T02:42:00.000Z(UTC)
- 使用第三方库:Day.js 和 Luxon 支持时区插件(如
dayjs.utc
或 luxon
的 DateTime
),能轻松处理复杂时区逻辑。
- 明确时区需求:在前端显示时,使用
Intl.DateTimeFormat
适配用户本地时区;在后端交互时,统一用 UTC。
7. 常见问题与解决方案
- 问题 1:月份从 0 开始
总是记得 getMonth()
和 setMonth()
的月份需要加减 1,或者直接用第三方库规避这个问题。
- 问题 2:格式化复杂
优先选择 Intl.DateTimeFormat
或 Day.js,避免手动拼接字符串。
- 问题 3:时区不一致
使用时间戳或 toISOString()
作为数据传输格式,确保前后端时间一致。
- 问题 4:日期计算复杂
复杂计算交给 Day.js 或 date-fns,减少手动操作的 bug。
8. 总结与最佳实践
- 核心是
Date
对象:它是 JavaScript 日期时间处理的基础,理解其工作原理(基于时间戳)是关键。
- 创建日期要谨慎:优先用 ISO 字符串或时间戳,避免数字参数的月份陷阱。
- 获取和设置要小心副作用:
set
方法会修改原对象,建议先复制。
- 格式化用现代工具:简单场景用
Intl.DateTimeFormat
,复杂项目用 Day.js 或 date-fns。
- 时区处理要统一:与服务器交互时用 UTC 或时间戳,前端显示时适配本地时区。
- 优先用第三方库:Day.js 或 date-fns 能大幅提升开发效率,减少 bug。
通过掌握原生 Date
和现代工具库,你可以轻松应对各种日期时间需求。无论是简单的日期显示,还是复杂的跨时区计算,这些技巧都能让你事半功倍!