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-082025年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.utcluxonDateTime),能轻松处理复杂时区逻辑。
  • 明确时区需求:在前端显示时,使用 Intl.DateTimeFormat 适配用户本地时区;在后端交互时,统一用 UTC。

7. 常见问题与解决方案

  • 问题 1:月份从 0 开始
    总是记得 getMonth()setMonth() 的月份需要加减 1,或者直接用第三方库规避这个问题。
  • 问题 2:格式化复杂
    优先选择 Intl.DateTimeFormat 或 Day.js,避免手动拼接字符串。
  • 问题 3:时区不一致
    使用时间戳或 toISOString() 作为数据传输格式,确保前后端时间一致。
  • 问题 4:日期计算复杂
    复杂计算交给 Day.js 或 date-fns,减少手动操作的 bug。

8. 总结与最佳实践

  1. 核心是 Date 对象:它是 JavaScript 日期时间处理的基础,理解其工作原理(基于时间戳)是关键。
  2. 创建日期要谨慎:优先用 ISO 字符串或时间戳,避免数字参数的月份陷阱。
  3. 获取和设置要小心副作用set 方法会修改原对象,建议先复制。
  4. 格式化用现代工具:简单场景用 Intl.DateTimeFormat,复杂项目用 Day.js 或 date-fns。
  5. 时区处理要统一:与服务器交互时用 UTC 或时间戳,前端显示时适配本地时区。
  6. 优先用第三方库:Day.js 或 date-fns 能大幅提升开发效率,减少 bug。

通过掌握原生 Date 和现代工具库,你可以轻松应对各种日期时间需求。无论是简单的日期显示,还是复杂的跨时区计算,这些技巧都能让你事半功倍!