|
|
|
@@ -0,0 +1,523 @@ |
|
|
|
|
|
|
|
/** |
|
|
|
* 支持的时间日期格式占位符: |
|
|
|
* 支持通用占位符和MySQL占位符(可以混合) |
|
|
|
* 格式化时以下都支持, 解析时不支持星期几 |
|
|
|
* 不支持时区, 保留z Z zz ZZ zzz ZZZ zzzz ZZZZ %z %Z |
|
|
|
* |
|
|
|
* 4位年: yyyy %Y |
|
|
|
* 2位年: yy %y |
|
|
|
* 2位月: MM %m |
|
|
|
* 数字月: M %c |
|
|
|
* 英文月: MMMM %M |
|
|
|
* 简写英文月: MMM %b |
|
|
|
* 2位日: dd %d |
|
|
|
* 数字日: d |
|
|
|
* 2位时(24): HH %H |
|
|
|
* 数字时(24): H %k |
|
|
|
* 2位时(12): hh %I %h |
|
|
|
* 数字时(12): h %l |
|
|
|
* 2位分: mm ii %i |
|
|
|
* 数字分: m i |
|
|
|
* 2位秒: ss %s %S |
|
|
|
* 数字秒: s |
|
|
|
* 3位毫秒: sss |
|
|
|
* 小写上下午(AM/PM): a p |
|
|
|
* 大写上下午(AM/PM): A P %p |
|
|
|
* 星期几(周一为1, 周日为7): u |
|
|
|
* 星期几(周日为0): %w |
|
|
|
* 英文星期几: dddd %W |
|
|
|
* 简写英文星期几: ddd %a |
|
|
|
* %: %% // MySQL特殊占位符% |
|
|
|
*/ |
|
|
|
// Eng/lish |
|
|
|
const DATE_TIME_FORMAT_TYPE_IGNORE = 'ign'; |
|
|
|
const DATE_TIME_FORMAT_TYPE_EXPRESSION = 'exp'; |
|
|
|
const DATE_TIME_FORMAT_TYPE_RAW = 'raw'; |
|
|
|
const DAYS_SIMPLE = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']; |
|
|
|
const DAYS = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']; |
|
|
|
const MONTHS_SIMPLE = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; |
|
|
|
const MONTHS = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; |
|
|
|
function parseDateTimeFormatter(format) |
|
|
|
{ |
|
|
|
let expect = function(target, str, range) { |
|
|
|
let i = 0; |
|
|
|
if(!range) |
|
|
|
range = 1; |
|
|
|
if(!Array.isArray(range)) |
|
|
|
{ |
|
|
|
let arr = []; |
|
|
|
for(let m = 1; m <= range; m++) |
|
|
|
arr.push(m); |
|
|
|
range = arr; |
|
|
|
} |
|
|
|
let res = 0; |
|
|
|
let max = Math.max(...range); |
|
|
|
let min = Math.min(...range); |
|
|
|
while(i < str.length) |
|
|
|
{ |
|
|
|
let ch = str[i++]; |
|
|
|
if(target.indexOf(ch) === -1) |
|
|
|
break; |
|
|
|
if(range.indexOf(i) === -1) |
|
|
|
{ |
|
|
|
if(i >= max) |
|
|
|
break; |
|
|
|
} |
|
|
|
if(range.indexOf(i) !== -1) |
|
|
|
res = i; |
|
|
|
} |
|
|
|
if(res > 0) |
|
|
|
{ |
|
|
|
return res; |
|
|
|
} |
|
|
|
return false; |
|
|
|
}; |
|
|
|
|
|
|
|
let i = 0; |
|
|
|
let arr = []; |
|
|
|
while(i < format.length) |
|
|
|
{ |
|
|
|
let ch = format[i]; |
|
|
|
let n = false; |
|
|
|
switch(ch) |
|
|
|
{ |
|
|
|
case 'y': |
|
|
|
n = expect('y', format.substr(i), [2, 4]); |
|
|
|
break; |
|
|
|
case 'M': |
|
|
|
n = expect('M', format.substr(i), 4); |
|
|
|
break; |
|
|
|
case 'd': |
|
|
|
n = expect('d', format.substr(i), 4); |
|
|
|
break; |
|
|
|
case 'H': |
|
|
|
case 'h': |
|
|
|
n = expect(ch, format.substr(i), 2); |
|
|
|
break; |
|
|
|
case 'm': |
|
|
|
case 'i': |
|
|
|
n = expect(ch, format.substr(i), 2); |
|
|
|
break; |
|
|
|
case 's': |
|
|
|
n = expect('s', format.substr(i), 3); |
|
|
|
break; |
|
|
|
case 'a': |
|
|
|
case 'p': |
|
|
|
case 'A': |
|
|
|
case 'P': |
|
|
|
case 'u': |
|
|
|
n = expect(ch, format.substr(i)); |
|
|
|
break; |
|
|
|
case 'z': |
|
|
|
case 'Z': |
|
|
|
n = expect(ch, format.substr(i), 4); |
|
|
|
break; |
|
|
|
case '%': |
|
|
|
n = expect('YmdHisSyecklIh%zZ', format.substr(i + 1)); |
|
|
|
if(n === 1) |
|
|
|
n++; |
|
|
|
break; |
|
|
|
default: |
|
|
|
arr.push({str: ch, type: DATE_TIME_FORMAT_TYPE_RAW}); |
|
|
|
i++; |
|
|
|
continue; |
|
|
|
} |
|
|
|
if(n) |
|
|
|
{ |
|
|
|
let s = format.substr(i, n); |
|
|
|
let ignore = ['z', 'zz', 'zzz', 'zzzz', 'Z', 'ZZ', 'ZZZ', 'ZZZZ', '%z', '%Z'].indexOf(s) !== -1; |
|
|
|
arr.push({str: s, type: ignore ? DATE_TIME_FORMAT_TYPE_IGNORE : DATE_TIME_FORMAT_TYPE_EXPRESSION}); |
|
|
|
i += n; |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
arr.push({str: format.substr(i), type: DATE_TIME_FORMAT_TYPE_RAW}); |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
return arr; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 格式化日期时间 |
|
|
|
* @param date 日期时间 Date对象/时间戳 默认当前日期时间 |
|
|
|
* @param format 格式化字符串 字符串 默认 yyyy-MM-dd HH:mm:ss |
|
|
|
* @returns {string} 日期时间格式化后的字符串 |
|
|
|
*/ |
|
|
|
export function date_format(date, format) { |
|
|
|
if(arguments.length === 1) |
|
|
|
{ |
|
|
|
format = date; |
|
|
|
date = null; |
|
|
|
} |
|
|
|
if (!date) |
|
|
|
date = new Date; |
|
|
|
else if(!date instanceof Date) |
|
|
|
date = new Date(date); |
|
|
|
format = format || 'yyyy-MM-dd HH:mm:ss'; |
|
|
|
let numpad = function(num, limit) { |
|
|
|
limit = limit || 10; |
|
|
|
if(num >= limit) |
|
|
|
return num; |
|
|
|
let res = num; |
|
|
|
while(num < limit && limit >= 10) |
|
|
|
{ |
|
|
|
res = '0' + res; |
|
|
|
limit /= 10; |
|
|
|
} |
|
|
|
return res; |
|
|
|
} |
|
|
|
const _y = date.getFullYear(); |
|
|
|
const _m = date.getMonth() + 1; |
|
|
|
const _d = date.getDate(); |
|
|
|
const _h = date.getHours(); |
|
|
|
const _i = date.getMinutes(); |
|
|
|
const _s = date.getSeconds(); |
|
|
|
const _a = date.getDay(); |
|
|
|
const _z = date.getMilliseconds(); |
|
|
|
// General style |
|
|
|
let Options = { |
|
|
|
yyyy: _y, |
|
|
|
MM: numpad(_m), |
|
|
|
dd: numpad(_d), |
|
|
|
HH: numpad(_h), |
|
|
|
mm: numpad(_i), |
|
|
|
ss: numpad(_s), |
|
|
|
sss: numpad(_z, 100), |
|
|
|
yy: _y % 100, |
|
|
|
M: _m, |
|
|
|
d: _d, |
|
|
|
H: _h, |
|
|
|
m: _i, |
|
|
|
s: _s, |
|
|
|
hh: numpad((_h % 12) || 12), |
|
|
|
h: (_h % 12) || 12, |
|
|
|
// Qt style |
|
|
|
ddd: DAYS_SIMPLE[_a - 1], |
|
|
|
dddd: DAYS[_a - 1], |
|
|
|
MMM: MONTHS_SIMPLE[_m - 1], |
|
|
|
MMMM: MONTHS[_m - 1], |
|
|
|
// Java style |
|
|
|
a: _h > 12 ? 'pm' : 'am', |
|
|
|
A: _h > 12 ? 'PM' : 'AM', |
|
|
|
u: _a, |
|
|
|
}; |
|
|
|
Options['p'] = Options.a; |
|
|
|
Options['P'] = Options.A; |
|
|
|
// PHP style |
|
|
|
Options['ii'] = Options.mm; |
|
|
|
Options['i'] = Options.m; |
|
|
|
// MySQL style |
|
|
|
Options['%Y'] = Options.yyyy; |
|
|
|
Options['%m'] = Options.MM; |
|
|
|
Options['%d'] = Options.dd; |
|
|
|
Options['%H'] = Options.HH; |
|
|
|
Options['%i'] = Options.mm; |
|
|
|
Options['%s'] = Options.ss; |
|
|
|
Options['%S'] = Options.ss; |
|
|
|
Options['%y'] = Options.yy; |
|
|
|
Options['%e'] = Options.d; |
|
|
|
Options['%c'] = Options.M; |
|
|
|
Options['%k'] = Options.H; |
|
|
|
Options['%l'] = Options.h; |
|
|
|
Options['%I'] = Options.hh; |
|
|
|
Options['%h'] = Options.hh; |
|
|
|
Options['%p'] = Options.A; |
|
|
|
Options['%w'] = Options.u - 1; |
|
|
|
Options['%a'] = Options.ddd; |
|
|
|
Options['%W'] = Options.dddd; |
|
|
|
Options['%M'] = Options.MMMM; |
|
|
|
Options['%b'] = Options.MMM; |
|
|
|
Options['%%'] = '%'; |
|
|
|
|
|
|
|
let arr = parseDateTimeFormatter(format); |
|
|
|
//console.log(arr); |
|
|
|
arr = arr.filter((x) => x.type !== DATE_TIME_FORMAT_TYPE_IGNORE).map((x) => { |
|
|
|
return x.type === DATE_TIME_FORMAT_TYPE_EXPRESSION ? (Options[x.str] || x.str) : x.str; |
|
|
|
}) |
|
|
|
//console.log(format, '|', arr.join('')); |
|
|
|
return arr.join(''); |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 解析日期时间 |
|
|
|
* @param date 日期时间字符串 字符串 |
|
|
|
* @param format 格式化字符串 字符串 默认 yyyy-MM-dd HH:mm:ss |
|
|
|
* @param initNow 缺省部分是否使用当前日期时间部分代替 bool 默认不使用当前 |
|
|
|
* @returns {Date} 解析日期时间出来的Date对象 |
|
|
|
*/ |
|
|
|
export function strtotime(date, format, initNow) |
|
|
|
{ |
|
|
|
if (!date) |
|
|
|
return null; |
|
|
|
let expect = function(target, str) { |
|
|
|
if(Array.isArray(target)) |
|
|
|
{ |
|
|
|
let res = 0; |
|
|
|
if(typeof(target[0]) === 'number') |
|
|
|
{ |
|
|
|
for(let t of target) |
|
|
|
{ |
|
|
|
if(str.length >= t) |
|
|
|
{ |
|
|
|
if(t > res && new RegExp(`^\\d{${t}}`).test(str)) |
|
|
|
res = t; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
for(let t of target) |
|
|
|
{ |
|
|
|
if(str.length < t.length) |
|
|
|
continue; |
|
|
|
if(str.startsWith(t)) |
|
|
|
{ |
|
|
|
res = t.length; |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
return res; |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
if(typeof(target) === 'number') |
|
|
|
{ |
|
|
|
if(str.length < target) |
|
|
|
return 0; |
|
|
|
if(!new RegExp(`^\\d{${target}}`).test(str)) |
|
|
|
return null; |
|
|
|
return target; |
|
|
|
} |
|
|
|
else if(typeof(target) === 'object') |
|
|
|
{ |
|
|
|
let res = 0; |
|
|
|
let min = target.min || 0; |
|
|
|
let max = target.max; |
|
|
|
for(let m = min; m <= max; m++) |
|
|
|
{ |
|
|
|
let t = '' + m; |
|
|
|
if(str.length >= t.length) |
|
|
|
{ |
|
|
|
if(str.startsWith(t)) |
|
|
|
res = t.length; |
|
|
|
} |
|
|
|
} |
|
|
|
return res; |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
if(str.length < target.length) |
|
|
|
return 0; |
|
|
|
if(!str.startsWith(target)) |
|
|
|
return 0; |
|
|
|
return target.length; |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
format = format || 'yyyy-MM-dd HH:mm:ss'; |
|
|
|
let arr = parseDateTimeFormatter(format); |
|
|
|
let i = 0; |
|
|
|
let _y = undefined; |
|
|
|
let _m = undefined; |
|
|
|
let _d = undefined; |
|
|
|
let _h = undefined; |
|
|
|
let _i = undefined; |
|
|
|
let _s = undefined; |
|
|
|
let _z = undefined; |
|
|
|
let _h_12 = undefined; |
|
|
|
let _ap = undefined; |
|
|
|
let get_token = function(str, target) { |
|
|
|
let r = expect(target, date.substr(i)); |
|
|
|
if(r <= 0) |
|
|
|
return null; |
|
|
|
let nstr = date.substr(i, r); |
|
|
|
i += r; |
|
|
|
return nstr; |
|
|
|
}; |
|
|
|
//console.log(arr); |
|
|
|
for(let part of arr) |
|
|
|
{ |
|
|
|
if(part.type === DATE_TIME_FORMAT_TYPE_RAW || part.type === DATE_TIME_FORMAT_TYPE_IGNORE) |
|
|
|
{ |
|
|
|
let r = expect(part.str, date.substr(i)); |
|
|
|
if(!r) |
|
|
|
return null; |
|
|
|
i += r; |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
let p = null; |
|
|
|
switch(part.str) |
|
|
|
{ |
|
|
|
case 'yyyy': |
|
|
|
case '%Y': |
|
|
|
p = get_token(part.str, 4); |
|
|
|
if(p) |
|
|
|
_y = parseInt(p); |
|
|
|
break; |
|
|
|
case 'yy': |
|
|
|
case '%y': |
|
|
|
p = get_token(part.str, 2); |
|
|
|
if(p) |
|
|
|
_y = Math.floor((date.getFullYear()) / 100) * 100 + parseInt(p); |
|
|
|
break; |
|
|
|
case 'MM': |
|
|
|
case '%m': |
|
|
|
p = get_token(part.str, 2); |
|
|
|
if(p) |
|
|
|
_m = parseInt(p); |
|
|
|
break; |
|
|
|
case 'M': |
|
|
|
case '%c': |
|
|
|
p = get_token(part.str, {min: 1, max: 12}); |
|
|
|
if(p) |
|
|
|
_m = parseInt(p); |
|
|
|
break; |
|
|
|
case 'MMMM': |
|
|
|
case '%M': |
|
|
|
p = get_token(part.str, MONTHS); |
|
|
|
if(p) |
|
|
|
_m = MONTHS.indexOf(p) + 1; |
|
|
|
break; |
|
|
|
case 'MMM': |
|
|
|
case '%b': |
|
|
|
p = get_token(part.str, MONTHS_SIMPLE); |
|
|
|
if(p) |
|
|
|
_m = MONTHS_SIMPLE.indexOf(p) + 1; |
|
|
|
break; |
|
|
|
case 'dd': |
|
|
|
case '%d': |
|
|
|
p = get_token(part.str, 2); |
|
|
|
if(p) |
|
|
|
_d = parseInt(p); |
|
|
|
break; |
|
|
|
case 'd': |
|
|
|
case '%e': |
|
|
|
p = get_token(part.str, {min: 1, max: 31}); |
|
|
|
if(p) |
|
|
|
_d = parseInt(p); |
|
|
|
break; |
|
|
|
case 'HH': |
|
|
|
case '%H': |
|
|
|
p = get_token(part.str, 2); |
|
|
|
if(p) |
|
|
|
_h = parseInt(p); |
|
|
|
break; |
|
|
|
case 'H': |
|
|
|
case '%k': |
|
|
|
p = get_token(part.str, {max: 23}); |
|
|
|
if(p) |
|
|
|
_h = parseInt(p); |
|
|
|
break; |
|
|
|
case 'hh': |
|
|
|
case '%I': |
|
|
|
case '%h': |
|
|
|
p = get_token(part.str, 2); |
|
|
|
if(p) |
|
|
|
_h_12 = parseInt(p); |
|
|
|
break; |
|
|
|
case 'h': |
|
|
|
case '%l': |
|
|
|
p = get_token(part.str, {max: 12}); |
|
|
|
if(p) |
|
|
|
_h_12 = parseInt(p); |
|
|
|
break; |
|
|
|
case 'mm': |
|
|
|
case 'ii': |
|
|
|
case '%i': |
|
|
|
p = get_token(part.str, 2); |
|
|
|
if(p) |
|
|
|
_i = parseInt(p); |
|
|
|
break; |
|
|
|
case 'm': |
|
|
|
case 'i': |
|
|
|
p = get_token(part.str, {max: 59}); |
|
|
|
if(p) |
|
|
|
_i = parseInt(p); |
|
|
|
break; |
|
|
|
case 'ss': |
|
|
|
case '%s': |
|
|
|
case '%S': |
|
|
|
p = get_token(part.str, 2); |
|
|
|
if(p) |
|
|
|
_s = parseInt(p); |
|
|
|
break; |
|
|
|
case 's': |
|
|
|
p = get_token(part.str, {max: 59}); |
|
|
|
if(p) |
|
|
|
_s = parseInt(p); |
|
|
|
break; |
|
|
|
case 'sss': |
|
|
|
p = get_token(part.str, {max: 999}); |
|
|
|
if(p) |
|
|
|
_z = parseInt(p); |
|
|
|
break; |
|
|
|
case 'A': |
|
|
|
case 'p': { |
|
|
|
const AP = ['AM', 'PM']; |
|
|
|
p = get_token(part.str, AP); |
|
|
|
if(p) |
|
|
|
_ap = p.toLowerCase(); |
|
|
|
} |
|
|
|
break; |
|
|
|
case 'a': |
|
|
|
case 'P': |
|
|
|
case '%p':{ |
|
|
|
const AP = ['am', 'pm']; |
|
|
|
p = get_token(part.str, AP); |
|
|
|
if(p) |
|
|
|
_ap = p; |
|
|
|
} |
|
|
|
break; |
|
|
|
case 'u': |
|
|
|
case '%w': |
|
|
|
case '%W': |
|
|
|
case 'dddd': |
|
|
|
case 'ddd': |
|
|
|
throw new Error('不支持星期几解析: ' + part.str); // not ignore and throw |
|
|
|
//break; |
|
|
|
case '%%': |
|
|
|
//TODO: ignore 最好不解析出来 |
|
|
|
break; |
|
|
|
default: |
|
|
|
throw new Error('不支持的格式解析: ' + part.str); |
|
|
|
break; |
|
|
|
} |
|
|
|
if(!p) |
|
|
|
return null; |
|
|
|
} |
|
|
|
} |
|
|
|
if(_h_12 !== undefined && _ap !== undefined) |
|
|
|
{ |
|
|
|
_h = (_ap === 'pm' ? _h_12 + 12 : _h_12) % 24; |
|
|
|
} |
|
|
|
let res = new Date; |
|
|
|
//console.log('1111111111',_y, _m, _d, _h, _i, _s, _z); |
|
|
|
if(_y === undefined) _y = initNow ? res.getFullYear() : 0; |
|
|
|
if(_m === undefined) _m = initNow ? res.getMonth() + 1 : 1; |
|
|
|
if(_d === undefined) _d = initNow ? res.getDate() : 1; |
|
|
|
if(_h === undefined) _h = initNow ? res.getHours() : 0; |
|
|
|
if(_i === undefined) _i = initNow ? res.getMinutes() : 0; |
|
|
|
if(_s === undefined) _s = initNow ? res.getSeconds() : 0; |
|
|
|
if(_z === undefined) _z = initNow ? res.getMilliseconds() : 0; |
|
|
|
//console.log('2222222222',_y, _m, _d, _h, _i, _s, _z); |
|
|
|
res.setFullYear(_y, _m - 1, _d); |
|
|
|
res.setHours(_h, _i, _s, _z); |
|
|
|
return res; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 转换日期时间格式 |
|
|
|
* @param dateStr 源日期时间字符串 字符串 |
|
|
|
* @param sourceFormat 源格式化字符串 字符串 默认 yyyy-MM-dd HH:mm:ss |
|
|
|
* @param targetFormat 目标格式化字符串 字符串 默认 yyyy-MM-dd HH:mm:ss |
|
|
|
* @param initNow 缺省部分是否使用当前日期时间部分代替 bool 默认不使用当前 |
|
|
|
* @returns {string} 重新格式化的日期时间出字符串 |
|
|
|
*/ |
|
|
|
export function date(dateStr, sourceFormat, targetFormat, initNow) |
|
|
|
{ |
|
|
|
return date_format(strtotime(dateStr, sourceFormat, initNow), targetFormat); |
|
|
|
} |