JS怎么将13位时间戳转换为YYYY-MM-DD HH:mm:ss格式
详细讲解在 JavaScript 中如何将 13 位毫秒级时间戳转换为标准的 YYYY-MM-DD HH:mm:ss 格式,包含原生 5 步拆解法、月份偏移与补零机制、核心陷阱规避、方案对比与高健壮性封装。
核心做法:直接将 13 位毫秒数值传入 new Date(Number(timestamp)) 构造实例,依次读取年、月、日、时、分、秒各分量,注意月份必须加 1,再配合现代标准方法 String(val).padStart(2, '0') 补齐双位,最后使用模板字符串拼接输出即可。
一、核心快速实现:轻量标准转换函数
13 位时间戳是 UNIX 时间戳的毫秒级表现形式(例如 1711929600000)。在原生 JavaScript 中,最直接且性能极佳的转换方式是利用内置的 Date 对象读取各时间维度分量,并通过 padStart 实现两位数对齐:
function formatTimestamp(timestamp) {
// 1. 确保入参转换为数值类型并实例化 Date
const date = new Date(Number(timestamp));
// 2. 提取各项分量并补零(注意:getMonth() 必须 +1)
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');
// 3. 按 YYYY-MM-DD HH:mm:ss 格式拼装
return `${Y}-${M}-${D} ${H}:${m}:${s}`;
}
formatTimestamp(1711929600000);// 输出示例: "2024-04-01 08:00:00"(依据宿主时区输出)
该方案无需依赖任何第三方库,在 Chrome、Firefox、Edge、Safari 以及 Node.js 等全平台环境下均可原生直接运行。
二、原生 5 步转换流程拆解
为了让逻辑清晰易懂,在面对不同数据源与兼容性场景时能够举一反三,我们可以将转换过程拆解为 5 个标准步骤:
- 步骤一:入参清洗与 Date 实例化:前端常通过接口或 URL 参数获取时间戳,后端返回的字段可能是字符串类型(如
"1711929600000")。使用Number(timestamp)确保转化为数字,直接作为参数传入new Date()。 - 步骤二:提取日期分量:调用
date.getFullYear()获取 4 位年份,调用date.getDate()获取当月几号(注意不要误用getDay(),后者返回的是星期几)。 - 步骤三:月份基数校准:JavaScript 的
getMonth()方法返回值遵循0 ~ 11的索引设计(0 代表 1 月,11 代表 12 月)。因此,提取月份后必须执行 +1 操作。 - 步骤四:双位数规范对齐:对于 1~9 的单月份、日期或小时,需要补足为两位(如
4需显示为04)。现代 ES2017 提供了String.prototype.padStart(2, '0'),在字符长度小于 2 时会在左侧自动填充'0'。 - 步骤五:组合输出目标格式:通过 ES6 模板字符串语法,将各变量用
-、空格和:拼接成符合 ISO 8601 视觉规范的标准日期时间文本。
三、底层机制与必须避开的 3 个关键陷阱
在实际业务开发中,时间戳转换看似简单,却极其容易因忽略底层细节而产生线上 Bug。以下是必须重点核查的机制与陷阱:
1. 13 位与 10 位时间戳的区别
UNIX 时间戳存在两种标准计量尺度:10 位(秒级)与 13 位(毫秒级)。JavaScript 的 Date 对象以毫秒为基准单位:
- 13 位时间戳:代表自 1970 年 1 月 1 日 00:00:00 UTC 以来的毫秒数,直接传给
new Date(ts)即可。 - 10 位时间戳:代表秒数。如果直接传入
new Date(10位),会被当成毫秒解析,计算出来的年份会错误地停留在 1970 年。必须显式执行new Date(ts * 1000)。
2. 月份索引 0~11 陷阱
这是新手甚至中级开发者最常遗漏的细节。date.getMonth() 返回的是数组索引,若不加 1,四月份会被格式化为 03,一月份会被格式化为 00。请务必牢记 date.getMonth() + 1。
3. 宿主本地时区与 UTC 时间差
getHours()、getMinutes() 取出的是运行代码所在宿主机器的本地时间。在东八区(UTC+8)环境下,同一时间戳的小时数会比 UTC 环境大 8 小时。如果业务需要输出统一的标准时间(例如国际化服务),应改用 getUTCFullYear()、getUTCMonth()、getUTCHours() 等 UTC 对应方法。
四、方案对比:原生手写、Intl API 与 Day.js 如何选择
除了纯手动拆解分量外,JavaScript 生态中还有其他主流实现方案。它们在不同项目规模和开发场景下各有优劣:
| 对比维度 | 方案 A:原生手动拼装 (推荐) | 方案 B:原生 Intl.DateTimeFormat | 方案 C:Day.js / date-fns |
|---|---|---|---|
| 依赖体积 | 0 KB(无需任何依赖) | 0 KB(浏览器/Node 内置) | ~2 KB(需 npm 引入) |
| 运行性能 | 极高(简单的数值运算与拼接) | 良好(构造格式化器有微小开销) | 优秀(封装内部有轻微包装层) |
| 时区支持 | 需手动调用 UTC 方法或算偏移 | 内置 timeZone: 'Asia/Shanghai' |
通过时区插件轻松切换 |
| 代码可读性 | 逻辑直观,但有数行样板代码 | 配置参数较多,自定义分隔符繁琐 | 极高:dayjs(ts).format(...) |
| 最佳适用场景 | 中小型项目、基础工具库、轻量脚本 | 需原生多语言、严格时区对齐的项目 | 已有工程化构建、频繁进行日期计算的大型项目 |
方案 B 示例:使用现代内置 Intl.DateTimeFormat
function formatWithIntl(timestamp) {
const date = new Date(Number(timestamp));
const formatter = new Intl.DateTimeFormat('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false
});
// 将分量数组还原为 key-value 映射
const parts = formatter.formatToParts(date).reduce((acc, p) => {
acc[p.type] = p.value;
return acc;
}, {});
return `${parts.year}-${parts.month}-${parts.day} ${parts.hour}:${parts.minute}:${parts.second}`;
}
五、生产级通用封装函数(防御式编程)
在企业级开发中,直接暴露裸逻辑容易受到脏数据污染。一个稳健的格式化工具应当具备:空值判断、非法时间戳防御、10/13位自动兼容以及自定义模板支持:
/**
* 格式化时间戳为指定模板字符串
* @param {number|string} timestamp 10位或13位时间戳
* @param {string} format 格式模板,默认为 'YYYY-MM-DD HH:mm:ss'
* @returns {string} 格式化结果,非法入参返回空字符串或提示
*/
function formatDate(timestamp, format = 'YYYY-MM-DD HH:mm:ss') {
if (!timestamp) return '';
// 1. 自动兼容 10 位秒级时间戳
let ts = Number(timestamp);
if (String(timestamp).trim().length === 10) {
ts *= 1000;
}
// 2. 防御非法输入(如非纯数字字符串导致的 NaN)
const date = new Date(ts);
if (isNaN(date.getTime())) {
console.warn('[formatDate] 无效的时间戳入参:', timestamp);
return '';
}
// 3. 分量映射表
const opt = {
'Y+': String(date.getFullYear()),
'M+': String(date.getMonth() + 1).padStart(2, '0'),
'D+': String(date.getDate()).padStart(2, '0'),
'H+': String(date.getHours()).padStart(2, '0'),
'm+': String(date.getMinutes()).padStart(2, '0'),
's+': String(date.getSeconds()).padStart(2, '0')
};
// 4. 正则动态替换模板占位符
let result = format;
for (const k in opt) {
const reg = new RegExp(k);
if (reg.test(result)) {
result = result.replace(reg, opt[k]);
}
}
return result;
}
formatDate(1711929600000); // "2024-04-01 08:00:00"formatDate("1711929600000"); // "2024-04-01 08:00:00"(支持字符串输入)formatDate(1711929600); // "2024-04-01 08:00:00"(自动识别 10 位并补乘 1000)formatDate(null); // ""(安全降级,避免应用崩溃)
六、上线自查清单与常见问题排查
在将时间转换函数部署到生产环境之前,可以通过以下清单快速进行代码核对:
- 页面显示
NaN-NaN-NaN NaN:NaN:NaN是怎么回事?
原因是传入的timestamp无法解析为有效数字(例如传入了undefined、"null"或包含非数字字符的字符串)。通过isNaN(date.getTime())进行前置拦截可有效避免此问题。 - 转换出来的日期永远比实际少一个月?
检查是否漏写了getMonth() + 1。由于历史原因,JavaScript 的月份是从0开始计数的。 - 显示效果为
2024-4-1 8:5:3,如何补齐为标准格式?
直接在每个分量后链式调用String(...).padStart(2, '0')。老旧环境若不支持padStart,可采用三元表达式num < 10 ? '0' + num : num达成相同效果。 - 为什么本地开发显示正常,部署到服务器或用户端时相差数小时?
因为getHours()遵循运行环境的系统本地时区。若服务端 Docker 镜像默认时区为 UTC(0 时区),而国内前端为东八区(UTC+8),便会出现 8 小时的时差。前后端交互时建议统一采用 13 位 UTC 毫秒时间戳传输,展示层再统一根据客户端本地时区进行渲染。
JS转整数出现NaN怎么办?常见异常处理方案
2026-11-05 16:27:31
推荐几款免安装的在线JS压缩工具
2026-11-05 14:54:53
JS怎么使用JSON.stringify实现JSON格式化与缩进
2026-11-04 10:47:30
CodePen、JSFiddle 与 StackBlitz:主流 JS 在线编辑器对比
2026-11-03 21:07:49
JS字符串转数字后怎么正确判断是否为有效数值
2026-11-03 20:04:15
相关标签:








