JavaScript 如何优雅的实现一个时间处理插件

优雅的实现一个时间处理插件

1. UMD 模式解析

1.1 立即执行函数 (IIFE)

外层是一个立即执行函数,接收 globalfactory 两个参数。

global 参数说明:

javascript 复制代码
typeof window !== "undefined" ? window : this;

根据不同的运行环境,global 参数会指向:

  • 浏览器环境window 对象
  • Node.js 环境global 对象
  • 其他环境 :当前上下文 this

1.2 UMD 模块定义

通过条件判断支持多种模块系统:

javascript 复制代码
if (typeof define === "function" && define.amd) {
  // AMD 模式 (RequireJS)
  define(function () {
    return factory();
  });
} else if (typeof module === "object" && module.exports) {
  // CommonJS 模式 (Node.js)
  module.exports = factory();
} else {
  // 浏览器全局变量模式
  global.SurveyTimezone = factory();
}

支持的模块系统:

  • AMD:用于 RequireJS 等加载器
  • CommonJS:用于 Node.js 环境
  • 全局变量:用于直接在浏览器中使用

1.3 工厂函数解析

工厂函数 factory 返回插件的构造函数:

javascript 复制代码
function () {
    'use strict';

    // 构造函数定义
    function SurveyTimezone(options) {
    }

    // 原型方法定义
    SurveyTimezone.prototype = {
        constructor: SurveyTimezone, // 修复 constructor 指向
        version: '1.0.0',
        _init: function () {
        }
    }

    // 返回构造函数
    return SurveyTimezone;
}

核心组成部分:

  • 严格模式 :使用 'use strict' 确保代码质量
  • 构造函数 :定义 SurveyTimezone
  • 原型方法:通过原型链添加共享方法
  • 返回值:导出构造函数供外部使用

1.4 执行流程

UMD 模块的加载和执行流程:

  1. 立即执行:代码加载后立即执行 IIFE
  2. 环境检测:检测当前支持的模块系统(AMD / CommonJS / 全局变量)
  3. 工厂调用 :执行 factory() 函数,返回 SurveyTimezone 构造函数
  4. 模块导出:根据环境将构造函数导出到相应位置
javascript 复制代码
(function (global, factory) {
    // 环境检测和模块导出逻辑
}(typeof window !== "undefined" ? window : this, function () {
    // 工厂函数:创建并返回构造函数
}));

2. 单例模式实现

2.1 为什么使用单例模式?

在时区处理插件中,我们通常只需要一个全局实例来管理配置和状态:

  • 避免重复实例:防止多次实例化造成的内存浪费
  • 全局状态管理:统一管理时区配置、数据映射表等
  • 配置一致性:确保整个应用使用相同的时区设置

2.2 单例模式实现

2.2.1 私有实例存储
javascript 复制代码
// 闭包中的私有变量,存储单例实例
var instance = null;
2.2.2 构造函数实现
javascript 复制代码
function SurveyTimezone(options) {
    // 1. 如果已存在实例,直接返回
    if (instance) {
        return instance;
    }
    
    // 2. 确保通过 new 调用
    if (!(this instanceof SurveyTimezone)) {
        return new SurveyTimezone(options);
    }
    
    // 3. 初始化配置
    this.options = options || {};
    
    // 4. 保存单例实例
    instance = this;
    
    // 5. 执行初始化
    this._init();
    
    return instance;
}

实现要点:

  1. 实例检查:首次检查是否已存在实例,有则直接返回
  2. new 检查 :确保即使不用 new 关键字也能正常工作
  3. 配置初始化:保存传入的配置选项
  4. 实例保存:将当前实例保存到闭包变量中
  5. 初始化执行:调用内部初始化方法
2.2.3 静态方法
javascript 复制代码
/**
 * 获取单例实例
 */
SurveyTimezone.getInstance = function (options) {
    if (!instance) {
        instance = new SurveyTimezone(options);
    }
    return instance;
}

/**
 * 重置单例(用于测试)
 */
SurveyTimezone.resetInstance = function () {
    instance = null;
}

2.3 使用方式

方式一:使用 new 关键字
javascript 复制代码
const instance1 = new SurveyTimezone({ timezone: 'Asia/Shanghai' });
const instance2 = new SurveyTimezone({ timezone: 'America/New_York' });

console.log(instance1 === instance2); // true(返回同一个实例)
方式二:使用 getInstance 静态方法
javascript 复制代码
const instance = SurveyTimezone.getInstance({ timezone: 'Asia/Shanghai' });
方式三:不使用 new(自动转换)
javascript 复制代码
const instance = SurveyTimezone({ timezone: 'Asia/Shanghai' });

2.4 单例模式的优势

优势 说明
内存优化 只创建一个实例,减少内存占用
状态一致 全局共享同一个实例,避免状态不一致
易于管理 集中管理配置和数据
防止冲突 避免多个实例之间的配置冲突

2.5 完整示例

javascript 复制代码
// 第一次创建实例
const timezone1 = new SurveyTimezone({ 
    timezone: 'Asia/Shanghai',
    locale: 'zh-CN'
});

// 第二次尝试创建(返回第一次的实例)
const timezone2 = new SurveyTimezone({ 
    timezone: 'America/New_York'  // 这个配置会被忽略
});

console.log(timezone1 === timezone2);        // true
console.log(timezone1.options.timezone);     // 'Asia/Shanghai'

// 重置单例后可以创建新实例
SurveyTimezone.resetInstance();
const timezone3 = new SurveyTimezone({ 
    timezone: 'Europe/London'
});

console.log(timezone1 === timezone3);        // false
console.log(timezone3.options.timezone);     // 'Europe/London'

2.6 初始化时传入日期时间

从 v1.0.0 开始,SurveyTimezone 支持在初始化时传入日期时间字符串或时间戳,使其更加灵活实用。

2.6.1 初始化方式

构造函数签名:

javascript 复制代码
new SurveyTimezone(input, format)

参数说明:

参数 类型 必填 说明
input `string number Date
format string 日期格式(当 input 为字符串时使用)
2.6.2 初始化示例

方式1:传入日期字符串

javascript 复制代码
const tz = new SurveyTimezone('2025-10-28 14:30:45');
console.log(tz.getDate());      // Date 对象
console.log(tz.format());       // '2025-10-28 14:30:45'

方式2:传入时间戳

javascript 复制代码
const tz = new SurveyTimezone(1698484245000);
console.log(tz.getDate());      // Date 对象
console.log(tz.format());       // 对应的日期时间字符串

方式3:传入日期字符串和格式

javascript 复制代码
const tz = new SurveyTimezone('28/10/2025', 'DD/MM/YYYY');
console.log(tz.format('YYYY-MM-DD'));  // '2025-10-28'

方式4:传入 Date 对象

javascript 复制代码
const tz = new SurveyTimezone(new Date());
console.log(tz.format());       // 当前时间

方式5:传入配置对象

javascript 复制代码
const tz = new SurveyTimezone({
    date: '2025-10-28 14:30:45',
    timezone: 'Asia/Shanghai',
    locale: 'zh-CN'
});
console.log(tz.getDate());      // Date 对象
console.log(tz.options);        // { date: '...', timezone: '...', locale: '...' }

方式6:不传参数(默认当前时间)

javascript 复制代码
const tz = new SurveyTimezone();
console.log(tz.format());       // 当前时间
2.6.3 新增实例方法

getDate() - 获取日期对象

javascript 复制代码
const tz = new SurveyTimezone('2025-10-28 14:30:45');
const date = tz.getDate();
console.log(date);  // Date 对象

setDate() - 设置日期(支持链式调用)

javascript 复制代码
const tz = new SurveyTimezone();

// 设置新日期
tz.setDate('2025-12-25 00:00:00');
console.log(tz.format());  // '2025-12-25 00:00:00'

// 链式调用
const result = tz.setDate('2026-01-01').format('YYYY/MM/DD');
console.log(result);  // '2026/01/01'
2.6.4 format 方法增强

现在 format 方法可以在不传参数时格式化实例的日期:

javascript 复制代码
const tz = new SurveyTimezone('2025-10-28 14:30:45');

// 格式化实例日期(无参数)
tz.format();                    // '2025-10-28 14:30:45'(默认格式)

// 格式化实例日期(指定格式)
tz.format('YYYY年MM月DD日');    // '2025年10月28日'

// 格式化指定日期
tz.format(new Date(), 'YYYY-MM-DD');  // 格式化其他日期
2.6.5 完整工作流示例
javascript 复制代码
// 场景:接收用户输入 → 解析 → 处理 → 格式化输出

// 1. 用户输入欧洲格式日期
const userInput = '28/10/2025';

// 2. 创建实例并解析
const tz = new SurveyTimezone(userInput, 'DD/MM/YYYY');

// 3. 验证解析结果
console.log(tz.getDate());  // Date 对象

// 4. 格式化为不同格式输出
console.log(tz.format('YYYY-MM-DD'));      // '2025-10-28'(ISO格式)
console.log(tz.format('YYYY年MM月DD日'));  // '2025年10月28日'(中文)
console.log(tz.format('MMM DD, yyyy'));    // 'Oct 28, 2025'(英文)

// 5. 修改日期并重新格式化
tz.setDate('2025-12-25');
console.log(tz.format('YYYY年MM月DD日'));  // '2025年12月25日'
2.6.6 与单例模式的配合

由于采用单例模式,第一次初始化时传入的日期会被保存,后续创建实例会返回同一个实例:

javascript 复制代码
// 第一次创建,指定日期
const tz1 = new SurveyTimezone('2025-10-28 14:30:45');
console.log(tz1.format());  // '2025-10-28 14:30:45'

// 第二次创建,尝试传入不同日期(但返回的是同一个实例)
const tz2 = new SurveyTimezone('2026-01-01 00:00:00');
console.log(tz2.format());  // '2025-10-28 14:30:45'(仍然是第一次的日期)
console.log(tz1 === tz2);   // true(同一个实例)

// 如果需要新的日期,可以使用 setDate 方法
tz2.setDate('2026-01-01 00:00:00');
console.log(tz2.format());  // '2026-01-01 00:00:00'
console.log(tz1.format());  // '2026-01-01 00:00:00'(tz1 也变了,因为是同一个实例)

// 或者先重置单例
SurveyTimezone.resetInstance();
const tz3 = new SurveyTimezone('2026-01-01 00:00:00');
console.log(tz3.format());  // '2026-01-01 00:00:00'(新实例,新日期)
2.6.7 错误处理

当传入无效日期时,会自动使用当前时间:

javascript 复制代码
// 无效日期字符串
const tz1 = new SurveyTimezone('invalid date');
console.log(tz1.getDate());  // 当前时间的 Date 对象

// 重置单例
SurveyTimezone.resetInstance();

// 无效时间戳
const tz2 = new SurveyTimezone(NaN);
console.log(tz2.getDate());  // 当前时间的 Date 对象
2.6.8 方法对比表
方法 类型 参数 返回值 说明
new SurveyTimezone(input, format) 构造函数 日期输入 实例 创建实例并初始化日期
getDate() 实例方法 - Date 获取实例的日期对象
setDate(input, format) 实例方法 日期输入 this 设置实例日期,支持链式调用
format(date, format) 实例方法 可选 string 格式化日期(无参数时格式化实例日期)
parse(dateString, format) 实例方法 必填 Date|null 解析日期字符串

3. format 方法详解

3.1 方法说明

format 方法用于格式化日期对象,兼容 dayjslaydate 两种流行的日期格式化风格。

方法签名:

javascript 复制代码
// 原型方法
instance.format(date, format)

// 静态方法(向后兼容)
SurveyTimezone.format(date, format)

参数:

参数 类型 必填 默认值 说明
date Date - 要格式化的 JavaScript Date 对象
format string 'YYYY-MM-DD HH:mm:ss' 格式化模板字符串

返回值:

  • 类型string
  • 说明 :格式化后的日期字符串,无效日期返回空字符串 ''

3.2 支持的格式化标记

3.2.1 年份标记
标记 说明 示例输出 兼容性
YYYY 四位年份 2025 dayjs
yyyy 四位年份 2025 laydate
YY 两位年份 25 dayjs
y 两位年份 25 laydate
3.2.2 月份标记
标记 说明 示例输出 兼容性
MMM 英文月份缩写 Jan, Feb, Mar... 通用
MM 两位月份(补零) 01, 02... 12 通用
M 月份(不补零) 1, 2... 12 通用
3.2.3 日期标记
标记 说明 示例输出 兼容性
DD 两位日期(补零) 01, 02... 31 dayjs
dd 两位日期(补零) 01, 02... 31 laydate
D 日期(不补零) 1, 2... 31 dayjs
d 日期(不补零) 1, 2... 31 laydate
3.2.4 时间标记
标记 说明 示例输出 兼容性
HH 24小时制小时(补零) 00, 01... 23 通用
H 24小时制小时(不补零) 0, 1... 23 通用
mm 分钟(补零) 00, 01... 59 通用
m 分钟(不补零) 0, 1... 59 通用
ss 秒(补零) 00, 01... 59 通用
s 秒(不补零) 0, 1... 59 通用
3.2.5 毫秒标记
标记 说明 示例输出 兼容性
SSS 三位毫秒 000, 001... 999 dayjs

3.3 使用示例

3.3.1 基础用法
javascript 复制代码
const tz = new SurveyTimezone();
const now = new Date('2025-10-28 14:30:45.123');

// 默认格式
tz.format(now);  // '2025-10-28 14:30:45'

// 自定义格式
tz.format(now, 'YYYY/MM/DD');  // '2025/10/28'
tz.format(now, 'HH:mm:ss');    // '14:30:45'
3.3.2 dayjs 风格格式
javascript 复制代码
const tz = new SurveyTimezone();
const now = new Date('2025-10-28 14:30:45.123');

tz.format(now, 'YYYY-MM-DD HH:mm:ss');      // '2025-10-28 14:30:45'
tz.format(now, 'YYYY-MM-DD HH:mm:ss.SSS');  // '2025-10-28 14:30:45.123'
tz.format(now, 'YY/M/D H:m:s');             // '25/10/28 14:30:45'
tz.format(now, 'MMM DD, YYYY');             // 'Oct 28, 2025'
3.3.3 laydate 风格格式
javascript 复制代码
const tz = new SurveyTimezone();
const now = new Date('2025-10-28 14:30:45');

tz.format(now, 'yyyy-MM-dd HH:mm:ss');  // '2025-10-28 14:30:45'
tz.format(now, 'yyyy年MM月dd日');        // '2025年10月28日'
tz.format(now, 'y-M-d H:m:s');          // '25-10-28 14:30:45'
tz.format(now, 'dd/MM/yyyy');           // '28/10/2025'
3.3.4 中文日期格式
javascript 复制代码
const tz = new SurveyTimezone();
const now = new Date('2025-10-28 14:30:45');

tz.format(now, 'YYYY年MM月DD日');               // '2025年10月28日'
tz.format(now, 'YYYY年MM月DD日 HH时mm分ss秒');   // '2025年10月28日 14时30分45秒'
tz.format(now, 'yyyy年MM月dd日 HH:mm');         // '2025年10月28日 14:30'
3.3.5 静态方法调用(无需实例化)
javascript 复制代码
const now = new Date('2025-10-28 14:30:45');

// 直接使用静态方法
SurveyTimezone.format(now, 'YYYY-MM-DD');      // '2025-10-28'
SurveyTimezone.format(now, 'yyyy年MM月dd日');  // '2025年10月28日'
SurveyTimezone.format(now, 'MMM DD, yyyy');    // 'Oct 28, 2025'

3.4 常用格式模板

格式模板 输出示例 使用场景
YYYY-MM-DD 2025-10-28 标准日期格式
YYYY-MM-DD HH:mm:ss 2025-10-28 14:30:45 完整日期时间
yyyy年MM月dd日 2025年10月28日 中文日期
MMM DD, yyyy Oct 28, 2025 英文日期
YYYY/MM/DD HH:mm 2025/10/28 14:30 简短日期时间
HH:mm:ss 14:30:45 仅时间
YY-M-D 25-10-28 简短日期
YYYY-MM-DD HH:mm:ss.SSS 2025-10-28 14:30:45.123 带毫秒

3.5 错误处理

javascript 复制代码
const tz = new SurveyTimezone();

// 无效日期返回空字符串
tz.format(null);                    // ''
tz.format(undefined);               // ''
tz.format(new Date('invalid'));     // ''
tz.format('2025-10-28');            // ''(字符串不是 Date 对象)

// 有效日期
tz.format(new Date());              // '2025-10-28 14:30:45'(当前时间)

3.6 实现原理

format 方法采用正则替换策略实现格式化:

  1. 标记解析 :将格式字符串中的标记(如 YYYYMM)识别出来
  2. 长度优先 :按标记长度从长到短处理,避免 YYYYYY 误替换
  3. 顺序处理:依次替换每个标记为对应的日期值
  4. 类型转换:使用 JavaScript Date 对象的原生方法获取年、月、日等值

关键代码逻辑:

javascript 复制代码
// 标记处理顺序(长的在前,短的在后)
var tokens = ['YYYY', 'yyyy', 'MMM', 'SSS', 'MM', 'DD', 'dd', 'HH', 'mm', 'ss', 'YY', 'M', 'D', 'd', 'H', 'm', 's', 'y'];

// 依次替换每个标记
for (var i = 0; i < tokens.length; i++) {
    var token = tokens[i];
    if (result.indexOf(token) !== -1) {
        result = result.replace(new RegExp(token, 'g'), matches[token]());
    }
}

3.7 原型方法 vs 静态方法

特性 原型方法 静态方法
调用方式 instance.format() SurveyTimezone.format()
是否需要实例化 ✅ 需要 ❌ 不需要
访问实例属性 ✅ 可以 ❌ 不可以
推荐使用场景 面向对象编程 工具函数调用
性能 略优 略低(多一层调用)

关系说明:

  • 静态方法内部调用原型方法实现
  • 两种方式返回结果完全一致
  • 静态方法保证向后兼容性
javascript 复制代码
// 静态方法实现(调用原型方法)
SurveyTimezone.format = function (date, format) {
    return SurveyTimezone.prototype.format.call(null, date, format);
}

4. parse 方法详解

4.1 方法说明

parse 方法用于将日期时间字符串解析为 JavaScript Date 对象,支持多种常见格式的自动识别。

方法签名:

javascript 复制代码
// 原型方法
instance.parse(dateString, format)

// 静态方法(向后兼容)
SurveyTimezone.parse(dateString, format)

参数:

参数 类型 必填 默认值 说明
dateString `string number` -
format string - 可选的格式模板,用于指定解析格式

返回值:

  • 类型Date | null
  • 说明 :解析成功返回 Date 对象,失败返回 null

4.2 支持的日期格式

4.2.1 自动识别格式(无需指定 format)
格式 示例 说明
ISO 8601 2025-10-28T14:30:45.123Z JavaScript 原生支持
标准格式(带时分秒毫秒) 2025-10-28 14:30:45.123 常用格式
标准格式(带时分秒) 2025-10-28 14:30:45 常用格式
标准格式(带时分) 2025-10-28 14:30 常用格式
标准日期 2025-10-28 仅日期
斜杠格式(带时间) 2025/10/28 14:30:45 常用格式
斜杠格式(日期) 2025/10/28 常用格式
中文格式(带时间) 2025年10月28日 14时30分45秒 中文日期时间
中文格式(日期) 2025年10月28日 中文日期
欧洲格式 28/10/2025 DD/MM/YYYY
美式格式 10-28-2025 MM-DD-YYYY
时间戳 1698484245000 毫秒时间戳
4.2.2 指定格式解析

当自动识别失败时,可以指定 format 参数明确告知解析格式:

javascript 复制代码
const tz = new SurveyTimezone();

// 指定格式解析
tz.parse('28-10-2025', 'DD-MM-YYYY');
tz.parse('10/28/2025', 'MM/DD/YYYY');
tz.parse('25/10/28 14:30', 'YY/MM/DD HH:mm');

支持的格式标记:

  • YYYY / yyyy - 四位年份
  • YY / yy - 两位年份(00-49 → 2000-2049,50-99 → 1950-1999)
  • MM - 月份
  • DD / dd - 日期
  • HH - 小时
  • mm - 分钟
  • ss - 秒
  • SSS - 毫秒

4.3 使用示例

4.3.1 基础用法(自动识别)
javascript 复制代码
const tz = new SurveyTimezone();

// 标准格式
tz.parse('2025-10-28 14:30:45');           // Date 对象
tz.parse('2025-10-28');                    // Date 对象

// 斜杠格式
tz.parse('2025/10/28 14:30:45');           // Date 对象
tz.parse('2025/10/28');                    // Date 对象

// 中文格式
tz.parse('2025年10月28日');                 // Date 对象
tz.parse('2025年10月28日 14时30分45秒');    // Date 对象

// 时间戳
tz.parse(1698484245000);                   // Date 对象

// 带毫秒
tz.parse('2025-10-28 14:30:45.123');       // Date 对象
4.3.2 指定格式解析
javascript 复制代码
const tz = new SurveyTimezone();

// 欧洲日期格式(DD/MM/YYYY)
tz.parse('28/10/2025', 'DD/MM/YYYY');

// 美式日期格式(MM/DD/YYYY)
tz.parse('10/28/2025', 'MM/DD/YYYY');

// 短年份格式
tz.parse('25/10/28', 'YY/MM/DD');          // 2025-10-28

// 自定义格式
tz.parse('28-10-2025 14:30', 'DD-MM-YYYY HH:mm');
4.3.3 错误处理
javascript 复制代码
const tz = new SurveyTimezone();

// 无效输入返回 null
tz.parse(null);                            // null
tz.parse(undefined);                       // null
tz.parse('');                              // null
tz.parse('invalid date');                  // null
tz.parse('2025-13-40');                    // null(无效日期)

// 类型检查
const result = tz.parse('2025-10-28');
if (result) {
    console.log('解析成功:', result);
} else {
    console.log('解析失败');
}
4.3.4 静态方法调用
javascript 复制代码
// 直接使用静态方法,无需实例化
const date1 = SurveyTimezone.parse('2025-10-28');
const date2 = SurveyTimezone.parse('2025/10/28');
const date3 = SurveyTimezone.parse('2025年10月28日');
const date4 = SurveyTimezone.parse('28-10-2025', 'DD-MM-YYYY');
4.3.5 parse + format 组合使用
javascript 复制代码
const tz = new SurveyTimezone();

// 解析后格式化输出
const parsedDate = tz.parse('2025-10-28 14:30:45');
if (parsedDate) {
    console.log(tz.format(parsedDate, 'YYYY年MM月DD日'));        // '2025年10月28日'
    console.log(tz.format(parsedDate, 'MMM DD, yyyy'));         // 'Oct 28, 2025'
    console.log(tz.format(parsedDate, 'HH:mm:ss'));             // '14:30:45'
}

// 格式转换
const input = '28/10/2025';
const date = tz.parse(input, 'DD/MM/YYYY');
const output = tz.format(date, 'YYYY-MM-DD');
console.log(output);  // '2025-10-28'

4.4 解析流程

parse 方法采用多层次解析策略

javascript 复制代码
输入值
  ↓
┌─────────────────────────────────┐
│ 1. 类型检查                      │
│    - null/undefined → null       │
│    - Date 对象 → 验证后返回      │
│    - 数字 → 时间戳解析           │
└─────────────────────────────────┘
  ↓
┌─────────────────────────────────┐
│ 2. 格式化参数检查                │
│    - 有 format → 使用格式模板解析 │
│    - 无 format → 自动识别         │
└─────────────────────────────────┘
  ↓
┌─────────────────────────────────┐
│ 3. 原生解析尝试                  │
│    - new Date(dateString)        │
│    - 成功 → 返回                 │
│    - 失败 → 继续                 │
└─────────────────────────────────┘
  ↓
┌─────────────────────────────────┐
│ 4. 正则匹配解析                  │
│    - 遍历预定义格式列表          │
│    - 匹配成功 → 返回             │
│    - 全部失败 → 返回 null        │
└─────────────────────────────────┘

4.5 常见场景示例

场景1:表单日期输入
javascript 复制代码
const tz = new SurveyTimezone();

// 用户输入的日期字符串
const userInput = document.getElementById('dateInput').value;  // '2025-10-28'
const date = tz.parse(userInput);

if (date) {
    // 转换为显示格式
    const displayText = tz.format(date, 'YYYY年MM月DD日');
    console.log(displayText);  // '2025年10月28日'
}
场景2:API 数据转换
javascript 复制代码
const tz = new SurveyTimezone();

// API 返回的日期字符串
const apiData = {
    createdAt: '2025-10-28T14:30:45.123Z',
    updatedAt: '2025/10/28 14:30:45'
};

// 解析并格式化
const createdDate = tz.parse(apiData.createdAt);
const updatedDate = tz.parse(apiData.updatedAt);

console.log(tz.format(createdDate, 'YYYY-MM-DD HH:mm:ss'));
console.log(tz.format(updatedDate, 'YYYY-MM-DD HH:mm:ss'));
场景3:日期格式统一化
javascript 复制代码
const tz = new SurveyTimezone();

// 不同格式的日期数组
const dates = [
    '2025-10-28',
    '2025/10/28',
    '2025年10月28日',
    '28/10/2025'  // 需要指定格式
];

// 统一转换为标准格式
const normalized = dates.map((dateStr, index) => {
    const format = index === 3 ? 'DD/MM/YYYY' : undefined;
    const date = tz.parse(dateStr, format);
    return date ? tz.format(date, 'YYYY-MM-DD') : null;
});

console.log(normalized);  // ['2025-10-28', '2025-10-28', '2025-10-28', '2025-10-28']

4.6 性能考虑

最佳实践:

  1. 优先使用标准格式:ISO 8601 格式解析最快
  2. 指定格式模板:已知格式时指定 format 参数可跳过自动识别
  3. 缓存解析结果:避免重复解析相同字符串
  4. 提前验证:在解析前进行基本格式验证
javascript 复制代码
const tz = new SurveyTimezone();

// ❌ 不推荐:每次都自动识别
for (let i = 0; i < 1000; i++) {
    tz.parse('28/10/2025');
}

// ✅ 推荐:指定格式
for (let i = 0; i < 1000; i++) {
    tz.parse('28/10/2025', 'DD/MM/YYYY');
}

4.7 与 format 方法的配合

parseformat 是互补的两个方法:

方法 输入 输出 用途
parse 字符串 → Date 将日期字符串转换为 Date 对象 数据输入、解析
format Date → 字符串 将 Date 对象转换为格式化字符串 数据显示、输出

完整的数据流:

javascript 复制代码
const tz = new SurveyTimezone();

// 数据输入 → 处理 → 输出
const input = '28/10/2025';                          // 用户输入
const date = tz.parse(input, 'DD/MM/YYYY');          // 解析为 Date 对象
const output = tz.format(date, 'YYYY年MM月DD日');    // 格式化为显示文本

console.log(output);  // '2025年10月28日'

4.8 原型方法 vs 静态方法

特性 原型方法 静态方法
调用方式 instance.parse() SurveyTimezone.parse()
是否需要实例化 ✅ 需要 ❌ 不需要
访问实例属性 ✅ 可以 ❌ 不可以
推荐使用场景 面向对象编程 工具函数调用

关系说明:

javascript 复制代码
// 静态方法实现(调用原型方法)
SurveyTimezone.parse = function (dateString, format) {
    // 使用原型对象作为上下文,以便访问内部方法
    return SurveyTimezone.prototype.parse.call(SurveyTimezone.prototype, dateString, format);
}

技术说明:

静态方法 parse 内部调用原型方法时,需要使用 SurveyTimezone.prototype 作为上下文(this),而不是 null。这是因为原型方法中可能会调用其他内部方法(如 _parseWithFormat),如果 thisnull 会导致错误。

源码

javascript 复制代码
/**
 * SurveyTimezone - 时间处理插件
 * @description 专门用于处理调查问卷中的时区转换和显示问题
 * @version 1.0.0
 * @author wjxcom
 */

(function (global, factory) {
    // UMD模式支持 - 兼容AMD、CommonJS和全局变量
    if (typeof define === 'function' && define.amd) {
        // AMD模式
        define(function () { return factory(); });
    } else if (typeof module === 'object' && module.exports) {
        // CommonJS模式
        module.exports = factory();
    } else {
        // 浏览器全局变量模式
        global.SurveyTimezone = factory();
    }
}(typeof window !== 'undefined' ? window : this, function () {
    'use strict';
    
    /**
     * 单例实例存储
     * @private
     */
    var instance = null;
    
    /**
     * 时区数据映射表(用于快速查找)
     * @private
     */
    var timezoneDataMap = {};


    /**
     * SurveyTimezone 主类(单例模式)
     * @param {string|number|Date|Object} input - 日期时间字符串、时间戳、Date对象或配置对象
     * @param {string} format - 可选的日期格式(当 input 为字符串时使用)
     * @returns {SurveyTimezone} 单例实例
     * @description 采用单例模式,多次实例化返回同一个对象
     * @example
     *   new SurveyTimezone('2025-10-28 14:30:45');
     *   new SurveyTimezone(1698484245000);
     *   new SurveyTimezone('28/10/2025', 'DD/MM/YYYY');
     *   new SurveyTimezone({ date: '2025-10-28', timezone: 'Asia/Shanghai' });
     */
    function SurveyTimezone(input, format) {
        // 单例模式:如果已存在实例,直接返回
        if (instance) {
            return instance;
        }
        
        // 确保通过 new 调用
        if (!(this instanceof SurveyTimezone)) {
            return new SurveyTimezone(input, format);
        }
        
        // 解析输入参数
        this._parseInput(input, format);
        
        // 保存单例实例
        instance = this;
        
        // 执行初始化
        this._init();
        
        return instance;
    }

    SurveyTimezone.prototype = {
        constructor: SurveyTimezone,
        version: '1.0.0',
        
        /**
         * 解析输入参数
         * @private
         * @param {string|number|Date|Object} input - 输入参数
         * @param {string} format - 日期格式
         */
        _parseInput: function (input, format) {
            // 初始化配置对象
            this.options = {};
            this.date = null;
            
            // 如果没有输入,使用当前时间
            if (input === undefined || input === null) {
                this.date = new Date();
                return;
            }
            
            // 如果是配置对象
            if (typeof input === 'object' && !(input instanceof Date)) {
                this.options = input;
                // 从配置中提取日期
                if (input.date !== undefined) {
                    this.date = this.parse(input.date, input.format || format);
                } else {
                    this.date = new Date();
                }
                return;
            }
            
            // 其他情况:字符串、数字、Date 对象
            this.date = this.parse(input, format);
            
            // 如果解析失败,使用当前时间
            if (!this.date) {
                this.date = new Date();
            }
        },
        
        /**
         * 初始化方法
         * @private
         */
        _init: function () {
            // 初始化逻辑
            // 可以在这里添加时区处理、本地化等逻辑
        },
        
        /**
         * 获取当前实例的日期对象
         * @returns {Date} 日期对象
         */
        getDate: function () {
            return this.date;
        },
        
        /**
         * 设置日期
         * @param {string|number|Date} dateInput - 日期时间字符串、时间戳或Date对象
         * @param {string} format - 可选的日期格式
         * @returns {SurveyTimezone} 返回当前实例(链式调用)
         */
        setDate: function (dateInput, format) {
            this.date = this.parse(dateInput, format);
            if (!this.date) {
                this.date = new Date();
            }
            return this;
        },
        
        /**
         * 格式化日期 - 兼容 dayjs 和 laydate 的格式化方式(原型方法)
         * @param {Date|string} date - 要格式化的日期对象(可选,默认使用实例日期)
         * @param {string} format - 格式化模板字符串(默认:'YYYY-MM-DD HH:mm:ss')
         * @returns {string} 格式化后的日期字符串
         * @description 支持的格式化标记:
         *   年份:
         *     YYYY/yyyy - 四位年份(2025)
         *     YY/y      - 两位年份(25)
         *   月份:
         *     MMM  - 英文月份缩写(Jan, Feb, Mar...)
         *     MM   - 两位月份(01-12)
         *     M    - 月份(1-12)
         *   日期:
         *     DD/dd - 两位日期(01-31)
         *     D/d   - 日期(1-31)
         *   时间:
         *     HH - 24小时制小时(00-23)
         *     H  - 24小时制小时(0-23)
         *     mm - 分钟(00-59)
         *     m  - 分钟(0-59)
         *     ss - 秒(00-59)
         *     s  - 秒(0-59)
         *   毫秒:
         *     SSS - 毫秒(000-999)
         * @example
         *   const tz = new SurveyTimezone('2025-10-28 14:30:45');
         *   tz.format();                                // '2025-10-28 14:30:45'(使用实例日期)
         *   tz.format('YYYY年MM月DD日');                // '2025年10月28日'(使用实例日期)
         *   tz.format(new Date(), 'YYYY-MM-DD');        // 格式化指定日期
         */
        format: function (date, format) {
            // 如果第一个参数是字符串,说明是格式参数
            if (typeof date === 'string' && !format) {
                format = date;
                date = this.date;
            }
            
            // 如果没有传入 date,使用实例的日期
            if (!date || typeof date === 'string') {
                date = this.date;
            }
            
            // 默认格式
            format = format || 'YYYY-MM-DD HH:mm:ss';
            
            // 验证日期对象
            if (!date || !(date instanceof Date) || isNaN(date.getTime())) {
                return '';
            }
            
            // 月份英文缩写
            var monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 
                              'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
            
            // 定义格式化标记映射
            var matches = {
                // 年份(支持 dayjs 和 laydate 格式)
                'YYYY': function() { return date.getFullYear(); },
                'yyyy': function() { return date.getFullYear(); },
                'YY': function() { return String(date.getFullYear()).slice(-2); },
                'y': function() { return String(date.getFullYear()).slice(-2); },
                // 月份
                'MMM': function() { return monthNames[date.getMonth()]; },
                'MM': function() { return ('0' + (date.getMonth() + 1)).slice(-2); },
                'M': function() { return date.getMonth() + 1; },
                // 日期(支持 dayjs 和 laydate 格式)
                'DD': function() { return ('0' + date.getDate()).slice(-2); },
                'dd': function() { return ('0' + date.getDate()).slice(-2); },
                'D': function() { return date.getDate(); },
                'd': function() { return date.getDate(); },
                // 时间
                'HH': function() { return ('0' + date.getHours()).slice(-2); },
                'H': function() { return date.getHours(); },
                'mm': function() { return ('0' + date.getMinutes()).slice(-2); },
                'm': function() { return date.getMinutes(); },
                'ss': function() { return ('0' + date.getSeconds()).slice(-2); },
                's': function() { return date.getSeconds(); },
                // 毫秒
                'SSS': function() { return ('00' + date.getMilliseconds()).slice(-3); }
            };
            
            // 按标记长度从长到短排序,先处理长标记避免冲突
            // 注意:MMM 要在 MM 之前处理,yyyy 要在 y 之前处理
            var tokens = ['YYYY', 'yyyy', 'MMM', 'SSS', 'MM', 'DD', 'dd', 'HH', 'mm', 'ss', 'YY', 'M', 'D', 'd', 'H', 'm', 's', 'y'];
            
            var result = format;
            for (var i = 0; i < tokens.length; i++) {
                var token = tokens[i];
                if (result.indexOf(token) !== -1) {
                    result = result.replace(new RegExp(token, 'g'), matches[token]());
                }
            }
            
            return result;
        },
        
        /**
         * 解析日期时间字符串(原型方法)
         * @param {string|number} dateString - 日期时间字符串或时间戳
         * @param {string} format - 可选的格式模板,用于指定解析格式
         * @returns {Date|null} 解析后的 Date 对象,解析失败返回 null
         * @description 支持多种常见日期格式的自动识别和解析
         * @example
         *   const tz = new SurveyTimezone();
         *   tz.parse('2025-10-28 14:30:45');        // Date 对象
         *   tz.parse('2025/10/28');                 // Date 对象
         *   tz.parse('2025年10月28日');             // Date 对象
         *   tz.parse(1698484245000);                // Date 对象(时间戳)
         *   tz.parse('invalid');                    // null
         */
        parse: function (dateString, format) {
            // 处理 null 或 undefined
            if (dateString == null) {
                return null;
            }
            
            // 如果已经是 Date 对象,直接返回
            if (dateString instanceof Date) {
                return isNaN(dateString.getTime()) ? null : dateString;
            }
            
            // 处理数字类型(时间戳)
            if (typeof dateString === 'number') {
                var date = new Date(dateString);
                return isNaN(date.getTime()) ? null : date;
            }
            
            // 转换为字符串
            dateString = String(dateString).trim();
            
            if (!dateString) {
                return null;
            }
            
            // 如果指定了格式模板,使用格式模板解析
            if (format) {
                return this._parseWithFormat(dateString, format);
            }
            
            // 尝试使用原生 Date 解析
            var nativeDate = new Date(dateString);
            if (!isNaN(nativeDate.getTime())) {
                return nativeDate;
            }
            
            // 尝试常见格式的正则匹配
            var patterns = [
                // YYYY-MM-DD HH:mm:ss.SSS
                {
                    regex: /^(\d{4})-(\d{1,2})-(\d{1,2})\s+(\d{1,2}):(\d{1,2}):(\d{1,2})\.(\d{1,3})$/,
                    handler: function(m) {
                        return new Date(m[1], m[2] - 1, m[3], m[4], m[5], m[6], m[7]);
                    }
                },
                // YYYY-MM-DD HH:mm:ss
                {
                    regex: /^(\d{4})-(\d{1,2})-(\d{1,2})\s+(\d{1,2}):(\d{1,2}):(\d{1,2})$/,
                    handler: function(m) {
                        return new Date(m[1], m[2] - 1, m[3], m[4], m[5], m[6]);
                    }
                },
                // YYYY-MM-DD HH:mm
                {
                    regex: /^(\d{4})-(\d{1,2})-(\d{1,2})\s+(\d{1,2}):(\d{1,2})$/,
                    handler: function(m) {
                        return new Date(m[1], m[2] - 1, m[3], m[4], m[5]);
                    }
                },
                // YYYY-MM-DD
                {
                    regex: /^(\d{4})-(\d{1,2})-(\d{1,2})$/,
                    handler: function(m) {
                        return new Date(m[1], m[2] - 1, m[3]);
                    }
                },
                // YYYY/MM/DD HH:mm:ss
                {
                    regex: /^(\d{4})\/(\d{1,2})\/(\d{1,2})\s+(\d{1,2}):(\d{1,2}):(\d{1,2})$/,
                    handler: function(m) {
                        return new Date(m[1], m[2] - 1, m[3], m[4], m[5], m[6]);
                    }
                },
                // YYYY/MM/DD
                {
                    regex: /^(\d{4})\/(\d{1,2})\/(\d{1,2})$/,
                    handler: function(m) {
                        return new Date(m[1], m[2] - 1, m[3]);
                    }
                },
                // YYYY年MM月DD日 HH时mm分ss秒
                {
                    regex: /^(\d{4})年(\d{1,2})月(\d{1,2})日\s*(\d{1,2})时(\d{1,2})分(\d{1,2})秒$/,
                    handler: function(m) {
                        return new Date(m[1], m[2] - 1, m[3], m[4], m[5], m[6]);
                    }
                },
                // YYYY年MM月DD日
                {
                    regex: /^(\d{4})年(\d{1,2})月(\d{1,2})日$/,
                    handler: function(m) {
                        return new Date(m[1], m[2] - 1, m[3]);
                    }
                },
                // DD/MM/YYYY
                {
                    regex: /^(\d{1,2})\/(\d{1,2})\/(\d{4})$/,
                    handler: function(m) {
                        return new Date(m[3], m[2] - 1, m[1]);
                    }
                },
                // MM/DD/YYYY (美式格式)
                {
                    regex: /^(\d{1,2})-(\d{1,2})-(\d{4})$/,
                    handler: function(m) {
                        return new Date(m[3], m[1] - 1, m[2]);
                    }
                }
            ];
            
            // 尝试匹配各种格式
            for (var i = 0; i < patterns.length; i++) {
                var match = dateString.match(patterns[i].regex);
                if (match) {
                    var parsedDate = patterns[i].handler(match);
                    if (!isNaN(parsedDate.getTime())) {
                        return parsedDate;
                    }
                }
            }
            
            // 解析失败
            return null;
        },
        
        /**
         * 使用指定格式解析日期字符串(内部方法)
         * @private
         * @param {string} dateString - 日期字符串
         * @param {string} format - 格式模板
         * @returns {Date|null} 解析后的 Date 对象
         */
        _parseWithFormat: function (dateString, format) {
            // 构建正则表达式,将格式标记替换为捕获组
            var formatRegex = format
                .replace(/YYYY|yyyy/g, '(\\d{4})')
                .replace(/YY|yy/g, '(\\d{2})')
                .replace(/MM/g, '(\\d{1,2})')
                .replace(/DD|dd/g, '(\\d{1,2})')
                .replace(/HH/g, '(\\d{1,2})')
                .replace(/mm/g, '(\\d{1,2})')
                .replace(/ss/g, '(\\d{1,2})')
                .replace(/SSS/g, '(\\d{1,3})');
            
            var regex = new RegExp('^' + formatRegex + '$');
            var match = dateString.match(regex);
            
            if (!match) {
                return null;
            }
            
            // 提取各个部分
            var tokens = format.match(/YYYY|yyyy|YY|yy|MM|DD|dd|HH|mm|ss|SSS/g) || [];
            var values = {
                year: 0,
                month: 0,
                day: 1,
                hour: 0,
                minute: 0,
                second: 0,
                millisecond: 0
            };
            
            for (var i = 0; i < tokens.length; i++) {
                var token = tokens[i];
                var value = parseInt(match[i + 1], 10);
                
                if (token === 'YYYY' || token === 'yyyy') {
                    values.year = value;
                } else if (token === 'YY' || token === 'yy') {
                    values.year = value < 50 ? 2000 + value : 1900 + value;
                } else if (token === 'MM') {
                    values.month = value - 1;
                } else if (token === 'DD' || token === 'dd') {
                    values.day = value;
                } else if (token === 'HH') {
                    values.hour = value;
                } else if (token === 'mm') {
                    values.minute = value;
                } else if (token === 'ss') {
                    values.second = value;
                } else if (token === 'SSS') {
                    values.millisecond = value;
                }
            }
            
            var date = new Date(
                values.year,
                values.month,
                values.day,
                values.hour,
                values.minute,
                values.second,
                values.millisecond
            );
            
            return isNaN(date.getTime()) ? null : date;
        }
    }
    
    /**
     * 获取单例实例(静态方法)
     * @param {Object} options - 配置选项
     * @returns {SurveyTimezone} 单例实例
     */
    SurveyTimezone.getInstance = function (options) {
        if (!instance) {
            instance = new SurveyTimezone(options);
        }
        return instance;
    }
    
    /**
     * 重置单例实例(用于测试或重新初始化)
     * @static
     */
    SurveyTimezone.resetInstance = function () {
        instance = null;
    }

    // ==================== 静态方法 ====================
    /**
     * 格式化日期 - 静态方法(向后兼容)
     * @static
     * @param {Date} date - 要格式化的日期对象
     * @param {string} format - 格式化模板字符串
     * @returns {string} 格式化后的日期字符串
     * @description 静态方法,可直接调用而无需实例化
     * @example
     *   SurveyTimezone.format(new Date(), 'YYYY-MM-DD');
     */
    SurveyTimezone.format = function (date, format) {
        // 调用原型方法实现
        return SurveyTimezone.prototype.format.call(null, date, format);
    }
    
    /**
     * 解析日期时间字符串 - 静态方法(向后兼容)
     * @static
     * @param {string|number} dateString - 日期时间字符串或时间戳
     * @param {string} format - 可选的格式模板
     * @returns {Date|null} 解析后的 Date 对象,解析失败返回 null
     * @description 静态方法,可直接调用而无需实例化
     * @example
     *   SurveyTimezone.parse('2025-10-28 14:30:45');
     *   SurveyTimezone.parse('2025/10/28');
     *   SurveyTimezone.parse('28/10/2025', 'DD/MM/YYYY');
     */
    SurveyTimezone.parse = function (dateString, format) {
        // 使用原型对象作为上下文调用原型方法
        return SurveyTimezone.prototype.parse.call(SurveyTimezone.prototype, dateString, format);
    }


    // 返回构造函数
    return SurveyTimezone;

}))

测试文件

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="index.js"></script>
</head>
<body>
    <script>
        console.log('=== 初始化测试 - 传入日期时间 ===');
        
        // 重置单例以便测试
        SurveyTimezone.resetInstance();
        
        // 方式1: 传入日期字符串
        const tz1 = new SurveyTimezone('2025-10-28 14:30:45');
        console.log('字符串初始化:', tz1.getDate());
        console.log('格式化输出:', tz1.format());
        console.log('自定义格式:', tz1.format('YYYY年MM月DD日 HH时mm分'));
        
        // 重置单例
        SurveyTimezone.resetInstance();
        
        // 方式2: 传入时间戳
        const tz2 = new SurveyTimezone(1698484245000);
        console.log('\n时间戳初始化:', tz2.getDate());
        console.log('格式化输出:', tz2.format());
        
        // 重置单例
        SurveyTimezone.resetInstance();
        
        // 方式3: 传入日期字符串和格式
        const tz3 = new SurveyTimezone('28/10/2025', 'DD/MM/YYYY');
        console.log('\n指定格式初始化:', tz3.getDate());
        console.log('格式化输出:', tz3.format('YYYY-MM-DD'));
        
        // 重置单例
        SurveyTimezone.resetInstance();
        
        // 方式4: 传入配置对象
        const tz4 = new SurveyTimezone({ 
            date: '2025-10-28 14:30:45',
            timezone: 'Asia/Shanghai' 
        });
        console.log('\n配置对象初始化:', tz4.getDate());
        console.log('格式化输出:', tz4.format());
        console.log('配置信息:', tz4.options);
        
        // 重置单例
        SurveyTimezone.resetInstance();
        
        // 方式5: 不传参数(使用当前时间)
        const tz5 = new SurveyTimezone();
        console.log('\n默认初始化(当前时间):', tz5.getDate());
        console.log('格式化输出:', tz5.format());
        
        console.log('\n=== setDate 方法测试 ===');
        
        // 修改日期
        tz5.setDate('2025-12-25 00:00:00');
        console.log('修改后的日期:', tz5.getDate());
        console.log('格式化输出:', tz5.format('YYYY年MM月DD日'));
        
        // 链式调用
        console.log('链式调用:', tz5.setDate('2026-01-01').format('YYYY/MM/DD'));
        
        console.log('\n=== 单例模式验证 ===');
        
        // 验证单例
        const instance1 = tz5;
        const instance2 = new SurveyTimezone('2025-10-28');
        console.log('单例验证:', instance1 === instance2 ? '✓ 通过(返回同一实例)' : '✗ 失败');
        
        console.log('\n=== 格式化方法测试 ===');
        
        // 格式化实例日期
        SurveyTimezone.resetInstance();
        const tzFormat = new SurveyTimezone('2025-10-28 14:30:45');
        console.log('格式化实例日期(无参):', tzFormat.format());
        console.log('格式化实例日期(指定格式):', tzFormat.format('YYYY年MM月DD日'));
        console.log('格式化实例日期(英文):', tzFormat.format('MMM DD, yyyy'));
        
        // 格式化指定日期
        const now = new Date();
        console.log('格式化指定日期:', tzFormat.format(now, 'YYYY-MM-DD HH:mm:ss'));
        
        console.log('\n=== 格式化方法测试 - 静态方法(向后兼容)===');
        console.log('静态方法 - 完整格式:', SurveyTimezone.format(now, 'YYYY-MM-DD HH:mm:ss'));
        console.log('静态方法 - laydate格式:', SurveyTimezone.format(now, 'yyyy-MM-dd HH:mm:ss'));
        console.log('静态方法 - 英文月份:', SurveyTimezone.format(now, 'MMM DD, yyyy'));
        console.log('静态方法 - 自定义格式:', SurveyTimezone.format(now, 'YYYY年MM月DD日 HH时mm分ss秒'));
        
        console.log('\n=== parse 方法测试(静态方法)===');
        
        // 测试静态方法 - 自动识别格式
        console.log('parse标准格式:', SurveyTimezone.parse('2025-10-28 14:30:45'));
        console.log('parse中文格式:', SurveyTimezone.parse('2025年10月28日'));
        console.log('parse时间戳:', SurveyTimezone.parse(1698484245000));
        
        // 测试静态方法 - 指定格式(这个会触发 _parseWithFormat)
        console.log('parse指定格式1:', SurveyTimezone.parse('28/10/2025', 'DD/MM/YYYY'));
        console.log('parse指定格式2:', SurveyTimezone.parse('2025年10月28日', 'YYYY年MM月DD日'));
        console.log('parse指定格式3:', SurveyTimezone.parse('25-10-28', 'YY-MM-DD'));
        
        console.log('\n=== 完整工作流测试 ===');
        
        // 场景:用户输入 → 解析 → 处理 → 格式化输出
        SurveyTimezone.resetInstance();
        const workflow = new SurveyTimezone('28/10/2025', 'DD/MM/YYYY');
        console.log('输入:', '28/10/2025');
        console.log('解析:', workflow.getDate());
        console.log('输出1:', workflow.format('YYYY-MM-DD'));
        console.log('输出2:', workflow.format('YYYY年MM月DD日'));
        console.log('输出3:', workflow.format('MMM DD, yyyy'));
    </script>
</body>
</html>
相关推荐
over6974 小时前
浏览器里的AI魔法:用JavaScript玩转自然语言处理
前端·javascript
Amy_cx4 小时前
搭建React Native开发环境
javascript·react native·react.js
代码AI弗森4 小时前
Python × NumPy」 vs 「JavaScript × TensorFlow.js」生态全景图
javascript·python·numpy
疏狂难除4 小时前
关于spiderdemo第二题的奇思妙想
javascript·爬虫
渣渣盟5 小时前
探索Word2Vec:从文本向量化到中文语料处理
前端·javascript·python·文本向量化
无羡仙5 小时前
JavaScript中的继承实现方式
javascript
一个处女座的程序猿O(∩_∩)O5 小时前
Vue CLI 插件开发完全指南:从原理到实战
前端·javascript·vue.js
小蜜蜂dry5 小时前
JavaScript 原型
前端·javascript
Achieve前端实验室5 小时前
【每日一面】async/await 的原理
前端·javascript·面试