UNPKG

luxon

Version:
210 lines (181 loc) 5.85 kB
import { Duration } from '../duration'; import { DateTime } from '../datetime'; import { Zone } from '../zone'; import { LocalZone } from '../zones/localZone'; import { IANAZone } from '../zones/IANAZone'; import { FixedOffsetZone } from '../zones/fixedOffsetZone'; import { Settings } from '../settings'; import { InvalidArgumentError } from '../errors'; /** * @private */ export class Util { static friendlyDuration(duration) { if (Util.isNumber(duration)) { return Duration.fromMillis(duration); } else if (duration instanceof Duration) { return duration; } else if (duration instanceof Object) { return Duration.fromObject(duration); } else { throw new InvalidArgumentError('Unknown duration argument'); } } static friendlyDateTime(dateTimeish) { if (dateTimeish instanceof DateTime) { return dateTimeish; } else if (dateTimeish.valueOf && Util.isNumber(dateTimeish.valueOf())) { return DateTime.fromJSDate(dateTimeish); } else if (dateTimeish instanceof Object) { return DateTime.fromObject(dateTimeish); } else { throw new InvalidArgumentError('Unknown datetime argument'); } } static maybeArray(thing) { return Array.isArray(thing) ? thing : [thing]; } static isUndefined(o) { return typeof o === 'undefined'; } static isNumber(o) { return typeof o === 'number'; } static isString(o) { return typeof o === 'string'; } static isDate(o) { return Object.prototype.toString.call(o) === '[object Date]'; } static numberBetween(thing, bottom, top) { return Util.isNumber(thing) && thing >= bottom && thing <= top; } static pad(input, n = 2) { return ('0'.repeat(n) + input).slice(-n); } static towardZero(input) { return input < 0 ? Math.ceil(input) : Math.floor(input); } // DateTime -> JS date such that the date's UTC time is the datetimes's local time static asIfUTC(dt) { const ts = dt.ts - dt.offset; return new Date(ts); } // http://stackoverflow.com/a/15030117 static flatten(arr) { return arr.reduce( (flat, toFlatten) => flat.concat(Array.isArray(toFlatten) ? Util.flatten(toFlatten) : toFlatten), [] ); } static bestBy(arr, by, compare) { return arr.reduce((best, next) => { const pair = [by(next), next]; if (!best) { return pair; } else if (compare.apply(null, [best[0], pair[0]]) === best[0]) { return best; } else { return pair; } }, null)[1]; } static pick(obj, keys) { return keys.reduce((a, k) => { a[k] = obj[k]; return a; }, {}); } static isLeapYear(year) { return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0); } static daysInYear(year) { return Util.isLeapYear(year) ? 366 : 365; } static daysInMonth(year, month) { if (month === 2) { return Util.isLeapYear(year) ? 29 : 28; } else { return [31, null, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month - 1]; } } static parseZoneInfo(ts, offsetFormat, locale, timeZone = null) { const date = new Date(ts), intl = { hour12: false, // avoid AM/PM year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' }; if (timeZone) { intl.timeZone = timeZone; } const modified = Object.assign({ timeZoneName: offsetFormat }, intl); if (Intl.DateTimeFormat.prototype.formatToParts) { const parsed = new Intl.DateTimeFormat(locale, modified) .formatToParts(date) .find(m => m.type.toLowerCase() === 'timezonename'); return parsed ? parsed.value : null; } else { // this probably doesn't work for all locales const without = new Intl.DateTimeFormat(locale, intl).format(date), included = new Intl.DateTimeFormat(locale, modified).format(date), diffed = included.substring(without.length), trimmed = diffed.replace(/^[, ]+/, ''); return trimmed; } } static normalizeZone(input) { if (input === null) { return LocalZone.instance; } else if (input instanceof Zone) { return input; } else if (Util.isString(input)) { const lowered = input.toLowerCase(); if (lowered === 'local') return LocalZone.instance; else if (lowered === 'utc') return FixedOffsetZone.utcInstance; else if (IANAZone.isValidSpecier(lowered)) return new IANAZone(input); else return FixedOffsetZone.parseSpecifier(lowered) || Settings.defaultZone; } else if (Util.isNumber(input)) { return FixedOffsetZone.instance(input); } else if (typeof input === 'object' && input.offset) { // This is dumb, but the instanceof check above doesn't seem to really work // so we're duck checking it return input; } else { return Settings.defaultZone; } } static normalizeObject(obj, normalizer, ignoreUnknown = false) { const normalized = {}; for (const u in obj) { if (obj.hasOwnProperty(u)) { const v = obj[u]; if (v !== null && !Util.isUndefined(v) && !Number.isNaN(v)) { const mapped = normalizer(u, ignoreUnknown); if (mapped) { normalized[mapped] = v; } } } } return normalized; } static timeObject(obj) { return Util.pick(obj, ['hour', 'minute', 'second', 'millisecond']); } static untrucateYear(year) { return year > 60 ? 1900 + year : 2000 + year; } // signedOffset('-5', '30') -> -330 static signedOffset(offHourStr, offMinuteStr) { const offHour = parseInt(offHourStr, 10) || 0, offMin = parseInt(offMinuteStr, 10) || 0, offMinSigned = offHour < 0 ? -offMin : offMin; return offHour * 60 + offMinSigned; } }