import PowerduckState from '../../app/powerduck-state'; const _isInteger = (val: any) => { const digits = '1234567890'; for (let i = 0; i < val.length; i++) { if (!digits.includes(val.charAt(i))) { return false; } } return true; }; const _getInt = ( str: string, i: number, minlength: number, maxlength: number, ): string => { for (let x = maxlength; x >= minlength; x--) { const token = str.substring(i, i + x); if (token.length < minlength) { return null; } if (_isInteger(token)) { return token; } } return null; }; export default class DateUtils { static formatDate(date: Temporal.PlainDateTime | number, format: string): string { if (date == null) { return null; } if (typeof date === 'number') { date = Temporal.Instant.fromEpochMilliseconds(date as number).toZonedDateTimeISO('UTC').toPlainDateTime(); } format = `${format}`; let result = ''; let i_format = 0; let c = ''; let token = ''; let y = `${date.year}`; const M = date.month; const d = date.day; const H = date.hour; const m = date.minute; const s = date.second; const LZ = x => (x < 0 || x > 9 ? '' : '0') + x; const locale = PowerduckState.getCurrentLanguage(); // Convert real date parts into formatted versions const value: any = new Object(); if (y.length < 4) { y = `${(y) - 0 + 1900}`; } value.y = `${y}`; value.yyyy = y; value.yy = y.substring(2, 4); value.M = M; value.MM = LZ(M); value.MMM = date.toLocaleString(locale, { month: 'short', timeZone: 'UTC' }); value.MMMM = date.toLocaleString(locale, { month: 'long', timeZone: 'UTC' }); value.d = d; value.dd = LZ(d); value.E = date.toLocaleString(locale, { weekday: 'short', timeZone: 'UTC' }); value.EEEE = date.toLocaleString(locale, { weekday: 'long', timeZone: 'UTC' }); value.H = H; value.HH = LZ(H); if (H == 0) { value.h = 12; } else if (H > 12) { value.h = H - 12; } else { value.h = H; } value.hh = LZ(value.h); if (H > 11) { value.K = H - 12; } else { value.K = H; } value.k = H + 1; value.KK = LZ(value.K); value.kk = LZ(value.k); if (H > 11) { value.a = 'PM'; } else { value.a = 'AM'; } value.m = m; value.mm = LZ(m); value.s = s; value.ss = LZ(s); while (i_format < format.length) { c = format.charAt(i_format); token = ''; while ((format.charAt(i_format) == c) && (i_format < format.length)) { token += format.charAt(i_format++); } if (value[token] != null) { result = result + value[token]; } else { result = result + token; } } return result; }; static getTemporalFromFormat(val: string, format: string): Temporal.PlainDateTime { if (val == null) { return null; } // In case somehow the date is already a datewrapper if ((val as any)._dte != null) { return (val as any); } val = `${val}`; format = `${format}`; let i_val = 0; let i_format = 0; let c = ''; let token = ''; let x, y; // eslint-disable-next-line no-restricted-syntax const now = new Date(); let year: any = now.getFullYear(); let month: any = now.getMonth() + 1; let date: any = 1; let hh: any = now.getHours(); let mm: any = now.getMinutes(); let ss: any = now.getSeconds(); let ampm = ''; const _isValidChar = (char: string): boolean => /[a-záčďéíĺľńóôŕšťúýž]/i.test(char); const locale = PowerduckState.getCurrentLanguage(); const getMonthFromName = (monthName: string, isShort: boolean = false): number => { // eslint-disable-next-line no-restricted-syntax const date = new Date( 2022, 0, 1, ); for (let monthIndex = 0; monthIndex < 12; monthIndex++) { date.setMonth(monthIndex); const name = date.toLocaleDateString(locale, { month: isShort ? 'short' : 'long', timeZone: 'UTC', }).toLowerCase(); if (name == monthName.toLowerCase()) { return monthIndex; } } return null; }; const getDayFromName = (dayName: string, isShort: boolean = false): number => { // eslint-disable-next-line no-restricted-syntax const date = new Date( 2022, 0, 2, ); for (let dayIndex = 0; dayIndex < 7; dayIndex++) { date.setDate(2 + dayIndex); const name = date.toLocaleDateString(locale, { weekday: isShort ? 'short' : 'long', timeZone: 'UTC', }).toLowerCase(); if (name == dayName.toLowerCase()) { return date.getDay(); } } return null; }; while (i_format < format.length) { // Get next token from format string c = format.charAt(i_format); token = ''; while ((format.charAt(i_format) == c) && (i_format < format.length)) { token += format.charAt(i_format++); } // Extract contents of value based on format token if (token == 'yyyy' || token == 'yy' || token == 'y') { if (token == 'yyyy') { x = 4; y = 4; } if (token == 'yy') { x = 2; y = 2; } if (token == 'y') { x = 2; y = 4; } year = _getInt( val, i_val, x, y, ); if (year == null) { return null; } i_val += year.length; if (year.length == 2) { if (year > 70) { year = 1900 + (year - 0); } else { year = 2000 + (year - 0); } } } else if (token == 'MM' || token == 'M') { month = _getInt( val, i_val, token.length, 2, ); if (month == null || (month < 1) || (month > 12)) { return null; } i_val += month.length; } else if (token == 'MMMM') { let monthName = ''; let tmpIndex = i_val; while (tmpIndex < val.length) { const char = val.charAt(tmpIndex); if (_isValidChar(char)) { monthName += char; tmpIndex++; } else { break; } } month = getMonthFromName(monthName, false); if (month == null) { return null; } i_val = tmpIndex; } else if (token == 'MMM') { let monthName = ''; let tempIndex = i_val; while (tempIndex < val.length && (tempIndex - i_val) < 4) { const char = val.charAt(tempIndex); if (_isValidChar(char)) { monthName += char; tempIndex++; } else { break; } } month = getMonthFromName(monthName, true); if (month == null) { return null; } i_val = tempIndex; } else if (token == 'dd' || token == 'd') { date = _getInt( val, i_val, token.length, 2, ); if (date == null || (date < 1) || (date > 31)) { return null; } i_val += date.length; } else if (token == 'EEEE') { let dayName = ''; let tempIndex = i_val; while (tempIndex < val.length) { const char = val.charAt(tempIndex); if (_isValidChar(char)) { dayName += char; tempIndex++; } else { break; } } const dayIndex = getDayFromName(dayName, false); if (dayIndex == null) { return null; } i_val = tempIndex; } else if (token == 'E') { let dayName = ''; let tempIndex = i_val; while (tempIndex < val.length && (tempIndex - i_val) < 4) { const char = val.charAt(tempIndex); if (_isValidChar(char)) { dayName += char; tempIndex++; } else { break; } } const dayIndex = getDayFromName(dayName, true); if (dayIndex == null) { return null; } i_val = tempIndex; } else if (token == 'hh' || token == 'h') { hh = _getInt( val, i_val, token.length, 2, ); if (hh == null || (hh < 1) || (hh > 12)) { return null; } i_val += hh.length; } else if (token == 'HH' || token == 'H') { hh = _getInt( val, i_val, token.length, 2, ); if (hh == null || (hh < 0) || (hh > 23)) { return null; } i_val += hh.length; } else if (token == 'KK' || token == 'K') { hh = _getInt( val, i_val, token.length, 2, ); if (hh == null || (hh < 0) || (hh > 11)) { return null; } i_val += hh.length; } else if (token == 'kk' || token == 'k') { hh = _getInt( val, i_val, token.length, 2, ); if (hh == null || (hh < 1) || (hh > 24)) { return null; } i_val += hh.length; hh--; } else if (token == 'mm' || token == 'm') { mm = _getInt( val, i_val, token.length, 2, ); if (mm == null || (mm < 0) || (mm > 59)) { return null; } i_val += mm.length; } else if (token == 'ss' || token == 's') { ss = _getInt( val, i_val, token.length, 2, ); if (ss == null || (ss < 0) || (ss > 59)) { return null; } i_val += ss.length; } else if (token == 'a') { if (val.substring(i_val, i_val + 2).toLowerCase() == 'am') { ampm = 'AM'; } else if (val.substring(i_val, i_val + 2).toLowerCase() == 'pm') { ampm = 'PM'; } else { return null; } i_val += 2; } else { if (val.substring(i_val, i_val + token.length) != token) { return null; } else { i_val += token.length; } } } // If there are any trailing characters left in the value, it doesn't match if (i_val != val.length) { return null; } // Is date valid for month? if (month == 2) { // Check for leap year if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0)) { // leap year if (date > 29) { return null; } } else { if (date > 28) { return null; } } } if ((month == 4) || (month == 6) || (month == 9) || (month == 11)) { if (date > 30) { return null; } } // Correct hours value if (hh < 12 && ampm == 'PM') { hh = hh - 0 + 12; } else if (hh > 11 && ampm == 'AM') { hh -= 12; } return Temporal.PlainDateTime.from({ year: Number(year), month: Number(month), day: Number(date), hour: Number(hh), minute: Number(mm), second: Number(ss), }); } /** * Check whether the date is valid * @param date Date to check * @returns True if the date is valid, false otherwise */ static isValidDate(date: Temporal.PlainDateTime | string): boolean { if (date == null) { return false; } if (typeof date === 'string') { try { Temporal.PlainDateTime.from(date); return true; } catch { return false; } } return date instanceof Temporal.PlainDateTime; } /** * Checks whether provided dates are overlapping * @param dateFrom1 * @param dateTo1 * @param dateFrom2 * @param dateTo2 * @returns True if the dates are overlapping, false otherwise */ static isOverlapping( dateFrom1: Temporal.PlainDateTime, dateTo1: Temporal.PlainDateTime, dateFrom2: Temporal.PlainDateTime, dateTo2: Temporal.PlainDateTime, ignoreTime = false, ): boolean { if (!this.isValidDate(dateFrom1) || !this.isValidDate(dateTo1) || !this.isValidDate(dateFrom2) || !this.isValidDate(dateTo2)) { return false; } if (ignoreTime) { const d1From = dateFrom1.toPlainDate(); const d1To = dateTo1.toPlainDate(); const d2From = dateFrom2.toPlainDate(); const d2To = dateTo2.toPlainDate(); return Temporal.PlainDate.compare(d1From, d2To) <= 0 && Temporal.PlainDate.compare(d2From, d1To) <= 0; } return Temporal.PlainDateTime.compare(dateFrom1, dateTo2) <= 0 && Temporal.PlainDateTime.compare(dateFrom2, dateTo1) <= 0; } }