基于JavaScript優(yōu)雅的實(shí)現(xiàn)一個(gè)時(shí)間處理插件
1. UMD 模式解析
1.1 立即執(zhí)行函數(shù) (IIFE)
外層是一個(gè)立即執(zhí)行函數(shù),接收 global 和 factory 兩個(gè)參數(shù)。
global 參數(shù)說(shuō)明:
typeof window !== "undefined" ? window : this;
根據(jù)不同的運(yùn)行環(huán)境,global 參數(shù)會(huì)指向:
- 瀏覽器環(huán)境:
window對(duì)象 - Node.js 環(huán)境:
global對(duì)象 - 其他環(huán)境:當(dāng)前上下文
this
1.2 UMD 模塊定義
通過(guò)條件判斷支持多種模塊系統(tǒng):
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();
}
支持的模塊系統(tǒng):
- AMD:用于 RequireJS 等加載器
- CommonJS:用于 Node.js 環(huán)境
- 全局變量:用于直接在瀏覽器中使用
1.3 工廠函數(shù)解析
工廠函數(shù) factory 返回插件的構(gòu)造函數(shù):
function () {
'use strict';
// 構(gòu)造函數(shù)定義
function SurveyTimezone(options) {
}
// 原型方法定義
SurveyTimezone.prototype = {
constructor: SurveyTimezone, // 修復(fù) constructor 指向
version: '1.0.0',
_init: function () {
}
}
// 返回構(gòu)造函數(shù)
return SurveyTimezone;
}
核心組成部分:
- 嚴(yán)格模式:使用
'use strict'確保代碼質(zhì)量 - 構(gòu)造函數(shù):定義
SurveyTimezone類 - 原型方法:通過(guò)原型鏈添加共享方法
- 返回值:導(dǎo)出構(gòu)造函數(shù)供外部使用
1.4 執(zhí)行流程
UMD 模塊的加載和執(zhí)行流程:
- 立即執(zhí)行:代碼加載后立即執(zhí)行 IIFE
- 環(huán)境檢測(cè):檢測(cè)當(dāng)前支持的模塊系統(tǒng)(AMD / CommonJS / 全局變量)
- 工廠調(diào)用:執(zhí)行
factory()函數(shù),返回SurveyTimezone構(gòu)造函數(shù) - 模塊導(dǎo)出:根據(jù)環(huán)境將構(gòu)造函數(shù)導(dǎo)出到相應(yīng)位置
(function (global, factory) {
// 環(huán)境檢測(cè)和模塊導(dǎo)出邏輯
}(typeof window !== "undefined" ? window : this, function () {
// 工廠函數(shù):創(chuàng)建并返回構(gòu)造函數(shù)
}));
2. 單例模式實(shí)現(xiàn)
2.1 為什么使用單例模式?
在時(shí)區(qū)處理插件中,我們通常只需要一個(gè)全局實(shí)例來(lái)管理配置和狀態(tài):
- 避免重復(fù)實(shí)例:防止多次實(shí)例化造成的內(nèi)存浪費(fèi)
- 全局狀態(tài)管理:統(tǒng)一管理時(shí)區(qū)配置、數(shù)據(jù)映射表等
- 配置一致性:確保整個(gè)應(yīng)用使用相同的時(shí)區(qū)設(shè)置
2.2 單例模式實(shí)現(xiàn)
2.2.1 私有實(shí)例存儲(chǔ)
// 閉包中的私有變量,存儲(chǔ)單例實(shí)例 var instance = null;
2.2.2 構(gòu)造函數(shù)實(shí)現(xiàn)
function SurveyTimezone(options) {
// 1. 如果已存在實(shí)例,直接返回
if (instance) {
return instance;
}
// 2. 確保通過(guò) new 調(diào)用
if (!(this instanceof SurveyTimezone)) {
return new SurveyTimezone(options);
}
// 3. 初始化配置
this.options = options || {};
// 4. 保存單例實(shí)例
instance = this;
// 5. 執(zhí)行初始化
this._init();
return instance;
}
實(shí)現(xiàn)要點(diǎn):
- 實(shí)例檢查:首次檢查是否已存在實(shí)例,有則直接返回
- new 檢查:確保即使不用
new關(guān)鍵字也能正常工作 - 配置初始化:保存?zhèn)魅氲呐渲眠x項(xiàng)
- 實(shí)例保存:將當(dāng)前實(shí)例保存到閉包變量中
- 初始化執(zhí)行:調(diào)用內(nèi)部初始化方法
2.2.3 靜態(tài)方法
/**
* 獲取單例實(shí)例
*/
SurveyTimezone.getInstance = function (options) {
if (!instance) {
instance = new SurveyTimezone(options);
}
return instance;
}
/**
* 重置單例(用于測(cè)試)
*/
SurveyTimezone.resetInstance = function () {
instance = null;
}
2.3 使用方式
方式一:使用 new 關(guān)鍵字
const instance1 = new SurveyTimezone({ timezone: 'Asia/Shanghai' });
const instance2 = new SurveyTimezone({ timezone: 'America/New_York' });
console.log(instance1 === instance2); // true(返回同一個(gè)實(shí)例)
方式二:使用 getInstance 靜態(tài)方法
const instance = SurveyTimezone.getInstance({ timezone: 'Asia/Shanghai' });
方式三:不使用 new(自動(dòng)轉(zhuǎn)換)
const instance = SurveyTimezone({ timezone: 'Asia/Shanghai' });
2.4 單例模式的優(yōu)勢(shì)
| 優(yōu)勢(shì) | 說(shuō)明 |
|---|---|
| 內(nèi)存優(yōu)化 | 只創(chuàng)建一個(gè)實(shí)例,減少內(nèi)存占用 |
| 狀態(tài)一致 | 全局共享同一個(gè)實(shí)例,避免狀態(tài)不一致 |
| 易于管理 | 集中管理配置和數(shù)據(jù) |
| 防止沖突 | 避免多個(gè)實(shí)例之間的配置沖突 |
2.5 完整示例
// 第一次創(chuàng)建實(shí)例
const timezone1 = new SurveyTimezone({
timezone: 'Asia/Shanghai',
locale: 'zh-CN'
});
// 第二次嘗試創(chuàng)建(返回第一次的實(shí)例)
const timezone2 = new SurveyTimezone({
timezone: 'America/New_York' // 這個(gè)配置會(huì)被忽略
});
console.log(timezone1 === timezone2); // true
console.log(timezone1.options.timezone); // 'Asia/Shanghai'
// 重置單例后可以創(chuàng)建新實(shí)例
SurveyTimezone.resetInstance();
const timezone3 = new SurveyTimezone({
timezone: 'Europe/London'
});
console.log(timezone1 === timezone3); // false
console.log(timezone3.options.timezone); // 'Europe/London'
2.6 初始化時(shí)傳入日期時(shí)間
從 v1.0.0 開(kāi)始,SurveyTimezone 支持在初始化時(shí)傳入日期時(shí)間字符串或時(shí)間戳,使其更加靈活實(shí)用。
2.6.1 初始化方式
構(gòu)造函數(shù)簽名:
new SurveyTimezone(input, format)
參數(shù)說(shuō)明:
| 參數(shù) | 類型 | 必填 | 說(shuō)明 |
|---|---|---|---|
input | string|number|Date|Object | ? | 日期時(shí)間字符串、時(shí)間戳、Date對(duì)象或配置對(duì)象 |
format | string | ? | 日期格式(當(dāng) input 為字符串時(shí)使用) |
2.6.2 初始化示例
方式1:傳入日期字符串
const tz = new SurveyTimezone('2025-10-28 14:30:45');
console.log(tz.getDate()); // Date 對(duì)象
console.log(tz.format()); // '2025-10-28 14:30:45'
方式2:傳入時(shí)間戳
const tz = new SurveyTimezone(1698484245000); console.log(tz.getDate()); // Date 對(duì)象 console.log(tz.format()); // 對(duì)應(yīng)的日期時(shí)間字符串
方式3:傳入日期字符串和格式
const tz = new SurveyTimezone('28/10/2025', 'DD/MM/YYYY');
console.log(tz.format('YYYY-MM-DD')); // '2025-10-28'
方式4:傳入 Date 對(duì)象
const tz = new SurveyTimezone(new Date()); console.log(tz.format()); // 當(dāng)前時(shí)間
方式5:傳入配置對(duì)象
const tz = new SurveyTimezone({
date: '2025-10-28 14:30:45',
timezone: 'Asia/Shanghai',
locale: 'zh-CN'
});
console.log(tz.getDate()); // Date 對(duì)象
console.log(tz.options); // { date: '...', timezone: '...', locale: '...' }
方式6:不傳參數(shù)(默認(rèn)當(dāng)前時(shí)間)
const tz = new SurveyTimezone(); console.log(tz.format()); // 當(dāng)前時(shí)間
2.6.3 新增實(shí)例方法
getDate() - 獲取日期對(duì)象
const tz = new SurveyTimezone('2025-10-28 14:30:45');
const date = tz.getDate();
console.log(date); // Date 對(duì)象
setDate() - 設(shè)置日期(支持鏈?zhǔn)秸{(diào)用)
const tz = new SurveyTimezone();
// 設(shè)置新日期
tz.setDate('2025-12-25 00:00:00');
console.log(tz.format()); // '2025-12-25 00:00:00'
// 鏈?zhǔn)秸{(diào)用
const result = tz.setDate('2026-01-01').format('YYYY/MM/DD');
console.log(result); // '2026/01/01'
2.6.4 format 方法增強(qiáng)
現(xiàn)在 format 方法可以在不傳參數(shù)時(shí)格式化實(shí)例的日期:
const tz = new SurveyTimezone('2025-10-28 14:30:45');
// 格式化實(shí)例日期(無(wú)參數(shù))
tz.format(); // '2025-10-28 14:30:45'(默認(rèn)格式)
// 格式化實(shí)例日期(指定格式)
tz.format('YYYY年MM月DD日'); // '2025年10月28日'
// 格式化指定日期
tz.format(new Date(), 'YYYY-MM-DD'); // 格式化其他日期
2.6.5 完整工作流示例
// 場(chǎng)景:接收用戶輸入 → 解析 → 處理 → 格式化輸出
// 1. 用戶輸入歐洲格式日期
const userInput = '28/10/2025';
// 2. 創(chuàng)建實(shí)例并解析
const tz = new SurveyTimezone(userInput, 'DD/MM/YYYY');
// 3. 驗(yàn)證解析結(jié)果
console.log(tz.getDate()); // Date 對(duì)象
// 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 與單例模式的配合
由于采用單例模式,第一次初始化時(shí)傳入的日期會(huì)被保存,后續(xù)創(chuàng)建實(shí)例會(huì)返回同一個(gè)實(shí)例:
// 第一次創(chuàng)建,指定日期
const tz1 = new SurveyTimezone('2025-10-28 14:30:45');
console.log(tz1.format()); // '2025-10-28 14:30:45'
// 第二次創(chuàng)建,嘗試傳入不同日期(但返回的是同一個(gè)實(shí)例)
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(同一個(gè)實(shí)例)
// 如果需要新的日期,可以使用 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 也變了,因?yàn)槭峭粋€(gè)實(shí)例)
// 或者先重置單例
SurveyTimezone.resetInstance();
const tz3 = new SurveyTimezone('2026-01-01 00:00:00');
console.log(tz3.format()); // '2026-01-01 00:00:00'(新實(shí)例,新日期)
2.6.7 錯(cuò)誤處理
當(dāng)傳入無(wú)效日期時(shí),會(huì)自動(dòng)使用當(dāng)前時(shí)間:
// 無(wú)效日期字符串
const tz1 = new SurveyTimezone('invalid date');
console.log(tz1.getDate()); // 當(dāng)前時(shí)間的 Date 對(duì)象
// 重置單例
SurveyTimezone.resetInstance();
// 無(wú)效時(shí)間戳
const tz2 = new SurveyTimezone(NaN);
console.log(tz2.getDate()); // 當(dāng)前時(shí)間的 Date 對(duì)象
2.6.8 方法對(duì)比表
| 方法 | 類型 | 參數(shù) | 返回值 | 說(shuō)明 |
|---|---|---|---|---|
new SurveyTimezone(input, format) | 構(gòu)造函數(shù) | 日期輸入 | 實(shí)例 | 創(chuàng)建實(shí)例并初始化日期 |
getDate() | 實(shí)例方法 | - | Date | 獲取實(shí)例的日期對(duì)象 |
setDate(input, format) | 實(shí)例方法 | 日期輸入 | this | 設(shè)置實(shí)例日期,支持鏈?zhǔn)秸{(diào)用 |
format(date, format) | 實(shí)例方法 | 可選 | string | 格式化日期(無(wú)參數(shù)時(shí)格式化實(shí)例日期) |
parse(dateString, format) | 實(shí)例方法 | 必填 | Date|null | 解析日期字符串 |
3. format 方法詳解
3.1 方法說(shuō)明
format 方法用于格式化日期對(duì)象,兼容 dayjs 和 laydate 兩種流行的日期格式化風(fēng)格。
方法簽名:
// 原型方法 instance.format(date, format) // 靜態(tài)方法(向后兼容) SurveyTimezone.format(date, format)
參數(shù):
| 參數(shù) | 類型 | 必填 | 默認(rèn)值 | 說(shuō)明 |
|---|---|---|---|---|
date | Date | ? | - | 要格式化的 JavaScript Date 對(duì)象 |
format | string | ? | 'YYYY-MM-DD HH:mm:ss' | 格式化模板字符串 |
返回值:
- 類型:
string - 說(shuō)明:格式化后的日期字符串,無(wú)效日期返回空字符串
''
3.2 支持的格式化標(biāo)記
3.2.1 年份標(biāo)記
| 標(biāo)記 | 說(shuō)明 | 示例輸出 | 兼容性 |
|---|---|---|---|
YYYY | 四位年份 | 2025 | dayjs |
yyyy | 四位年份 | 2025 | laydate |
YY | 兩位年份 | 25 | dayjs |
y | 兩位年份 | 25 | laydate |
3.2.2 月份標(biāo)記
| 標(biāo)記 | 說(shuō)明 | 示例輸出 | 兼容性 |
|---|---|---|---|
MMM | 英文月份縮寫 | Jan, Feb, Mar... | 通用 |
MM | 兩位月份(補(bǔ)零) | 01, 02... 12 | 通用 |
M | 月份(不補(bǔ)零) | 1, 2... 12 | 通用 |
3.2.3 日期標(biāo)記
| 標(biāo)記 | 說(shuō)明 | 示例輸出 | 兼容性 |
|---|---|---|---|
DD | 兩位日期(補(bǔ)零) | 01, 02... 31 | dayjs |
dd | 兩位日期(補(bǔ)零) | 01, 02... 31 | laydate |
D | 日期(不補(bǔ)零) | 1, 2... 31 | dayjs |
d | 日期(不補(bǔ)零) | 1, 2... 31 | laydate |
3.2.4 時(shí)間標(biāo)記
| 標(biāo)記 | 說(shuō)明 | 示例輸出 | 兼容性 |
|---|---|---|---|
HH | 24小時(shí)制小時(shí)(補(bǔ)零) | 00, 01... 23 | 通用 |
H | 24小時(shí)制小時(shí)(不補(bǔ)零) | 0, 1... 23 | 通用 |
mm | 分鐘(補(bǔ)零) | 00, 01... 59 | 通用 |
m | 分鐘(不補(bǔ)零) | 0, 1... 59 | 通用 |
ss | 秒(補(bǔ)零) | 00, 01... 59 | 通用 |
s | 秒(不補(bǔ)零) | 0, 1... 59 | 通用 |
3.2.5 毫秒標(biāo)記
| 標(biāo)記 | 說(shuō)明 | 示例輸出 | 兼容性 |
|---|---|---|---|
SSS | 三位毫秒 | 000, 001... 999 | dayjs |
3.3 使用示例
3.3.1 基礎(chǔ)用法
const tz = new SurveyTimezone();
const now = new Date('2025-10-28 14:30:45.123');
// 默認(rèn)格式
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 風(fēng)格格式
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 風(fēng)格格式
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 中文日期格式
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時(shí)mm分ss秒'); // '2025年10月28日 14時(shí)30分45秒'
tz.format(now, 'yyyy年MM月dd日 HH:mm'); // '2025年10月28日 14:30'
3.3.5 靜態(tài)方法調(diào)用(無(wú)需實(shí)例化)
const now = new Date('2025-10-28 14:30:45');
// 直接使用靜態(tài)方法
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 常用格式模板
| 格式模板 | 輸出示例 | 使用場(chǎng)景 |
|---|---|---|
YYYY-MM-DD | 2025-10-28 | 標(biāo)準(zhǔn)日期格式 |
YYYY-MM-DD HH:mm:ss | 2025-10-28 14:30:45 | 完整日期時(shí)間 |
yyyy年MM月dd日 | 2025年10月28日 | 中文日期 |
MMM DD, yyyy | Oct 28, 2025 | 英文日期 |
YYYY/MM/DD HH:mm | 2025/10/28 14:30 | 簡(jiǎn)短日期時(shí)間 |
HH:mm:ss | 14:30:45 | 僅時(shí)間 |
YY-M-D | 25-10-28 | 簡(jiǎn)短日期 |
YYYY-MM-DD HH:mm:ss.SSS | 2025-10-28 14:30:45.123 | 帶毫秒 |
3.5 錯(cuò)誤處理
const tz = new SurveyTimezone();
// 無(wú)效日期返回空字符串
tz.format(null); // ''
tz.format(undefined); // ''
tz.format(new Date('invalid')); // ''
tz.format('2025-10-28'); // ''(字符串不是 Date 對(duì)象)
// 有效日期
tz.format(new Date()); // '2025-10-28 14:30:45'(當(dāng)前時(shí)間)
3.6 實(shí)現(xiàn)原理
format 方法采用正則替換策略實(shí)現(xiàn)格式化:
- 標(biāo)記解析:將格式字符串中的標(biāo)記(如
YYYY、MM)識(shí)別出來(lái) - 長(zhǎng)度優(yōu)先:按標(biāo)記長(zhǎng)度從長(zhǎng)到短處理,避免
YYYY被YY誤替換 - 順序處理:依次替換每個(gè)標(biāo)記為對(duì)應(yīng)的日期值
- 類型轉(zhuǎn)換:使用 JavaScript Date 對(duì)象的原生方法獲取年、月、日等值
關(guān)鍵代碼邏輯:
// 標(biāo)記處理順序(長(zhǎng)的在前,短的在后)
var tokens = ['YYYY', 'yyyy', 'MMM', 'SSS', 'MM', 'DD', 'dd', 'HH', 'mm', 'ss', 'YY', 'M', 'D', 'd', 'H', 'm', 's', 'y'];
// 依次替換每個(gè)標(biāo)記
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 靜態(tài)方法
| 特性 | 原型方法 | 靜態(tài)方法 |
|---|---|---|
| 調(diào)用方式 | instance.format() | SurveyTimezone.format() |
| 是否需要實(shí)例化 | ? 需要 | ? 不需要 |
| 訪問(wèn)實(shí)例屬性 | ? 可以 | ? 不可以 |
| 推薦使用場(chǎng)景 | 面向?qū)ο缶幊?/td> | 工具函數(shù)調(diào)用 |
| 性能 | 略優(yōu) | 略低(多一層調(diào)用) |
關(guān)系說(shuō)明:
- 靜態(tài)方法內(nèi)部調(diào)用原型方法實(shí)現(xiàn)
- 兩種方式返回結(jié)果完全一致
- 靜態(tài)方法保證向后兼容性
// 靜態(tài)方法實(shí)現(xiàn)(調(diào)用原型方法)
SurveyTimezone.format = function (date, format) {
return SurveyTimezone.prototype.format.call(null, date, format);
}
4. parse 方法詳解
4.1 方法說(shuō)明
parse 方法用于將日期時(shí)間字符串解析為 JavaScript Date 對(duì)象,支持多種常見(jiàn)格式的自動(dòng)識(shí)別。
方法簽名:
// 原型方法 instance.parse(dateString, format) // 靜態(tài)方法(向后兼容) SurveyTimezone.parse(dateString, format)
參數(shù):
| 參數(shù) | 類型 | 必填 | 默認(rèn)值 | 說(shuō)明 |
|---|---|---|---|---|
dateString | string|number | ? | - | 日期時(shí)間字符串或時(shí)間戳 |
format | string | ? | - | 可選的格式模板,用于指定解析格式 |
返回值:
- 類型:
Date | null - 說(shuō)明:解析成功返回 Date 對(duì)象,失敗返回
null
4.2 支持的日期格式
4.2.1 自動(dòng)識(shí)別格式(無(wú)需指定 format)
| 格式 | 示例 | 說(shuō)明 |
|---|---|---|
| ISO 8601 | 2025-10-28T14:30:45.123Z | JavaScript 原生支持 |
| 標(biāo)準(zhǔn)格式(帶時(shí)分秒毫秒) | 2025-10-28 14:30:45.123 | 常用格式 |
| 標(biāo)準(zhǔn)格式(帶時(shí)分秒) | 2025-10-28 14:30:45 | 常用格式 |
| 標(biāo)準(zhǔn)格式(帶時(shí)分) | 2025-10-28 14:30 | 常用格式 |
| 標(biāo)準(zhǔn)日期 | 2025-10-28 | 僅日期 |
| 斜杠格式(帶時(shí)間) | 2025/10/28 14:30:45 | 常用格式 |
| 斜杠格式(日期) | 2025/10/28 | 常用格式 |
| 中文格式(帶時(shí)間) | 2025年10月28日 14時(shí)30分45秒 | 中文日期時(shí)間 |
| 中文格式(日期) | 2025年10月28日 | 中文日期 |
| 歐洲格式 | 28/10/2025 | DD/MM/YYYY |
| 美式格式 | 10-28-2025 | MM-DD-YYYY |
| 時(shí)間戳 | 1698484245000 | 毫秒時(shí)間戳 |
4.2.2 指定格式解析
當(dāng)自動(dòng)識(shí)別失敗時(shí),可以指定 format 參數(shù)明確告知解析格式:
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');
支持的格式標(biāo)記:
YYYY/yyyy- 四位年份YY/yy- 兩位年份(00-49 → 2000-2049,50-99 → 1950-1999)MM- 月份DD/dd- 日期HH- 小時(shí)mm- 分鐘ss- 秒SSS- 毫秒
4.3 使用示例
4.3.1 基礎(chǔ)用法(自動(dòng)識(shí)別)
const tz = new SurveyTimezone();
// 標(biāo)準(zhǔn)格式
tz.parse('2025-10-28 14:30:45'); // Date 對(duì)象
tz.parse('2025-10-28'); // Date 對(duì)象
// 斜杠格式
tz.parse('2025/10/28 14:30:45'); // Date 對(duì)象
tz.parse('2025/10/28'); // Date 對(duì)象
// 中文格式
tz.parse('2025年10月28日'); // Date 對(duì)象
tz.parse('2025年10月28日 14時(shí)30分45秒'); // Date 對(duì)象
// 時(shí)間戳
tz.parse(1698484245000); // Date 對(duì)象
// 帶毫秒
tz.parse('2025-10-28 14:30:45.123'); // Date 對(duì)象
4.3.2 指定格式解析
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 錯(cuò)誤處理
const tz = new SurveyTimezone();
// 無(wú)效輸入返回 null
tz.parse(null); // null
tz.parse(undefined); // null
tz.parse(''); // null
tz.parse('invalid date'); // null
tz.parse('2025-13-40'); // null(無(wú)效日期)
// 類型檢查
const result = tz.parse('2025-10-28');
if (result) {
console.log('解析成功:', result);
} else {
console.log('解析失敗');
}
4.3.4 靜態(tài)方法調(diào)用
// 直接使用靜態(tài)方法,無(wú)需實(shí)例化
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 組合使用
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'
}
// 格式轉(zhuǎn)換
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/undefined → null │ │ - Date 對(duì)象 → 驗(yàn)證后返回 │ │ - 數(shù)字 → 時(shí)間戳解析 │ └─────────────────────────────────┘ ↓ ┌─────────────────────────────────┐ │ 2. 格式化參數(shù)檢查 │ │ - 有 format → 使用格式模板解析 │ │ - 無(wú) format → 自動(dòng)識(shí)別 │ └─────────────────────────────────┘ ↓ ┌─────────────────────────────────┐ │ 3. 原生解析嘗試 │ │ - new Date(dateString) │ │ - 成功 → 返回 │ │ - 失敗 → 繼續(xù) │ └─────────────────────────────────┘ ↓ ┌─────────────────────────────────┐ │ 4. 正則匹配解析 │ │ - 遍歷預(yù)定義格式列表 │ │ - 匹配成功 → 返回 │ │ - 全部失敗 → 返回 null │ └─────────────────────────────────┘
4.5 常見(jiàn)場(chǎng)景示例
場(chǎng)景1:表單日期輸入
const tz = new SurveyTimezone();
// 用戶輸入的日期字符串
const userInput = document.getElementById('dateInput').value; // '2025-10-28'
const date = tz.parse(userInput);
if (date) {
// 轉(zhuǎn)換為顯示格式
const displayText = tz.format(date, 'YYYY年MM月DD日');
console.log(displayText); // '2025年10月28日'
}
場(chǎng)景2:API 數(shù)據(jù)轉(zhuǎn)換
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'));
場(chǎng)景3:日期格式統(tǒng)一化
const tz = new SurveyTimezone();
// 不同格式的日期數(shù)組
const dates = [
'2025-10-28',
'2025/10/28',
'2025年10月28日',
'28/10/2025' // 需要指定格式
];
// 統(tǒng)一轉(zhuǎn)換為標(biāo)準(zhǔn)格式
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 性能考慮
最佳實(shí)踐:
- 優(yōu)先使用標(biāo)準(zhǔn)格式:ISO 8601 格式解析最快
- 指定格式模板:已知格式時(shí)指定 format 參數(shù)可跳過(guò)自動(dòng)識(shí)別
- 緩存解析結(jié)果:避免重復(fù)解析相同字符串
- 提前驗(yàn)證:在解析前進(jìn)行基本格式驗(yàn)證
const tz = new SurveyTimezone();
// ? 不推薦:每次都自動(dòng)識(shí)別
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 方法的配合
parse 和 format 是互補(bǔ)的兩個(gè)方法:
| 方法 | 輸入 | 輸出 | 用途 |
|---|---|---|---|
parse | 字符串 → Date | 將日期字符串轉(zhuǎn)換為 Date 對(duì)象 | 數(shù)據(jù)輸入、解析 |
format | Date → 字符串 | 將 Date 對(duì)象轉(zhuǎn)換為格式化字符串 | 數(shù)據(jù)顯示、輸出 |
完整的數(shù)據(jù)流:
const tz = new SurveyTimezone(); // 數(shù)據(jù)輸入 → 處理 → 輸出 const input = '28/10/2025'; // 用戶輸入 const date = tz.parse(input, 'DD/MM/YYYY'); // 解析為 Date 對(duì)象 const output = tz.format(date, 'YYYY年MM月DD日'); // 格式化為顯示文本 console.log(output); // '2025年10月28日'
4.8 原型方法 vs 靜態(tài)方法
| 特性 | 原型方法 | 靜態(tài)方法 |
|---|---|---|
| 調(diào)用方式 | instance.parse() | SurveyTimezone.parse() |
| 是否需要實(shí)例化 | ? 需要 | ? 不需要 |
| 訪問(wèn)實(shí)例屬性 | ? 可以 | ? 不可以 |
| 推薦使用場(chǎng)景 | 面向?qū)ο缶幊?/td> | 工具函數(shù)調(diào)用 |
關(guān)系說(shuō)明:
// 靜態(tài)方法實(shí)現(xiàn)(調(diào)用原型方法)
SurveyTimezone.parse = function (dateString, format) {
// 使用原型對(duì)象作為上下文,以便訪問(wèn)內(nèi)部方法
return SurveyTimezone.prototype.parse.call(SurveyTimezone.prototype, dateString, format);
}
技術(shù)說(shuō)明:
靜態(tài)方法 parse 內(nèi)部調(diào)用原型方法時(shí),需要使用 SurveyTimezone.prototype 作為上下文(this),而不是 null。這是因?yàn)樵头椒ㄖ锌赡軙?huì)調(diào)用其他內(nèi)部方法(如 _parseWithFormat),如果 this 為 null 會(huì)導(dǎo)致錯(cuò)誤。
源碼
/**
* SurveyTimezone - 時(shí)間處理插件
* @description 專門用于處理調(diào)查問(wèn)卷中的時(shí)區(qū)轉(zhuǎn)換和顯示問(wèn)題
* @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';
/**
* 單例實(shí)例存儲(chǔ)
* @private
*/
var instance = null;
/**
* 時(shí)區(qū)數(shù)據(jù)映射表(用于快速查找)
* @private
*/
var timezoneDataMap = {};
/**
* SurveyTimezone 主類(單例模式)
* @param {string|number|Date|Object} input - 日期時(shí)間字符串、時(shí)間戳、Date對(duì)象或配置對(duì)象
* @param {string} format - 可選的日期格式(當(dāng) input 為字符串時(shí)使用)
* @returns {SurveyTimezone} 單例實(shí)例
* @description 采用單例模式,多次實(shí)例化返回同一個(gè)對(duì)象
* @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) {
// 單例模式:如果已存在實(shí)例,直接返回
if (instance) {
return instance;
}
// 確保通過(guò) new 調(diào)用
if (!(this instanceof SurveyTimezone)) {
return new SurveyTimezone(input, format);
}
// 解析輸入?yún)?shù)
this._parseInput(input, format);
// 保存單例實(shí)例
instance = this;
// 執(zhí)行初始化
this._init();
return instance;
}
SurveyTimezone.prototype = {
constructor: SurveyTimezone,
version: '1.0.0',
/**
* 解析輸入?yún)?shù)
* @private
* @param {string|number|Date|Object} input - 輸入?yún)?shù)
* @param {string} format - 日期格式
*/
_parseInput: function (input, format) {
// 初始化配置對(duì)象
this.options = {};
this.date = null;
// 如果沒(méi)有輸入,使用當(dāng)前時(shí)間
if (input === undefined || input === null) {
this.date = new Date();
return;
}
// 如果是配置對(duì)象
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;
}
// 其他情況:字符串、數(shù)字、Date 對(duì)象
this.date = this.parse(input, format);
// 如果解析失敗,使用當(dāng)前時(shí)間
if (!this.date) {
this.date = new Date();
}
},
/**
* 初始化方法
* @private
*/
_init: function () {
// 初始化邏輯
// 可以在這里添加時(shí)區(qū)處理、本地化等邏輯
},
/**
* 獲取當(dāng)前實(shí)例的日期對(duì)象
* @returns {Date} 日期對(duì)象
*/
getDate: function () {
return this.date;
},
/**
* 設(shè)置日期
* @param {string|number|Date} dateInput - 日期時(shí)間字符串、時(shí)間戳或Date對(duì)象
* @param {string} format - 可選的日期格式
* @returns {SurveyTimezone} 返回當(dāng)前實(shí)例(鏈?zhǔn)秸{(diào)用)
*/
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 - 要格式化的日期對(duì)象(可選,默認(rèn)使用實(shí)例日期)
* @param {string} format - 格式化模板字符串(默認(rèn):'YYYY-MM-DD HH:mm:ss')
* @returns {string} 格式化后的日期字符串
* @description 支持的格式化標(biāo)記:
* 年份:
* YYYY/yyyy - 四位年份(2025)
* YY/y - 兩位年份(25)
* 月份:
* MMM - 英文月份縮寫(Jan, Feb, Mar...)
* MM - 兩位月份(01-12)
* M - 月份(1-12)
* 日期:
* DD/dd - 兩位日期(01-31)
* D/d - 日期(1-31)
* 時(shí)間:
* HH - 24小時(shí)制小時(shí)(00-23)
* H - 24小時(shí)制小時(shí)(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'(使用實(shí)例日期)
* tz.format('YYYY年MM月DD日'); // '2025年10月28日'(使用實(shí)例日期)
* tz.format(new Date(), 'YYYY-MM-DD'); // 格式化指定日期
*/
format: function (date, format) {
// 如果第一個(gè)參數(shù)是字符串,說(shuō)明是格式參數(shù)
if (typeof date === 'string' && !format) {
format = date;
date = this.date;
}
// 如果沒(méi)有傳入 date,使用實(shí)例的日期
if (!date || typeof date === 'string') {
date = this.date;
}
// 默認(rèn)格式
format = format || 'YYYY-MM-DD HH:mm:ss';
// 驗(yàn)證日期對(duì)象
if (!date || !(date instanceof Date) || isNaN(date.getTime())) {
return '';
}
// 月份英文縮寫
var monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
// 定義格式化標(biāo)記映射
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(); },
// 時(shí)間
'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); }
};
// 按標(biāo)記長(zhǎng)度從長(zhǎng)到短排序,先處理長(zhǎng)標(biāo)記避免沖突
// 注意: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;
},
/**
* 解析日期時(shí)間字符串(原型方法)
* @param {string|number} dateString - 日期時(shí)間字符串或時(shí)間戳
* @param {string} format - 可選的格式模板,用于指定解析格式
* @returns {Date|null} 解析后的 Date 對(duì)象,解析失敗返回 null
* @description 支持多種常見(jiàn)日期格式的自動(dòng)識(shí)別和解析
* @example
* const tz = new SurveyTimezone();
* tz.parse('2025-10-28 14:30:45'); // Date 對(duì)象
* tz.parse('2025/10/28'); // Date 對(duì)象
* tz.parse('2025年10月28日'); // Date 對(duì)象
* tz.parse(1698484245000); // Date 對(duì)象(時(shí)間戳)
* tz.parse('invalid'); // null
*/
parse: function (dateString, format) {
// 處理 null 或 undefined
if (dateString == null) {
return null;
}
// 如果已經(jīng)是 Date 對(duì)象,直接返回
if (dateString instanceof Date) {
return isNaN(dateString.getTime()) ? null : dateString;
}
// 處理數(shù)字類型(時(shí)間戳)
if (typeof dateString === 'number') {
var date = new Date(dateString);
return isNaN(date.getTime()) ? null : date;
}
// 轉(zhuǎn)換為字符串
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;
}
// 嘗試常見(jiàn)格式的正則匹配
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時(shí)mm分ss秒
{
regex: /^(\d{4})年(\d{1,2})月(\d{1,2})日\(chéng)s*(\d{1,2})時(shí)(\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;
},
/**
* 使用指定格式解析日期字符串(內(nèi)部方法)
* @private
* @param {string} dateString - 日期字符串
* @param {string} format - 格式模板
* @returns {Date|null} 解析后的 Date 對(duì)象
*/
_parseWithFormat: function (dateString, format) {
// 構(gòu)建正則表達(dá)式,將格式標(biāo)記替換為捕獲組
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;
}
// 提取各個(gè)部分
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;
}
}
/**
* 獲取單例實(shí)例(靜態(tài)方法)
* @param {Object} options - 配置選項(xiàng)
* @returns {SurveyTimezone} 單例實(shí)例
*/
SurveyTimezone.getInstance = function (options) {
if (!instance) {
instance = new SurveyTimezone(options);
}
return instance;
}
/**
* 重置單例實(shí)例(用于測(cè)試或重新初始化)
* @static
*/
SurveyTimezone.resetInstance = function () {
instance = null;
}
// ==================== 靜態(tài)方法 ====================
/**
* 格式化日期 - 靜態(tài)方法(向后兼容)
* @static
* @param {Date} date - 要格式化的日期對(duì)象
* @param {string} format - 格式化模板字符串
* @returns {string} 格式化后的日期字符串
* @description 靜態(tài)方法,可直接調(diào)用而無(wú)需實(shí)例化
* @example
* SurveyTimezone.format(new Date(), 'YYYY-MM-DD');
*/
SurveyTimezone.format = function (date, format) {
// 調(diào)用原型方法實(shí)現(xiàn)
return SurveyTimezone.prototype.format.call(null, date, format);
}
/**
* 解析日期時(shí)間字符串 - 靜態(tài)方法(向后兼容)
* @static
* @param {string|number} dateString - 日期時(shí)間字符串或時(shí)間戳
* @param {string} format - 可選的格式模板
* @returns {Date|null} 解析后的 Date 對(duì)象,解析失敗返回 null
* @description 靜態(tài)方法,可直接調(diào)用而無(wú)需實(shí)例化
* @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) {
// 使用原型對(duì)象作為上下文調(diào)用原型方法
return SurveyTimezone.prototype.parse.call(SurveyTimezone.prototype, dateString, format);
}
// 返回構(gòu)造函數(shù)
return SurveyTimezone;
}))
測(cè)試文件
<!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('=== 初始化測(cè)試 - 傳入日期時(shí)間 ===');
// 重置單例以便測(cè)試
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時(shí)mm分'));
// 重置單例
SurveyTimezone.resetInstance();
// 方式2: 傳入時(shí)間戳
const tz2 = new SurveyTimezone(1698484245000);
console.log('\n時(shí)間戳初始化:', 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: 傳入配置對(duì)象
const tz4 = new SurveyTimezone({
date: '2025-10-28 14:30:45',
timezone: 'Asia/Shanghai'
});
console.log('\n配置對(duì)象初始化:', tz4.getDate());
console.log('格式化輸出:', tz4.format());
console.log('配置信息:', tz4.options);
// 重置單例
SurveyTimezone.resetInstance();
// 方式5: 不傳參數(shù)(使用當(dāng)前時(shí)間)
const tz5 = new SurveyTimezone();
console.log('\n默認(rèn)初始化(當(dāng)前時(shí)間):', tz5.getDate());
console.log('格式化輸出:', tz5.format());
console.log('\n=== setDate 方法測(cè)試 ===');
// 修改日期
tz5.setDate('2025-12-25 00:00:00');
console.log('修改后的日期:', tz5.getDate());
console.log('格式化輸出:', tz5.format('YYYY年MM月DD日'));
// 鏈?zhǔn)秸{(diào)用
console.log('鏈?zhǔn)秸{(diào)用:', tz5.setDate('2026-01-01').format('YYYY/MM/DD'));
console.log('\n=== 單例模式驗(yàn)證 ===');
// 驗(yàn)證單例
const instance1 = tz5;
const instance2 = new SurveyTimezone('2025-10-28');
console.log('單例驗(yàn)證:', instance1 === instance2 ? '? 通過(guò)(返回同一實(shí)例)' : '? 失敗');
console.log('\n=== 格式化方法測(cè)試 ===');
// 格式化實(shí)例日期
SurveyTimezone.resetInstance();
const tzFormat = new SurveyTimezone('2025-10-28 14:30:45');
console.log('格式化實(shí)例日期(無(wú)參):', tzFormat.format());
console.log('格式化實(shí)例日期(指定格式):', tzFormat.format('YYYY年MM月DD日'));
console.log('格式化實(shí)例日期(英文):', tzFormat.format('MMM DD, yyyy'));
// 格式化指定日期
const now = new Date();
console.log('格式化指定日期:', tzFormat.format(now, 'YYYY-MM-DD HH:mm:ss'));
console.log('\n=== 格式化方法測(cè)試 - 靜態(tài)方法(向后兼容)===');
console.log('靜態(tài)方法 - 完整格式:', SurveyTimezone.format(now, 'YYYY-MM-DD HH:mm:ss'));
console.log('靜態(tài)方法 - laydate格式:', SurveyTimezone.format(now, 'yyyy-MM-dd HH:mm:ss'));
console.log('靜態(tài)方法 - 英文月份:', SurveyTimezone.format(now, 'MMM DD, yyyy'));
console.log('靜態(tài)方法 - 自定義格式:', SurveyTimezone.format(now, 'YYYY年MM月DD日 HH時(shí)mm分ss秒'));
console.log('\n=== parse 方法測(cè)試(靜態(tài)方法)===');
// 測(cè)試靜態(tài)方法 - 自動(dòng)識(shí)別格式
console.log('parse標(biāo)準(zhǔn)格式:', SurveyTimezone.parse('2025-10-28 14:30:45'));
console.log('parse中文格式:', SurveyTimezone.parse('2025年10月28日'));
console.log('parse時(shí)間戳:', SurveyTimezone.parse(1698484245000));
// 測(cè)試靜態(tài)方法 - 指定格式(這個(gè)會(huì)觸發(fā) _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=== 完整工作流測(cè)試 ===');
// 場(chǎng)景:用戶輸入 → 解析 → 處理 → 格式化輸出
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>
以上就是基于JavaScript優(yōu)雅的實(shí)現(xiàn)一個(gè)時(shí)間處理插件的詳細(xì)內(nèi)容,更多關(guān)于JavaScript時(shí)間處理插件的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Bootstrap基本組件學(xué)習(xí)筆記之下拉菜單(7)
這篇文章主要為大家詳細(xì)介紹了Bootstrap基本組件學(xué)習(xí)筆記之下拉菜單,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-12-12
layer.confirm()右邊按鈕實(shí)現(xiàn)href的例子
今天小編就為大家分享一篇layer.confirm()右邊按鈕實(shí)現(xiàn)href的例子,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-09-09
JS判斷對(duì)象是否為空對(duì)象的幾種實(shí)用方法匯總
判斷是否為空對(duì)象在實(shí)際開(kāi)發(fā)中很常見(jiàn),下面這篇文章主要給大家介紹了關(guān)于JS判斷對(duì)象是否為空對(duì)象的幾種實(shí)用方法,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-05-05
JavaScript中null與undefined的區(qū)別總結(jié)
在JavaScript中,null和undefined是兩個(gè)特殊的值,這篇文章主要介紹了JavaScript中null與undefined區(qū)別的相關(guān)資料,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2025-09-09
javascript實(shí)現(xiàn)的左右無(wú)縫滾動(dòng)效果
這篇文章主要介紹了javascript實(shí)現(xiàn)的左右無(wú)縫滾動(dòng)效果,可實(shí)現(xiàn)左右平滑無(wú)縫滾動(dòng)的效果,并且可響應(yīng)鼠標(biāo)滑過(guò)而停止?jié)L動(dòng),非常簡(jiǎn)便實(shí)用,需要的朋友可以參考下2016-09-09
前端實(shí)現(xiàn)各種類型文件保存的方案總結(jié)
在Web開(kāi)發(fā)中,文件下載是常見(jiàn)的交互需求,本文將系統(tǒng)總結(jié)前端實(shí)現(xiàn)文件保存的三大核心場(chǎng)景(圖片、文本、網(wǎng)頁(yè))及對(duì)應(yīng)技術(shù)方案,幫助開(kāi)發(fā)者根據(jù)具體需求選擇最優(yōu)解,需要的朋友可以參考下2025-04-04

