Files
2021-12-29 01:57:42 -08:00

467 lines
16 KiB
JavaScript

/**
* @preserve date-and-time (c) KNOWLEDGECODE | MIT
*/
var locales = {},
plugins = {},
lang = 'en',
_res = {
MMMM: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
MMM: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
dddd: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
ddd: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
dd: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'],
A: ['AM', 'PM']
},
_formatter = {
YYYY: function (d/*, formatString*/) { return ('000' + d.getFullYear()).slice(-4); },
YY: function (d/*, formatString*/) { return ('0' + d.getFullYear()).slice(-2); },
Y: function (d/*, formatString*/) { return '' + d.getFullYear(); },
MMMM: function (d/*, formatString*/) { return this.res.MMMM[d.getMonth()]; },
MMM: function (d/*, formatString*/) { return this.res.MMM[d.getMonth()]; },
MM: function (d/*, formatString*/) { return ('0' + (d.getMonth() + 1)).slice(-2); },
M: function (d/*, formatString*/) { return '' + (d.getMonth() + 1); },
DD: function (d/*, formatString*/) { return ('0' + d.getDate()).slice(-2); },
D: function (d/*, formatString*/) { return '' + d.getDate(); },
HH: function (d/*, formatString*/) { return ('0' + d.getHours()).slice(-2); },
H: function (d/*, formatString*/) { return '' + d.getHours(); },
A: function (d/*, formatString*/) { return this.res.A[d.getHours() > 11 | 0]; },
hh: function (d/*, formatString*/) { return ('0' + (d.getHours() % 12 || 12)).slice(-2); },
h: function (d/*, formatString*/) { return '' + (d.getHours() % 12 || 12); },
mm: function (d/*, formatString*/) { return ('0' + d.getMinutes()).slice(-2); },
m: function (d/*, formatString*/) { return '' + d.getMinutes(); },
ss: function (d/*, formatString*/) { return ('0' + d.getSeconds()).slice(-2); },
s: function (d/*, formatString*/) { return '' + d.getSeconds(); },
SSS: function (d/*, formatString*/) { return ('00' + d.getMilliseconds()).slice(-3); },
SS: function (d/*, formatString*/) { return ('0' + (d.getMilliseconds() / 10 | 0)).slice(-2); },
S: function (d/*, formatString*/) { return '' + (d.getMilliseconds() / 100 | 0); },
dddd: function (d/*, formatString*/) { return this.res.dddd[d.getDay()]; },
ddd: function (d/*, formatString*/) { return this.res.ddd[d.getDay()]; },
dd: function (d/*, formatString*/) { return this.res.dd[d.getDay()]; },
Z: function (d/*, formatString*/) {
var offset = d.getTimezoneOffset() / 0.6 | 0;
return (offset > 0 ? '-' : '+') + ('000' + Math.abs(offset - (offset % 100 * 0.4 | 0))).slice(-4);
},
post: function (str) { return str; },
res: _res
},
_parser = {
YYYY: function (str/*, formatString */) { return this.exec(/^\d{4}/, str); },
Y: function (str/*, formatString */) { return this.exec(/^\d{1,4}/, str); },
MMMM: function (str/*, formatString */) {
var result = this.find(this.res.MMMM, str);
result.value++;
return result;
},
MMM: function (str/*, formatString */) {
var result = this.find(this.res.MMM, str);
result.value++;
return result;
},
MM: function (str/*, formatString */) { return this.exec(/^\d\d/, str); },
M: function (str/*, formatString */) { return this.exec(/^\d\d?/, str); },
DD: function (str/*, formatString */) { return this.exec(/^\d\d/, str); },
D: function (str/*, formatString */) { return this.exec(/^\d\d?/, str); },
HH: function (str/*, formatString */) { return this.exec(/^\d\d/, str); },
H: function (str/*, formatString */) { return this.exec(/^\d\d?/, str); },
A: function (str/*, formatString */) { return this.find(this.res.A, str); },
hh: function (str/*, formatString */) { return this.exec(/^\d\d/, str); },
h: function (str/*, formatString */) { return this.exec(/^\d\d?/, str); },
mm: function (str/*, formatString */) { return this.exec(/^\d\d/, str); },
m: function (str/*, formatString */) { return this.exec(/^\d\d?/, str); },
ss: function (str/*, formatString */) { return this.exec(/^\d\d/, str); },
s: function (str/*, formatString */) { return this.exec(/^\d\d?/, str); },
SSS: function (str/*, formatString */) { return this.exec(/^\d{1,3}/, str); },
SS: function (str/*, formatString */) {
var result = this.exec(/^\d\d?/, str);
result.value *= 10;
return result;
},
S: function (str/*, formatString */) {
var result = this.exec(/^\d/, str);
result.value *= 100;
return result;
},
Z: function (str/*, formatString */) {
var result = this.exec(/^[\+-]\d{2}[0-5]\d/, str);
result.value = (result.value / 100 | 0) * -60 - result.value % 100;
return result;
},
h12: function (h, a) { return (h === 12 ? 0 : h) + a * 12; },
exec: function (re, str) {
var result = (re.exec(str) || [''])[0];
return { value: result | 0, length: result.length };
},
find: function (array, str) {
var index = -1, length = 0;
for (var i = 0, len = array.length, item; i < len; i++) {
item = array[i];
if (!str.indexOf(item) && item.length > length) {
index = i;
length = item.length;
}
}
return { value: index, length: length };
},
pre: function (str) { return str; },
res: _res
},
extend = function (base, props, override, res) {
var obj = {}, key;
for (key in base) {
obj[key] = base[key];
}
for (key in props || {}) {
if (!(!!override ^ !!obj[key])) {
obj[key] = props[key];
}
}
if (res) {
obj.res = res;
}
return obj;
};
var proto = {
_formatter: _formatter,
_parser: _parser
};
/**
* Compiling a format string
* @param {string} formatString - a format string
* @returns {Array.<string>} a compiled object
*/
proto.compile = function (formatString) {
var re = /\[([^\[\]]|\[[^\[\]]*])*]|([A-Za-z])\2+|\.{3}|./g, keys, pattern = [formatString];
while ((keys = re.exec(formatString))) {
pattern[pattern.length] = keys[0];
}
return pattern;
};
/**
* Formatting a Date and Time
* @param {Date} dateObj - a Date object
* @param {string|Array.<string>} arg - a format string or its compiled object
* @param {boolean} [utc] - output as UTC
* @returns {string} a formatted string
*/
proto.format = function (dateObj, arg, utc) {
var pattern = typeof arg === 'string' ? this.compile(arg) : arg,
offset = dateObj.getTimezoneOffset(),
d = this.addMinutes(dateObj, utc ? offset : 0),
formatter = this._formatter, str = '';
d.getTimezoneOffset = function () { return utc ? 0 : offset; };
for (var i = 1, len = pattern.length, token; i < len; i++) {
token = pattern[i];
str += formatter[token] ? formatter.post(formatter[token](d, pattern[0])) : token.replace(/\[(.*)]/, '$1');
}
return str;
};
/**
* Pre-parsing a Date and Time string
* @param {string} dateString - a date string
* @param {string|Array.<string>} arg - a format string or its compiled object
* @returns {Object} a date structure
*/
proto.preparse = function (dateString, arg) {
var pattern = typeof arg === 'string' ? this.compile(arg) : arg,
dt = { Y: 1970, M: 1, D: 1, H: 0, A: 0, h: 0, m: 0, s: 0, S: 0, Z: 0, _index: 0, _length: 0, _match: 0 },
comment = /\[(.*)]/, parser = this._parser, offset = 0;
dateString = parser.pre(dateString);
for (var i = 1, len = pattern.length, token, result; i < len; i++) {
token = pattern[i];
if (parser[token]) {
result = parser[token](dateString.slice(offset), pattern[0]);
if (!result.length) {
break;
}
offset += result.length;
dt[result.token || token.charAt(0)] = result.value;
dt._match++;
} else if (token === dateString.charAt(offset) || token === ' ') {
offset++;
} else if (comment.test(token) && !dateString.slice(offset).indexOf(comment.exec(token)[1])) {
offset += token.length - 2;
} else if (token === '...') {
offset = dateString.length;
break;
} else {
break;
}
}
dt.H = dt.H || parser.h12(dt.h, dt.A);
dt._index = offset;
dt._length = dateString.length;
return dt;
};
/**
* Parsing a Date and Time string
* @param {string} dateString - a date string
* @param {string|Array.<string>} arg - a format string or its compiled object
* @param {boolean} [utc] - input as UTC
* @returns {Date} a constructed date
*/
proto.parse = function (dateString, arg, utc) {
var dt = this.preparse(dateString, arg);
if (this.isValid(dt)) {
dt.M -= dt.Y < 100 ? 22801 : 1; // 22801 = 1900 * 12 + 1
if (utc || dt.Z) {
return new Date(Date.UTC(dt.Y, dt.M, dt.D, dt.H, dt.m + dt.Z, dt.s, dt.S));
}
return new Date(dt.Y, dt.M, dt.D, dt.H, dt.m, dt.s, dt.S);
}
return new Date(NaN);
};
/**
* Validation
* @param {Object|string} arg1 - a date structure or a date string
* @param {string|Array.<string>} [arg2] - a format string or its compiled object
* @returns {boolean} whether the date string is a valid date
*/
proto.isValid = function (arg1, arg2) {
var dt = typeof arg1 === 'string' ? this.preparse(arg1, arg2) : arg1,
last = [31, 28 + this.isLeapYear(dt.Y) | 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][dt.M - 1];
return !(
dt._index < 1 || dt._length < 1 || dt._index - dt._length || dt._match < 1 ||
dt.Y < 1 || dt.Y > 9999 || dt.M < 1 || dt.M > 12 || dt.D < 1 || dt.D > last ||
dt.H < 0 || dt.H > 23 || dt.m < 0 || dt.m > 59 || dt.s < 0 || dt.s > 59 || dt.S < 0 || dt.S > 999 ||
dt.Z < -720 || dt.Z > 840
);
};
/**
* Transforming a Date and Time string
* @param {string} dateString - a date string
* @param {string|Array.<string>} arg1 - a format string or its compiled object
* @param {string|Array.<string>} arg2 - a transformed format string or its compiled object
* @param {boolean} [utc] - output as UTC
* @returns {string} a formatted string
*/
proto.transform = function (dateString, arg1, arg2, utc) {
return this.format(this.parse(dateString, arg1), arg2, utc);
};
/**
* Adding years
* @param {Date} dateObj - a date object
* @param {number} years - number of years to add
* @returns {Date} a date after adding the value
*/
proto.addYears = function (dateObj, years) {
return this.addMonths(dateObj, years * 12);
};
/**
* Adding months
* @param {Date} dateObj - a date object
* @param {number} months - number of months to add
* @returns {Date} a date after adding the value
*/
proto.addMonths = function (dateObj, months) {
var d = new Date(dateObj.getTime());
d.setMonth(d.getMonth() + months);
return d;
};
/**
* Adding days
* @param {Date} dateObj - a date object
* @param {number} days - number of days to add
* @returns {Date} a date after adding the value
*/
proto.addDays = function (dateObj, days) {
var d = new Date(dateObj.getTime());
d.setDate(d.getDate() + days);
return d;
};
/**
* Adding hours
* @param {Date} dateObj - a date object
* @param {number} hours - number of hours to add
* @returns {Date} a date after adding the value
*/
proto.addHours = function (dateObj, hours) {
return this.addMinutes(dateObj, hours * 60);
};
/**
* Adding minutes
* @param {Date} dateObj - a date object
* @param {number} minutes - number of minutes to add
* @returns {Date} a date after adding the value
*/
proto.addMinutes = function (dateObj, minutes) {
return this.addSeconds(dateObj, minutes * 60);
};
/**
* Adding seconds
* @param {Date} dateObj - a date object
* @param {number} seconds - number of seconds to add
* @returns {Date} a date after adding the value
*/
proto.addSeconds = function (dateObj, seconds) {
return this.addMilliseconds(dateObj, seconds * 1000);
};
/**
* Adding milliseconds
* @param {Date} dateObj - a date object
* @param {number} milliseconds - number of milliseconds to add
* @returns {Date} a date after adding the value
*/
proto.addMilliseconds = function (dateObj, milliseconds) {
return new Date(dateObj.getTime() + milliseconds);
};
/**
* Subtracting two dates
* @param {Date} date1 - a Date object
* @param {Date} date2 - a Date object
* @returns {Object} a result object subtracting date2 from date1
*/
proto.subtract = function (date1, date2) {
var delta = date1.getTime() - date2.getTime();
return {
toMilliseconds: function () {
return delta;
},
toSeconds: function () {
return delta / 1000;
},
toMinutes: function () {
return delta / 60000;
},
toHours: function () {
return delta / 3600000;
},
toDays: function () {
return delta / 86400000;
}
};
};
/**
* Whether year is leap year
* @param {number} y - year
* @returns {boolean} whether year is leap year
*/
proto.isLeapYear = function (y) {
return (!(y % 4) && !!(y % 100)) || !(y % 400);
};
/**
* Comparison of two dates
* @param {Date} date1 - a Date object
* @param {Date} date2 - a Date object
* @returns {boolean} whether the two dates are the same day (time is ignored)
*/
proto.isSameDay = function (date1, date2) {
return date1.toDateString() === date2.toDateString();
};
/**
* Defining new locale
* @param {string} code - language code
* @param {Function} locale - locale installer
* @returns {string} current language code
*/
proto.locale = function (code, locale) {
if (!locales[code]) {
locales[code] = locale;
}
};
/**
* Defining new plugin
* @param {string} name - plugin name
* @param {Function} plugin - plugin installer
* @returns {void}
*/
proto.plugin = function (name, plugin) {
if (!plugins[name]) {
plugins[name] = plugin;
}
};
var localized_proto = extend(proto);
var date = extend(proto);
/**
* Changing locale
* @param {Function|string} [locale] - locale object | language code
* @returns {string} current language code
*/
date.locale = function (locale) {
var install = typeof locale === 'function' ? locale : date.locale[locale];
if (!install) {
return lang;
}
lang = install(proto);
var extension = locales[lang] || {};
var res = extend(_res, extension.res, true);
var formatter = extend(_formatter, extension.formatter, true, res);
var parser = extend(_parser, extension.parser, true, res);
date._formatter = localized_proto._formatter = formatter;
date._parser = localized_proto._parser = parser;
for (var plugin in plugins) {
date.extend(plugins[plugin]);
}
return lang;
};
/**
* Feature extension
* @param {Object} extension - extension object
* @returns {void}
*/
date.extend = function (extension) {
var res = extend(date._parser.res, extension.res);
var extender = extension.extender || {};
date._formatter = extend(date._formatter, extension.formatter, false, res);
date._parser = extend(date._parser, extension.parser, false, res);
for (var key in extender) {
if (!date[key]) {
date[key] = extender[key];
}
}
};
/**
* Importing plugin
* @param {Function|string} plugin - plugin object | plugin name
* @returns {void}
*/
date.plugin = function (plugin) {
var install = typeof plugin === 'function' ? plugin : date.plugin[plugin];
if (install) {
date.extend(plugins[install(proto, localized_proto)] || {});
}
};
export { date as default };