JS时间戳与日期字符串怎么互转
深入浅出讲解JavaScript中时间戳与日期字符串的双向转换方案,涵盖13位毫秒与10位秒级换算、跨浏览器时区解析陷阱、补零格式化技巧与生产级通用转换函数封装。
在 JavaScript 开发中,时间戳与日期字符串互转的核心纽带是原生的 Date 对象。把日期字符串转换为时间戳,主要使用 new Date(str).getTime() 或 Date.parse(str);把时间戳转换为日期字符串,则先通过 new Date(timestamp) 生成实例,再借助格式化方法或提取各个时间分量进行拼接。虽然调用路径并不复杂,但实际业务中极易因为 10 位(秒级)与 13 位(毫秒级)精度混淆、以及跨浏览器对日期字符串时区解析规范的差异而产生 Bug。本文将系统梳理两者的互转路径、执行细节与避坑策略。
一、核心概念与流转模型
在深入转换代码之前,需要明确 JavaScript 对“时间戳”与“日期字符串”的定义与底层表示:
- 时间戳(Timestamp):指自协调世界时(UTC)1970年1月1日 00:00:00 起经过的时间偏移量。在 JavaScript 内部,时间戳的标准单位是毫秒(13 位数字),例如
1735689600000。而许多后端语言(如 Python、PHP、Go)或 Unix 数据库默认使用秒级时间戳(10 位数字),例如1735689600。两者混用时必须进行千倍换算。 - 日期字符串(Date String):指按特定人类可读格式排列的文本。常见的有 ISO 8601 标准格式(如
2025-05-01T08:00:00.000Z)、常规业务格式(如2025-05-01 16:00:00)以及斜杠分隔格式(如2025/05/01)。
原生运行时中,无论是从字符串提取时间戳,还是将时间戳渲染为字符串,都以 Date 对象作为数据枢纽。整个流转关系如下图所示:
二、日期字符串转时间戳(String → Timestamp)
将日期字符串转为时间戳,本质是将文本解析为对应的 UTC 毫秒数。常见的实现写法有以下几种:
1. 常用转换语法
// 方式一:实例化后调用 getTime()(推荐,表达清晰规范)
const dateStr = "2025-05-01 16:00:00";
const timestamp1 = new Date(dateStr).getTime();
// 方式二:直接调用静态方法 Date.parse()
const timestamp2 = Date.parse(dateStr);
// 方式三:一元加号运算符(隐式转换为数值)
const timestamp3 = +new Date(dateStr);
这三种方式在有效字符串入参下返回的结果完全相同,均为 13 位毫秒数值。在现代工程中,推荐使用 new Date(dateStr).getTime(),它的语法意图最为直接,且便于在实例化后增加合法性判断。
2. 毫秒级与秒级时间戳的转换
如果后端接口要求传递 10 位 Unix 时间戳(秒级),前端需要将获取到的 13 位毫秒数除以 1000 并向下取整:
const ms = new Date("2025-05-01 16:00:00").getTime();
// 转换为 10 位秒级时间戳
const unixTimestamp = Math.floor(ms / 1000);
console.log(unixTimestamp); // 示例输出:1746086400
3. 标准化转换步骤
为了保证在不同浏览器与异常入参下的健壮性,生产环境中建议遵循“入参防御 → 格式规整 → 实例解析 → 有效性检验 → 精度适配”的标准流程:
在 iOS Safari 或某些 WebView 环境中,直接传入带横杠的完整时间字符串(如 "2025-05-01 16:00:00")可能返回 NaN(Invalid Date)。为确保各端兼容,建议在解析前将横杠统一替换为斜杠:dateStr.replace(/-/g, '/')。
三、时间戳转日期字符串(Timestamp → String)
将时间戳转换为格式化字符串,需要先将时间戳传入 new Date(timestamp) 构造函数。这一步有两项关键前提:确保输入为数值类型,且必须是 13 位毫秒。
1. 10 位秒级时间戳的处理
若接收到的时间戳为 10 位整数,必须先乘以 1000 放大为毫秒,否则 Date 会把秒数当成毫秒,导致时间被解析到 1970 年 1 月:
function normalizeTimestamp(ts) {
// 如果是 10 位秒级时间戳(小于 1000 亿),乘以 1000
return ts < 1e11 ? ts * 1000 : ts;
}
const inputTs = 1746086400; // 后端传来的 10 位时间戳
const date = new Date(normalizeTimestamp(inputTs));
2. 原生内置转换方法
date.toISOString():输出标准 UTC 时间串,格式为2025-05-01T08:00:00.000Z,适合接口传输与跨系统持久化存储。date.toLocaleString('zh-CN', { hour12: false }):输出当前操作系统本地时区的格式化串,如"2025/5/1 16:00:00",适合快速本地显示。
3. 生产环境推荐:自定义补零格式化
由于 toLocaleString 的输出文本受用户系统语言、操作系统版本差异的影响,难以在前后端约定的统一格式(如 YYYY-MM-DD HH:mm:ss)下保持稳定。最可靠的做法是提取各时间分量,配合 String.prototype.padStart() 手动拼接:
function formatTimestamp(timestamp) {
if (!timestamp) return "";
const ms = timestamp < 1e11 ? timestamp * 1000 : Number(timestamp);
const date = new Date(ms);
if (isNaN(date.getTime())) {
return "";
}
const Y = date.getFullYear();
const M = String(date.getMonth() + 1).padStart(2, "0");
const D = String(date.getDate()).padStart(2, "0");
const h = String(date.getHours()).padStart(2, "0");
const m = String(date.getMinutes()).padStart(2, "0");
const s = String(date.getSeconds()).padStart(2, "0");
return `${Y}-${M}-${D} ${h}:${m}:${s}`;
}
// 示例运行
console.log(formatTimestamp(1746086400));
// 示例输出:2025-05-01 16:00:00 (以东八区为例)
四、容易混淆的深坑与辨析
1. 混淆辨析一:“new Date('2025-05-01') 得到的是本地零点吗?”
看似合理之处:很多开发者直觉认为,只写了年月日,没有写时间部分,Date 就会默认从当天的 00:00:00 开始计算。
实际机制与偏差:根据 ECMAScript 规范,仅包含日期的短 ISO 字符串(YYYY-MM-DD)会被默认按 UTC 零点解析。在东八区(UTC+8)环境中,UTC 00:00:00 转换为本地时间后变成了上午 08:00:00,生成的时间戳比本地真正零点整整大了 8 小时(28,800,000 毫秒)。相反,斜杠分隔符 2025/05/01 或包含时分秒的字符串会被按本地时间解析。
2. 混淆辨析二:“后端传过来的时间戳可以直接传给 new Date() 吗?”
看似合理之处:概念上两者都叫“时间戳”,直接调用 new Date(ts) 语法上不会报错。
实际机制与修正:当后端返回 10 位秒级时间戳(例如 1735689600)时,JavaScript 实际上将其当做自 1970 年经过的 1,735,689 秒(约 20 天),输出的年份会停留在 1970 年 1 月。务必通过长度判断或数值区间校验,在传入前乘以 1000。
3. 混淆辨析三:“只要在 Chrome 里能正常解析的字符串,上线就没问题吗?”
看似合理之处:V8 引擎容错率高,能够兼容许多非标字符串。
实际机制与修正:Safari 对日期字符串非常严格。例如 2025-05-01 12:00:00 在旧版 Safari 中会直接报 Invalid Date。如果必须解析非标准字符串,建议使用正则预处理为 2025/05/01 12:00:00,或者使用正则捕获年月日时分秒数字后,使用 new Date(year, month - 1, day, hours, minutes, seconds) 的构造形式。
五、生产级通用互转封装与决策速查
在实际项目封装通用时间工具库时,应当将入参类型识别、时区兼容、单位归一化与模板替换统一收口:
/**
* 生产级安全时间互转工具对象
*/
const DateUtils = {
/**
* 将各类输入(字符串、10位/13位时间戳)转换为标准 Date 实例
*/
parseToDate(input) {
if (!input) return null;
// 如果是数字或纯数字字符串
if (typeof input === 'number' || /^\d+$/.test(input)) {
const num = Number(input);
return new Date(num < 1e11 ? num * 1000 : num);
}
// 如果是日期字符串:替换横杠为斜杠提升移动端兼容性
if (typeof input === 'string') {
const normalized = input.trim().replace(/-/g, '/');
const d = new Date(normalized);
return isNaN(d.getTime()) ? null : d;
}
if (input instanceof Date && !isNaN(input.getTime())) {
return input;
}
return null;
},
/**
* 转换为 13位 或 10位 时间戳
*/
toTimestamp(input, isSeconds = false) {
const date = this.parseToDate(input);
if (!date) return null;
const ms = date.getTime();
return isSeconds ? Math.floor(ms / 1000) : ms;
},
/**
* 格式化输出为 YYYY-MM-DD HH:mm:ss
*/
format(input, pattern = "YYYY-MM-DD HH:mm:ss") {
const date = this.parseToDate(input);
if (!date) return "";
const pad = (n) => String(n).padStart(2, "0");
const map = {
YYYY: date.getFullYear(),
MM: pad(date.getMonth() + 1),
DD: pad(date.getDate()),
HH: pad(date.getHours()),
mm: pad(date.getMinutes()),
ss: pad(date.getSeconds())
};
return pattern.replace(/YYYY|MM|DD|HH|mm|ss/g, (matched) => map[matched]);
}
};
// 示例用法:
// 1. 字符串转时间戳
const t1 = DateUtils.toTimestamp("2025-05-01 16:00:00"); // 1746086400000
const t2 = DateUtils.toTimestamp("2025-05-01 16:00:00", true); // 1746086400
// 2. 时间戳转格式化字符串
const str1 = DateUtils.format(1746086400); // "2025-05-01 16:00:00"
const str2 = DateUtils.format(1746086400, "YYYY/MM/DD"); // "2025/05/01"
核心互转方法对比速查表
| 转换需求 | 推荐使用方法 | 返回值类型 | 主要注意事项 |
|---|---|---|---|
| 标准字符串 → 13位毫秒时间戳 | new Date(str).getTime() |
Number | 移动端建议先做 replace(/-/g, '/') 避免 Invalid Date |
| 标准字符串 → 10位秒级时间戳 | Math.floor(new Date(str) / 1000) |
Number | 向下整除,确保去除毫秒尾数 |
| 时间戳 → 本地固定格式字符串 | getFullYear() + padStart() |
String | 10 位时间戳必须先乘 1000;月份需 +1 |
| 时间戳 → 标准 UTC 传输字符串 | new Date(ts).toISOString() |
String | 输出结尾含 Z,为 0 时区时间 |
| 非标中文串(如“2025年5月1日”) | 正则提取各数字,传给构造函数 | Date / Number | new Date(y, m-1, d, ...) 注意月份索引从 0 开始 |
通过遵循上述流转机制与防御规则,可以杜绝时间戳跨端失真、时区 8 小时偏移以及 Safari 兼容性故障,在日常开发中实现稳定可靠的日期与时间互转。
D3.js 比例尺与坐标轴的配置详解
2026-11-05 16:54:46
JavaScript怎么遍历对象的Symbol键和不可枚举属性
2026-11-05 13:13:39
封装一个健壮的JS字符串判空工具函数
2026-11-05 12:16:14
JavaScript合并数组的三种常用方法:concat、扩展运算符与push
2026-11-05 11:22:29
JavaScript立即执行函数(IIFE)是什么及基本语法
2026-11-05 10:51:19








