JS原生实现日期格式化:不依赖库的代码示例

作者:函数茶馆时间:2026-11-11 15:39:37浏览:648

本文介绍在不依赖第三方库的情况下,使用原生JavaScript实现日期时间格式化的两种主流方案:基于正则替换与补零的自定义通用函数,以及现代标准API Intl.DateTimeFormat,并提供常见格式的直接可用代码与避坑指南。

在日常前端与 Node.js 开发中,展示格式化的日期(例如 YYYY-MM-DD HH:mm:ss)是一个极为普遍的需求。引入像 Day.js 或 Moment.js 这样的第三方库虽然省事,但会增加项目依赖和打包体积。现代 JavaScript 原生提供了足够完善的能力,开发者可以通过基于正则替换的轻量通用函数或官方标准的 Intl.DateTimeFormat API 两种途径,完全不依赖任何外部库实现各种格式的日期输出。

JS原生实现日期格式化:不依赖库的代码示例
图 1:原生 JavaScript 实现日期格式化的技术路线概览

一、轻量通用的自定义格式化函数(模板替换法)

对于大部分常规业务场景,开发者最希望能够传入类似 "YYYY-MM-DD HH:mm:ss" 的模板字符串,并自动得到对应的结果。基于原生 Date 对象的 getter 方法配合 ES2017 引入的 String.prototype.padStart(),可以写出一个兼具可读性与通用性的轻量格式化函数。

1. 完整实现代码

/**
 * 原生格式化日期函数
 * @param {Date|number|string} dateInput - 可转换为有效日期的参数
 * @param {string} pattern - 格式化模板,如 "YYYY-MM-DD HH:mm:ss"
 * @returns {string} 格式化后的字符串
 */
function formatDate(dateInput = new Date(), pattern = 'YYYY-MM-DD HH:mm:ss') {
  const date = dateInput instanceof Date ? dateInput : new Date(dateInput);

  // 校验日期有效性
  if (Number.isNaN(date.getTime())) {
    throw new TypeError('Invalid Date: 传入的参数无法转换为有效日期');
  }

  const padZero = (num, length = 2) => String(num).padStart(length, '0');

  const year = date.getFullYear();
  const month = date.getMonth() + 1; // getMonth 返回 0-11,需加 1
  const day = date.getDate();
  const hours = date.getHours();
  const minutes = date.getMinutes();
  const seconds = date.getSeconds();
  const milliseconds = date.getMilliseconds();

  // 规则替换映射表(长标记优先匹配,避免被短标记提前替换)
  const tokenMap = {
    YYYY: String(year),
    YY: String(year).slice(-2),
    MM: padZero(month),
    M: String(month),
    DD: padZero(day),
    D: String(day),
    HH: padZero(hours),
    H: String(hours),
    mm: padZero(minutes),
    m: String(minutes),
    ss: padZero(seconds),
    s: String(seconds),
    SSS: padZero(milliseconds, 3),
  };

  // 生成正则:匹配所有支持的 token
  const regex = /YYYY|YY|MM|M|DD|D|HH|H|mm|m|ss|s|SSS/g;

  return pattern.replace(regex, (match) => tokenMap[match]);
}

// 调用示例
const now = new Date('2026-11-10T14:05:09.042');

console.log(formatDate(now, 'YYYY-MM-DD HH:mm:ss'));
console.log(formatDate(now, 'YYYY/MM/DD'));
console.log(formatDate(now, 'YY年M月D日 HH:mm:ss.SSS'));
console.log(formatDate(now, 'HH:mm'));
控制台示例输出: 2026-11-10 14:05:09 2026/11/10 26年11月10日 14:05:09.042 14:05

2. 执行逻辑拆解

该函数的执行流程遵循清晰的数据流水线:从入参类型统一转化,到各维度时间提取,再到字典映射与正则匹配输出。

JS原生实现日期格式化:不依赖库的代码示例
图 2:自定义模板函数的数据处理阶段与关键工程节点

二、使用原生标准 API:Intl.DateTimeFormat

如果项目要求遵循 ECMAScript 国际化规范、需要处理特定语言环境(如中文“2026年11月10日 星期二”)或特定时区转换,推荐使用浏览器与 Node.js 均内置的 Intl.DateTimeFormat。

1. 基础用法:配置 Options

// 针对中文环境的基础格式化
const formatterCN = new Intl.DateTimeFormat('zh-CN', {
  year: 'numeric',
  month: '2-digit',
  day: '2-digit',
  hour: '2-digit',
  minute: '2-digit',
  second: '2-digit',
  hour12: false, // 24小时制
});

const targetDate = new Date('2026-11-10T14:30:45');
console.log(formatterCN.format(targetDate));
控制台示例输出: 2026/11/10 14:30:45

2. 进阶用法:利用 formatToParts 自由组装任意格式

format() 方法生成的连接符(如斜杠或短横线)受特定系统和区域规则约束。如果必须强制输出 YYYY-MM-DD HH:mm:ss 格式,可以调用 formatToParts(),它会将时间拆解成结构化对象数组,再由开发者自由拼接。

/**
 * 基于 Intl.DateTimeFormat 封装的精准格式化函数
 * @param {Date} date - 目标日期
 * @param {string} timeZone - 目标时区,默认为运行环境本地时区
 * @returns {string} 格式为 "YYYY-MM-DD HH:mm:ss" 的字符串
 */
function formatWithIntlParts(date = new Date(), timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone) {
  const formatter = new Intl.DateTimeFormat('en-US', {
    timeZone,
    year: 'numeric',
    month: '2-digit',
    day: '2-digit',
    hour: '2-digit',
    minute: '2-digit',
    second: '2-digit',
    hour12: false,
  });

  const parts = formatter.formatToParts(date);
  const partMap = {};

  // 将 parts 数组转化为键值对
  for (const { type, value } of parts) {
    if (type !== 'literal') {
      partMap[type] = value;
    }
  }

  // 强制组装目标格式
  return `${partMap.year}-${partMap.month}-${partMap.day} ${partMap.hour}:${partMap.minute}:${partMap.second}`;
}

console.log(formatWithIntlParts(new Date('2026-11-10T08:09:10')));
// 指定时区输出(例如东京时间 UTC+9)
console.log(formatWithIntlParts(new Date('2026-11-10T08:09:10Z'), 'Asia/Tokyo'));
控制台示例输出: 2026-11-10 08:09:10 2026-11-10 17:09:10
JS原生实现日期格式化:不依赖库的代码示例
图 3:Intl.DateTimeFormat 及其 formatToParts 部件拆解结构

三、高频场景的极简原生片段(即拷即用)

在某些轻量场景下,为了极短的代码量,开发者往往只需要单个特定格式。以下是几种常见模式的原生写法:

1. 仅提取当前日期的 YYYY-MM-DD(本地时间安全版)

function getLocalDateString(d = new Date()) {
  const pad = (n) => String(n).padStart(2, '0');
  return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
}

console.log(getLocalDateString()); // "2026-11-10"

注意:避免使用 new Date().toISOString().slice(0, 10) 来获取“今天”的日期。因为 toISOString() 固定输出 UTC 零时区时间。对于东八区(UTC+8)的用户,在早晨 00:00 至 07:59 期间调用该方法,会得到昨天的日期,导致日期漂移Bug。

2. 快速生成中文“年月日 星期X”

function formatChineseDate(date = new Date()) {
  return new Intl.DateTimeFormat('zh-CN', {
    year: 'numeric',
    month: 'long',
    day: 'numeric',
    weekday: 'long',
  }).format(date);
}

console.log(formatChineseDate(new Date('2026-11-10')));
// 输出示例:"2026年11月10日 星期二"

四、原生日期处理的三个关键陷阱

JavaScript 原生 Date 对象设计源自早期的 Java 1.0 API,存在诸多历史遗留特征。在编写原生格式化逻辑时,需要重点避开以下三点:

JS原生实现日期格式化:不依赖库的代码示例
图 4:原生 JavaScript 日期格式化的高频陷阱排查清单

五、方案选型与对比建议

为了在实际工程中做出恰当的技术选型,可根据场景复杂度参考下述对比矩阵:

考量维度 自定义模板函数(formatDate) Intl.DateTimeFormat 第三方库(Day.js / date-fns)
引入成本 0 依赖,直接嵌入工具函数(< 1KB) 0 依赖,原生标准内置(0KB) 需 npm 安装,打包体积 2KB ~ 20KB+
模板自由度 极高(完全受控于 pattern 占位符) 中(需通过 formatToParts 手动拼接) 极高(链式调用,API丰富)
国际化与本地化 较弱(需手动维护语言映射表) 极强(支持数百种语言、星期、月份全称) 较强(需单独加载对应 locale 包)
时区转换支持 需手动计算 UTC offset 原生支持 timeZone 参数转换 需加载 timezone 插件
推荐使用场景 常规业务、后台管理系统、日期展示 跨国多语言界面、精准时区展示 极复杂的日期加减运算、业务倒计时库
JS原生实现日期格式化:不依赖库的代码示例
图 5:原生 JavaScript 日期格式化方案决策与选型路线

综上所述,在现代前端开发中,除非项目有密集的日期算术计算(如链式日期加减、复杂的周期重合计算),绝大多数针对日期展示和格式化的需求,完全可以用不到 30 行的原生代码或标准的 Intl API 高效解决,无需引入沉重的外部包。

作者最新文章
相关文章
更多