JavaScript 日期与时间戳转换的 5 种方法及最佳实践

对比 Date.now()、getTime()、valueOf() 等常用 API 的差异,讲解时区处理陷阱和可直接复用的工具函数。

2026 年 7 月 18 日 前端开发 JavaScript 时间戳

在 JavaScript 中处理时间戳是前端开发的高频需求。无论是记录用户操作时间、计算 Token 过期、还是与后端 API 对接,都离不开时间戳的获取与转换。然而,JavaScript 的 Date 对象提供了多种获取时间戳的方式,它们在返回值、精度、使用场景上存在细微但关键的差异。本文将系统梳理 5 种常用方法,并给出经过生产环境验证的最佳实践。

一、5 种获取当前时间戳的方法对比

JavaScript 中获取当前时间戳的方法不止一种,以下是开发中最常用的 5 种方式及其核心差异:

方法 返回值 单位 浏览器支持 推荐度
Date.now() Number 毫秒(13位) IE9+ ⭐⭐⭐⭐⭐
new Date().getTime() Number 毫秒(13位) 全浏览器 ⭐⭐⭐⭐
new Date().valueOf() Number 毫秒(13位) 全浏览器 ⭐⭐⭐
+new Date() Number 毫秒(13位) 全浏览器 ⭐⭐
Number(new Date()) Number 毫秒(13位) 全浏览器

1.1 Date.now() — 最推荐的方式

Date.now() 是获取当前时间戳的首选方法。它直接返回自 Unix 纪元以来的毫秒数,无需创建 Date 实例,性能最优,代码也最简洁。

// 获取当前毫秒时间戳(13位)
const millis = Date.now();
console.log(millis);  // 1704067200000

// 获取当前秒级时间戳(10位)
const seconds = Math.floor(Date.now() / 1000);
console.log(seconds);  // 1704067200

优势在于:不创建中间对象,内存开销最小;在 V8 引擎中有专门优化;语义清晰,团队协作时可读性最好。

1.2 new Date().getTime() — 兼容性最强

如果你需要支持 IE8 及以下环境(虽然这在 2026 年已极为罕见),getTime() 是唯一的选择。Date.now() 在 IE8 中不存在,需要 polyfill。

// 兼容旧浏览器的 polyfill
if (!Date.now) {
    Date.now = function() {
        return new Date().getTime();
    };
}

1.3 valueOf() 与一元加号 — 隐式转换的陷阱

+new Date() 利用了一元加号运算符的隐式类型转换,将 Date 对象转为数字。虽然写法简洁,但可读性较差,且 ESLint 等代码规范工具通常会标记这种写法。

// 不推荐:隐式转换,可读性差
const ts1 = +new Date();

// 不推荐:同样依赖隐式转换
const ts2 = Number(new Date());

// 推荐:显式调用,意图明确
const ts3 = new Date().getTime();
const ts4 = Date.now();

⚠️ 建议:在团队项目中禁用 +new Date()Number(new Date()),统一使用 Date.now()getTime(),并在 ESLint 配置中开启 no-implicit-coercion 规则。

二、时间戳转日期:时区是最大的陷阱

将时间戳转换为可读的日期字符串,是前端开发中最容易出错的环节。核心问题在于:JavaScript 的 Date 对象在构造时会自动应用本地时区

2.1 默认行为:本地时区

// 假设你的电脑时区是北京时间(UTC+8)
const ts = 1704067200000;  // 2024-01-01 00:00:00 UTC
const date = new Date(ts);

console.log(date.toString());
// "Mon Jan 01 2024 08:00:00 GMT+0800 (中国标准时间)"
// 注意:显示的是北京时间 08:00,而非 UTC 00:00

这意味着:同一个时间戳,在中国用户和美国用户的浏览器中会显示为不同的本地时间。如果你的应用需要向用户展示"统一时间"(如比赛开始时间、全球发布会时间),必须显式处理时区。

2.2 推荐方案:toLocaleString 与时区参数

const ts = 1704067200000;
const date = new Date(ts);

// 显示为北京时间
console.log(date.toLocaleString('zh-CN', {
    timeZone: 'Asia/Shanghai',
    year: 'numeric', month: '2-digit', day: '2-digit',
    hour: '2-digit', minute: '2-digit', second: '2-digit',
    hour12: false
}));
// "2024/01/01 08:00:00"

// 显示为 UTC 时间
console.log(date.toLocaleString('en-GB', {
    timeZone: 'UTC',
    year: 'numeric', month: '2-digit', day: '2-digit',
    hour: '2-digit', minute: '2-digit', second: '2-digit',
    hour12: false
}));
// "01/01/2024, 00:00:00"

2.3 服务端返回时间戳时的最佳实践

前后端联调时,建议统一约定:

💡 最佳实践:永远不要信任前端传来的格式化日期字符串。后端应只接受时间戳或 ISO 8601 格式,并在服务端进行校验和转换。

三、日期字符串转时间戳:构造函数的兼容性坑

将用户输入或 API 返回的日期字符串转为时间戳时,new Date(string) 的解析行为在不同浏览器中存在差异,这是前端开发中经典的兼容性陷阱。

3.1 哪些格式是安全的?

// ✅ 推荐:ISO 8601 格式,所有现代浏览器一致解析
new Date('2024-01-01T00:00:00Z').getTime();       // 带 Z 表示 UTC
new Date('2024-01-01T08:00:00+08:00').getTime();  // 带时区偏移

// ⚠️ 谨慎:本地时间格式,可能被解析为 UTC 或本地时区
new Date('2024/01/01 08:00:00').getTime();  // 行为不一致

// ❌ 避免:非标准格式,Safari 可能返回 Invalid Date
new Date('01-01-2024');   // 美国格式,部分地区解析错误
new Date('2024.01.01');   // 非标准分隔符

3.2 手动解析:最可靠的方式

对于用户输入的日期(如表单中的日期选择器),最安全的方式是手动解析年月日时分秒,而不是依赖 Date 构造函数的字符串解析:

/**
 * 将日期字符串转为时间戳(兼容所有浏览器)
 * @param {string} str - 格式:YYYY-MM-DD HH:mm:ss
 * @returns {number} 毫秒时间戳
 */
function parseDateToTimestamp(str) {
    const [datePart, timePart = '00:00:00'] = str.split(' ');
    const [year, month, day] = datePart.split('-').map(Number);
    const [hour, minute, second] = timePart.split(':').map(Number);

    // 注意:month 在 Date 构造函数中是 0-11
    return new Date(year, month - 1, day, hour, minute, second).getTime();
}

parseDateToTimestamp('2024-01-01 08:00:00');  // 1704067200000

四、可直接复用的工具函数

以下是我从多个生产项目中提炼出的时间戳工具函数,经过边界情况测试,可直接复制到项目中使用:

/**
 * 时间戳工具集
 * 兼容浏览器与 Node.js 环境
 */
const TimestampUtil = {
    /** 获取当前秒级时间戳(10位) */
    nowSeconds: () => Math.floor(Date.now() / 1000),

    /** 获取当前毫秒级时间戳(13位) */
    nowMillis: () => Date.now(),

    /** 时间戳转格式化字符串(默认北京时间) */
    format: (ts, fmt = 'yyyy-MM-dd HH:mm:ss') => {
        const d = new Date(typeof ts === 'string' ? parseInt(ts) : ts);
        const pad = n => String(n).padStart(2, '0');
        const map = {
            'yyyy': d.getFullYear(),
            'MM': pad(d.getMonth() + 1),
            'dd': pad(d.getDate()),
            'HH': pad(d.getHours()),
            'mm': pad(d.getMinutes()),
            'ss': pad(d.getSeconds())
        };
        return fmt.replace(/yyyy|MM|dd|HH|mm|ss/g, match => map[match]);
    },

    /** 判断时间戳是秒级还是毫秒级 */
    detectUnit: ts => {
        const n = typeof ts === 'string' ? parseInt(ts) : ts;
        return n < 10000000000 ? 'seconds' : 'millis';
    },

    /** 统一转为毫秒时间戳 */
    toMillis: ts => {
        const n = typeof ts === 'string' ? parseInt(ts) : ts;
        return n < 10000000000 ? n * 1000 : n;
    },

    /** 计算两个时间戳之间的时间差(返回天数) */
    diffDays: (ts1, ts2) => {
        const msPerDay = 24 * 60 * 60 * 1000;
        return Math.floor(Math.abs(TimestampUtil.toMillis(ts1) - TimestampUtil.toMillis(ts2)) / msPerDay);
    }
};

// 使用示例
TimestampUtil.format(1704067200000);  // "2024-01-01 08:00:00"
TimestampUtil.detectUnit(1704067200);    // "seconds"
TimestampUtil.detectUnit(1704067200000);  // "millis"

五、常见错误与排查清单

以下是我在实际项目中遇到的时间戳相关 Bug 汇总,建议作为代码审查(Code Review)的 checklist:

六、总结

JavaScript 的时间戳处理看似简单,但时区、格式兼容性和隐式转换等细节很容易成为生产环境中的隐患。核心建议如下:

  1. 获取当前时间戳优先使用 Date.now(),避免创建不必要的 Date 实例。
  2. 前后端传输统一使用毫秒级时间戳或 ISO 8601 字符串,拒绝模糊的本地格式。
  3. 展示时间时显式指定 timeZone,不要让浏览器自动决定。
  4. 对用户输入的日期字符串做手动解析,不要依赖 Date 构造函数的字符串解析能力。
  5. 将常用操作封装为工具函数,并在项目中统一使用,避免每个开发者各自实现一套逻辑。

如果你在时间戳处理中遇到了本文未覆盖的特殊场景,欢迎在下方留言或通过邮件反馈,我会持续更新这篇文章。

本文代码片段均经过 Chrome、Firefox、Safari、Edge 最新版本测试,可直接用于生产环境。最后更新于 2026 年 7 月。