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

215 阅读34分钟

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

1. UMD 模式解析

1.1 立即执行函数 (IIFE)

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

global 参数说明:


typeof window !== "undefined" ? window : this;

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

  • 浏览器环境window 对象

  • Node.js 环境global 对象

  • 其他环境:当前上下文 this

1.2 UMD 模块定义

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


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.Timezone = factory();

    }

支持的模块系统:

  • AMD:用于 RequireJS 等加载器

  • CommonJS:用于 Node.js 环境

  • 全局变量:用于直接在浏览器中使用

1.3 工厂函数解析

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


function () {

     'use strict';

  


    // 构造函数定义

    function Timezone(options) {

    }

  


    // 原型方法定义

    Timezone.prototype = {

        constructor: Timezone, // 修复 constructor 指向

        version: '1.0.0',

        _init: function () {

        }

    }

  


    // 返回构造函数

    return Timezone;

}

核心组成部分:

  • 严格模式:使用 'use strict' 确保代码质量

  • 构造函数:定义 Timezone

  • 原型方法:通过原型链添加共享方法

  • 返回值:导出构造函数供外部使用

1.4 执行流程

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

  1. 立即执行:代码加载后立即执行 IIFE

  2. 环境检测:检测当前支持的模块系统(AMD / CommonJS / 全局变量)

  3. 工厂调用:执行 factory() 函数,返回 Timezone 构造函数

  4. 模块导出:根据环境将构造函数导出到相应位置


(function (global, factory) {

    // 环境检测和模块导出逻辑

}(typeof window !== "undefined" ? window : this, function () {

    // 工厂函数:创建并返回构造函数

}));

2. 单例模式实现

2.1 为什么使用单例模式?

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

  • 避免重复实例:防止多次实例化造成的内存浪费

  • 全局状态管理:统一管理时区配置、数据映射表等

  • 配置一致性:确保整个应用使用相同的时区设置

2.2 单例模式实现

2.2.1 私有实例存储

// 闭包中的私有变量,存储单例实例

var instance = null;

2.2.2 构造函数实现

function Timezone(options) {

    // 1. 如果已存在实例,直接返回

    if (instance) {

        return instance;

    }

   

    // 2. 确保通过 new 调用

    if (!(this instanceof Timezone)) {

        return new Timezone(options);

    }

   

    // 3. 初始化配置

    this.options = options || {};

   

    // 4. 保存单例实例

    instance = this;

   

    // 5. 执行初始化

    this._init();

   

    return instance;

}

实现要点:

  1. 实例检查:首次检查是否已存在实例,有则直接返回

  2. new 检查:确保即使不用 new 关键字也能正常工作

  3. 配置初始化:保存传入的配置选项

  4. 实例保存:将当前实例保存到闭包变量中

  5. 初始化执行:调用内部初始化方法

2.2.3 静态方法

/**

 * 获取单例实例

 */

Timezone.getInstance = function (options) {

    if (!instance) {

        instance = new Timezone(options);

    }

    return instance;

}

  


/**

 * 重置单例(用于测试)

 */

Timezone.resetInstance = function () {

    instance = null;

}

2.3 使用方式

方式一:使用 new 关键字

const instance1 = new Timezone({ timezone: 'Asia/Shanghai' });

const instance2 = new Timezone({ timezone: 'America/New_York' });

  


console.log(instance1 === instance2); // true(返回同一个实例)

方式二:使用 getInstance 静态方法

const instance = Timezone.getInstance({ timezone: 'Asia/Shanghai' });

方式三:不使用 new(自动转换)

const instance = Timezone({ timezone: 'Asia/Shanghai' });

2.4 单例模式的优势

| 优势 | 说明 |

|------|------|

| 内存优化 | 只创建一个实例,减少内存占用 |

| 状态一致 | 全局共享同一个实例,避免状态不一致 |

| 易于管理 | 集中管理配置和数据 |

| 防止冲突 | 避免多个实例之间的配置冲突 |

2.5 完整示例


// 第一次创建实例

const timezone1 = new Timezone({

    timezone: 'Asia/Shanghai',

    locale: 'zh-CN'

});

  


// 第二次尝试创建(返回第一次的实例)

const timezone2 = new Timezone({

    timezone: 'America/New_York'  // 这个配置会被忽略

});

  


console.log(timezone1 === timezone2);        // true

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

  


// 重置单例后可以创建新实例

Timezone.resetInstance();

const timezone3 = new Timezone({

    timezone: 'Europe/London'

});

  


console.log(timezone1 === timezone3);        // false

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

3. format 方法详解

3.1 方法说明

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

方法签名:


// 原型方法

instance.format(date, format)

  


// 静态方法(向后兼容)

Timezone.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 时间标记(24小时制)

| 标记 | 说明 | 示例输出 | 兼容性 |

|------|------|----------|--------|

| 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 时间标记(12小时制)⭐ 新增

| 标记 | 说明 | 示例输出 | 兼容性 |

|------|------|----------|--------|

| hh | 12小时制小时(补零) | 01, 02... 12 | dayjs |

| h | 12小时制小时(不补零) | 1, 2... 12 | dayjs |

转换规则:

  • 00:00 (24小时) → 12:00 AM (12小时)

  • 01:00 (24小时) → 01:00 AM (12小时)

  • 12:00 (24小时) → 12:00 PM (12小时)

  • 13:00 (24小时) → 01:00 PM (12小时)

  • 23:00 (24小时) → 11:00 PM (12小时)

3.2.6 上午/下午标记 ⭐ 新增

| 标记 | 说明 | 示例输出 | 兼容性 |

|------|------|----------|--------|

| A | 大写 AM/PM | AM, PM | dayjs |

| a | 小写 am/pm | am, pm | dayjs |

判断规则:

  • AM(上午):小时 < 12(00:00 - 11:59)

  • PM(下午):小时 ≥ 12(12:00 - 23:59)

3.2.7 毫秒标记

| 标记 | 说明 | 示例输出 | 兼容性 |

|------|------|----------|--------|

| SSS | 三位毫秒 | 000, 001... 999 | dayjs |

3.3 使用示例

3.3.1 基础用法

const tz = new Timezone();

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 风格格式

const tz = new Timezone();

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 风格格式

const tz = new Timezone();

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 中文日期格式

const tz = new Timezone();

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 12小时制和AM/PM格式 ⭐ 新增

const tz = new Timezone();

const morning = new Date('2025-10-28 06:30:00');

const afternoon = new Date('2025-10-28 14:30:00');

  


// 12小时制(补零)+ 大写AM/PM

tz.format(morning, 'YYYY-MM-DD hh:mm:ss A');    // '2025-10-28 06:30:00 AM'

tz.format(afternoon, 'YYYY-MM-DD hh:mm:ss A');  // '2025-10-28 02:30:00 PM'

  


// 12小时制(不补零)+ 小写am/pm

tz.format(morning, 'h:mm a');                   // '6:30 am'

tz.format(afternoon, 'h:mm a');                 // '2:30 pm'

  


// 英文格式

tz.format(afternoon, 'MMM DD, YYYY h:mm A');    // 'Oct 28, 2025 2:30 PM'

  


// 中文格式

tz.format(afternoon, 'yyyy年MM月dd日 hh:mm A'); // '2025年10月28日 02:30 PM'

  


// 24小时制对比

tz.format(afternoon, 'HH:mm');                  // '14:30' (24小时)

tz.format(afternoon, 'hh:mm A');                // '02:30 PM' (12小时)

特殊时间点示例:


const midnight = new Date('2025-10-28 00:00:00');

const noon = new Date('2025-10-28 12:00:00');

  


tz.format(midnight, 'hh:mm A');   // '12:00 AM' (午夜)

tz.format(noon, 'hh:mm A');       // '12:00 PM' (正午)

3.3.6 静态方法调用(无需实例化)

const now = new Date('2025-10-28 14:30:45');

  


// 直接使用静态方法

Timezone.format(now, 'YYYY-MM-DD');      // '2025-10-28'

Timezone.format(now, 'yyyy年MM月dd日');  // '2025年10月28日'

Timezone.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 | 完整日期时间(24小时制) |

| YYYY-MM-DD hh:mm:ss A ⭐ | 2025-10-28 02:30:45 PM | 完整日期时间(12小时制) |

| yyyy年MM月dd日 | 2025年10月28日 | 中文日期 |

| MMM DD, yyyy h:mm A ⭐ | Oct 28, 2025 2:30 PM | 英文日期(12小时制) |

| MMM DD, yyyy | Oct 28, 2025 | 英文日期 |

| YYYY/MM/DD HH:mm | 2025/10/28 14:30 | 简短日期时间 |

| HH:mm:ss | 14:30:45 | 仅时间(24小时制) |

| hh:mm A ⭐ | 02:30 PM | 仅时间(12小时制) |

| YY-M-D | 25-10-28 | 简短日期 |

| YYYY-MM-DD HH:mm:ss.SSS | 2025-10-28 14:30:45.123 | 带毫秒 |

3.5 错误处理


const tz = new Timezone();

  


// 无效日期返回空字符串

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 对象的原生方法获取年、月、日等值

关键代码逻辑:


// 标记处理顺序(长的在前,短的在后)

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() | Timezone.format() |

| 是否需要实例化 | ✅ 需要 | ❌ 不需要 |

| 访问实例属性 | ✅ 可以 | ❌ 不可以 |

| 推荐使用场景 | 面向对象编程 | 工具函数调用 |

| 性能 | 略优 | 略低(多一层调用) |

关系说明:

  • 静态方法内部调用原型方法实现

  • 两种方式返回结果完全一致

  • 静态方法保证向后兼容性


// 静态方法实现(调用原型方法)

Timezone.format = function (date, format) {

    return Timezone.prototype.format.call(null, date, format);

}

4. parse 方法详解

4.1 方法说明

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

方法签名:


// 原型方法

instance.parse(dateString, format)

  


// 静态方法(向后兼容)

Timezone.parse(dateString, format)

参数:

| 参数 | 类型 | 必填 | 默认值 | 说明 |

|------|------|------|--------|------|

| dateString | string\|number | ✅ | - | 日期时间字符串或时间戳 |

| format | string | ❌ | - | 可选的格式模板,用于指定解析格式 |

返回值:

  • 类型Date | null

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

4.2 支持的日期格式

4.2.1 自动识别格式(无需指定 format)

| 格式 | 示例 | 说明 |

|------|------|------|

| ISO 8601 UTC(带毫秒) ⭐ | 2025-10-28T06:30:00.000Z | UTC时间,按本地时区解析 |

| ISO 8601 UTC(无毫秒) ⭐ | 2025-10-28T06:30:00Z | UTC时间,按本地时区解析 |

| ISO 8601 本地(带毫秒) ⭐ | 2025-10-28T14:30:00.000 | 本地时间格式 |

| ISO 8601 本地(无毫秒) ⭐ | 2025-10-28T14:30:00 | 本地时间格式 |

| 标准格式(带时分秒毫秒) | 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 参数明确告知解析格式:


const tz = new Timezone();

  


// 指定格式解析

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 基础用法(自动识别)

const tz = new Timezone();

  


// ISO 8601 格式 ⭐ 新增

tz.parse('2025-10-28T06:30:00.000Z');     // Date 对象(UTC时间)

tz.parse('2025-10-28T06:30:00Z');         // Date 对象(UTC时间)

tz.parse('2025-10-28T14:30:00.000');      // Date 对象(本地时间)

tz.parse('2025-10-28T14:30:00');          // Date 对象(本地时间)

  


// 标准格式

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 指定格式解析

const tz = new Timezone();

  


// 欧洲日期格式(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 错误处理

const tz = new Timezone();

  


// 无效输入返回 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 静态方法调用

// 直接使用静态方法,无需实例化

const date1 = Timezone.parse('2025-10-28');

const date2 = Timezone.parse('2025/10/28');

const date3 = Timezone.parse('2025年10月28日');

const date4 = Timezone.parse('28-10-2025', 'DD-MM-YYYY');

4.3.5 parse + format 组合使用

const tz = new Timezone();

  


// 解析后格式化输出

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 方法采用多层次解析策略


输入值

  ↓

┌─────────────────────────────────┐

│ 1. 类型检查                      │

│    - null/undefinednull       │

│    - Date 对象 → 验证后返回      │

│    - 数字 → 时间戳解析           │

└─────────────────────────────────┘

  ↓

┌─────────────────────────────────┐

│ 2. 格式化参数检查                │

│    - 有 format → 使用格式模板解析 │

│    - 无 format → 自动识别         │

└─────────────────────────────────┘

  ↓

┌─────────────────────────────────┐

│ 3. 原生解析尝试                  │

│    - new Date(dateString)        │

│    - 成功 → 返回                 │

│    - 失败 → 继续                 │

└─────────────────────────────────┘

  ↓

┌─────────────────────────────────┐

│ 4. 正则匹配解析                  │

│    - 遍历预定义格式列表          │

│    - 匹配成功 → 返回             │

│    - 全部失败 → 返回 null        │

└─────────────────────────────────┘

4.5 常见场景示例

场景1:表单日期输入

const tz = new Timezone();

  


// 用户输入的日期字符串

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 数据转换

const tz = new Timezone();

  


// 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:日期格式统一化

const tz = new Timezone();

  


// 不同格式的日期数组

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. 提前验证:在解析前进行基本格式验证


const tz = new Timezone();

  


// ❌ 不推荐:每次都自动识别

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 对象转换为格式化字符串 | 数据显示、输出 |

完整的数据流:


const tz = new Timezone();

  


// 数据输入 → 处理 → 输出

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() | Timezone.parse() |

| 是否需要实例化 | ✅ 需要 | ❌ 不需要 |

| 访问实例属性 | ✅ 可以 | ❌ 不可以 |

| 推荐使用场景 | 面向对象编程 | 工具函数调用 |

关系说明:


// 静态方法实现(调用原型方法)

Timezone.parse = function (dateString, format) {

    // 使用原型对象作为上下文,以便访问内部方法

    return Timezone.prototype.parse.call(Timezone.prototype, dateString, format);

}

技术说明:

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

5. 时区功能详解

5.1 功能概述

Timezone 插件内置了全球时区数据库,包含 400+ 个时区信息,支持:

  • 自动检测:获取当前浏览器时区

  • 时区查询:根据时区名称查询国家代码和时间偏移量

  • 实例缓存:初始化时自动缓存当前时区信息

  • 别名支持:支持时区别名查询(如 PRCAsia/Shanghai

5.2 时区数据结构

每个时区包含以下信息:


{

    name: "Asia/Shanghai",                  // IANA 时区标识符(主名称)

    alternativeName: "China Time",           // 可读的时区名称

    group: ["Asia/Shanghai", "PRC", ...],   // 时区别名组

    continentCode: "AS",                     // 洲代码

    continentName: "Asia",                   // 洲名称

    countryName: "China",                    // 国家名称

    countryCode: "CN",                       // 国家代码(ISO 3166-1)

    mainCities: ["Shanghai", "Beijing", ...],// 主要城市

    rawOffsetInMinutes: 480,                 // UTC 偏移量(分钟)

    abbreviation: "CST",                     // 时区缩写

    rawFormat: "+08:00 China Time - Shanghai, Beijing, ..."  // 完整格式

}

5.3 静态方法:getCurrentTimezone()

功能说明:

获取当前浏览器的时区设置。

方法签名:


Timezone.getCurrentTimezone() → string

返回值:

  • 返回 IANA 时区标识符(如 'Asia/Shanghai'

  • 如果 Intl API 不可用,返回 'UTC'

使用示例:


// 静态方法调用(无需实例化)

const timezone = Timezone.getCurrentTimezone();

console.log(timezone);  // 'Asia/Shanghai'(根据浏览器设置)

技术实现:


Timezone.getCurrentTimezone = function () {

    try {

        // 使用 Intl API 获取时区

        return Intl.DateTimeFormat().resolvedOptions().timeZone;

    } catch (e) {

        // 如果 Intl API 不可用,返回默认值

        console.warn('无法获取时区信息,使用默认值');

        return 'UTC';

    }

}

浏览器兼容性:

  • ✅ Chrome 24+

  • ✅ Firefox 29+

  • ✅ Safari 10+

  • ✅ Edge 12+

  • ❌ IE 11 以下(返回 'UTC'

5.4 实例方法:getCountryCode()

功能说明:

根据时区名称获取对应的国家代码(ISO 3166-1)。

方法签名:


instance.getCountryCode(timezone?) → string | null

参数:

| 参数 | 类型 | 必填 | 说明 |

|------|------|------|------|

| timezone | string | ❌ | 时区名称(可选,默认使用当前时区)|

返回值:

  • 成功:返回国家代码(如 'CN''US'

  • 失败:返回 null

使用示例:


const tz = new Timezone();

  


// 1. 不传参数,获取当前时区的国家代码

console.log(tz.getCountryCode());  // 'CN'(如果当前时区是 Asia/Shanghai)

  


// 2. 传入时区名称

console.log(tz.getCountryCode('Asia/Shanghai'));       // 'CN'

console.log(tz.getCountryCode('America/New_York'));    // 'US'

console.log(tz.getCountryCode('Europe/London'));       // 'GB'

console.log(tz.getCountryCode('Asia/Tokyo'));          // 'JP'

  


// 3. 使用时区别名

console.log(tz.getCountryCode('PRC'));                 // 'CN'

console.log(tz.getCountryCode('Hongkong'));            // 'HK'

console.log(tz.getCountryCode('US/Eastern'));          // 'US'

  


// 4. 时区不存在

console.log(tz.getCountryCode('Invalid/Timezone'));    // null

应用场景:

  1. 地区检测:根据用户时区自动判断所在国家

  2. 内容本地化:根据国家代码提供本地化内容

  3. 数据统计:按国家统计用户分布

  4. 合规检查:根据国家代码应用不同的法规要求

示例:根据时区显示国旗


const tz = new Timezone();

const countryCode = tz.getCountryCode();

  


const flagEmoji = {

    'CN': '🇨🇳',

    'US': '🇺🇸',

    'GB': '🇬🇧',

    'JP': '🇯🇵'

};

  


console.log('您的国家:' + flagEmoji[countryCode]);  // 您的国家:🇨🇳

5.5 实例方法:getTimezoneOffset()

功能说明:

根据时区名称获取相对于 UTC 的时间偏移量(单位:分钟)。✅ 支持夏令时自动判断

方法签名:


instance.getTimezoneOffset(timezone?, date?) → number | null

参数:

| 参数 | 类型 | 必填 | 说明 |

|------|------|------|------|

| timezone | string | ❌ | 时区名称(可选,默认使用当前时区)|

| date | Date\|string\|number | ❌ | 可选的日期参数,用于计算该日期的偏移量(考虑夏令时)|

返回值:

  • 成功:返回偏移量(分钟,正数表示东时区,负数表示西时区)

  • 失败:返回 null

使用示例:


const tz = new Timezone();

  


// 1. 不传参数,获取当前时区的偏移量

console.log(tz.getTimezoneOffset());  // 480(如果当前时区是 Asia/Shanghai,UTC+8)

  


// 2. 传入时区名称(自动使用当前日期)

console.log(tz.getTimezoneOffset('Asia/Shanghai'));       // 480  (UTC+8)

console.log(tz.getTimezoneOffset('America/New_York'));    // -300 或 -240(取决于当前是否夏令时)

console.log(tz.getTimezoneOffset('Europe/London'));       // 0 或 60(取决于当前是否夏令时)

console.log(tz.getTimezoneOffset('Asia/Tokyo'));          // 540  (UTC+9)

  


// 3. 指定日期,考虑夏令时

// 纽约冬季(1月):UTC-5

console.log(tz.getTimezoneOffset('America/New_York', new Date('2025-01-15')));  // -300

  


// 纽约夏季(7月):UTC-4(夏令时)

console.log(tz.getTimezoneOffset('America/New_York', new Date('2025-07-15')));  // -240

  


// 4. 转换为小时

const offsetMinutes = tz.getTimezoneOffset('Asia/Shanghai');

const offsetHours = offsetMinutes / 60;

console.log('UTC+' + offsetHours);  // 'UTC+8'

  


// 5. 使用时区别名

console.log(tz.getTimezoneOffset('PRC'));                 // 480

console.log(tz.getTimezoneOffset('US/Pacific'));          // -480 或 -420(取决于夏令时)

  


// 6. 时区不存在

console.log(tz.getTimezoneOffset('Invalid/Timezone'));    // null

偏移量说明:

| 偏移量(分钟) | 偏移量(小时) | 示例时区 |

|---------------|---------------|----------|

| 480 | +8 | Asia/Shanghai (中国) |

| 540 | +9 | Asia/Tokyo (日本) |

| 0 | ±0 | Europe/London (英国) |

| -300 | -5 | America/New_York (美国东部) |

| -480 | -8 | America/Los_Angeles (美国西部) |

应用场景:

  1. 时间转换:计算不同时区的时间差

  2. 会议安排:确定跨时区会议的合适时间

  3. 显示本地时间:将 UTC 时间转换为用户本地时间

  4. 时区提示:显示时区偏移信息

示例:计算两个时区的时间差


const tz = new Timezone();

  


const shanghaiOffset = tz.getTimezoneOffset('Asia/Shanghai');      // 480

const newYorkOffset = tz.getTimezoneOffset('America/New_York');    // -300

  


const diffMinutes = shanghaiOffset - newYorkOffset;  // 780

const diffHours = diffMinutes / 60;                   // 13

  


console.log('上海比纽约早 ' + diffHours + ' 小时');  // 上海比纽约早 13 小时

示例:格式化显示 UTC 偏移


function formatUTCOffset(offsetMinutes) {

    const sign = offsetMinutes >= 0 ? '+' : '-';

    const absMinutes = Math.abs(offsetMinutes);

    const hours = Math.floor(absMinutes / 60);

    const minutes = absMinutes % 60;

   

    if (minutes === 0) {

        return 'UTC' + sign + hours;

    } else {

        return 'UTC' + sign + hours + ':' + (minutes < 10 ? '0' : '') + minutes;

    }

}

  


const tz = new Timezone();

console.log(formatUTCOffset(tz.getTimezoneOffset('Asia/Shanghai')));       // 'UTC+8'

console.log(formatUTCOffset(tz.getTimezoneOffset('America/New_York')));    // 'UTC-5'

console.log(formatUTCOffset(tz.getTimezoneOffset('Asia/Kolkata')));        // 'UTC+5:30'

5.6 实例属性:时区信息缓存

功能说明:

在实例初始化时,自动缓存当前时区相关信息,无需重复查询。

缓存属性:

| 属性 | 类型 | 说明 |

|------|------|------|

| currentTimezone | string | 当前时区名称(IANA 标识符)|

| currentCountryCode | string \| null | 当前时区对应的国家代码 |

| currentTimezoneOffset | number \| null | 当前时区偏移量(分钟)|

使用示例:


const tz = new Timezone();

  


// 直接访问缓存的时区信息

console.log('当前时区:', tz.currentTimezone);           // 'Asia/Shanghai'

console.log('国家代码:', tz.currentCountryCode);        // 'CN'

console.log('偏移量(分钟):', tz.currentTimezoneOffset);  // 480

console.log('偏移量(小时):', tz.currentTimezoneOffset / 60);  // 8

初始化逻辑:


_init: function () {

    // 获取当前浏览器时区

    this.currentTimezone = Timezone.getCurrentTimezone();

   

    // 根据时区获取时区数据

    var tzData = timezoneDataMap[this.currentTimezone];

   

    if (tzData) {

        // 缓存当前时区对应的国家代码

        this.currentCountryCode = tzData.countryCode;

       

        // 缓存当前时区对应的时区偏移量(分钟)

        this.currentTimezoneOffset = tzData.rawOffsetInMinutes;

    } else {

        // 如果找不到时区数据,使用默认值

        this.currentCountryCode = null;

        this.currentTimezoneOffset = null;

    }

}

性能优势:

  • ✅ 一次初始化,多次使用

  • ✅ 避免重复查询时区数据

  • ✅ 提升访问速度

5.7 时区别名支持

功能说明:

除了标准的 IANA 时区标识符,插件还支持常用的时区别名。

常用别名示例:

| 标准名称 | 别名 | 国家 |

|---------|------|------|

| Asia/Shanghai | PRC, Asia/Chongqing, Asia/Harbin | 中国 |

| Asia/Hong_Kong | Hongkong | 香港 |

| America/New_York | US/Eastern, EST5EDT | 美国东部 |

| America/Los_Angeles | US/Pacific, PST8PDT | 美国西部 |

| America/Chicago | US/Central, CST6CDT | 美国中部 |

| Europe/London | GB, GB-Eire | 英国 |

| Asia/Tokyo | Japan | 日本 |

使用示例:


const tz = new Timezone();

  


// 使用标准名称和别名效果相同

console.log(tz.getCountryCode('Asia/Shanghai'));    // 'CN'

console.log(tz.getCountryCode('PRC'));               // 'CN'(别名)

  


console.log(tz.getTimezoneOffset('America/New_York'));  // -300

console.log(tz.getTimezoneOffset('US/Eastern'));        // -300(别名)

技术实现:

时区数据初始化时,会将 group 中的所有别名都映射到同一个时区数据对象:


(function initTimezoneDataMap() {

    for (var i = 0; i < TIMEZONE_CONFIG.length; i++) {

        var timezone = TIMEZONE_CONFIG[i];

        // 主时区名称作为 key

        timezoneDataMap[timezone.name] = timezone;

       

        // 同时将 group 中的所有别名也映射到同一个时区数据

        if (timezone.group && timezone.group.length > 0) {

            for (var j = 0; j < timezone.group.length; j++) {

                timezoneDataMap[timezone.group[j]] = timezone;

            }

        }

    }

})();

5.8 完整应用示例

示例1:显示用户所在时区信息


const tz = new Timezone();

  


// 获取并显示时区信息

const timezone = tz.currentTimezone;

const country = tz.currentCountryCode;

const offset = tz.currentTimezoneOffset;

const offsetHours = offset >= 0 ? '+' + (offset / 60) : (offset / 60);

  


console.log('您的时区信息:');

console.log('时区:' + timezone);

console.log('国家:' + country);

console.log('UTC 偏移:UTC' + offsetHours);

  


// 输出示例:

// 您的时区信息:

// 时区:Asia/Shanghai

// 国家:CN

// UTC 偏移:UTC+8

示例2:批量查询多个时区


const tz = new Timezone();

  


const timezones = [

    'Asia/Shanghai',

    'America/New_York',

    'Europe/London',

    'Asia/Tokyo',

    'Australia/Sydney'

];

  


console.log('=== 全球主要时区信息 ===');

timezones.forEach(function(timezone) {

    const countryCode = tz.getCountryCode(timezone);

    const offset = tz.getTimezoneOffset(timezone);

    const offsetHours = (offset / 60).toFixed(1);

   

    console.log(timezone + ':');

    console.log('  国家代码: ' + countryCode);

    console.log('  UTC偏移: ' + (offset >= 0 ? '+' : '') + offsetHours + ' 小时');

});

示例3:根据时区显示当前时间


const tz = new Timezone();

  


function getTimeInTimezone(timezoneName) {

    const offset = tz.getTimezoneOffset(timezoneName);

    if (offset === null) {

        return '时区不存在';

    }

   

    const now = new Date();

    const utcTime = now.getTime() + (now.getTimezoneOffset() * 60000);

    const timezoneTime = new Date(utcTime + (offset * 60000));

   

    return tz.format(timezoneTime, 'YYYY-MM-DD HH:mm:ss');

}

  


console.log('上海时间:' + getTimeInTimezone('Asia/Shanghai'));

console.log('纽约时间:' + getTimeInTimezone('America/New_York'));

console.log('伦敦时间:' + getTimeInTimezone('Europe/London'));

示例4:时区选择器


const tz = new Timezone();

  


// 常用时区列表

const popularTimezones = [

    { name: 'Asia/Shanghai', label: '中国 - 北京' },

    { name: 'Asia/Hong_Kong', label: '中国 - 香港' },

    { name: 'America/New_York', label: '美国 - 纽约' },

    { name: 'America/Los_Angeles', label: '美国 - 洛杉矶' },

    { name: 'Europe/London', label: '英国 - 伦敦' },

    { name: 'Asia/Tokyo', label: '日本 - 东京' }

];

  


// 生成选择器选项

popularTimezones.forEach(function(tz) {

    const offset = tz.getTimezoneOffset(tz.name);

    const offsetHours = (offset / 60).toFixed(1);

    const utcString = 'UTC' + (offset >= 0 ? '+' : '') + offsetHours;

   

    console.log('<option value="' + tz.name + '">' +

                tz.label + ' (' + utcString + ')' +

                '</option>');

});

5.9 实例方法:时区转换

5.9.1 toUTC() - 将特定时区时间转为 UTC

功能说明:

将指定时区的本地时间转换为 UTC 时间。特别注意:特定时区可能不是本地浏览器时区。

方法签名:


instance.toUTC(date, timezone) → Date | null

参数:

| 参数 | 类型 | 必填 | 默认值 | 说明 |

|------|------|------|--------|------|

| date | Date\|string\|number | ✅ | - | 日期对象、日期字符串或时间戳 |

| timezone | string | ❌ | 当前浏览器时区 ⭐ | 源时区名称(该时间所在的时区)|

返回值:

  • 成功:返回 UTC 时间的 Date 对象

  • 失败:返回 null

默认时区行为 ⭐ 新增:

当不传入 timezone 参数时,自动使用当前浏览器时区,简化常见用例:


const tz = new Timezone();

const localTime = new Date(2025, 9, 28, 14, 30, 0);

  


// 不传时区参数,自动使用浏览器时区

const utc = tz.toUTC(localTime);  

  


// 等价于显式指定浏览器时区

const utc2 = tz.toUTC(localTime, Timezone.getCurrentTimezone());

转换公式:


UTC 时间 = 本地时间 - 时区偏移量

使用示例:


const tz = new Timezone();

  


// 示例1:将上海时间转为 UTC

const shanghaiTime = new Date(2025, 9, 28, 14, 30, 0);  // 假设这是上海本地时间

const utcTime = tz.toUTC(shanghaiTime, 'Asia/Shanghai');

console.log(tz.format(utcTime, 'YYYY-MM-DD HH:mm:ss'));  // '2025-10-28 06:30:00'

// 说明:上海是 UTC+8,所以 14:30 - 8小时 = 06:30 UTC

  


// 示例2:将纽约时间转为 UTC

const nyTime = new Date(2025, 9, 28, 14, 30, 0);  // 假设这是纽约本地时间

const utcTime2 = tz.toUTC(nyTime, 'America/New_York');

console.log(tz.format(utcTime2, 'YYYY-MM-DD HH:mm:ss'));  // '2025-10-28 19:30:00'

// 说明:纽约是 UTC-5,所以 14:30 - (-5小时) = 19:30 UTC

  


// 示例3:使用字符串输入

const utcTime3 = tz.toUTC('2025-10-28 14:30:00', 'Asia/Tokyo');

console.log(tz.format(utcTime3, 'YYYY-MM-DD HH:mm:ss'));  // '2025-10-28 05:30:00'

  


// 示例4:使用时间戳

const utcTime4 = tz.toUTC(1698484245000, 'Europe/London');

重要说明:

本方法的 date 参数表示指定时区的本地时间,而不是浏览器的本地时间。例如:


const tz = new Timezone();

  


// 假设浏览器时区是 Asia/Shanghai (UTC+8)

// 但我们要转换的是 America/New_York (UTC-5) 的时间

  


const nyLocalTime = new Date(2025, 9, 28, 14, 30, 0);  // 这表示纽约的 14:30

const utc = tz.toUTC(nyLocalTime, 'America/New_York');  // 转为 UTC

// 结果是 19:30 UTC,而不是基于上海时区计算

5.9.2 fromUTC() - 将 UTC 时间转为特定时区

功能说明:

将 UTC 时间转换为指定时区的本地时间。特别注意:特定时区可能不是本地浏览器时区。

方法签名:


instance.fromUTC(date, timezone) → Date | null

参数:

| 参数 | 类型 | 必填 | 默认值 | 说明 |

|------|------|------|--------|------|

| date | Date\|string\|number | ✅ | - | UTC 时间的 Date 对象、日期字符串或时间戳 |

| timezone | string | ❌ | 当前浏览器时区 ⭐ | 目标时区名称(要转换到的时区)|

返回值:

  • 成功:返回目标时区的本地时间 Date 对象

  • 失败:返回 null

默认时区行为 ⭐ 新增:

当不传入 timezone 参数时,自动使用当前浏览器时区,简化常见用例:


const tz = new Timezone();

const utcTime = new Date(Date.UTC(2025, 9, 28, 6, 30, 0));

  


// 不传时区参数,自动转换为浏览器本地时间

const local = tz.fromUTC(utcTime);  

  


// 等价于显式指定浏览器时区

const local2 = tz.fromUTC(utcTime, Timezone.getCurrentTimezone());

转换公式:


本地时间 = UTC 时间 + 时区偏移量

使用示例:


const tz = new Timezone();

  


// 示例1:将 UTC 时间转为上海时间

const utcTime = new Date(Date.UTC(2025, 9, 28, 6, 30, 0));  // UTC 时间

const shanghaiTime = tz.fromUTC(utcTime, 'Asia/Shanghai');

console.log(tz.format(shanghaiTime, 'YYYY-MM-DD HH:mm:ss'));  // '2025-10-28 14:30:00'

// 说明:上海是 UTC+8,所以 06:30 UTC + 8小时 = 14:30

  


// 示例2:将 UTC 时间转为纽约时间

const nyTime = tz.fromUTC(utcTime, 'America/New_York');

console.log(tz.format(nyTime, 'YYYY-MM-DD HH:mm:ss'));  // '2025-10-28 01:30:00'

// 说明:纽约是 UTC-5,所以 06:30 UTC + (-5小时) = 01:30

  


// 示例3:使用字符串输入

const tokyoTime = tz.fromUTC('2025-10-28 06:30:00', 'Asia/Tokyo');

console.log(tz.format(tokyoTime, 'YYYY-MM-DD HH:mm:ss'));  // '2025-10-28 15:30:00'

  


// 示例4:使用时间戳

const londonTime = tz.fromUTC(1698484245000, 'Europe/London');

5.9.3 跨时区转换应用

场景1:将一个时区的时间转换到另一个时区


const tz = new Timezone();

  


// 需求:将上海时间 14:30 转换为纽约时间

const shanghaiTime = new Date(2025, 9, 28, 14, 30, 0);

console.log('上海时间:', tz.format(shanghaiTime, 'YYYY-MM-DD HH:mm:ss'));

  


// 步骤1: 上海时间 -> UTC

const utcTime = tz.toUTC(shanghaiTime, 'Asia/Shanghai');

console.log('UTC 时间:', tz.format(utcTime, 'YYYY-MM-DD HH:mm:ss'));

  


// 步骤2: UTC -> 纽约时间

const nyTime = tz.fromUTC(utcTime, 'America/New_York');

console.log('纽约时间:', tz.format(nyTime, 'YYYY-MM-DD HH:mm:ss'));

  


// 输出:

// 上海时间: 2025-10-28 14:30:00

// UTC 时间: 2025-10-28 06:30:00

// 纽约时间: 2025-10-28 01:30:00

场景2:显示全球多个时区的同一时刻


const tz = new Timezone();

  


// 一个固定的 UTC 时间

const utcTime = new Date(Date.UTC(2025, 9, 28, 12, 0, 0));

console.log('UTC 时间:', tz.format(utcTime, 'YYYY-MM-DD HH:mm:ss'));

  


// 转换为多个时区

const timezones = [

    'Asia/Shanghai',

    'Asia/Tokyo',

    'Europe/London',

    'America/New_York',

    'America/Los_Angeles'

];

  


timezones.forEach(function(timezone) {

    const localTime = tz.fromUTC(utcTime, timezone);

    const offset = tz.getTimezoneOffset(timezone);

    const offsetHours = (offset / 60).toFixed(1);

   

    console.log(timezone + ':',

        tz.format(localTime, 'YYYY-MM-DD HH:mm:ss'),

        '(UTC' + (offset >= 0 ? '+' : '') + offsetHours + ')');

});

  


// 输出:

// Asia/Shanghai: 2025-10-28 20:00:00 (UTC+8.0)

// Asia/Tokyo: 2025-10-28 21:00:00 (UTC+9.0)

// Europe/London: 2025-10-28 12:00:00 (UTC+0.0)

// America/New_York: 2025-10-28 07:00:00 (UTC-5.0)

// America/Los_Angeles: 2025-10-28 04:00:00 (UTC-8.0)

场景3:验证往返转换


const tz = new Timezone();

  


// 原始时间

const original = new Date(2025, 9, 28, 14, 30, 0);

console.log('原始时间:', tz.format(original, 'YYYY-MM-DD HH:mm:ss'));

  


// 往返转换:本地 -> UTC -> 本地

const toUtc = tz.toUTC(original, 'Asia/Shanghai');

const backToLocal = tz.fromUTC(toUtc, 'Asia/Shanghai');

console.log('往返后:', tz.format(backToLocal, 'YYYY-MM-DD HH:mm:ss'));

  


// 验证

console.log('时间相等:', original.getTime() === backToLocal.getTime());

// 输出: true

场景4:计算不同时区的会议时间


const tz = new Timezone();

  


// 纽约团队的会议时间:上午 10:00

const nyMeetingTime = new Date(2025, 9, 28, 10, 0, 0);

console.log('纽约会议时间:', tz.format(nyMeetingTime, 'YYYY-MM-DD HH:mm:ss'));

  


// 转换为 UTC

const utc = tz.toUTC(nyMeetingTime, 'America/New_York');

  


// 上海团队需要在什么时间参加?

const shanghaiTime = tz.fromUTC(utc, 'Asia/Shanghai');

console.log('上海对应时间:', tz.format(shanghaiTime, 'YYYY-MM-DD HH:mm:ss'));

  


// 伦敦团队需要在什么时间参加?

const londonTime = tz.fromUTC(utc, 'Europe/London');

console.log('伦敦对应时间:', tz.format(londonTime, 'YYYY-MM-DD HH:mm:ss'));

  


// 输出:

// 纽约会议时间: 2025-10-28 10:00:00

// 上海对应时间: 2025-10-28 23:00:00 (晚上11点)

// 伦敦对应时间: 2025-10-28 15:00:00 (下午3点)

5.9.4 错误处理

两个方法都包含完善的错误处理:


const tz = new Timezone();

const testDate = new Date(2025, 9, 28, 14, 30, 0);

  


// 错误1:时区不存在

const result1 = tz.toUTC(testDate, 'Invalid/Timezone');

console.log(result1);  // null

// 控制台错误:toUTC: 时区 "Invalid/Timezone" 不存在

  


// 错误2:缺少时区参数

const result2 = tz.toUTC(testDate);

console.log(result2);  // null

// 控制台错误:toUTC: 必须指定时区名称

  


// 错误3:无效的日期

const result3 = tz.fromUTC('invalid date', 'Asia/Shanghai');

console.log(result3);  // null

// 控制台错误:fromUTC: 无效的日期

  


// 错误4:无效的日期参数类型

const result4 = tz.toUTC({}, 'Asia/Shanghai');

console.log(result4);  // null

// 控制台错误:toUTC: 无效的日期参数

5.9.5 注意事项

1. Date 对象的时区陷阱

JavaScript 的 Date 对象内部使用 UTC 时间戳,但在显示时会自动应用浏览器的本地时区。使用这些方法时需要理解:


const tz = new Timezone();

  


// 创建一个 Date 对象

const date = new Date(2025, 9, 28, 14, 30, 0);

  


// date 内部存储的是时间戳(UTC),但这个构造函数会被解释为浏览器本地时区的时间

// 如果浏览器在上海(UTC+8),date 实际表示 UTC 时间 06:30

// 如果浏览器在纽约(UTC-5),date 实际表示 UTC 时间 19:30

  


// 使用 toUTC 时,我们指定这个时间属于哪个时区

const utc = tz.toUTC(date, 'Asia/Shanghai');  // 明确告诉函数:date 是上海时间

2. 使用 Date.UTC() 创建真正的 UTC 时间


// 推荐:创建真正的 UTC 时间

const utcTime = new Date(Date.UTC(2025, 9, 28, 6, 30, 0));

  


// 不推荐:这会受到浏览器时区影响

const localTime = new Date(2025, 9, 28, 6, 30, 0);

3. 夏令时(DST)支持

已支持夏令时自动判断

插件已经实现了夏令时的自动处理。getTimezoneOffsettoUTCfromUTC 方法会根据具体日期自动判断并使用正确的偏移量。

夏令时工作原理:


const tz = new Timezone();

  


// 纽约冬季(1月)偏移量:UTC-5

const winterOffset = tz.getTimezoneOffset('America/New_York', new Date('2025-01-15'));

console.log(winterOffset);  // -300 分钟 (UTC-5)

  


// 纽约夏季(7月)偏移量:UTC-4(夏令时)

const summerOffset = tz.getTimezoneOffset('America/New_York', new Date('2025-07-15'));

console.log(summerOffset);  // -240 分钟 (UTC-4)

常见时区的夏令时规则:

| 时区 | 冬季偏移 | 夏季偏移 | 是否实行夏令时 |

|------|---------|---------|--------------|

| America/New_York | UTC-5 | UTC-4 | ✅ 是 |

| America/Los_Angeles | UTC-8 | UTC-7 | ✅ 是 |

| Europe/London | UTC+0 | UTC+1 | ✅ 是 |

| Europe/Paris | UTC+1 | UTC+2 | ✅ 是 |

| Asia/Shanghai | UTC+8 | UTC+8 | ❌ 否 |

| Asia/Tokyo | UTC+9 | UTC+9 | ❌ 否 |

技术实现:

插件使用 JavaScript 的 Intl.DateTimeFormat API 自动获取指定日期在指定时区的精确偏移量:


// 通过比较 UTC 时间和本地时间来计算偏移量

var localStr = date.toLocaleString('en-US', { timeZone: timezone });

var localDate = new Date(localStr);

var offset = Math.round((localDate.getTime() - date.getTime()) / 60000);

如果 Intl API 不可用(旧浏览器),会回退到使用静态配置中的 rawOffsetInMinutes

夏令时转换示例:


const tz = new Timezone();

  


// 示例1:纽约冬季时间转 UTC

const nyWinter = new Date('2025-01-15T14:30:00');

const utcWinter = tz.toUTC(nyWinter, 'America/New_York');

console.log(tz.format(utcWinter, 'YYYY-MM-DD HH:mm:ss'));

// 输出: 2025-01-15 19:30:00 (14:30 + 5小时)

  


// 示例2:纽约夏季时间转 UTC

const nySummer = new Date('2025-07-15T14:30:00');

const utcSummer = tz.toUTC(nySummer, 'America/New_York');

console.log(tz.format(utcSummer, 'YYYY-MM-DD HH:mm:ss'));

// 输出: 2025-07-15 18:30:00 (14:30 + 4小时,夏令时)

5.10 方法总结

| 类型 | 方法 | 参数 | 返回值 | 说明 |

|------|------|------|--------|------|

| 静态方法 | getCurrentTimezone() | - | string | 获取当前浏览器时区 |

| 实例方法 | getCountryCode(timezone?) | string? | string\|null | 根据时区获取国家代码 |

| 实例方法 | getTimezoneOffset(timezone?) | string? | number\|null | 根据时区获取偏移量(分钟)|

| 实例方法 | toUTC(date, timezone) | Date\|string\|number, string | Date\|null | 将特定时区时间转为 UTC |

| 实例方法 | fromUTC(date, timezone) | Date\|string\|number, string | Date\|null | 将 UTC 时间转为特定时区 |

| 实例属性 | currentTimezone | - | string | 当前时区名称(缓存)|

| 实例属性 | currentCountryCode | - | string\|null | 当前国家代码(缓存)|

| 实例属性 | currentTimezoneOffset | - | number\|null | 当前偏移量(缓存)|

5.11 时区数据范围

插件内置时区数据涵盖:

  • 400+ 时区:覆盖全球所有国家和地区

  • 洲分布

  - 亚洲(AS):120+ 时区

  - 欧洲(EU):80+ 时区

  - 北美洲(NA):90+ 时区

  - 南美洲(SA):30+ 时区

  - 大洋洲(OC):50+ 时区

  - 非洲(AF):50+ 时区

  - 南极洲(AN):10+ 时区

  • IANA 标准:使用 IANA 时区数据库标识符

  • 别名支持:支持常用时区别名

数据来源:

基于 IANA 时区数据库(tzdata),定期更新以确保准确性。