UNPKG

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