UNPKG

quasar

Version:

Build high-performance VueJS user interfaces (SPA, PWA, SSR, Mobile and Desktop) in record time

1,041 lines (876 loc) 23.7 kB
import { isDate } from '../is/is.js' import { pad, capitalize } from '../format/format.js' import { jalaaliMonthLength } from './private.persian.js' import Lang, { defaultLang } from '../../plugins/lang/Lang.js' const MILLISECONDS_IN_DAY = 86400000, MILLISECONDS_IN_HOUR = 3600000, MILLISECONDS_IN_MINUTE = 60000, defaultMask = 'YYYY-MM-DDTHH:mm:ss.SSSZ', token = /\[((?:[^\]\\]|\\]|\\)*)\]|do|d{1,4}|Mo|M{1,4}|m{1,2}|wo|w{1,2}|Qo|Do|DDDo|D{1,4}|YY(?:YY)?|H{1,2}|h{1,2}|s{1,2}|S{1,3}|Z{1,2}|a{1,2}|[AQExX]/g, reverseToken = /(\[[^\]]*\])|do|d{1,4}|Mo|M{1,4}|m{1,2}|wo|w{1,2}|Qo|Do|DDDo|D{1,4}|YY(?:YY)?|H{1,2}|h{1,2}|s{1,2}|S{1,3}|Z{1,2}|a{1,2}|[AQExX]|([.*+:?^,\s${}()|\\]+)/g, regexStore = {} function getRegexData(mask, dateLocale) { const days = '(' + dateLocale.days.join('|') + ')', key = mask + days if (regexStore[key] !== void 0) { return regexStore[key] } const daysShort = '(' + dateLocale.daysShort.join('|') + ')', months = '(' + dateLocale.months.join('|') + ')', monthsShort = '(' + dateLocale.monthsShort.join('|') + ')' const map = {} let index = 0 const regexText = mask.replace(reverseToken, match => { index++ switch (match) { case 'YY': map.YY = index return '(-?\\d{1,2})' case 'YYYY': map.YYYY = index return '(-?\\d{1,4})' case 'M': map.M = index return '(\\d{1,2})' case 'Mo': map.M = index++ // bumping to M return '(\\d{1,2}(st|nd|rd|th))' case 'MM': map.M = index // bumping to M return '(\\d{2})' case 'MMM': map.MMM = index return monthsShort case 'MMMM': map.MMMM = index return months case 'D': map.D = index return '(\\d{1,2})' case 'Do': map.D = index++ // bumping to D return '(\\d{1,2}(st|nd|rd|th))' case 'DD': map.D = index // bumping to D return '(\\d{2})' case 'H': map.H = index return '(\\d{1,2})' case 'HH': map.H = index // bumping to H return '(\\d{2})' case 'h': map.h = index return '(\\d{1,2})' case 'hh': map.h = index // bumping to h return '(\\d{2})' case 'm': map.m = index return '(\\d{1,2})' case 'mm': map.m = index // bumping to m return '(\\d{2})' case 's': map.s = index return '(\\d{1,2})' case 'ss': map.s = index // bumping to s return '(\\d{2})' case 'S': map.S = index return '(\\d{1})' case 'SS': map.S = index // bump to S return '(\\d{2})' case 'SSS': map.S = index // bump to S return '(\\d{3})' case 'A': map.A = index return '(AM|PM)' case 'a': map.a = index return '(am|pm)' case 'aa': map.aa = index return '(a\\.m\\.|p\\.m\\.)' case 'ddd': return daysShort case 'dddd': return days case 'Q': case 'd': case 'E': return '(\\d{1})' case 'do': index++ return '(\\d{1}(st|nd|rd|th))' case 'Qo': return '(1st|2nd|3rd|4th)' case 'DDD': case 'DDDD': return '(\\d{1,3})' case 'DDDo': index++ return '(\\d{1,3}(st|nd|rd|th))' case 'w': return '(\\d{1,2})' case 'wo': index++ return '(\\d{1,2}(st|nd|rd|th))' case 'ww': return '(\\d{2})' case 'Z': // to split: (?:(Z)()()|([+-])?(\\d{2}):?(\\d{2})) map.Z = index return '(Z|[+-]\\d{2}:\\d{2})' case 'ZZ': map.ZZ = index return '(Z|[+-]\\d{2}\\d{2})' case 'X': map.X = index return '(-?\\d+)' case 'x': map.x = index return '(-?\\d{4,})' default: index-- if (match[0] === '[') { match = match.substring(1, match.length - 1) } return match.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') } }) const res = { map, regex: new RegExp('^' + regexText) } regexStore[key] = res return res } function getDateLocale(paramDateLocale, langProps) { return paramDateLocale !== void 0 ? paramDateLocale : langProps !== void 0 ? langProps.date : defaultLang.date } function formatTimezone(offset, delimeter = '') { const sign = offset > 0 ? '-' : '+', absOffset = Math.abs(offset), hours = Math.floor(absOffset / 60), minutes = absOffset % 60 return sign + pad(hours) + delimeter + pad(minutes) } function applyYearMonthDayChange(date, mod, sign) { let year = date.getFullYear(), month = date.getMonth() const day = date.getDate() if (mod.year !== void 0) { year += sign * mod.year delete mod.year } if (mod.month !== void 0) { month += sign * mod.month delete mod.month } date.setDate(1) date.setMonth(2) date.setFullYear(year) date.setMonth(month) date.setDate(Math.min(day, daysInMonth(date))) if (mod.date !== void 0) { date.setDate(date.getDate() + sign * mod.date) delete mod.date } return date } function applyYearMonthDay(date, mod, middle) { const year = mod.year !== void 0 ? mod.year : date[`get${middle}FullYear`](), month = mod.month !== void 0 ? mod.month - 1 : date[`get${middle}Month`](), maxDay = new Date(year, month + 1, 0).getDate(), day = Math.min( maxDay, mod.date !== void 0 ? mod.date : date[`get${middle}Date`]() ) date[`set${middle}Date`](1) date[`set${middle}Month`](2) date[`set${middle}FullYear`](year) date[`set${middle}Month`](month) date[`set${middle}Date`](day) delete mod.year delete mod.month delete mod.date return date } function getChange(date, rawMod, sign) { const mod = normalizeMod(rawMod), d = new Date(date), t = mod.year !== void 0 || mod.month !== void 0 || mod.date !== void 0 ? applyYearMonthDayChange(d, mod, sign) // removes year/month/day : d for (const key in mod) { const op = capitalize(key) t[`set${op}`](t[`get${op}`]() + sign * mod[key]) } return t } function normalizeMod(mod) { const acc = { ...mod } if (mod.years !== void 0) { acc.year = mod.years delete acc.years } if (mod.months !== void 0) { acc.month = mod.months delete acc.months } if (mod.days !== void 0) { acc.date = mod.days delete acc.days } if (mod.day !== void 0) { acc.date = mod.day delete acc.day } if (mod.hour !== void 0) { acc.hours = mod.hour delete acc.hour } if (mod.minute !== void 0) { acc.minutes = mod.minute delete acc.minute } if (mod.second !== void 0) { acc.seconds = mod.second delete acc.second } if (mod.millisecond !== void 0) { acc.milliseconds = mod.millisecond delete acc.millisecond } return acc } export function adjustDate(date, rawMod, utc) { const mod = normalizeMod(rawMod), middle = utc === true ? 'UTC' : '', d = new Date(date), t = mod.year !== void 0 || mod.month !== void 0 || mod.date !== void 0 ? applyYearMonthDay(d, mod, middle) // removes year/month/day : d for (const key in mod) { const op = key.charAt(0).toUpperCase() + key.slice(1) t[`set${middle}${op}`](mod[key]) } return t } export function extractDate(str, mask, dateLocale) { const d = __splitDate(str, mask, dateLocale) const date = new Date( d.year, d.month === null ? null : d.month - 1, d.day === null ? 1 : d.day, d.hour, d.minute, d.second, d.millisecond ) const tzOffset = date.getTimezoneOffset() return d.timezoneOffset === null || d.timezoneOffset === tzOffset ? date : getChange(date, { minutes: d.timezoneOffset - tzOffset }, 1) } export function __splitDate(str, mask, dateLocale, calendar, defaultModel) { const date = { year: null, month: null, day: null, hour: null, minute: null, second: null, millisecond: null, timezoneOffset: null, dateHash: null, timeHash: null } if (defaultModel !== void 0) Object.assign(date, defaultModel) if (str === void 0 || str === null || str === '' || typeof str !== 'string') { return date } if (mask === void 0) { mask = defaultMask } const langOpts = getDateLocale(dateLocale, Lang.props), months = langOpts.months, monthsShort = langOpts.monthsShort const { regex, map } = getRegexData(mask, langOpts) const match = str.match(regex) if (match === null) { return date } let tzString = '' if (map.X !== void 0 || map.x !== void 0) { const stamp = parseInt(match[map.X !== void 0 ? map.X : map.x], 10) if (isNaN(stamp) === true || stamp < 0) { return date } const d = new Date(stamp * (map.X !== void 0 ? 1000 : 1)) date.year = d.getFullYear() date.month = d.getMonth() + 1 date.day = d.getDate() date.hour = d.getHours() date.minute = d.getMinutes() date.second = d.getSeconds() date.millisecond = d.getMilliseconds() } else { if (map.YYYY !== void 0) { date.year = parseInt(match[map.YYYY], 10) } else if (map.YY !== void 0) { const y = parseInt(match[map.YY], 10) date.year = y < 0 ? y : 2000 + y } if (map.M !== void 0) { date.month = parseInt(match[map.M], 10) if (date.month < 1 || date.month > 12) { return date } } else if (map.MMM !== void 0) { date.month = monthsShort.indexOf(match[map.MMM]) + 1 } else if (map.MMMM !== void 0) { date.month = months.indexOf(match[map.MMMM]) + 1 } if (map.D !== void 0) { date.day = parseInt(match[map.D], 10) if (date.year === null || date.month === null || date.day < 1) { return date } const maxDay = calendar !== 'persian' ? new Date(date.year, date.month, 0).getDate() : jalaaliMonthLength(date.year, date.month) if (date.day > maxDay) { return date } } if (map.H !== void 0) { date.hour = parseInt(match[map.H], 10) % 24 } else if (map.h !== void 0) { date.hour = parseInt(match[map.h], 10) % 12 if ( (map.A && match[map.A] === 'PM') || (map.a && match[map.a] === 'pm') || (map.aa && match[map.aa] === 'p.m.') ) { date.hour += 12 } date.hour = date.hour % 24 } if (map.m !== void 0) { date.minute = parseInt(match[map.m], 10) % 60 } if (map.s !== void 0) { date.second = parseInt(match[map.s], 10) % 60 } if (map.S !== void 0) { date.millisecond = parseInt(match[map.S], 10) * 10 ** (3 - match[map.S].length) } if (map.Z !== void 0 || map.ZZ !== void 0) { tzString = map.Z !== void 0 ? match[map.Z].replace(':', '') : match[map.ZZ] date.timezoneOffset = (tzString[0] === '+' ? -1 : 1) * (60 * tzString.slice(1, 3) + Number(tzString.slice(3, 5))) } } date.dateHash = pad(date.year, 4) + '/' + pad(date.month) + '/' + pad(date.day) date.timeHash = pad(date.hour) + ':' + pad(date.minute) + ':' + pad(date.second) + tzString return date } export function isValid(date) { return typeof date === 'number' ? true : isNaN(Date.parse(date)) === false } export function buildDate(mod, utc) { return adjustDate(new Date(), mod, utc) } export function getDayOfWeek(date) { const dow = new Date(date).getDay() return dow === 0 ? 7 : dow } export function getWeekOfYear(date) { // Remove time components of date const thursday = new Date(date.getFullYear(), date.getMonth(), date.getDate()) // Change date to Thursday same week thursday.setDate(thursday.getDate() - ((thursday.getDay() + 6) % 7) + 3) // Take January 4th as it is always in week 1 (see ISO 8601) const firstThursday = new Date(thursday.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 = thursday.getTimezoneOffset() - firstThursday.getTimezoneOffset() thursday.setHours(thursday.getHours() - ds) // Number of weeks between target Thursday and first Thursday const weekDiff = (thursday - firstThursday) / (MILLISECONDS_IN_DAY * 7) return 1 + Math.floor(weekDiff) } function getDayIdentifier(date) { return date.getFullYear() * 10000 + date.getMonth() * 100 + date.getDate() } function getDateIdentifier(date, onlyDate /* = false */) { const d = new Date(date) return onlyDate === true ? getDayIdentifier(d) : d.getTime() } export function isBetweenDates(date, from, to, opts = {}) { const d1 = getDateIdentifier(from, opts.onlyDate), d2 = getDateIdentifier(to, opts.onlyDate), cur = getDateIdentifier(date, opts.onlyDate) return ( (cur > d1 || (opts.inclusiveFrom === true && cur === d1)) && (cur < d2 || (opts.inclusiveTo === true && cur === d2)) ) } export function addToDate(date, mod) { return getChange(date, mod, 1) } export function subtractFromDate(date, mod) { return getChange(date, mod, -1) } export function startOfDate(date, unit, utc) { const t = new Date(date), prefix = `set${utc === true ? 'UTC' : ''}` switch (unit) { case 'year': case 'years': t[`${prefix}Month`](0) case 'month': case 'months': t[`${prefix}Date`](1) case 'day': case 'days': case 'date': t[`${prefix}Hours`](0) case 'hour': case 'hours': t[`${prefix}Minutes`](0) case 'minute': case 'minutes': t[`${prefix}Seconds`](0) case 'second': case 'seconds': t[`${prefix}Milliseconds`](0) } return t } export function endOfDate(date, unit, utc) { const t = new Date(date), prefix = `set${utc === true ? 'UTC' : ''}` switch (unit) { case 'year': case 'years': t[`${prefix}Month`](11) case 'month': case 'months': t[`${prefix}Date`](daysInMonth(t)) case 'day': case 'days': case 'date': t[`${prefix}Hours`](23) case 'hour': case 'hours': t[`${prefix}Minutes`](59) case 'minute': case 'minutes': t[`${prefix}Seconds`](59) case 'second': case 'seconds': t[`${prefix}Milliseconds`](999) } return t } export function getMaxDate(date /* , ...args */) { let t = new Date(date) Array.prototype.slice.call(arguments, 1).forEach(d => { t = Math.max(t, new Date(d)) }) return new Date(t) } export function getMinDate(date /*, ...args */) { let t = new Date(date) Array.prototype.slice.call(arguments, 1).forEach(d => { t = Math.min(t, new Date(d)) }) return new Date(t) } function getDiff(t, sub, interval) { return ( (t.getTime() - t.getTimezoneOffset() * MILLISECONDS_IN_MINUTE - (sub.getTime() - sub.getTimezoneOffset() * MILLISECONDS_IN_MINUTE)) / interval ) } export function getDateDiff(date, subtract, unit = 'days') { const t = new Date(date), sub = new Date(subtract) switch (unit) { case 'years': case 'year': return t.getFullYear() - sub.getFullYear() case 'months': case 'month': return ( (t.getFullYear() - sub.getFullYear()) * 12 + t.getMonth() - sub.getMonth() ) case 'days': case 'day': case 'date': return getDiff( startOfDate(t, 'day'), startOfDate(sub, 'day'), MILLISECONDS_IN_DAY ) case 'hours': case 'hour': return getDiff( startOfDate(t, 'hour'), startOfDate(sub, 'hour'), MILLISECONDS_IN_HOUR ) case 'minutes': case 'minute': return getDiff( startOfDate(t, 'minute'), startOfDate(sub, 'minute'), MILLISECONDS_IN_MINUTE ) case 'seconds': case 'second': return getDiff(startOfDate(t, 'second'), startOfDate(sub, 'second'), 1000) } } export function getDayOfYear(date) { return getDateDiff(date, startOfDate(date, 'year'), 'days') + 1 } export function inferDateFormat(date) { return isDate(date) === true ? 'date' : typeof date === 'number' ? 'number' : 'string' } export function getDateBetween(date, min, max) { const t = new Date(date) if (min) { const low = new Date(min) if (t < low) { return low } } if (max) { const high = new Date(max) if (t > high) { return high } } return t } export function isSameDate(date, date2, unit) { const t = new Date(date), d = new Date(date2) if (unit === void 0) { return t.getTime() === d.getTime() } switch (unit) { case 'second': case 'seconds': if (t.getSeconds() !== d.getSeconds()) { return false } case 'minute': // intentional fall-through case 'minutes': if (t.getMinutes() !== d.getMinutes()) { return false } case 'hour': // intentional fall-through case 'hours': if (t.getHours() !== d.getHours()) { return false } case 'day': // intentional fall-through case 'days': case 'date': if (t.getDate() !== d.getDate()) { return false } case 'month': // intentional fall-through case 'months': if (t.getMonth() !== d.getMonth()) { return false } case 'year': // intentional fall-through case 'years': if (t.getFullYear() !== d.getFullYear()) { return false } break default: throw new Error(`date isSameDate unknown unit ${unit}`) } return true } export function daysInMonth(date) { return new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate() } function getOrdinal(n) { if (n >= 11 && n <= 13) { return `${n}th` } switch (n % 10) { case 1: return `${n}st` case 2: return `${n}nd` case 3: return `${n}rd` } return `${n}th` } const formatter = { // Year: 00, 01, ..., 99 YY(date, dateLocale, forcedYear) { // workaround for < 1900 with new Date() const y = this.YYYY(date, dateLocale, forcedYear) % 100 // oxlint-disable-line new-cap return y >= 0 ? pad(y) : '-' + pad(Math.abs(y)) }, // Year: 1900, 1901, ..., 2099 YYYY(date, _dateLocale, forcedYear) { // workaround for < 1900 with new Date() return forcedYear !== void 0 && forcedYear !== null ? forcedYear : date.getFullYear() }, // Month: 1, 2, ..., 12 M(date) { return date.getMonth() + 1 }, // Month: 1st, 2nd, ..., 12th Mo(date) { return getOrdinal(date.getMonth() + 1) }, // Month: 01, 02, ..., 12 MM(date) { return pad(date.getMonth() + 1) }, // Month Short Name: Jan, Feb, ... MMM(date, dateLocale) { return dateLocale.monthsShort[date.getMonth()] }, // Month Name: January, February, ... MMMM(date, dateLocale) { return dateLocale.months[date.getMonth()] }, // Quarter: 1, 2, 3, 4 Q(date) { return Math.ceil((date.getMonth() + 1) / 3) }, // Quarter: 1st, 2nd, 3rd, 4th Qo(date) { return getOrdinal(this.Q(date)) // oxlint-disable-line new-cap }, // Day of month: 1, 2, ..., 31 D(date) { return date.getDate() }, // Day of month: 1st, 2nd, ..., 31st Do(date) { return getOrdinal(date.getDate()) }, // Day of month: 01, 02, ..., 31 DD(date) { return pad(date.getDate()) }, // Day of year: 1, 2, ..., 366 DDD(date) { return getDayOfYear(date) }, // Day of year: 1st, 2nd, ..., 366th DDDo(date) { return getOrdinal(getDayOfYear(date)) }, // Day of year: 001, 002, ..., 366 DDDD(date) { return pad(getDayOfYear(date), 3) }, // Day of week: 0, 1, ..., 6 d(date) { return date.getDay() }, // Day of week: 0th, 1st, ..., 6th do(date) { return getOrdinal(date.getDay()) }, // Day of week: Su, Mo, ... dd(date, dateLocale) { return dateLocale.days[date.getDay()].slice(0, 2) }, // Day of week: Sun, Mon, ... ddd(date, dateLocale) { return dateLocale.daysShort[date.getDay()] }, // Day of week: Sunday, Monday, ... dddd(date, dateLocale) { return dateLocale.days[date.getDay()] }, // Day of ISO week: 1, 2, ..., 7 E(date) { return date.getDay() || 7 }, // Week of Year: 1 2 ... 52 53 w(date) { return getWeekOfYear(date) }, // Week of Year: 1st 2nd ... 52nd 53rd wo(date) { return getOrdinal(getWeekOfYear(date)) }, // Week of Year: 01 02 ... 52 53 ww(date) { return pad(getWeekOfYear(date)) }, // Hour: 0, 1, ... 23 H(date) { return date.getHours() }, // Hour: 00, 01, ..., 23 HH(date) { return pad(date.getHours()) }, // Hour: 1, 2, ..., 12 h(date) { const hours = date.getHours() return hours === 0 ? 12 : hours > 12 ? hours % 12 : hours }, // Hour: 01, 02, ..., 12 hh(date) { return pad(this.h(date)) }, // Minute: 0, 1, ..., 59 m(date) { return date.getMinutes() }, // Minute: 00, 01, ..., 59 mm(date) { return pad(date.getMinutes()) }, // Second: 0, 1, ..., 59 s(date) { return date.getSeconds() }, // Second: 00, 01, ..., 59 ss(date) { return pad(date.getSeconds()) }, // 1/10 of second: 0, 1, ..., 9 S(date) { return Math.floor(date.getMilliseconds() / 100) }, // 1/100 of second: 00, 01, ..., 99 SS(date) { return pad(Math.floor(date.getMilliseconds() / 10)) }, // Millisecond: 000, 001, ..., 999 SSS(date) { return pad(date.getMilliseconds(), 3) }, // Meridiem: AM, PM A(date) { return date.getHours() < 12 ? 'AM' : 'PM' }, // Meridiem: am, pm a(date) { return date.getHours() < 12 ? 'am' : 'pm' }, // Meridiem: a.m., p.m. aa(date) { return date.getHours() < 12 ? 'a.m.' : 'p.m.' }, // Timezone: -01:00, +00:00, ... +12:00 Z(date, _dateLocale, _forcedYear, forcedTimezoneOffset) { const tzOffset = forcedTimezoneOffset === void 0 || forcedTimezoneOffset === null ? date.getTimezoneOffset() : forcedTimezoneOffset return formatTimezone(tzOffset, ':') }, // Timezone: -0100, +0000, ... +1200 ZZ(date, _dateLocale, _forcedYear, forcedTimezoneOffset) { const tzOffset = forcedTimezoneOffset === void 0 || forcedTimezoneOffset === null ? date.getTimezoneOffset() : forcedTimezoneOffset return formatTimezone(tzOffset) }, // Seconds timestamp: 512969520 X(date) { return Math.floor(date.getTime() / 1000) }, // Milliseconds timestamp: 512969520900 x(date) { return date.getTime() } } export function formatDate( val, mask, dateLocale, __forcedYear, __forcedTimezoneOffset ) { if ((val !== 0 && !val) || val === Infinity || val === -Infinity) return const date = new Date(val) if (isNaN(date)) return if (mask === void 0) { mask = defaultMask } const locale = getDateLocale(dateLocale, Lang.props) return mask.replace(token, (match, text) => match in formatter ? formatter[match](date, locale, __forcedYear, __forcedTimezoneOffset) : text === void 0 ? match : text.split('\\]').join(']') ) } export function clone(date) { return isDate(date) === true ? new Date(date.getTime()) : date } export default { isValid, extractDate, buildDate, getDayOfWeek, getWeekOfYear, isBetweenDates, addToDate, subtractFromDate, adjustDate, startOfDate, endOfDate, getMaxDate, getMinDate, getDateDiff, getDayOfYear, inferDateFormat, getDateBetween, isSameDate, daysInMonth, formatDate, clone }