首頁 >web前端 >js教程 >JavaScript自訂日期格式化函數詳細解析_javascript技巧

JavaScript自訂日期格式化函數詳細解析_javascript技巧

WBOY
WBOY原創
2016-05-16 17:03:521253瀏覽

我們對 JavaScript 擴充功能其中一個較常的做法便是對 Date.prototype 的擴充。因為我們知道,Date 類別只提供了若干取得日期元素的方法,如 getDate(),getMinute()…卻沒有一個轉換為特定字串的格式化方法。故所以,利用這些細微的方法,加以封裝,組合我們想要的日期字串形式。一般來說,這個格式化函數可以定義在 Date 物件的原型身上,也可以獨立一個方法寫出。定義原型方法的操作如 Date.prototype.format = function(date){……},使用時候直接 new Date().format(YYYY:MM:DD) 即可,彷彿就是 Date 物件的原生方法。但定義原型方法卻略嫌有「入侵」 JS 原型的不足。設計 API 之時必須考慮這個問題。我的建議是,使用者按照自己的判斷去做決定,只是呼叫的方式不同,不影響過程的邏輯即可。

下面的一個例子就是以獨立函數寫出的 JavaScript 日期格式化函數,獨立的 format 函數。回到格式化的這項知識點上,我們考查的是怎麼實現的、運用了哪些原理。傳統字串拼接如 indexOf() substr() 雖然能夠實現,但明顯不僅效率低下,而且程式碼冗長,還是適宜引入正則表達式的方法,先寫出字串正則然後再進行結果的命中匹配。我們先來看看來自 Steven Levithan 的範例:

複製程式碼 程式碼如下:

/**
 * 日期格式1.2.3
 * @credit Steven Levithan ;包括Scott Trenda 的增強功能和Kris Kowal
 * 接受日期、遮罩或日期和遮罩。
 * 傳回給定日期的格式化版本。
* 日期預設為目前日期/時間。
 * 遮罩預設為 dateFormat.masks.default。
 */
dateFormat = (function(){
    // 正規筆記,1、token,(?:)表示非捕獲分組;/1 反向引用(思考:{1,2}可否和/1一樣意思?);根據這裡的意義[LloSZ]表示括號內的任一個字元拿去匹配,很簡單,但暫時不明白/L|l|o|S |Z/在解析日期時的作用;最後的兩組「或」是匹配引號和引號內的內容(無所謂雙引號或單引號)。 1,4}|yy(?:yy)?|([HhMsTt])/1?|[LloSZ]|"[^"]*"|'[^']*'/g,
    // 2 、timezone, [PMCEA][SDP]產生兩個字元的消耗;該reg的都是非捕獲分組,可加快正則速度。 :Pacific|Mountain|Central|Eastern|Atlantic) (?:Standard|Daylight|Prevailing) Time|(?:GMT|UTC)(?:[- ]/d{4})?)/b/g,
        timezoneClip = /[^- /dA-Z]/g,

        // 少於兩個填號){
            val = String(val);
            len = len || 2;
           turn val;
        };
    //為什麼回傳一個function,因為前面說明的變數都變成常數,下面回傳的參數才是真正到時執行的函數。 >    // Regexes and supporting functions are cached through closure
    // 參數說明:date: Date 解析的日期或新日期;mask:String 格式化日期的範本;utc:Stirng 可選的UTC。
    return function (date, mask, utc) {
        var i18n  = dateFormat.i18n;
    You can't provide utc if you skip other args ( use the "UTC:" mask prefix)
        // 若只有一個參數,其該參數是不包含數字的字串,則視為此參數為mask。 date是由下一個if中的new Date產生,那麼date就是現在的日期。
        if (arguments.length == 1 && Object.prototype.toString.call(date) == "[object String]" && !//d/.test(date)) {
            date = undefined;
        }

      date = date ? new Date(date) : new Date;
        if (isNaN(date)) throw SyntaxError("invalid date");
        // 以判斷多種情況明確mask是什麼,無論前面是如何指定的。留意 || 的技巧。
        mask = String(masks[mask] || mask || masks["default"]);
        //  == "UTC:") {
            mask = mask.slice(4);
            // 分兩種情況,用UTC格式的情況和一般的。注意透過JS的字面索引也可以傳回方法的成員。
        var _ = utc ? "getUTC" : "get",
            d = date[_ "Date"](),            m = date[_ "Month"](),
            y = date[_ "FullYear"](),
          M = date[_ " Minutes"](),
            s = date[_ "Seconds"](),
           o = utc ? 0 : date.getTimezoneOffset() ,
            flags = {
                 pad(d),
                ddd:  i18n.dayNames[D],
        / 位寬:7, 見dateFormat.dayNames。
                m:    m 1, // 從0開始起月份
                   mmm:  i18n.monthNames[m],
                mmmm: i18n.monthNames[m 12] , // 位寬:12,見dateFormat.monthNames
                yy:   String(y).slice( : y,
                h:    H % 12 || 12, // h表示12小時制,h除以12(因為十二進位),取餘的結果為12小時制的。
                hh:                     HH:   pad(H),
                 ),
                s:    s,
                 l:    pad(L, 3), // Max,999ms
                L:             L: 10) : L),
                // 大小寫有影響力
                        tt:   H                 T:    H     🎜>                // 此步驟求timezone,而要處理。
                // 前文有timezone,timezoneClip = /[^- /dA-Z]/g,
  🎜>                / / 假如沒有,則[""].pop() 回傳空字
                Z:    utc ? "UTC" : (String(date).match(p.D. (timezoneClip, ""),
                // 4位的TimezoneOffset
      oor(Math.abs(o) / 60) * 100 Math.abs(o) % 60, 4),
                // 求英文的["th", "st", "nd", "rd ["th", "st", "nd", "rd"][d % 10 > 3 ? 0 : (d % 100 - d % 10 != 10) * d % 10]
        >        return mask.replace(token, function ($0 /*很好$0,須知$1、$2由系統佔用了*/) {
          ! 🎜>        });
    };
})();


該段程式碼對日期處理考慮得比較周全,我們就進入原理看看它的奧秘,--看看它的奧秘,我們就進入原理看看它的奧秘,--看看它的奧秘,看看它的奧秘原理看看它是怎麼處理日期的!

日期字串範本中,我們約定用yyyy/mm/dd 等的有意義的符號分別表示日期中某一個元素,像y 即year 某一年份,m 即month 某一月份,d 即day 某有一天,如果是大寫的話還要注意區分開來,大寫M 代表分鐘,小寫m 是月份。總之,這是一份我們人為規範好的約定,即上述程式碼所謂的“mask”,遵照此約定輸入欲格式化模式的參數,便可將日期類型的值輸出可供列印的字串。至於解析的日期過程是,先按照Mask 的全部要求,逐一取得到日期的每一個元素(getDate(),getMinute()…可以很快取得到),接著依照Mask 真實的條件是什麼,即Mask .replace(正規, 元素)方法進行字串模板與元素之間的替換,替換的過程還是以flag 為標誌去逐一匹配的對照表。至於正規部分,關鍵在於理解 token 和 replace() 函數的過程。參加上述程式碼註釋,即可了解內部細節。

如果每次都要輸入冗長的 Mask 字串豈不是很累?我們可以透過定義常數的方法來縮減我們的工作:

複製程式碼


程式碼如下:

dateFormat.masks = {
    "預設":      "ddd mmm dd HH HH:MM:ss",
    ShortDate:  HH:MM:ss",
     "yy /m/d/h:MM:ss",
    中等日期:     "mmm d, yyyy",
    長日期:      ",
    短時間:      "h:MM TT",
    中時間:     "h:MM:ss TT",
   長時間Date :        "yyyy-mm -dd",
    isoTime:        "HH:MM:ss",
    isoDate: Time: "UTC :yyyy-mm- dd'T'HH:MM:ss'Z'"
    // 加入中國類型的日期@Edit 2010.8.11
    ,ChineseDate   :'yyyy年mm月dd '
}

dateFormat.i18n = {
    dayNames: [
       "星期六",
        「星期日」、「星期一」、「星期二」、「星期三」、「星期四」、「星期五」、「星期六」
    ]、
    月份名稱:[
[
「一月」、「二月」、 「三月」、「四月」、「五月」、「六月」、「七月」、「八月」、「九月」、「十月」、 「十一月」、「十二月」、
        「一月」、「二月」 , "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"
    ]
};


Steve 的dateFormat 足可以完成大部分日期轉換的任務,不過在茫茫程式碼中,我們找到了更優的解法,不出20 行程式碼,把正則運用得收放自如,就是來自月影前輩的JS!


複製程式碼程式碼如下:
Date.prototype.format = function(format) //作者: meizz
{
  var o = {
    "M " : this.getMonth() 1, //月份
    "d " : this.getDate(),
    "d " : this.getDate(),
"h " : this.getHours(),   //小時
    "m " : this.getMinutes(), //分鐘
    "s " : this.getSeconds(), // Second
" " : Math.floor((this.getMonth() 3)/3),  //quarter
    "S" : this.getMilliseconds() //毫秒
  }
  if (/(y )/ .test(format)) format=format.replace(RegExp.$1,
    (this.getFullYear() "").substr(4 - RegExp.$1.length));
for(var k in o )if(new RegExp("(" k ")").test(format))
    format = format.replace(RegExp.$1,
      RegExp.$1.length==1 ? o[k] :    RegExp.$1.length==1 ? o[k] :    RegExp.$1.length==1 ? o[k]:   
        ("00" o[k]).substr(("" o[k]).length));
  回傳格式;
}
alert(new Date) ().format ("yyyy-MM-dd hh:mm:ss"));


原理上與 Steve 方法相似,但更濃縮的程式碼,卻集技巧性和全面性於一身。從原始碼第12行開始,test() 方法不但可以檢測是否匹配的這個起碼功能,而且實際上是有記憶匹配結果的,產生RegExp.$1 結果組來處理年份(開始我認為test() 效率高並不會產生結果,實則不然)。然後,再使用 new RegExp 在字串形式建立正規表示式的實例,又是一個高明的地方,--因為直接與 o 的 hash 表直接對接起來了!繼而依法瓢葫蘆,先測試是否命中匹配,有的話就進行替換。

另外,程式碼中的 ("00" o[k]).substr(String(o[k]).length) 也是有趣的地方,前面加上兩個什麼意思呢?原來目的是為了取數組的最後兩位。這是綜合利用 substr() 方法的一個技巧,substr 第一個參數是開始截取的 index,若不指定第二個參數 index 則保留字串到最後(str.length)。於是,我們事先加多了多少位,原本固定的字串長度不變(String(o[k].length))的情況下,那麼就留下多少個位。 (p.s 「00」相當於佔位符,亦可用其他字串「XX」代替無差別)

仍然覺得這段程式碼有不少的困難?我們嘗試把月影的函數重寫為可讀性較強的程式碼,原理上趨於一致可是沒那麼多的技巧,相信這樣可以節省大家的時間,回頭再去看月影的程式碼也不遲。

複製程式碼 程式碼如下:

date = {

date = {

date = { format){
  date = new Date(date); // force con.
  date = {
    year : date.getFullYear()
   year : date.getFullYear()
   year : date.getFullYear()
   year : date.getFullYear()
  , 月份從零算起
   ,day : date.getDate()
   ,hour : date.getHours()
   ,minute : date.getMinutes()
   ,minute : date.getMinutes()   ,milute : date.getMilliseconds()
  };
  var
    match
   ,reg = /(y )|Y | |u /g;
  while((match = reg.exec(format)) != null){
      match = match[0];
      match = match[0];
      match = match[0];
      match = match[0];
     ¢test(/y). {
       format = format.replace(match, date.year);
      }
   if(match.indexOf('M') }
   if(match.indexOf('M') 決定🎜>   if(match.indexOf('M') 決定🎜>   if) , date.month);
      }
   if(match.indexOf('d') != -1){
        
   if(match.indexOf('h') != -1){
       format = format.replace(match, date.hour);
  ) != -1){
       format = format.replace(match, date.minute);
      } 
  = format.replace(match, date.second);
      }
   if(match.indexOf('u') != -1){
      ;      }           

  }
  return format;

 }};從 ext 4.0 淘到的日期格式化的程式碼,怎麼講字串轉為 js 標準日期?看看新 ext 是怎麼做的?
複製程式碼 程式碼如下:

    /**
     * 依照特定的格式模式格式化日期。
     * Parse a value into a formatted date using the specified format pattern.
      * @param {String/Date} value 要格式化的值(字串格式必須符合JavaScript日期物件的目標="http://www.w3schools.com/jsref/jsref_parse.asp" mce_href="http://www.w3schools.com/jsref/jsref_parse.asp">parse())The value to format (Strings must conform to the format expected by the javascript
     * Date object's parse() method)
     * @param {String} format (可選的)任意的日期格式化字串。 (預設為'm/d/Y')(optional) Any valid date format string (defaults to 'm/d/Y')
     * @return {String} 已格式化字串。 The formatted date string
    */
    date: function(v, format) {
              }
        if (!Ext.isDate(v)) {
            v = new Date(Date.parse(v));
     DateFormat);
    }


date 構造器還可以透過計算距離1970起為多久的毫秒數來確定日期? ——的確,這樣也行,——也就說,舉一反三,從這個問題說明,js 日期最小的單位是毫秒。
最終版本:


複製程式碼 程式碼如下:/**
 * 日期格式化。詳見部落格文章:http://blog.csdn.net/zhangxin09/archive/2011/01/01/6111294.aspx
 * e.g: new Date().format("yyyy-MM-dd hh:mm :ss")
 * @param  {String} format
 * @return {String}
/** */
Date.prototype.format = function (format) {
    var $1, o = {
        "M ": this.getMonth() 1,       "M ": this.getMonth() 1,       "M ": this.getMonth() 1,  // 月"d ": this.getDate(),     // 日期
        "h ": this.getHours(),     // 小時                // 季度quarter
        // 季度quarter
        // 季度quarter
        "S": this .getMilliseconds() // 千秒
    };
    var key, value;

    if (/(y )/.test(format)) {
        $1 = RegExp.$1,
      get format = formatmatmatH片($1,4m $1));
    }

    for (key in o) { // 如果沒有指定參數,子字串將延續到 stringvar 的最後。
        if (new RegExp("(" key ")").test(format)) {
           >      value = $1.length == 1 ? value : ("00" value).substr(value.length),
      format = format.replace($1, value);

        }

    }
    return format ;
}


陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn