一個優秀的程式必須兼顧I18n和L10N,但Javascript在Date處理這一方面做得很不友好,問題反映在以下幾個方面:
雖然Js也設定了UTC時間函數,但腳本運行時的時區是自動從系統獲得的,且無法更改。而在實際應用中,往往需要接受一個非當前系統時區的時間資料來轉換,而這時候Js的自作聰明就帶來了許多麻煩。
Js對日期的自動解析和格式化輸出根據系統環境、瀏覽器環境不同,表現也不同,這主要反映在Date.parse和toLocaleString方法上,有興趣的同學可以自行測試。
為了盡可能簡單的改善Js的Date處理能力,這裡只對Js的Date物件做了兩個擴充
/**
* 獲得當前時間的UTC時間戳
* @return {int} unix timestamp
*/
Date.prototype.getTimeUTC = function() {
return this.getTime() + this.getTimezoneOffset() * 60 * 1000;
}
/**
* 將目前操作的時間變更時區(主要用於轉換一個其他時區的時間)
*
* @param {int} tzo 原時區-12~13
* @param {int} tzo 目標時區-12~13 預設為目前時區
*/
Date.prototype.changeTimezone = function(tzo,tzn) {
tzo = tzo * 60;
tzn = tzn ? tzn * 60 : -this.getTimezoneOffset();
this.setTime(this.getTime() - (tzo - tzn) * 60 * 1000);
}
/**
* 獲得當前時間的UTC時間戳
* @return {int} unix timestamp
*/
Date.prototype.getTimeUTC = function() {
return this.getTime() + this.getTimezoneOffset() * 60 * 1000;
}
/**
* 將目前操作的時間變更時區(主要用於轉換一個其他時區的時間)
*
* @param {int} tzo 原時區-12~13
* @param {int} tzo 目標時區-12~13 預設為目前時區
*/
Date.prototype.changeTimezone = function(tzo,tzn) {
tzo = tzo * 60;
tzn = tzn ? tzn * 60 : -this.getTimezoneOffset();
this.setTime(this.getTime() - (tzo - tzn) * 60 * 1000);
}
至此,就可以基於這個擴展過的Js Date物件進行新的開發。
想法很簡單,就是把任意格式任意時區的時間先透過changeTimezone方法轉到和目前系統同一時區下,然後再對getTimeUTC產生的UTC Unix時間戳進行操作。
例如我要計算+8 時區下4 Jun 2008, 16:30時刻與+9 時區下的當前時刻相差的時間
//自動解析成unix時間戳
var p = Date.parse('4 Jun 2008, 16:30');
var time_parse = new Date(p);
//轉換到要比較的時區
time_parse.changeTimezone(8,9);
var time_now = new Date();
//都轉換為UTC進行對比
var der = time_now.getTimeUTC() - time_parse.getTimeUTC();
alert('相差' + parseInt(der / 1000 / 60) + '分鐘');
//自動解析成unix時間戳
var p = Date.parse('4 Jun 2008, 16:30');
var time_parse = new Date(p);
//轉換到要比較的時區
time_parse.changeTimezone(8,9);
var time_now = new Date();
//都轉換為UTC進行對比
var der = time_now.getTimeUTC() - time_parse.getTimeUTC();
alert('相差' + parseInt(der / 1000 / 60) + '分鐘');
當然有更簡單的編碼,但在複雜運用中理清思路更加不容易出錯。
如果想要實現本Blog左側欄XX天XX月前這樣更人性化的提示,這裡可以依照自己的需求做進一步擴充。實現的函數如下
/**
* 表示指定時間與現在的差值
*
* @param {int} t 所要比較的時間unix timestamp (UTC)
* @param {int} n 作為標準的時間,預設為現在時刻unix timestamp (UTC)
* @return {string} 相差時間的陳述
*/
Date.prototype.derTime = function(t,n) {
var n = n ? n : this.getTimeUTC();
function ms2min(ms) {
return parseInt(ms / 1000 / 60);
}
var der = ms2min(n - t);
var ba = der > 0 ? '前' : '後';
der = Math.abs(der);
var res = '';
if(der == 0) {
res = '剛剛';
}
else if(0 < der && der < 60) {
res = der + '分鐘' + ba;
}
else if(60 <= der && der < 24 * 60) {
var min = der % 60 == 0 ? '' : String(der % 60) + '分鐘';
res = String(parseInt(der / 60)) + '小時' + min + ba;
}
else if(der >= 24 * 60 && der < 24 * 60 * 31) {
res = String(parseInt(der / 60 / 24)) + '天' + ba;
}
else if(der >= 24 * 60 * 31 && der < 24* 60 * 365) {
res = String(parseInt(der / 60 / 24 / 31)) + '個月' + ba;
}
else if(der > 24 * 60 * 365) {
res = String(parseInt(der / 60 / 24 / 365)) + '年' + ba;
}
return res;
}
/**
* 解析一個時間欄位與目前時間的差
* @param {string} i
* @param {int} 時區-12~13
*/
function time_der(i,tz) {
var p = Date.parse(i);
if(!p) {
return i;
}
var time_parse = new Date(p);
if(tz != undefined) {
time_parse.changeTimezone(tz);
}
var time_now = new Date();
return time_now.derTime(time_parse.getTimeUTC());