@onereach/time-interpreter
Version:
Convert dates, durations and time to canonical format (dates -> ISO 8601, durations -> milliseconds).
1,379 lines (1,240 loc) • 83.1 kB
JavaScript
const moment = require("moment-timezone");
const _ = require("lodash");
// timezones list
const tzList = moment.tz.names();
// regular expressions for date format definition
const months =
"(?:Jan(?:uary)?|Feb(?:ruary)?|Mar(?:ch)?|Apr(?:il)?|May|June?|July?|Aug(?:ust)?|Sep(?:tember)?|Oct(?:ober)?|Nov(?:ember)?|Dec(?:ember)?)";
const date_sep = "\(\\s|\\/|-|\\.\)";
const ordinalDay = "((([1-2][0-9])|(3[0-1])|([1-9]))(th|rd|st|nd))";
const stringDay =
"\(thirtieth|twentieth|\(thirty\(\(\\s|-\)?\(first|one\)\)?\)|\(\(twenty\(\\s|-\)\)?\(first|one|two|second|three|third|four\(th\)?\\b|fi\(ve|fth\)\\b|six\(th\)?\\b|seven\(th\)?\\b|eight\(h\)?\\b|nin\(e|th\)\\b\)\)|\(ten\(th\)?\\b|eleven\(th\)?|twel\(ve|fth\)|eighteen\(th\)?|\(thir|four|fif|six|seven|nine\)teen\(th\)?|twenty\)\)";
const DATE_ISO =
"^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(Z|((\\.\\d{3}Z)|((\\+|-)\\d{2}:?\\d{2})))";
const DAY_OF_WEEK = new RegExp(
/(?:sun(?:day)?|mon(?:day)?|tue(?:sday)?|wed(?:nesday)?|thu(?:rsday)?|fri(?:day)?|sat(?:urday)?)(,?\s)?/,
"i"
);
const TIME_STR = new RegExp(
/\d+\s?(h(ours)?|m(inutes)?|s(econds)?|ms|milliseconds)/,
"ig"
);
const TZ_OFFSET = new RegExp(
/(\+(((0?\d|[1][0-4]):?00)|((0?([3-6]|[9])|[10]):?30)|((0?[5,8]):?45)))|((-|−)((((0?\d)|([1][0-2])):?00)|(0?[2,3,9]:?30)))/
);
const UTC_OFFSET = new RegExp(/UTC((\+|-)((1[0-2])|(0?\d)))|(\+1[3,4])/);
const HH_OFFSET_RANGE = "((\\+|-)((1[0-2])|(0?\\d)))\\b|(\\+1[3,4])";
const TIMEZONES = new RegExp(
"/" +
tzList
.sort((a, b) => b.length - a.length)
.reduce((acc, tz, i) => {
tz = tz.replace(/\//g, "\\$&").replace(/\+/g, "\\$&");
if (i) acc = acc + "|" + tz;
else acc = tz;
return acc;
}, "") +
"/"
);
const DDMMYYYY = "((\\d\\d?-\\d\\d?-(\\d\\d\\d\\d))|(\\d\\d?\\.\\d\\d?\\.(\\d\\d\\d\\d))|(\\d\\d?\\/\\d\\d?\\/(\\d\\d\\d\\d)))";
const YYYYMMDD = "(((\\d\\d\\d\\d)-\\d\\d?-\\d\\d?)|((\\d\\d\\d\\d)\\.\\d\\d?\\.\\d\\d?)|((\\d\\d\\d\\d)\\/\\d\\d?\\/\\d\\d?))";
const YY_MM_DD = new RegExp(/(^((\d\d?-\d\d?-\d\d?)|(\d\d?\/\d\d?\/\d\d?)|(\d\d?\.\d\d?\.\d\d?)|(\d\d?\s\d\d?\s\d\d?))$)/);
const MMYYYY = /^((\d\d?(-|\/|\.|\s)\d{4})|(\d{4}(-|\/|\.|\s)\d\d?))$/;
const test_short_date = {
YY_MM_DD: "(((\\d\\d?-\\d\\d?-\\d\\d?)|(\\d\\d?\\/\\d\\d?\\/\\d\\d?)|(\\d\\d?\\.\\d\\d?\\.\\d\\d?)))", // 12/12/12, 12.12.12, 12-12-12
month_DD_YY: "(" + months + "\\/\\d\\d?\\/\\d\\d\\b)|(" + months + "\\.\\d\\d?\\.\\d\\d\\b)|(" + months + "-\\d\\d?-\\d\\d\\b)", // sep/12/12, sep.12.12, sep-12-12
YY_DD_month: "(\\b\\d\\d\\/\\d\\d?\\/" + months + ")|(\\b\\d\\d\\.\\d\\d?\\." + months + ")|(\\b\\d\\d-\\d\\d?-" + months + ")", // 12/12/sep, 12.12.sep, 12-12-sep
YY_month_DD: "(\\b\\d\\d?\\/" + months + "\\/\\d\\d?\\b)|(\\b\\d\\d?\\." + months + "\\.\\d\\d?\\b)|(\\b\\d\\d?-" + months + "-\\d\\d?\\b)" // 12/sep/12, 12.sep.12, 12-sep-12
}
const DURATION_STR = new RegExp(
/\d+\s?(ms|millisecond(s)?|y(ear(s)?)?|M|month(s)?|w(eek(s)?)?|d(ay(s)?)?|h(our(s)?)?|min\b|m(inute(s)?)?|sec\b|s(econd(s)?)?)/,
"g"
);
const DURATION_ISO =
"(-?)P(?=\\d|T\\d)(?:(\\d+)Y)?(?:(\\d+)M)?(?:(\\d+)([DW]))?(?:T(?:(\\d+)H)?(?:(\\d+)M)?(?:(\\d+(?:\\.\\d+)?)S)?)?";
const dg = {
zero: 0,
one: "first",
two: "second",
three: "third",
four: "fourth",
five: "fifth",
six: "sixth",
seven: "seventh",
eight: "eighth",
nine: "ninth",
ten: "tenth",
eleven: "eleventh",
twelve: "twelfth",
thirteen: "thirteenth",
fourteen: "fourteenth",
fifteen: "fifteenth",
sixteen: "sixteenth",
seventeen: "seventeenth",
eighteen: "eighteenth",
nineteen: "nineteenth",
twenty: "twentieth",
};
let AMPM_TIME = {};
let TIMES = {};
const shortDateTimeRegex = () => {
// time
const hh_a = "(\\b(([0]?[1-9])|1[0-2])\\s((am|pm)\\b))"; // 7 pm
const hha = "(\\b(([0]?[1-9])|1[0-2])((am|pm)\\b))"; // 7pm
const hhmmssA = "(\\b(([0]\\d)|([1][0-2]))([0-5]\\d)(([0-5]\\d))((am|pm)\\b))"; // 123000am
const hhmmss_A = "(\\b(([0]\\d)|([1][0-2]))([0-5]\\d)(([0-5]\\d))\\s((am|pm)\\b))"; // 123000 am
const hh_mm_a =
"(\\b(([0]?[1-9])|1[0-2]):[0-5][0-9]\\s((am|pm)\\b))"; // 12:30 am
const hh_mma =
"(\\b(([0]?[1-9])|1[0-2]):[0-5][0-9]((am|pm)\\b))"; // 12:30am
const hh_mm_ss_a =
"(\\b(([0]?[1-9])|1[0-2]):[0-5][0-9]:[0-5][0-9]\\s((am|pm)\\b))"; // 12:30:00 am
const hh_mm_ssa =
"(\\b(([0]?[1-9])|1[0-2]):[0-5][0-9]:[0-5][0-9]((am|pm)\\b))"; // 12:30:00am
const ampm_TIME = {
[hh_a]: "hh a",
[hha]: "hha",
[hhmmssA]: "hhmmssa",
[hhmmss_A]: "hhmmss a",
[hh_mm_a]: "hh:mm a",
[hh_mma]: "hh:mma",
[hh_mm_ss_a]: "hh:mm:ss a",
[hh_mm_ssa]: "hh:mm:ssa"
};
const hhmmss = "(\\b(((0|1)[0-9])|(2[0-4]))[0-5][0-9][0-5][0-9]\\b)"; // 123000
const hh_mmZ =
"((\\b(([1-9])|([0-1][0-9])|([2][0-4])):[0-5][0-9]((\\+|-)\\d{2}:?\\d{2}))\\b)"; // 12:30+0200, 12:45-03:00
const h_mm_ss =
"((\\b\\d:[0-5][0-9]:[0-5][0-9])\\b)"; // 2:30:00
const hh_mm_ss =
"((\\b(([1-9])|([0-1][0-9])|([2][0-4])):[0-5][0-9]:[0-5][0-9])\\b)"; // 12:30:00
const hh_mm_ss_SSSz =
"((\\b(([1-9])|([0-1][0-9])|([2][0-4])):[0-5][0-9]:[0-5][0-9]((\\.\\d{3}Z)))\\b)"; // 12:30:00.123Z
const hh_mm_ssz =
"((\\b(([1-9])|([0-1][0-9])|([2][0-4])):[0-5][0-9]:[0-5][0-9]Z)\\b)"; // 12:30:00Z
const hh_mm = "(\\b(([1-9])|([0-1][0-9])|([2][0-4])):[0-5][0-9]\\b)"; // 12:30
const hh_mm_ssZ =
"((\\b(([1-9])|([0-1][0-9])|([2][0-4])):[0-5][0-9]:[0-5][0-9])((\\+|-)\\d{2}:?\\d{2})\\b)"; // 12:30:00+01:00
const hh_mm_sszZ =
"((\\b(([1-9])|([0-1][0-9])|([2][0-4])):[0-5][0-9]:[0-5][0-9])Z((\\+|-)\\d{2}:?\\d{2})\\b)"; // 12:30:00Z+01:00
const simple_TIMES = {
[hhmmss]: "HHmmss",
[hh_mmZ]: "HH:mmZ",
[h_mm_ss]: "H:mm:ss",
[hh_mm_ss]: "HH:mm:ss",
[hh_mm_ss_SSSz]: "HH:mm:ss.SSS[Z]",
[hh_mm_ssz]: "HH:mm:ss[Z]",
[hh_mm]: "HH:mm",
[hh_mm_ssZ]: "HH:mm:ssZ",
[hh_mm_sszZ]: "HH:mm:ss[Z]Z",
}
const month_date = {
[test_short_date.month_DD_YY]: 'MMMM-DD-YY',
[test_short_date.YY_DD_month]: 'YY-DD-MMMM',
[test_short_date.YY_month_DD]: {
format: 'YY-MMMM-DD',
additional: ['DD-MMMM-YY']
}
};
const simple_date = {
[test_short_date.YY_MM_DD]: {
format: 'MM-DD-YY',
additional: ['DD-MM-YY', 'YY-MM-DD', 'YY-DD-MM']
}
};
const STR_MONTH_DATE_TIME = getShortDatesTimesRegex(simple_TIMES, month_date);
const STR_MONTH_DATE_AMPM_TIME = getShortDatesTimesRegex(ampm_TIME, month_date);
const DATE_TIME = getShortDatesTimesRegex(simple_TIMES, simple_date);
const DATE_AMPM_TIME = getShortDatesTimesRegex(ampm_TIME, simple_date);
return {
STR_MONTH_DATE_TIME,
STR_MONTH_DATE_AMPM_TIME,
DATE_TIME,
DATE_AMPM_TIME
}
}
const getShortDatesTimesRegex = (times, dates) => {
const formats = Object.keys(times).reduce((obj, time) => {
let dateFormat, timeFormat, additional;
Object.keys(dates).forEach(date => {
if (typeof dates[date] === "object") {
dateFormat = dates[date].format;
additional = dates[date].additional;
} else {
dateFormat = dates[date];
}
if (typeof times[time] === "object") timeFormat = times[time].format;
else timeFormat = times[time];
obj["(" + date + ")\\s" + time] = {
format: dateFormat + " " + timeFormat,
additionalFormats: (() => {
if (additional) return additional.map(f => f + " " + timeFormat);
})(),
date: date,
};
obj[time + "\\s(" + date + ")"] = {
format: timeFormat + " " + dateFormat,
additionalFormats: (() => {
if (additional) return additional.map(f => timeFormat + " " + f);
})(),
date: date,
};
});
return obj;
}, {});
return formats;
}
const getDatesTimesRegex = () => {
// time part
const hh_a = "(\\b(([0]?[1-9])|1[0-2])\\s((am|pm)\\b))"; // 7 pm
const hha = "(\\b(([0]?[1-9])|1[0-2])((am|pm)\\b))";
const hhmmssA = "(\\b(([0]\\d)|([1][0-2]))([0-5]\\d)(([0-5]\\d))((am|pm)\\b))";
const hhmmss_A = "(\\b(([0]\\d)|([1][0-2]))([0-5]\\d)(([0-5]\\d))\\s((am|pm)\\b))";
const hh_mm_a =
"((\\b(([0]?[1-9])|1[0-2])\\s[0-5][0-9]\\s((am|pm)\\b))|((([0]?[1-9])|1[0-2]):[0-5][0-9]\\s((am|pm)\\b))|(\\b(([0]?[1-9])|1[0-2])\\.[0-5][0-9]\\s((am|pm)\\b))|(\\b(([0]?[1-9])|1[0-2])-[0-5][0-9]\\s((am|pm)\\b))\\b)"; // 12 30 am
const hh_mma =
"((\\b(([0]?[1-9])|1[0-2])\\s[0-5][0-9]((am|pm)\\b))|(\\b(([0]?[1-9])|1[0-2]):[0-5][0-9]((am|pm)\\b))|(\\b(([0]?[1-9])|1[0-2])\\.[0-5][0-9]((am|pm)\\b))|(\\b(([0]?[1-9])|1[0-2])-[0-5][0-9]((am|pm)\\b))\\b)"; // 12 30am
const hh_mm_ss_a =
"((\\b(([0]?[1-9])|1[0-2])\\s[0-5][0-9]\\s[0-5][0-9]\\s((am|pm)\\b))|(\\b(([0]?[1-9])|1[0-2]):[0-5][0-9]:[0-5][0-9]\\s((am|pm)\\b))|(\\b(([0]?[1-9])|1[0-2])\\.[0-5][0-9]\\.[0-5][0-9]\\s((am|pm)\\b))|(\\b(([0]?[1-9])|1[0-2])-[0-5][0-9]-[0-5][0-9]\\s((am|pm)\\b))\\b)"; // 12 30 00 am
const hh_mm_ssa =
"((\\b(([0]?[1-9])|1[0-2])\\s[0-5][0-9]\\s[0-5][0-9]((am|pm)\\b))|(\\b(([0]?[1-9])|1[0-2]):[0-5][0-9]:[0-5][0-9]((am|pm)\\b))|(\\b(([0]?[1-9])|1[0-2])\\.[0-5][0-9]\\.[0-5][0-9]((am|pm)\\b))|(\\b(([0]?[1-9])|1[0-2])-[0-5][0-9]-[0-5][0-9]((am|pm)\\b))\\b)"; // 12 30 00am
AMPM_TIME = {
[hh_a]: "hh a",
[hha]: "hha",
[hhmmssA]: "hhmmssa",
[hhmmss_A]: "hhmmss a",
[hh_mm_a]: {
format: "hh:mm a",
sep: true,
},
[hh_mma]: {
format: "hh:mma",
sep: true,
},
[hh_mm_ss_a]: {
format: "hh:mm:ss a",
sep: true,
},
[hh_mm_ssa]: {
format: "hh:mm:ssa",
sep: true,
},
};
const h_mm_ss =
"((\\b\\d\\s[0-5][0-9]\\s[0-5][0-9])|(\\b\\d:[0-5][0-9]:[0-5][0-9])|(\\b\\d\\.[0-5][0-9]\\.[0-5][0-9])|(\\b\\d-[0-5][0-9]-[0-5][0-9])\\b)"; // 2:30:00
const hh_mm_ss =
"((\\b(([1-9])|([0-1][0-9])|([2][0-4]))\\s[0-5][0-9]\\s[0-5][0-9])|(\\b(([1-9])|([0-1][0-9])|([2][0-4])):[0-5][0-9]:[0-5][0-9])|(\\b(([1-9])|([0-1][0-9])|([2][0-4]))\\.[0-5][0-9]\\.[0-5][0-9])|(\\b(([1-9])|([0-1][0-9])|([2][0-4]))-[0-5][0-9]-[0-5][0-9])\\b)"; // 12:30:00
const hh_mmZ =
"((\\b(([1-9])|([0-1][0-9])|([2][0-4])):[0-5][0-9]((\\+|-)\\d{2}:?\\d{2}))|(\\b(([1-9])|([0-1][0-9])|([2][0-4]))\\.[0-5][0-9]((\\+|-)\\d{2}:?\\d{2}))|(\\b(([1-9])|([0-1][0-9])|([2][0-4]))-[0-5][0-9]((\\+|-)\\d{2}:?\\d{2}))\\b)"; // 12 30+0200, 12 45-03:00
//12:30:00Z+01:00
const hh_mm_ssz =
"((\\b(([1-9])|([0-1][0-9])|([2][0-4]))\\s[0-5][0-9]\\s[0-5][0-9]Z)|(\\b(([1-9])|([0-1][0-9])|([2][0-4])):[0-5][0-9]:[0-5][0-9]Z)|(\\b(([1-9])|([0-1][0-9])|([2][0-4]))\\.[0-5][0-9]\\.[0-5][0-9]Z)|(\\b(([1-9])|([0-1][0-9])|([2][0-4]))-[0-5][0-9]-[0-5][0-9]Z)\\b)";
const hh_mm_ss_SSSz =
"((\\b(([1-9])|([0-1][0-9])|([2][0-4]))\\s[0-5][0-9]\\s[0-5][0-9]((\\.\\d{3}Z)))|(\\b(([1-9])|([0-1][0-9])|([2][0-4])):[0-5][0-9]:[0-5][0-9]((\\.\\d{3}Z)))|(\\b(([1-9])|([0-1][0-9])|([2][0-4]))\\.[0-5][0-9]\\.[0-5][0-9]((\\.\\d{3}Z)))|(\\b(([1-9])|([0-1][0-9])|([2][0-4]))-[0-5][0-9]-[0-5][0-9]((\\.\\d{3}Z)))\\b)";
const hhmmss = "(\\b(((0|1)[0-9])|(2[0-4]))[0-5][0-9][0-5][0-9]\\b)"; // 123000
TIMES = [" ", "-", ".", ":"].reduce(
(acc, sep) => {
const s =
sep === " " ? "\\s" : sep === "-" ? "-" : sep === ":" ? ":" : "\\.";
const hh_mm = "(\\b(([1-9])|([0-1][0-9])|([2][0-4]))" + s + "[0-5][0-9]\\b)"; // 12:30
const hh_mmZ =
"(\\b(([1-9])|([0-1][0-9])|([2][0-4]))" + s + "[0-5][0-9]((\\+|-)\\d{2}:?\\d{2})\\b)"; // 12 30+0200, 12 45-03:00
//12:30:00Z+01:00
const hh_mm_ssZ =
"((\\b(([1-9])|([0-1][0-9])|([2][0-4]))" +
s +
"[0-5][0-9]" +
s +
"[0-5][0-9])((\\+|-)\\d{2}:?\\d{2})\\b)";
const hh_mm_sszZ =
"((\\b(([1-9])|([0-1][0-9])|([2][0-4]))" +
s +
"[0-5][0-9]" +
s +
"[0-5][0-9])Z((\\+|-)\\d{2}:?\\d{2})\\b)";
acc[hh_mm] = "HH" + sep + "mm";
acc[hh_mmZ] = "HH" + sep + "mmZ";
acc[hh_mm_ssZ] = "HH" + sep + "mm" + sep + "ssZ";
acc[hh_mm_sszZ] = "HH" + sep + "mm" + sep + "ss[Z]Z";
return acc;
}, {
[hhmmss]: "HHmmss",
[hh_mmZ]: {
format: "HH:mmZ",
sep: true,
},
[h_mm_ss]: {
format: "H:mm:ss",
sep: true,
},
[hh_mm_ss]: {
format: "HH:mm:ss",
sep: true,
},
[hh_mm_ss_SSSz]: {
format: "HH:mm:ss.SSS[Z]",
sep: true,
},
[hh_mm_ssz]: {
format: "HH:mm:ss[Z]",
sep: true,
},
}
);
// date part
const yearReg = "(\\b\\d\\d\\d\\d\\b)";
const numDayOfMonth = "(([1-2][0-9])|(3[0-1])|([1-9])|(0\\d))";
let ordinalDay_date = {},
stringDay_date = {},
stringDay_year_date = {},
ordinalDay_year_date = {},
simple_date_year = {},
simple_day_month = {};
simple_day_month[numDayOfMonth + date_sep + months] = "DD-MMMM";
simple_day_month[months + date_sep + numDayOfMonth] = "MMMM-DD";
simple_date_year[numDayOfMonth + date_sep + months + date_sep + yearReg] =
"DD-MMMM-YYYY";
simple_date_year[yearReg + date_sep + numDayOfMonth + date_sep + months] =
"YYYY-DD-MMMM";
simple_date_year[months + date_sep + numDayOfMonth + date_sep + yearReg] =
"MMMM-DD-YYYY";
simple_date_year[yearReg + date_sep + months + date_sep + numDayOfMonth] =
"YYYY-MMMM-DD";
ordinalDay_date[ordinalDay + date_sep + months] = "Do-MMMM";
ordinalDay_date[months + date_sep + ordinalDay] = "MMMM-Do";
ordinalDay_year_date[ordinalDay + date_sep + months + date_sep + yearReg] =
"Do-MMMM-YYYY";
ordinalDay_year_date[yearReg + date_sep + ordinalDay + date_sep + months] =
"YYYY-Do-MMMM";
ordinalDay_year_date[months + date_sep + ordinalDay + date_sep + yearReg] =
"MMMM-Do-YYYY";
ordinalDay_year_date[yearReg + date_sep + months + date_sep + ordinalDay] =
"YYYY-MMMM-Do";
stringDay_date[stringDay + date_sep + months] = {
format: "DD-MMMM",
stringDay: true,
};
stringDay_date[months + date_sep + stringDay] = {
format: "MMMM-DD",
stringDay: true,
};
stringDay_year_date[stringDay + date_sep + months + date_sep + yearReg] = {
format: "DD-MMMM-YYYY",
stringDay: true,
};
stringDay_year_date[yearReg + date_sep + stringDay + date_sep + months] = {
format: "YYYY-DD-MMMM",
stringDay: true,
};
stringDay_year_date[yearReg + date_sep + months + date_sep + stringDay] = {
format: "YYYY-MMMM-DD",
stringDay: true,
};
stringDay_year_date[months + date_sep + stringDay + date_sep + yearReg] = {
format: "MMMM-DD-YYYY",
stringDay: true,
};
return {
simple_day_month,
simple_date_year,
stringDay_date,
ordinalDay_date,
ordinalDay_year_date,
stringDay_year_date,
TIMES,
AMPM_TIME,
};
};
const generator = (dates, times) => {
const formats = Object.keys(dates).reduce((acc, date, i) => {
acc = Object.assign(
acc,
Object.keys(times).reduce((obj, time) => {
let dateFormat, timeFormat;
if (typeof dates[date] === "object") dateFormat = dates[date].format;
else dateFormat = dates[date];
if (typeof times[time] === "object") timeFormat = times[time].format;
else timeFormat = times[time];
obj[date + "\\s" + time] = {
stringDay: typeof dates[date] === "object",
timeSep: typeof times[time] === "object",
format: dateFormat + " " + timeFormat,
date: date,
time: time,
};
obj[time + "\\s" + date] = {
stringDay: typeof dates[date] === "object",
timeSep: typeof times[time] === "object",
format: timeFormat + " " + dateFormat,
date: date,
time: time,
};
return obj;
}, {})
);
return acc;
}, {});
return formats;
};
const generateRegex = () => {
const {
simple_day_month,
simple_date_year,
TIMES,
AMPM_TIME,
stringDay_date,
ordinalDay_date,
stringDay_year_date,
ordinalDay_year_date,
} = getDatesTimesRegex();
const simple_date_time = generator(simple_day_month, TIMES);
const simple_date_ampm = generator(simple_day_month, AMPM_TIME);
const simple_date_year_time = generator(simple_date_year, TIMES);
const simple_date_year_ampm = generator(simple_date_year, AMPM_TIME);
const stringDay_time = generator(stringDay_date, TIMES);
const stringDay_date_ampm = generator(stringDay_date, AMPM_TIME);
const stringDay_year_ampm = generator(stringDay_year_date, AMPM_TIME);
const stringDay_year_time = generator(stringDay_year_date, TIMES);
const ordinalDay_time = generator(ordinalDay_date, TIMES);
const ordinalDay_date_ampm = generator(ordinalDay_date, AMPM_TIME);
const ordinalDay_year_ampm = generator(ordinalDay_year_date, AMPM_TIME);
const ordinalDay_year_time = generator(ordinalDay_year_date, TIMES);
const year_month_day_time = generator({
[YYYYMMDD]: 'YYYY-MM-DD'
}, TIMES);
const year_month_day_ampm = generator({
[YYYYMMDD]: 'YYYY-MM-DD'
}, AMPM_TIME);
const day_month_year_time = generator({
[DDMMYYYY]: 'MM-DD-YYYY'
}, TIMES);
const day_month_year_ampm = generator({
[DDMMYYYY]: 'MM-DD-YYYY'
}, AMPM_TIME);
return {
simple_date_time, // simple day + month + time
simple_date_ampm, // simple day + month + am/pm time
simple_date_year_time, // simple day + month + year + time
simple_date_year_ampm, // simple day + month + year + am/pm time
stringDay_time, // string day + month + time
stringDay_date_ampm, // string day + month + am/pm time
stringDay_year_date, // string day + month + year
stringDay_year_ampm, // string day + month + year + am/pm time
stringDay_year_time, // string day + month + year + time
ordinalDay_time, // ordinal day + month + time
ordinalDay_date_ampm, // ordinal day + month + am/pm time
ordinalDay_year_date, // ordinal day + month + year
ordinalDay_year_ampm, // ordinal day + month + year + am/pm time
ordinalDay_year_time, // ordinal day + month + year + time
simple_date_year, // simple day + month + year
year_month_day_time, // 12-12-2021
year_month_day_ampm,
day_month_year_time,
day_month_year_ampm
};
};
const MATCH_DATE = generateRegex();
const MATCH_SHORT_DATE_TIME = shortDateTimeRegex()
// end regexp
class timeInterpreter {
// methods
getTimezonesList() {
return tzList;
}
parse(xValue, xTimezone, yTimezone, xYear, xDate) {
const weekDays = [
"Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
];
let result = {
time: null,
duration: null,
iso: null,
isoWithFractSec: null,
date: null,
},
type = "datetime",
tz,
momentDate,
original;
if (!xYear || !/(\b\d\d\d\d\b)/.test(xYear)) xYear = moment().get("year");
if (!xTimezone) xTimezone = "GMT+0";
switch (typeof xValue) {
case "object": // value object
// ['milliseconds', 'seconds', 'minutes', 'hours', 'days', 'weeks', 'months', 'years']
type = "datetime";
result.duration = moment.duration(xValue).asMilliseconds();
if (
!xValue.years &&
!xValue.year &&
!xValue.y &&
!xValue.months &&
!xValue.month &&
!xValue["M"] &&
!xValue.date &&
!xValue.day &&
!xValue.days &&
!xValue.d
) {
const syntax = () => {
const temp1 = ["y", "M", "d", "h", "m", "s", "ms"];
const temp2 = [
"year",
"month",
"day",
"hour",
"minute",
"second",
"millisecond",
];
const temp3 = [
"years",
"months",
"days",
"date",
"hours",
"minutes",
"seconds",
"milliseconds",
];
if (Object.keys(xValue).some((item) => temp1.indexOf(item) !== -1))
return temp1;
if (Object.keys(xValue).some((item) => temp2.indexOf(item) !== -1))
return temp2;
if (Object.keys(xValue).some((item) => temp3.indexOf(item) !== -1))
return temp3;
};
if (xDate) {
let datePart = moment.utc(xDate, "YYYY-MM-DD");
if (datePart.format() === "Invalid date") return null;
xValue[syntax()[0]] = datePart.get("year");
xValue[syntax()[1]] = datePart.get("month");
xValue[syntax()[2]] = datePart.get("date");
if (tzList.indexOf(xTimezone) !== -1) original = moment.tz(xValue, xTimezone);
else original = moment.parseZone(setTimezone(moment.utc(xValue).format("YYYY-MM-DD[T]HH:mm:ss.SSSZ"), '', xTimezone));
} else {
type = "time";
if (tzList.indexOf(xTimezone) !== -1) {
original = moment.tz(xValue, xTimezone);
} else {
original = setTimezone(moment.utc(xValue), '', xTimezone);
original = moment.parseZone(original);
}
}
} else if (
!xValue.years &&
!xValue.year &&
!xValue.y &&
(xValue.months || xValue.month || xValue["M"]) &&
(xValue.date || xValue.day || xValue.days || xValue.d)
) {
if (
!xValue.hours &&
!xValue.hour &&
!xValue.h &&
!xValue.minutes &&
!xValue.minute &&
!xValue["m"] &&
!xValue.seconds &&
!xValue.second &&
!xValue.s
) {
type = 'date';
}
if (tzList.indexOf(xTimezone) !== -1) {
original = moment.tz(xValue, xTimezone).set("year", xYear);
} else {
original = setTimezone(moment(xValue).set("year", xYear).format("YYYY-MM-DD[T]HH:mm:ss.SSSZ"), '', xTimezone);
original = moment.parseZone(original);
}
console.warn(`Input date value ${xValue} does not include a year value. The date is assigned to the year '${xYear}'`);
result.duration = moment.duration(result.iso).asMilliseconds();
} else {
if (
!xValue.hours &&
!xValue.hour &&
!xValue.h &&
!xValue.minutes &&
!xValue.minute &&
!xValue["m"] &&
!xValue.seconds &&
!xValue.second &&
!xValue.s
) {
type = 'date';
}
if (tzList.indexOf(xTimezone) !== -1) {
original = moment.tz(xValue, xTimezone);
} else {
original = setTimezone(moment(xValue).format("YYYY-MM-DD[T]HH:mm:ss.SSSZ"), '', xTimezone);
original = moment.parseZone(original);
}
if (!/(\b\d\d\d\d\b)/.test(original.year())) type = "time";
}
tz = xTimezone;
if (original && original.format() !== "Invalid date") {
if (yTimezone && type !== "date" && type !== "time") {
tz = yTimezone;
if (tzList.indexOf(yTimezone) !== -1) momentDate = moment(original.format()).tz(yTimezone);
else momentDate = moment(original.format()).utcOffset(yTimezone);
} else {
momentDate = original.clone();
}
switch (type) {
case "time":
result.iso = momentDate.clone().format("HH:mm:ssZ");
result.isoWithFractSec = momentDate.clone().format("HH:mm:ss.SSSZ");
break;
default:
result.iso = momentDate.clone().format("YYYY-MM-DD[T]HH:mm:ssZ");
result.isoWithFractSec = momentDate.clone().format("YYYY-MM-DD[T]HH:mm:ss.SSSZ");
}
result.timezone = {
name: tzList.indexOf(tz) !== -1 ? tz : "",
offsetNum: momentDate.utcOffset() / 60,
offsetText: momentDate.clone().format("Z"),
};
}
break;
case "number": // ms duration, UNIX sec/milisec
original = moment.unix(xValue).utc();
if (original.format() !== "Invalid date") {
if (
original.toISOString().split("-")[0].length > 4 ||
!/(\b[1-2][0-9]{3}\b)/.test(original.year())
) {
original = moment.utc(xValue);
}
}
tz = xTimezone;
if (tzList.indexOf(xTimezone) !== -1) {
momentDate = moment.tz(
original.format("YYYY-MM-DD[T]HH:mm:ss.SSS"),
xTimezone
);
} else {
original = setTimezone(original.format("YYYY-MM-DD[T]HH:mm:ss.SSSZ"), '', xTimezone);
momentDate = moment.parseZone(original);
}
if (yTimezone) {
tz = yTimezone;
if (tzList.indexOf(yTimezone) !== -1) momentDate = momentDate.tz(yTimezone);
else momentDate = momentDate.utcOffset(yTimezone);
}
result.timezone = {
name: tzList.indexOf(tz) !== -1 ? tz : "",
offsetNum: momentDate.utcOffset() / 60,
offsetText: momentDate.clone().format("Z"),
};
result.duration = momentDate.clone().valueOf();
result.iso = momentDate.clone().format("YYYY-MM-DD[T]HH:mm:ssZ");
result.isoWithFractSec = momentDate.clone().format("YYYY-MM-DD[T]HH:mm:ss.SSSZ");
type = "datetime";
break;
case "string":
let parsed;
type = "datetime";
const isoDate = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(:\d{2})?(((\.\d{3}Z)|(Z?(\+|-)\d{2}:?\d{2})|Z))$/;
if (xValue.match(isoDate)) {
// default format
tz = xValue.match(/(((\.\d{3}Z)|(Z?(\+|-)\d{2}:?\d{2})|Z))$/)[0];
if (/^(Z(\+|-)\d{2}:?\d{2})$/.test(tz)) {
tz = tz.replace('Z', '');
xValue = xValue.replace('Z', '');
}
original = moment.parseZone(xValue).format("YYYY-MM-DD[T]HH:mm:ss.SSSZ");
} else if (xValue.match(new RegExp(DATE_ISO + "?\\/" + DURATION_ISO))) {
const date = xValue.match(new RegExp(DATE_ISO + "?"))[0];
const dur = xValue.match(new RegExp(DURATION_ISO))[0];
original = moment
.utc(date)
.add(moment.duration(dur).asMilliseconds())
.format("YYYY-MM-DD[T]HH:mm:ss.SSSZ");
} else if (
/^(\d\d\d\d)((0\d)|(1[0-2]))((0\d)|(2\d)|(3[0-1]))(\s|T)((0\d)|(1\d)|(2[0-3]))([0-5]\d[0-5]\d)$/.test(
xValue
)
) {
original = moment.utc(
xValue.replace("T", " "),
"YYYYMMDD HHmmss"
).format("YYYY-MM-DD[T]HH:mm:ss.SSS[Z]");
} else if (
/\d{4}((1[0-2])|(0\d))(([0-2]\d)|(3[0,1]))(\s|T)?([0-2]\d[0-5]\d[0-5]\d)\s?((\+(((0?\d|[1][0-4]):?00)|((0?([3-6]|[9])|[10]):?30)|((0?[5,8]):?45)))|((-|−)((((0?\d)|([1][0-2])):?00)|(0?[2,3,9]:?30))))/.test(
xValue
)
) {
let match = xValue.match(/(\+|-)\d{2}(\d{2})?/);
tz = getTimezoneOffset(match[0]).offset;
original =
moment
.utc(_.trim(xValue.substring(0, match.index), " "))
.toISOString()
.slice(0, -5) + ".000" + tz;
} else if (new RegExp("^(\\d\\d" + months + "\\d\\d\\d\\d)$",'i').test(xValue)){
type = "date";
original = moment.utc(xValue, 'DDMMMMYYYY').format("YYYY-MM-DD[T]HH:mm:ss.SSSZ");
} else {
if (
new RegExp(
"^(([4-9]\\d)|(3[2-9]))" + date_sep + months + "$",
"i"
).test(xValue) ||
new RegExp(
"^" + months + date_sep + "(([4-9]\\d)|(3[2-9]))$",
"i"
).test(xValue)
) {
result = null;
} else {
parsed = matchDateFormat(xValue, xYear); // string MONTH or/and string DAY (optional + year)
if (!parsed) {
let trimmed = getZoneAndWeekday(xValue, tz);
xValue = trimmed.date;
tz = trimmed.tz;
parsed = parseAmTime(xValue, tz); // am/pm time
if (!parsed) {
parsed = processDateString(xValue, xYear, tz, trimmed.weekDay); // date + time match
if (parsed) {
if (parsed.type) type = parsed.type;
else type = "datetime";
} else {
parsed = parseTime(xValue, tz); // TIME STRING
if (!parsed) parsed = getTime(xValue, tz); // unusual type
if (parsed) {
type = "time";
original = parsed.time;
tz = parsed.tz;
}
}
} else {
type = "time";
original = parsed.time;
tz = parsed.tz;
}
// Time
if (type === "time" && original && original !== "Invalid date") {
if ((!tz || !tz.offset) && original.match(/(\+|-)\d{2}:\d{2}$/)) {
if (!tz) tz = {
offset: original.match(/(\+|-)\d{2}:\d{2}$/)[0]
};
else tz.offset = original.match(/(\+|-)\d{2}:\d{2}$/)[0];
}
if (!tz) {
if (tzList.indexOf(xTimezone) !== -1) tz = {
location: xTimezone
};
else tz = {
offset: xTimezone
};
}
if (xDate) {
type = "datetime";
original = xDate + "T" + original;
if (tz.offset !== "Z") {
if (tz.location) original = setTimezone(moment.parseZone(original).format("YYYY-MM-DD[T]HH:mm:ss.SSSZ"), '', tz.location);
else original = setTimezone(moment.parseZone(original).format("YYYY-MM-DD[T]HH:mm:ss.SSSZ"), '', tz.offset);
}
} else {
if (tz.offset === "Z") original = '2020-10-10T' + original;
else if (tz.location) original = setTimezone(moment.parseZone(original, "HH:mm:ss.SSSZ").format("YYYY-MM-DD[T]HH:mm:ss.SSSZ"), '', tz.location);
else original = setTimezone(moment.parseZone(original, "HH:mm:ss.SSSZ").format("YYYY-MM-DD[T]HH:mm:ss.SSSZ"), '', tz.offset);
momentDate = moment.parseZone(original);
if (tz.offset === "Z") result.iso = momentDate.format("HH:mm:ss") + "Z";
else result.iso = momentDate.format("HH:mm:ssZ");
result.isoWithFractSec = original.split("T")[1];
result.timezone = {
name: tz.location ? tz.location : "",
offsetNum: momentDate.utcOffset() / 60,
offsetText: momentDate.clone().format("Z"),
};
result.duration = moment
.duration(momentDate.format("HH:mm:ss"))
.asMilliseconds();
}
}
}
}
// Date/time
if (
(type === "datetime" || type === "date") &&
!original &&
(parsed && parsed !== "Invalid date") &&
(parsed.date && parsed.date !== "Invalid date")
) {
original = parsed.date;
tz = parsed.tz;
if (parsed.type) type = parsed.type;
}
}
if (!_.isNull(result) && original && type !== "time") {
if (((!tz && xTimezone) || yTimezone)) {
original = setTimezone(original, tz, xTimezone, yTimezone);
if (_.isNull(original)) return null;
tz = yTimezone ? yTimezone : xTimezone;
}
if (tz && tz !== 'Z') {
if (tzList.indexOf(tz) === -1) {
momentDate = moment.parseZone(original, "YYYY-MM-DD[T]HH:mm:ss.SSSZ");
result.iso = momentDate.format("YYYY-MM-DD[T]HH:mm:ssZ");
result.timezone = {
name: "",
};
} else {
momentDate = moment.parseZone(original, "YYYY-MM-DD[T]HH:mm:ss.SSSZ");
result.iso = momentDate.format("YYYY-MM-DD[T]HH:mm:ssZ");
result.timezone = {
name: tz,
};
}
} else {
result.timezone = {
name: "",
};
momentDate = moment(original, "YYYY-MM-DD[T]HH:mm:ss.SSSZ");
result.iso = momentDate.format();
}
result.timezone["offsetNum"] = momentDate.utcOffset() / 60;
result.timezone["offsetText"] = momentDate.clone().format("Z");
result.isoWithFractSec = momentDate.format("YYYY-MM-DD[T]HH:mm:ss.SSSZ");
result.duration = moment(result.iso).valueOf();
}
break;
}
if (result && momentDate) {
if (type !== "time") {
result["date"] = {
dayOfWeek: weekDays[momentDate.day()],
isWeekend: false,
day: momentDate.get("date"),
month: momentDate.get("month") + 1,
monthName: momentDate.clone().format("MMMM"),
year: momentDate.get("year"),
};
if (["Sunday", "Saturday"].includes(result.date.dayOfWeek))
result.date.isWeekend = true;
else result.date.isWeekend = false;
}
result["time"] = {
hourMinute12: momentDate.clone().format("hh:mm"),
hourMinute24: momentDate.clone().format("HH:mm"),
hour12: Number(momentDate.clone().format("hh")),
hour24: momentDate.get("hour"),
amPm: momentDate.clone().format("a"),
minute: momentDate.get("minute"),
};
}
return result;
}
getParsed({value, inputTimezone, outputTimezone, assumedYear, assumedDate}) {
return this.parse(value, inputTimezone, outputTimezone, assumedYear, assumedDate);
}
formatDatetime(xDate, ignoreTime, xTimezone, yTimezone, xYear) {
let result, isoWithFractSec, tz, timezoneOffset, zoneName, offsetNum, type = '';
if (!xYear || !/(\b\d\d\d\d\b)/.test(xYear)) xYear = moment().get("year");
switch (typeof xDate) {
case "object": // date object
if (
!xDate.hours &&
!xDate.hour &&
!xDate.h &&
!xDate.minutes &&
!xDate.minute &&
!xDate["m"] &&
!xDate.seconds &&
!xDate.second &&
!xDate.s
) {
type = 'date';
}
if (
!xDate.years &&
!xDate.year &&
!xDate.y &&
(xDate.months || xDate.month || xDate["M"]) &&
(xDate.date || xDate.day || xDate.days || xDate.d)
) {
result = moment.utc(xDate).set('year', xYear).toISOString();
console.warn(`Input date value ${xDate} does not include a year value. The date is assigned to the year '${xYear}'`);
} else {
result = moment.utc(xDate).toISOString();
}
break;
case "number": // ms duration, UNIX sec/milisec
result = moment.unix(xDate);
if (result.format() !== "Invalid date") {
if (
result.toISOString().split("-")[0].length > 4 ||
!/(\b[1-2][0-9]{3}\b)/.test(result.year())
) {
result = moment.utc(xDate).toISOString();
} else {
result = result.toISOString();
}
}
break;
case "string":
if (
xDate.length < 4 ||
(xDate.length === 4 && !/\d\d\d\d/.test(xDate))
) {
result = "Invalid date";
} else {
const isoDate = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(:\d{2})?(((\.\d{3}Z)|(Z?(\+|-)\d{2}:?\d{2})|Z))$/;
if (xDate.match(isoDate)) {
// default format
tz = xDate.match(/(((\.\d{3}Z)|(Z?(\+|-)\d{2}:?\d{2})|Z))$/)[0];
if (/^(Z(\+|-)\d{2}:?\d{2})$/.test(tz)) {
tz = tz.replace('Z', '');
xDate = xDate.replace('Z', '');
}
if (/Z$/.test(xDate)) result = moment.parseZone(xDate).format("YYYY-MM-DD[T]HH:mm:ss.SSS[Z]");
else result = moment.parseZone(xDate).format("YYYY-MM-DD[T]HH:mm:ss.SSSZ");
} else if (
xDate.match(new RegExp(DATE_ISO + "?\\/" + DURATION_ISO))
) {
const date = xDate.match(new RegExp(DATE_ISO + "?"))[0];
const dur = xDate.match(new RegExp(DURATION_ISO))[0];
result = moment
.utc(date)
.add(moment.duration(dur).asMilliseconds())
.format("YYYY-MM-DD[T]HH:mm:ss.SSS[Z]");
} else if (
/^(\d\d\d\d)((0\d)|(1[0-2]))((0\d)|(2\d)|(3[0-1]))(\s|T)((0\d)|(1\d)|(2[0-3]))([0-5]\d[0-5]\d)$/.test(
xDate
)
) {
result = moment.utc(
xDate,
"YYYYMMDD[T]HHmmss"
).format("YYYY-MM-DD[T]HH:mm:ss.SSS[Z]");
} else if (
/\d{4}((1[0-2])|(0\d))(([0-2]\d)|(3[0,1]))(\s|T)?([0-2]\d[0-5]\d[0-5]\d)\s?((\+(((0?\d|[1][0-4]):?00)|((0?([3-6]|[9])|[10]):?30)|((0?[5,8]):?45)))|((-|−)((((0?\d)|([1][0-2])):?00)|(0?[2,3,9]:?30))))/.test(
xDate
)
) {
let match = xDate.match(/(\+|-)\d{2}(\d{2})?/);
tz = getTimezoneOffset(match[0]).offset;
const date = _.trim(xDate.substring(0, match.index), " ");
result = moment.utc(date);
result = result.toISOString().slice(0, -5) + ".000" + tz;
} else if (new RegExp("^(\\d\\d" + months + "\\d\\d\\d\\d)$",'i').test(xDate)){ // DDMMMMYYYY
type = "date";
result = moment.utc(xDate, 'DDMMMMYYYY').format("YYYY-MM-DD[T]HH:mm:ss.SSS[Z]");
} else {
if (
new RegExp(
"^(([4-9]\\d)|(3[2-9]))" + date_sep + months + "$",
"i"
).test(xDate) ||
new RegExp(
"^" + months + date_sep + "(([4-9]\\d)|(3[2-9]))$",
"i"
).test(xDate)
) {
result === null;
} else {
xDate = trimString(xDate);
result = matchDateFormat(xDate, xYear, ignoreTime); // DD MMMM, DD MMMM HH:mm:ss
if (!result) result = processDateString(xDate, xYear);
if (result) {
tz = result.tz;
if (result.type) type = result.type;
result = result.date;
if (tz && tz !== "Z") result = setTimezone(result, '', tz);
}
}
}
}
break;
}
if (result) {
if (!ignoreTime) {
if (((!tz && xTimezone) || yTimezone)) {
result = setTimezone(result, tz, xTimezone, yTimezone);
if (_.isNull(result)) return null;
tz = yTimezone ? yTimezone : xTimezone;
}
if (tz && tzList.indexOf(tz) === -1) {
timezoneOffset = tz ? tz : /Z$/.test(result) ? "Z" : "+00:00";
offsetNum = moment(result, "YYYY-MM-DD[T]HH:mm:ss.SSSZ").utcOffset(timezoneOffset).utcOffset() / 60;
zoneName = "";
} else if (tz && tzList.indexOf(tz) !== -1) {
timezoneOffset = moment(result).tz(tz).format("Z");
offsetNum = moment(result).tz(tz).utcOffset() / 60;
zoneName = tz;
} else if (!tz) {
if (/Z$/.test(result)) {
timezoneOffset = 'Z';
} else {
timezoneOffset = moment.parseZone(result).format("Z");
}
offsetNum = moment.parseZone(result).utcOffset() / 60;
zoneName = "";
}
} else if (type === 'date') {
result = result.split('T')[0];
}
}
if (result) {
if (!tz || tz.offset === "Z" || tz === "Z") {
if (type === 'date' && ignoreTime) {
if (!/^\d\d\d\d-\d\d-\d\d$/.test(result)) {
result = moment.parseZone(result, "YYYY-MM-DD[T]HH:mm:ss.SSSZ").format("YYYY-MM-DD");
isoWithFractSec = moment.parseZone(result, "YYYY-MM-DD[T]HH:mm:ss.SSSZ").format("YYYY-MM-DD[T]HH:mm:ss.SSS[Z]");
} else {
isoWithFractSec = result + "T00:00:00.000Z";
}
} else {
isoWithFractSec = result;
result = moment.parseZone(result, "YYYY-MM-DD[T]HH:mm:ss.SSSZ").format("YYYY-MM-DD[T]HH:mm:ss[Z]");
}
} else {
if (/^\d\d\d\d-\d\d-\d\d$/.test(result)) isoWithFractSec = result + "T00:00:00.000Z";
else isoWithFractSec = result;
if (ignoreTime && /^\d\d\d\d-\d\d-\d\d$/.test(result)) result = moment.parseZone(result, "YYYY-MM-DD[T]HH:mm:ss.SSSZ").format("YYYY-MM-DD");
else result = moment.parseZone(result, "YYYY-MM-DD[T]HH:mm:ss.SSSZ").format("YYYY-MM-DD[T]HH:mm:ssZ");
}
}
return {
date: result ? result : 'Invalid date',
isoWithFractSec,
timezoneOffset,
zoneName,
offsetNum,
};
}
getISODatetime({value, ignoreTime, inputTimezone, outputTimezone, assumedYear}){
return this.formatDatetime(value, ignoreTime, inputTimezone, outputTimezone, assumedYear);
}
formatTime(xTime, xTimezone) {
let result, time, timeWithFractSec, timezoneOffset, zoneName, offsetNum, tz;
switch (typeof xTime) {
case "string":
xTime = trimString(xTime);
if (TIMEZONES.test(xTime)) {
let value;
const matchZone = xTime.match(TIMEZONES);
tz = {
location: matchZone[0]
};
if (matchZone.index)
value =
xTime.slice(0, matchZone.index) +
xTime.slice(tz.location.length + matchZone.index);
else value = xTime.slice(tz.location.length);
value = trimString(value);
result = processTimeString(value, tz);
} else {
result = processTimeString(xTime, tz);
}
break;
default:
let hh, mm, ss;
hh = moment.duration(xTime).get("hours");
mm = moment.duration(xTime).get("minutes");
ss = moment.duration(xTime).get("seconds");
if (hh.toString().length === 1) hh = '0' + hh.toString();
if (mm.toString().length === 1) mm = '0' + mm.toString();
if (ss.toString().length === 1) ss = '0' + ss.toString();
result = {
time: hh + ":" + mm + ":" + ss + ".000"
}
}
if (result && result.time && result.time !== "Invalid date") {
time = result.time;
tz = result.tz;
if ((!tz || !tz.offset) && time.match(/(\+|-)\d{2}:\d{2}$/)) {
if (!tz) tz = {
offset: time.match(/(\+|-)\d{2}:\d{2}$/)[0]
};
else tz.offset = time.match(/(\+|-)\d{2}:\d{2}$/)[0];
}
if (!tz && xTimezone) {
if (tzList.indexOf(xTimezone) !== -1) {
tz = {
location: xTimezone
};
time = setTimezone(moment.parseZone(time, "HH:mm:ssZ"), '', tz.location);
zoneName = tz.location;
} else {
tz = {
offset: xTimezone
};
time = setTimezone(moment.parseZone(time, "HH:mm:ssZ"), '', tz.offset);
zoneName = "";
};
time = moment.parseZone(time);
timezoneOffset = time.clone().format("Z");
offsetNum = time.clone().utcOffset() / 60;
timeWithFractSec = time.format("HH:mm:ss.SSSZ");
time = time.format("HH:mm:ssZ");
} else if (tz) {
if (tz.location) {
time = setTimezone(moment.parseZone(time, "HH:mm:ssZ").format("YYYY-MM-DD[T]HH:mm:ssZ"), '', tz.location);
time = moment.parseZone(time);
timeWithFractSec = time.format("HH:mm:ss.SSSZ");
timezoneOffset = time.clone().format("Z");
offsetNum = time.clone().utcOffset() / 60;
time = time.format("HH:mm:ssZ");
zoneName = tz.location;
} else if (tz.offset) {
timeWithFractSec = time;
time = moment.parseZone(time, "HH:mm:ss.SSSZ");
if (tz.offset === "Z") time = time.format("HH:mm:ss") + "Z";
else time = time.format("HH:mm:ssZ");
timezoneOffset = tz.offset;
offsetNum = moment.parseZone(time, "HH:mm:ssZ").utcOffset() / 60;
zoneName = "";
}
} else {
timeWithFractSec = time + 'Z';
time = moment.parseZone(time, "HH:mm:ss.SSS");
time = time.format("HH:mm:ss[Z]");
timezoneOffset = "Z";
offsetNum = 0;
zoneName = "";
}
}
return {
time,
timezoneOffset,
timeWithFractSec,
zoneName,
offsetNum,
};
}
formatTimezone(inputValue, asObject) {
const tz_Offset = /^\+((((0?\d)|([1][0-4])):00$)|(((0\d)|([1][0-4])):?00$)|(((0?([3-6]|[9]))|[10]):?30$)|(0?[5,8]:?45$))|(^(-|−)((((0?\d)|([1][0-2])):00$)|(((0\d)|([1][0-2])):?00$)|(0?[2,3,9]:?30$)))/;
const OFFSET_IN_MIN = new RegExp(/(\+|-)((\d{3}\s?$)|(20)|(30)|(60)|(90))$/);
const UTC_OFFSET = new RegExp(/^UTC(((\+|-)((1[0-2])|(0?\d)))|(\+1[3,4]))$/);
let result, zoneName, match, sep, hh;
sep = /\+/.test(inputValue) ? "+" : "-";
match =
inputValue.match(UTC_OFFSET) ||
inputValue.match(tz_Offset) ||
inputValue.match(OFFSET_IN_MIN) ||
inputValue.match(new RegExp(HH_OFFSET_RANGE));
match = match ? match[0] : "";
if (inputValue === '+12:45') {
result = '+12:45';
} else if (UTC_OFFSET.test(inputValue)) {
match = match.match(new RegExp(HH_OFFSET_RANGE))[0];
match = match.match(/((1[0-2])|(0?\d))|(\+1[3,4])/)[0];
match = match.length === 1 ? "0" + match : match;
if (match[0] === sep) result = match + ":00";
else result = sep + match + ":00";
} else if (tzList.indexOf(inputValue) !== -1) {
result = moment().tz(inputValue).format("Z");
zoneName = inputValue;
} else if (tz_Offset.test(inputValue) && !/^(\+|-|−)\d\d\d$/.test(inputValue)) {
if (/:/.test(match)) {
match = match.split(":");
hh = match[0].match(/\d\d?/)[0];
hh = hh.length === 1 ? "0" + hh : hh;
result = sep + hh + ":" + match[1];
} else {
hh = match.slice(0, 3);
let mm = match.slice(3).length === 1 ? '0' + match.slice(3) : match.slice(3);
result = hh + ":" + mm;
}
} else if (OFFSET_IN_MIN.test(inputValue)) {
match = match.slice(1);
if ((_.inRange(match, 0, 841) && sep === '+') || (/(-|−)/.test(sep) && (Number(match) <= 720))) {
hh = _.toString(_.floor(match / 60));
let mm = match % 60;
hh = hh.length === 1 ? "0" + hh : hh;
if (mm !== 30 && mm !== 0 && mm !== 45) {
result = null;
} else {
if (mm === 0) mm = '00';
result = sep + hh + ':' + mm;
if (mm === 45 && !['+05:45', '+08:45', '+12:45'].includes(result)) result = null;
}
}
} else if (new RegExp("^(" + HH_OFFSET_RANGE + ")$").test(inputValue)) {
let hh = inputValue.match(/\d\d?/)[0];
hh = hh.length === 1 ? "0" + hh : hh;
result = sep + hh + ":00";
}
if (result && (tz_Offset.test(result) || ['+05:45', '+08:45', '+12:45'].includes(result))) {
if (asObject)
return {
name: zoneName,
offsetNum: moment().utcOffset(result).utcOffset() / 60,
offsetText: result,
};
else return result;
} else {
return null;
}
}
formatDuration(xDuration, addInfo) {
let result, asObject = null, humanized = '';
if (isNegative(xDuration)) {
console.warn('A negative number was received as duration value.');
return addInfo ? { asMilliseconds: null,
asObject: null,
humanized: null } : 'NaN';
}
switch (typeof xDuration) {
case "string":
if (
(DURATION_STR.test(xDuration) &&
!new RegExp(DURATION_ISO).test(xDuration)) ||
MMYYYY.test(xDuration)
) {
const processed = processDurationString(xDuration, addInfo);
result = processed.result,
asObject = processed.asObject;
} else {
if (addInfo && new RegExp(DURATION_ISO).test(xDuration)) {
asObject = getObjectDuration(xDuration.split('T'));
}
result = moment.duration(xDuration).asMilliseconds();
if (!result)
result = moment.duration(Number(xDuration)).asMilliseconds();
}
break;
default:
if (typeof xDuration === 'object') asObject = xDuration;
result = moment.duration(xDuration).asMilliseconds();
}
if (!addInfo) {
return result || result === 0 ? result : 'NaN';
} else {
if (!_.isNull(asObject)) Object.keys(asObject).forEach(key => {
humanized = humanized + ' ' + asObject[key] + ' ' + key;
asObject[key] = Number(asObject[key]);
});
return {
asMilliseconds: result,
asObject,
humanized: humanized ? humanized.trim() : null
}
}
}
} // end methods
const isNegative = value => {
switch(typeof value) {
case "string":
return value[0] === '-';
break;
case "number":
return value < 0;
break;
case "object":
return Object.keys(value).find(key => {
return Number(value[key]) < 0;
});
break;
}
}
// PROCESS TIME STRING
const processTimeString = (xTime, tz) => {
let result;