JS时间戳与日期字符串怎么互转

作者:异步漫步

时间:2026-11-03

浏览:612

深入浅出讲解JavaScript中时间戳与日期字符串的双向转换方案,涵盖13位毫秒与10位秒级换算、跨浏览器时区解析陷阱、补零格式化技巧与生产级通用转换函数封装。

文章目录
JS时间戳与日期字符串怎么互转
图示:JS 时间戳与日期字符串双向转换机制概览

在 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 对象作为数据枢纽。整个流转关系如下图所示:

JS时间戳与日期字符串怎么互转
图1:时间戳与日期字符串双向流转架构

二、日期字符串转时间戳(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. 标准化转换步骤

为了保证在不同浏览器与异常入参下的健壮性,生产环境中建议遵循“入参防御 → 格式规整 → 实例解析 → 有效性检验 → 精度适配”的标准流程:

JS时间戳与日期字符串怎么互转
图2:日期字符串转时间戳的标准处理步骤
⚠️ 移动端与 Safari 兼容性提醒

在 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 或包含时分秒的字符串会被按本地时间解析。

JS时间戳与日期字符串怎么互转
图3:时区解析机制与 8 小时偏差深度剖析

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"
JS时间戳与日期字符串怎么互转
图4:生产环境互转方法与场景选择决策树

核心互转方法对比速查表

转换需求 推荐使用方法 返回值类型 主要注意事项
标准字符串 → 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 兼容性故障,在日常开发中实现稳定可靠的日期与时间互转。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章