|
|
|
/*
|
|
|
|
* Date Format 1.2.3
|
|
|
|
* (c) 2007-2009 Steven Levithan <stevenlevithan.com>
|
|
|
|
* MIT license
|
|
|
|
*
|
|
|
|
* Includes enhancements by Scott Trenda <scott.trenda.net>
|
|
|
|
* and Kris Kowal <cixar.com/~kris.kowal/>
|
|
|
|
*
|
|
|
|
* Accepts a date, a mask, or a date and a mask.
|
|
|
|
* Returns a formatted version of the given date.
|
|
|
|
* The date defaults to the current date/time.
|
|
|
|
* The mask defaults to dateFormat.masks.default.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* eslint-disable */
|
|
|
|
|
|
|
|
const dateFormat = (() => {
|
|
|
|
const token = /d{1,4}|D{3,4}|m{1,4}|yy(?:yy)?|([HhMsTt])\1?|W{1,2}|[LlopSZN]|"[^"]*"|'[^']*'/g;
|
|
|
|
const timezone = /\b(?:[PMCEA][SDP]T|(?:Pacific|Mountain|Central|Eastern|Atlantic) (?:Standard|Daylight|Prevailing) Time|(?:GMT|UTC)(?:[-+]\d{4})?)\b/g;
|
|
|
|
const timezoneClip = /[^-+\dA-Z]/g;
|
|
|
|
|
|
|
|
// Regexes and supporting functions are cached through closure
|
|
|
|
function f(date?: number | Date, mask?: string, utc?: boolean, gmt?: boolean): string {
|
|
|
|
// You can't provide utc if you skip other args (use the 'UTC:' mask prefix)
|
|
|
|
/* if(
|
|
|
|
arguments.length === 1 &&
|
|
|
|
kindOf(date) === "string" &&
|
|
|
|
!/\d/.test(date)
|
|
|
|
) {
|
|
|
|
mask = date;
|
|
|
|
date = undefined;
|
|
|
|
} */
|
|
|
|
|
|
|
|
date = date || date === 0 ? date : new Date();
|
|
|
|
|
|
|
|
if(!(date instanceof Date)) {
|
|
|
|
date = new Date(date);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* if(isNaN(date)) {
|
|
|
|
throw TypeError("Invalid date");
|
|
|
|
} */
|
|
|
|
|
|
|
|
/* mask = String(
|
|
|
|
dateFormat.masks[mask] || mask || dateFormat.masks["default"]
|
|
|
|
); */
|
|
|
|
|
|
|
|
// Allow setting the utc/gmt argument via the mask
|
|
|
|
const maskSlice = mask.slice(0, 4);
|
|
|
|
if(maskSlice === "UTC:" || maskSlice === "GMT:") {
|
|
|
|
mask = mask.slice(4);
|
|
|
|
utc = true;
|
|
|
|
if(maskSlice === "GMT:") {
|
|
|
|
gmt = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const _ = () => (utc ? "getUTC" : "get");
|
|
|
|
const d = (): number => (date as any)[_() + "Date"]();
|
|
|
|
const D = (): number => (date as any)[_() + "Day"]();
|
|
|
|
const m = (): number => (date as any)[_() + "Month"]();
|
|
|
|
const y = (): number => (date as any)[_() + "FullYear"]();
|
|
|
|
const H = (): number => (date as any)[_() + "Hours"]();
|
|
|
|
const M = (): number => (date as any)[_() + "Minutes"]();
|
|
|
|
const s = (): number => (date as any)[_() + "Seconds"]();
|
|
|
|
const L = (): number => (date as any)[_() + "Milliseconds"]();
|
|
|
|
const o = (): number => (utc ? 0 : (date as Date).getTimezoneOffset());
|
|
|
|
const W = (): number => getWeek(date as Date);
|
|
|
|
const N = (): number => getDayOfWeek(date as Date);
|
|
|
|
|
|
|
|
const flags = {
|
|
|
|
d: () => d(),
|
|
|
|
dd: () => pad(d()),
|
|
|
|
ddd: () => dateFormat.i18n.dayNames[D()],
|
|
|
|
DDD: () => getDayName({
|
|
|
|
y: y(),
|
|
|
|
m: m(),
|
|
|
|
d: d(),
|
|
|
|
_: _(),
|
|
|
|
dayName: dateFormat.i18n.dayNames[D()],
|
|
|
|
short: true
|
|
|
|
}),
|
|
|
|
dddd: () => dateFormat.i18n.dayNames[D() + 7],
|
|
|
|
DDDD: () => getDayName({
|
|
|
|
y: y(),
|
|
|
|
m: m(),
|
|
|
|
d: d(),
|
|
|
|
_: _(),
|
|
|
|
dayName: dateFormat.i18n.dayNames[D() + 7]
|
|
|
|
}),
|
|
|
|
m: () => m() + 1,
|
|
|
|
mm: () => pad(m() + 1),
|
|
|
|
mmm: () => dateFormat.i18n.monthNames[m()],
|
|
|
|
mmmm: () => dateFormat.i18n.monthNames[m() + 12],
|
|
|
|
yy: () => String(y()).slice(2),
|
|
|
|
yyyy: () => pad(y(), 4),
|
|
|
|
h: () => H() % 12 || 12,
|
|
|
|
hh: () => pad(H() % 12 || 12),
|
|
|
|
H: () => H(),
|
|
|
|
HH: () => pad(H()),
|
|
|
|
M: () => M(),
|
|
|
|
MM: () => pad(M()),
|
|
|
|
s: () => s(),
|
|
|
|
ss: () => pad(s()),
|
|
|
|
l: () => pad(L(), 3),
|
|
|
|
L: () => pad(Math.floor(L() / 10)),
|
|
|
|
t: () =>
|
|
|
|
H() < 12 ?
|
|
|
|
dateFormat.i18n.timeNames[0] : dateFormat.i18n.timeNames[1],
|
|
|
|
tt: () =>
|
|
|
|
H() < 12 ?
|
|
|
|
dateFormat.i18n.timeNames[2] : dateFormat.i18n.timeNames[3],
|
|
|
|
T: () =>
|
|
|
|
H() < 12 ?
|
|
|
|
dateFormat.i18n.timeNames[4] : dateFormat.i18n.timeNames[5],
|
|
|
|
TT: () =>
|
|
|
|
H() < 12 ?
|
|
|
|
dateFormat.i18n.timeNames[6] : dateFormat.i18n.timeNames[7],
|
|
|
|
Z: () =>
|
|
|
|
gmt ?
|
|
|
|
"GMT" : utc ?
|
|
|
|
"UTC" : (String(date).match(timezone) || [""])
|
|
|
|
.pop()
|
|
|
|
.replace(timezoneClip, "")
|
|
|
|
.replace(/GMT\+0000/g, "UTC"),
|
|
|
|
o: () =>
|
|
|
|
(o() > 0 ? "-" : "+") +
|
|
|
|
pad(Math.floor(Math.abs(o()) / 60) * 100 + (Math.abs(o()) % 60), 4),
|
|
|
|
p: () =>
|
|
|
|
(o() > 0 ? "-" : "+") +
|
|
|
|
pad(Math.floor(Math.abs(o()) / 60), 2) +
|
|
|
|
":" +
|
|
|
|
pad(Math.floor(Math.abs(o()) % 60), 2),
|
|
|
|
S: () => ["th", "st", "nd", "rd"][
|
|
|
|
// @ts-ignore
|
|
|
|
d() % 10 > 3 ? 0 : (((d() % 100) - (d() % 10) != 10) * d()) % 10
|
|
|
|
],
|
|
|
|
W: () => W(),
|
|
|
|
WW: () => pad(W()),
|
|
|
|
N: () => N(),
|
|
|
|
};
|
|
|
|
|
|
|
|
return mask.replace(token, (match) => {
|
|
|
|
if(match in flags) {
|
|
|
|
// @ts-ignore
|
|
|
|
return flags[match]();
|
|
|
|
}
|
|
|
|
return match.slice(1, match.length - 1);
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
// Internationalization strings
|
|
|
|
f.i18n = {
|
|
|
|
dayNames: [
|
|
|
|
"Sun",
|
|
|
|
"Mon",
|
|
|
|
"Tue",
|
|
|
|
"Wed",
|
|
|
|
"Thu",
|
|
|
|
"Fri",
|
|
|
|
"Sat",
|
|
|
|
"Sunday",
|
|
|
|
"Monday",
|
|
|
|
"Tuesday",
|
|
|
|
"Wednesday",
|
|
|
|
"Thursday",
|
|
|
|
"Friday",
|
|
|
|
"Saturday",
|
|
|
|
],
|
|
|
|
monthNames: [
|
|
|
|
"Jan",
|
|
|
|
"Feb",
|
|
|
|
"Mar",
|
|
|
|
"Apr",
|
|
|
|
"May",
|
|
|
|
"Jun",
|
|
|
|
"Jul",
|
|
|
|
"Aug",
|
|
|
|
"Sep",
|
|
|
|
"Oct",
|
|
|
|
"Nov",
|
|
|
|
"Dec",
|
|
|
|
"January",
|
|
|
|
"February",
|
|
|
|
"March",
|
|
|
|
"April",
|
|
|
|
"May",
|
|
|
|
"June",
|
|
|
|
"July",
|
|
|
|
"August",
|
|
|
|
"September",
|
|
|
|
"October",
|
|
|
|
"November",
|
|
|
|
"December",
|
|
|
|
],
|
|
|
|
timeNames: ["a", "p", "am", "pm", "A", "P", "AM", "PM"],
|
|
|
|
};
|
|
|
|
|
|
|
|
f.setLocale = function(code: string) {
|
|
|
|
const date = new Date();
|
|
|
|
|
|
|
|
{
|
|
|
|
const dateTimeFormat = new Intl.DateTimeFormat(code, {month: 'long'});
|
|
|
|
const dateTimeFormatShort = new Intl.DateTimeFormat(code, {month: 'short'});
|
|
|
|
for(let i = 0; i < 12; ++i) {
|
|
|
|
date.setMonth(i);
|
|
|
|
f.i18n.monthNames[i] = dateTimeFormatShort.format(date);
|
|
|
|
f.i18n.monthNames[i + 12] = dateTimeFormat.format(date);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
const day = date.getDay();
|
|
|
|
if(day !== 0) {
|
|
|
|
date.setDate(date.getDate() - day);
|
|
|
|
}
|
|
|
|
|
|
|
|
const dateTimeFormat = new Intl.DateTimeFormat(code, {weekday: 'long'});
|
|
|
|
const dateTimeFormatShort = new Intl.DateTimeFormat(code, {weekday: 'short'});
|
|
|
|
for(let i = 0; i < 7; ++i) {
|
|
|
|
date.setDate(date.getDate() + 1);
|
|
|
|
f.i18n.dayNames[i] = dateTimeFormatShort.format(date);
|
|
|
|
f.i18n.dayNames[i + 7] = dateTimeFormat.format(date);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
return f;
|
|
|
|
})();
|
|
|
|
|
|
|
|
export default dateFormat;
|
|
|
|
|
|
|
|
(window as any).dateFormat = dateFormat;
|
|
|
|
|
|
|
|
/* dateFormat.masks = {
|
|
|
|
default: "ddd mmm dd yyyy HH:MM:ss",
|
|
|
|
shortDate: "m/d/yy",
|
|
|
|
paddedShortDate: "mm/dd/yyyy",
|
|
|
|
mediumDate: "mmm d, yyyy",
|
|
|
|
longDate: "mmmm d, yyyy",
|
|
|
|
fullDate: "dddd, mmmm d, yyyy",
|
|
|
|
shortTime: "h:MM TT",
|
|
|
|
mediumTime: "h:MM:ss TT",
|
|
|
|
longTime: "h:MM:ss TT Z",
|
|
|
|
isoDate: "yyyy-mm-dd",
|
|
|
|
isoTime: "HH:MM:ss",
|
|
|
|
isoDateTime: "yyyy-mm-dd'T'HH:MM:sso",
|
|
|
|
isoUtcDateTime: "UTC:yyyy-mm-dd'T'HH:MM:ss'Z'",
|
|
|
|
expiresHeaderFormat: "ddd, dd mmm yyyy HH:MM:ss Z",
|
|
|
|
}; */
|
|
|
|
|
|
|
|
const pad = (val: number | string, len = 2) => {
|
|
|
|
val = String(val);
|
|
|
|
while(val.length < len) {
|
|
|
|
val = "0" + val;
|
|
|
|
}
|
|
|
|
return val;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get day name
|
|
|
|
* Yesterday, Today, Tomorrow if the date lies within, else fallback to Monday - Sunday
|
|
|
|
* @param {Object}
|
|
|
|
* @return {String}
|
|
|
|
*/
|
|
|
|
const getDayName = ({
|
|
|
|
y,
|
|
|
|
m,
|
|
|
|
d,
|
|
|
|
_,
|
|
|
|
dayName,
|
|
|
|
short = false
|
|
|
|
}: {
|
|
|
|
y: number,
|
|
|
|
m: number,
|
|
|
|
d: number,
|
|
|
|
_: any,
|
|
|
|
dayName: any,
|
|
|
|
short?: boolean
|
|
|
|
}) => {
|
|
|
|
const today = new Date();
|
|
|
|
const yesterday = new Date();
|
|
|
|
yesterday.setDate((yesterday as any)[_ + 'Date']() - 1);
|
|
|
|
const tomorrow = new Date();
|
|
|
|
tomorrow.setDate((tomorrow as any)[_ + 'Date']() + 1);
|
|
|
|
const today_d = (): number => (today as any)[_ + 'Date']();
|
|
|
|
const today_m = (): number => (today as any)[_ + 'Month']();
|
|
|
|
const today_y = (): number => (today as any)[_ + 'FullYear']();
|
|
|
|
const yesterday_d = (): number => (yesterday as any)[_ + 'Date']();
|
|
|
|
const yesterday_m = (): number => (yesterday as any)[_ + 'Month']();
|
|
|
|
const yesterday_y = (): number => (yesterday as any)[_ + 'FullYear']();
|
|
|
|
const tomorrow_d = (): number => (tomorrow as any)[_ + 'Date']();
|
|
|
|
const tomorrow_m = (): number => (tomorrow as any)[_ + 'Month']();
|
|
|
|
const tomorrow_y = (): number => (tomorrow as any)[_ + 'FullYear']();
|
|
|
|
|
|
|
|
if(today_y() === y && today_m() === m && today_d() === d) {
|
|
|
|
return short ? 'Tdy' : 'Today';
|
|
|
|
} else if(yesterday_y() === y && yesterday_m() === m && yesterday_d() === d) {
|
|
|
|
return short ? 'Ysd' : 'Yesterday';
|
|
|
|
} else if(tomorrow_y() === y && tomorrow_m() === m && tomorrow_d() === d) {
|
|
|
|
return short ? 'Tmw' : 'Tomorrow';
|
|
|
|
}
|
|
|
|
return dayName;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the ISO 8601 week number
|
|
|
|
* Based on comments from
|
|
|
|
* http://techblog.procurios.nl/k/n618/news/view/33796/14863/Calculate-ISO-8601-week-and-year-in-javascript.html
|
|
|
|
*
|
|
|
|
* @param {Object} `date`
|
|
|
|
* @return {Number}
|
|
|
|
*/
|
|
|
|
const getWeek = (date: Date) => {
|
|
|
|
// Remove time components of date
|
|
|
|
const targetThursday = new Date(
|
|
|
|
date.getFullYear(),
|
|
|
|
date.getMonth(),
|
|
|
|
date.getDate()
|
|
|
|
);
|
|
|
|
|
|
|
|
// Change date to Thursday same week
|
|
|
|
targetThursday.setDate(
|
|
|
|
targetThursday.getDate() - ((targetThursday.getDay() + 6) % 7) + 3
|
|
|
|
);
|
|
|
|
|
|
|
|
// Take January 4th as it is always in week 1 (see ISO 8601)
|
|
|
|
const firstThursday = new Date(targetThursday.getFullYear(), 0, 4);
|
|
|
|
|
|
|
|
// Change date to Thursday same week
|
|
|
|
firstThursday.setDate(
|
|
|
|
firstThursday.getDate() - ((firstThursday.getDay() + 6) % 7) + 3
|
|
|
|
);
|
|
|
|
|
|
|
|
// Check if daylight-saving-time-switch occurred and correct for it
|
|
|
|
const ds =
|
|
|
|
targetThursday.getTimezoneOffset() - firstThursday.getTimezoneOffset();
|
|
|
|
targetThursday.setHours(targetThursday.getHours() - ds);
|
|
|
|
|
|
|
|
// Number of weeks between target Thursday and first Thursday
|
|
|
|
const weekDiff = (targetThursday.getTime() - firstThursday.getTime()) / (86400000 * 7);
|
|
|
|
return 1 + Math.floor(weekDiff);
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get ISO-8601 numeric representation of the day of the week
|
|
|
|
* 1 (for Monday) through 7 (for Sunday)
|
|
|
|
*
|
|
|
|
* @param {Object} `date`
|
|
|
|
* @return {Number}
|
|
|
|
*/
|
|
|
|
const getDayOfWeek = (date: Date) => {
|
|
|
|
let dow = date.getDay();
|
|
|
|
if(dow === 0) {
|
|
|
|
dow = 7;
|
|
|
|
}
|
|
|
|
return dow;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* kind-of shortcut
|
|
|
|
* @param {*} val
|
|
|
|
* @return {String}
|
|
|
|
*/
|
|
|
|
/* const kindOf = (val: any) => {
|
|
|
|
if(val === null) {
|
|
|
|
return "null";
|
|
|
|
}
|
|
|
|
|
|
|
|
if(val === undefined) {
|
|
|
|
return "undefined";
|
|
|
|
}
|
|
|
|
|
|
|
|
if(typeof val !== "object") {
|
|
|
|
return typeof val;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(Array.isArray(val)) {
|
|
|
|
return "array";
|
|
|
|
}
|
|
|
|
|
|
|
|
return {}.toString.call(val).slice(8, -1).toLowerCase();
|
|
|
|
}; */
|