js日期格式化器,format,parse
阿新 • • 發佈:2018-11-05
js日期格式化器,format,parse
//字串格式化為固定長度,預設將佔位符補到右側 //@param {string} str 源字串 //@param {int} length 固定長度 //@param {string} place 佔位符,預設為 "0" //@param {boolean} left 是否將佔位符補到字串左側 var fix = function(str,length,place,left){ if (typeof str !== 'string') { return str; }; length = length || 0; if (str.length >= length) { return str; }; place = place || '0'; var len = length - str.length, times = Math.floor(len / place.length), arr = []; for (var i = 0; i < times; i++) { arr.push(place); }; if (left) { if (str.length + arr.length < length) { arr.push(place.substr(0,str.length - length)); }; str = arr.join('') + str; }else{ str += arr.join(''); if (str.length < length) { str += place.substr(0,str.length - length); }; } return str; }; //月份全稱 var MONTHNAMES = ['January','February','March','April','May','June','July','August','September','October','November','December'];
/* 將日期格式的物件格式化為字串,如果不提供格式化器,將採用系統預設的Date.toString @see https://msdn.microsoft.com/zh-cn/library/8kb3ddd4(v=vs.100).aspx @warning 採用format格式化的日期字串,如果還需要parse為日期格式,請使用完全格式化器,否則將導致解析失敗 For example: 推薦 :format(new Date(),'yyyy-MM-dd HH:mm:ss.fff') 錯誤 :format(new Date(),'yyyy-M-d H:m:s.f'),簡化的格式化器轉化出的字串無法再被正確的parse 格式說明符 說明 示例 "d" 一個月中的某一天(1 到 31)。 6/1/2009 1:45:30 PM -> 1 6/15/2009 1:45:30 PM -> 15 "dd" 一個月中的某一天(01 到 31)。 6/1/2009 1:45:30 PM -> 01 6/15/2009 1:45:30 PM -> 15 "f" 日期和時間值的十分之幾秒。 6/15/2009 13:45:30.617 -> 6 6/15/2009 13:45:30.050 -> 0 "ff" 日期和時間值的百分之幾秒。 6/15/2009 13:45:30.617 -> 61 6/15/2009 13:45:30.005 -> 00 "fff" 日期和時間值的毫秒。 6/15/2009 13:45:30.617 -> 617 6/15/2009 13:45:30.0005 -> 000 "h" 採用 12 小時制的小時(從 1 到 12)。 6/15/2009 1:45:30 AM -> 1 6/15/2009 1:45:30 PM -> 1 "hh" 採用 12 小時制的小時(從 01 到 12)。 6/15/2009 1:45:30 AM -> 01 6/15/2009 1:45:30 PM -> 01 "H" 採用 24 小時制的小時(從 0 到 23)。 6/15/2009 1:45:30 AM -> 1 6/15/2009 1:45:30 PM -> 13 "HH" 採用 24 小時制的小時(從 00 到 23)。 6/15/2009 1:45:30 AM -> 01 6/15/2009 1:45:30 PM -> 13 "m" 分鐘(0 到 59)。 6/15/2009 1:09:30 AM -> 9 6/15/2009 1:09:30 PM -> 9 "mm" 分鐘(00 到 59)。 6/15/2009 1:09:30 AM -> 09 6/15/2009 1:09:30 PM -> 09 "M" 月份(1 到 12)。 6/15/2009 1:45:30 PM -> 6 "MM" 月份(01 到 12)。 6/15/2009 1:45:30 PM -> 06 "MMM" 月份的縮寫名稱。 6/15/2009 1:45:30 PM -> Jun "MMMM" 月份的完整名稱。 6/15/2009 1:45:30 PM -> June "s" 秒(0 到 59)。 6/15/2009 1:45:09 PM -> 9 "ss" 秒(00 到 59)。 6/15/2009 1:45:09 PM -> 09 "t" AM/PM 指示符的第一個字元。 6/15/2009 1:45:30 PM -> P "tt" AM/PM 指示符。 6/15/2009 1:45:30 PM -> PM "y" 年份(0 到 99)。 6/15/2009 1:45:30 PM -> 9 "yy" 年份(00 到 99)。 1/1/1900 12:00:00 AM -> 00 6/15/2009 1:45:30 PM -> 09 "yyy" 年份(最少三位數字)。 1/1/0900 12:00:00 AM -> 900 1/1/1900 12:00:00 AM -> 1900 6/15/2009 1:45:30 PM -> 2009 "yyyy" 由四位數字表示的年份。 6/15/2009 1:45:30 PM -> 2009 "z" 相對於 UTC 的小時偏移量,無前導零。 6/15/2009 1:45:30 PM -07:00 -> -7 "zz" 相對於 UTC 的小時偏移量,帶有表示一 6/15/2009 1:45:30 PM -07:00 -> -07 位數值的前導零。 "g" 公元紀年 6/15/0600 1:45:30 PM -07:00 -> -7 6/15/2009 1:45:30 PM -07:00 -> -21 "gg" 公元紀年(有前導0) 6/15/0600 1:45:30 PM -07:00 -> -07 6/15/2009 1:45:30 PM -07:00 -> -21 */ //@param {Date} date 要格式化的日期 //@param {string} frm 格式化的字串佔位符 var format = function(date,frm){ if (!(date instanceof Date)) { return date; }; if (!frm) { return date.toString(); }; var year = Math.abs(date.getFullYear()), n = date.getFullYear() < 0 ? '-' : '', offset = date.getTimezoneOffset() / 60, yearStr = n + fix(year.toString(),4,'0',true), y = parseInt(yearStr.substr(yearStr.length - 2)), month = date.getMonth() + 1, d = date.getDate(), H = date.getHours(), h = H != 12 ? H % 12 : H, m = date.getMinutes(), s = date.getSeconds(), f = date.getMilliseconds(), g = year % 100 == 0 ? (year / 100 + 1) : Math.ceil(year / 100); return frm.replace(/y+/g,function(match){ var z = year < 0 ? '-' : ''; switch(match.length){ case 1: return y; case 2: return n+fix(y.toString(),2,'0',true); default: return n+fix(year.toString(),match.length,'0',true); } }).replace(/d{1,2}/g,function(match){ if (match.length == 1) { return d; } return fix(d.toString(),match.length,'0',true); }).replace(/h{1,2}/g,function(match){ if (match.length == 1) { return h; } return fix(h.toString(),match.length,'0',true); }).replace(/H{1,2}/g,function(match){ if (match.length == 1) { return H; } return fix(H.toString(),match.length,'0',true); }).replace(/H{1,2}/g,function(match){ if (match.length == 1) { return H; } return fix(H.toString(),match.length,'0',true); }).replace(/m{1,2}/g,function(match){ if (match.length == 1) { return m; } return fix(m.toString(),match.length,'0',true); }).replace(/s{1,2}/g,function(match){ if (match.length == 1) { return s; } return fix(s.toString(),match.length,'0',true); }).replace(/f+/g,function(match){ var ms = f.toString(); if (ms.length > match.length) { return ms.substr(0,match.length); }; return fix(ms.toString(),match.length,'0',true); }).replace(/g{1,2}/g,function(match){ if (match.length == 1) { return g; } return n+fix(g.toString(),match.length,'0',true); }).replace(/z{1,2}/g,function(match){ if (match.length == 1) { return offset; } return (offset < 0 ? '-' : '') + fix(Math.abs(offset).toString(),match.length,'0',true); }).replace(/t{1,2}/g,function(match){ if (match.length == 1) { return H >= 12 ? 'P' : 'A'; } return H >= 12 ? 'P{#}' : 'A{#}'; }).replace(/M{1,4}/g,function(match){ switch(match.length){ case 1: return month; case 2: return fix(month.toString(),match.length,'0',true); default: var M = MONTHNAMES[month-1]; return match.length < 4 ? M.substr(0,3) : M; } }).replace(/\{\#\}/g,'M'); };
/* 將字串轉換為日期格式,如果未提供格式化器,則採用系統預設的Date.parse 格式化器見format @param {string} dateStr 字串形式的日期 @param {string} frm 自定義的日期格式化器 @example 日期字串 格式化器 '2015-11-23 16:30:25.506' 'yyyy-MM-dd HH:mm:ss.fff' '-03世紀 -0200年December月01日 00時00分00秒000毫秒 AM -08' 'gg世紀 yyyy年MMMM月dd日 hh時mm分ss秒fff毫秒 tt zz' */ var parse = function(dateStr,frm){ if (!dateStr) { return null; }; if (!frm) { return new Date(Date.parse(dateStr)); }; var place, last, FORMATS = 'yMdhHmsfztg', date = new Date(0,0,1), h, z, t, //format字串佔位符相對於日期字串的偏移量 //年份、世紀、時區 會出現負數,導致位移 //月份全稱長度不固定,導致位移 p = 0, <span style="white-space:pre"> </span>str; frm = frm.split(''); //i <= frm.length 多取一位,在迴圈中完全處理掉日期替換 for (var i = 0; i <= frm.length; i++) { if(frm[i]!==last){ if (place) { place = place.join(''); str = subDateStr(dateStr,place,i,p); p = str.p; set(str.str, place); }; place = null; if (FORMATS.indexOf(frm[i]) >= 0) { place = [frm[i]]; }; }else{ place && place.push(frm[i]); } last = frm[i]; }; if (h) { t == 'PM' && (h += 12); date.setHours(h); }; if (z) { z = new Date().getTimezoneOffset() / 60 - z; date.setHours(date.getHours() + z); }; return date; function subDateStr(dateStr,place,i,p){ if (!place || !place.length) { return ''; }; var start = 0,str; if (place == 'MMMM') { //月份全稱,長度不固定,特殊處理 start = p + i - place.length; str = dateStr.substring(start,p + i - 1); for (var j = 0; j < MONTHNAMES.length; j++) { if(MONTHNAMES[j].indexOf(str) >= 0){ str = MONTHNAMES[j]; break; } }; p+=(str.length - place.length); }else{ start = p + i - place.length; str = dateStr.substring(start,p + i); if (place.indexOf('y') == 0 || place.indexOf('z') == 0 || place.indexOf('g') == 0) { if (str.indexOf('-') == 0) { //處理負數 str = dateStr.substring(start,p + i+1); p++; }; } } return { str:str || '', p:p }; }; function set(str,place){ if (!place || !place.length || !str || !str.length) { return; }; var v,c; if (/M{3,}/.test(place)) { for (var i = 0; i < MONTHNAMES.length; i++) { if(MONTHNAMES[i].indexOf(str) == 0){ v = i; break; } }; v != null && date.setMonth(v); }else if (place == 'yy' || place == 'y') { var Y = fix(new Date().getFullYear().toString(),4,'0',true); Y = Y.substr(0,Y.length-2); Y += fix(str,2,'0',true); v = parseInt(Y); date.setFullYear(v); }else{ c = place.substr(0,1); if (c == 't') { t = place.length == 1 ? str + 'M' : str; }else{ v = parseInt(str); switch(c){ case 'h': h = v; break; case 'z': z = v; break; case 'y': date.setFullYear(v); break; case 'M': date.setMonth(v-1); break; case 'd': date.setDate(v); break; case 'H': date.setHours(v); break; case 'm': date.setMinutes(v); break; case 's': date.setSeconds(v); break; case 'f': date.setMilliseconds(v); break; } } } } };