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;
}
}
}
}
};