正在加载,请稍候…

JavaScript 日期和时间:完整指南

全面解析 JavaScript 日期处理——Date 对象、时间戳、格式化、时区以及新的 Temporal API,包含每个常见日期操作的实用示例。

JavaScript 日期和时间:完整指南

Date 对象

JavaScript 的 Date 表示自 Unix 纪元(1970 年 1 月 1 日 00:00:00 UTC)以来的毫秒数。

// 创建日期
new Date()                          // 当前时刻
new Date(timestamp)                 // 从毫秒数
new Date('2026-05-26')              // 从 ISO 字符串(UTC 午夜)
new Date('2026-05-26T14:30:00Z')    // 带 UTC 时间的 ISO
new Date('2026-05-26T14:30:00+05:30') // 带时区偏移的 ISO
new Date(2026, 4, 26)               // 年、月(从0开始!)、日——本地时间
new Date(2026, 4, 26, 14, 30, 0)   // 带小时、分钟、秒

注意: 月份从 0 开始(0=一月,11=十二月),日期从 1 开始。这种不一致性经常让人困惑。

JavaScript 日期和时间:完整指南插图

获取当前时间戳

Date.now()              // 自纪元以来的毫秒数(最高效)
new Date().getTime()    // 相同
+new Date()             // 相同,通过强制转换(避免——可读性差)

提取组件

const d = new Date('2026-05-26T14:30:00Z');

// UTC 方法——独立于系统时区
d.getUTCFullYear()   // 2026
d.getUTCMonth()      // 4(五月,从0开始)
d.getUTCDate()       // 26(月中的日)
d.getUTCHours()      // 14
d.getUTCMinutes()    // 30
d.getUTCSeconds()    // 0
d.getUTCDay()        // 1(星期一,0=星期日)

// 本地方法——依赖于系统时区
d.getFullYear()      // 可能因时区而异
d.getMonth()         // 同样注意
d.getDate()
d.getHours()

规则: 除非你特别需要本地时间行为,否则使用 UTC 方法。混合使用会导致微妙的错误。

格式化日期

Intl.DateTimeFormat(内置,无需库)

const d = new Date('2026-05-26T14:30:00Z');

// 美国格式
new Intl.DateTimeFormat('en-US').format(d)
// => "5/26/2026"

// 英国格式
new Intl.DateTimeFormat('en-GB').format(d)
// => "26/05/2026"

// 完整可读格式
new Intl.DateTimeFormat('en-US', {
  year: 'numeric',
  month: 'long',
  day: 'numeric',
  hour: '2-digit',
  minute: '2-digit',
  timeZone: 'America/New_York',
}).format(d)
// => "May 26, 2026 at 10:30 AM"

// 相对时间(需要参考点)
new Intl.RelativeTimeFormat('en').format(-3, 'days')
// => "3 days ago"

ISO 8601 字符串

new Date().toISOString()
// => "2026-05-26T14:30:00.000Z"

// 仅日期
new Date().toISOString().split('T')[0]
// => "2026-05-26"

JavaScript 日期和时间:完整指南插图

手动格式化

function formatDate(date, format = 'YYYY-MM-DD') {
  const y = date.getUTCFullYear();
  const m = String(date.getUTCMonth() + 1).padStart(2, '0');
  const d = String(date.getUTCDate()).padStart(2, '0');
  const H = String(date.getUTCHours()).padStart(2, '0');
  const M = String(date.getUTCMinutes()).padStart(2, '0');

  return format
    .replace('YYYY', y)
    .replace('MM', m)
    .replace('DD', d)
    .replace('HH', H)
    .replace('mm', M);
}

formatDate(new Date(), 'YYYY-MM-DD HH:mm')
// => "2026-05-26 14:30"

日期运算

JavaScript 日期没有内置的算术方法。使用时间戳:

const now = new Date();

// 加天数
const nextWeek = new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000);

// 加月份(时区安全)
const nextMonth = new Date(now);
nextMonth.setUTCMonth(nextMonth.getUTCMonth() + 1);

// 天数差
function daysBetween(a, b) {
  const msPerDay = 24 * 60 * 60 * 1000;
  return Math.round((b.getTime() - a.getTime()) / msPerDay);
}

daysBetween(new Date('2026-01-01'), new Date('2026-05-26'))
// => 145

比较日期

const a = new Date('2026-05-26');
const b = new Date('2026-06-01');

// 比较时间戳
a < b   // true
a > b   // false
a <= b  // true

// 检查相等性(不能使用 == 或 === —— 比较对象引用)
a.getTime() === b.getTime()  // false
a.toISOString() === b.toISOString()  // false

// 日期是否在过去?
new Date('2026-01-01') < Date.now()  // true(自动强制转换有效)

时区

处理日期最困难的部分。JavaScript 的 Date 内部存储 UTC;显示受系统本地时区影响。

const d = new Date('2026-05-26T12:00:00Z');  // UTC 中午

// 在不同时区中这是什么时间?
d.toLocaleTimeString('en-US', { timeZone: 'America/New_York' })
// => "8:00:00 AM" (EDT, UTC-4)

d.toLocaleTimeString('en-US', { timeZone: 'Asia/Tokyo' })
// => "9:00:00 PM" (JST, UTC+9)

d.toLocaleTimeString('en-US', { timeZone: 'Europe/Berlin' })
// => "2:00:00 PM" (CEST, UTC+2)

转换为特定时区

// 获取特定时刻的时区 UTC 偏移量
function getTimezoneOffset(timezone, date = new Date()) {
  const utcDate = new Date(date.toLocaleString('en-US', { timeZone: 'UTC' }));
  const tzDate = new Date(date.toLocaleString('en-US', { timeZone: timezone }));
  return (utcDate - tzDate) / 60000;  // 分钟
}

// 将本地时间字符串解析为特定时区的时间
function parseDateInTimezone(dateStr, timezone) {
  return new Date(
    new Intl.DateTimeFormat('en-CA', {
      timeZone: timezone,
      year: 'numeric', month: '2-digit', day: '2-digit',
    }).format(new Date(dateStr))
  );
}

JavaScript 日期和时间:完整指南插图

最佳实践:存储 UTC,显示本地时间

// ✅ 以 UTC 存储时间戳(ISO 8601 或 Unix 毫秒)
const event = {
  title: '团队站会',
  startTime: '2026-05-26T09:00:00Z',  // UTC
  timezone: 'America/New_York',       // 创建者的时区
};

// 以用户的本地时区显示
function displayEventTime(event, userTimezone) {
  return new Intl.DateTimeFormat('en-US', {
    dateStyle: 'medium',
    timeStyle: 'short',
    timeZone: userTimezone,
  }).format(new Date(event.startTime));
}

常见模式

一天的开始 / 一天的结束(UTC)

function startOfDay(date) {
  const d = new Date(date);
  d.setUTCHours(0, 0, 0, 0);
  return d;
}

function endOfDay(date) {
  const d = new Date(date);
  d.setUTCHours(23, 59, 59, 999);
  return d;
}

是否为同一天?

function isSameDay(a, b, timezone = 'UTC') {
  const fmt = new Intl.DateTimeFormat('en-CA', {
    timeZone: timezone,
    year: 'numeric', month: '2-digit', day: '2-digit',
  });
  return fmt.format(a) === fmt.format(b);
}

格式化“时间之前”

function timeAgo(date) {
  const seconds = Math.floor((Date.now() - new Date(date).getTime()) / 1000);
  const rtf = new Intl.RelativeTimeFormat('en', { numeric: 'auto' });

  if (seconds < 60) return rtf.format(-seconds, 'second');
  if (seconds < 3600) return rtf.format(-Math.floor(seconds / 60), 'minute');
  if (seconds < 86400) return rtf.format(-Math.floor(seconds / 3600), 'hour');
  if (seconds < 2592000) return rtf.format(-Math.floor(seconds / 86400), 'day');
  if (seconds < 31536000) return rtf.format(-Math.floor(seconds / 2592000), 'month');
  return rtf.format(-Math.floor(seconds / 31536000), 'year');
}

timeAgo(new Date(Date.now() - 3 * 60 * 1000))   // "3 minutes ago"
timeAgo(new Date(Date.now() - 2 * 3600 * 1000)) // "2 hours ago"

Temporal API(即将推出的标准)

Temporal API 是一个新标准(截至 2026 年为 Stage 3 提案),通过为不同概念提供单独的类型来修复 Date 的设计缺陷:

// Polyfill: npm install @js-temporal/polyfill
import { Temporal } from '@js-temporal/polyfill';

// PlainDate:不带时间的日期
const date = Temporal.PlainDate.from('2026-05-26');
date.year;    // 2026
date.month;   // 5(从1开始,与 Date 不同!)
date.day;     // 26

// ZonedDateTime:日期 + 时间 + 时区
const zdt = Temporal.ZonedDateTime.from('2026-05-26T09:00:00[America/New_York]');
zdt.toInstant().epochMilliseconds;  // UTC 毫秒

// 算术
const tomorrow = date.add({ days: 1 });
const nextMonth = date.add({ months: 1 });

// 比较
Temporal.PlainDate.compare(date, tomorrow)  // -1 (date < tomorrow)

Temporal 提供了不可变的日期对象、正确的时区处理以及从 1 开始的月份——这些都是 Date 做错的地方。

→ 使用 日期时间转换器 在日期格式和时间戳之间进行转换。