1;!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="5f354246-f192-44db-93f8-16258885782d",e._sentryDebugIdIdentifier="sentry-dbid-5f354246-f192-44db-93f8-16258885782d")}catch(e){}}(); 2 3 var _global = 4 typeof window !== 'undefined' ? 5 window : 6 typeof global !== 'undefined' ? 7 global : 8 typeof globalThis !== 'undefined' ? 9 globalThis : 10 typeof self !== 'undefined' ? 11 self : 12 {}; 13 14 _global.SENTRY_RELEASE={id:"bf8ecb3987fc28ef4f4fa70826513d8bf936c9ad"}; 15"use strict"; 16(self["webpackChunk_xapp_oc_studio"] = self["webpackChunk_xapp_oc_studio"] || []).push([["vendors-node_modules_luxon_build_node_luxon_js"],{ 17 18/***/ "../../node_modules/luxon/build/node/luxon.js" 19/*!****************************************************!*\ 20 !*** ../../node_modules/luxon/build/node/luxon.js ***! 21 \****************************************************/ 22(__unused_webpack_module, exports) { 23 24 25 26Object.defineProperty(exports, "__esModule", ({ value: true })
vendor: 36,527 bytes, lines 26-1469
26); 27 28// these aren't really private, but nor are they really useful to document 29 30/** 31 * @private 32 */ 33class LuxonError extends Error {} 34 35/** 36 * @private 37 */ 38class InvalidDateTimeError extends LuxonError { 39 constructor(reason) { 40 super(`Invalid DateTime: ${reason.toMessage()}`); 41 } 42} 43 44/** 45 * @private 46 */ 47class InvalidIntervalError extends LuxonError { 48 constructor(reason) { 49 super(`Invalid Interval: ${reason.toMessage()}`); 50 } 51} 52 53/** 54 * @private 55 */ 56class InvalidDurationError extends LuxonError { 57 constructor(reason) { 58 super(`Invalid Duration: ${reason.toMessage()}`); 59 } 60} 61 62/** 63 * @private 64 */ 65class ConflictingSpecificationError extends LuxonError {} 66 67/** 68 * @private 69 */ 70class InvalidUnitError extends LuxonError { 71 constructor(unit) { 72 super(`Invalid unit ${unit}`); 73 } 74} 75 76/** 77 * @private 78 */ 79class InvalidArgumentError extends LuxonError {} 80 81/** 82 * @private 83 */ 84class ZoneIsAbstractError extends LuxonError { 85 constructor() { 86 super("Zone is an abstract class"); 87 } 88} 89 90/** 91 * @private 92 */ 93 94const n = "numeric", 95 s = "short", 96 l = "long"; 97const DATE_SHORT = { 98 year: n, 99 month: n, 100 day: n 101}; 102const DATE_MED = { 103 year: n, 104 month: s, 105 day: n 106}; 107const DATE_MED_WITH_WEEKDAY = { 108 year: n, 109 month: s, 110 day: n, 111 weekday: s 112}; 113const DATE_FULL = { 114 year: n, 115 month: l, 116 day: n 117}; 118const DATE_HUGE = { 119 year: n, 120 month: l, 121 day: n, 122 weekday: l 123}; 124const TIME_SIMPLE = { 125 hour: n, 126 minute: n 127}; 128const TIME_WITH_SECONDS = { 129 hour: n, 130 minute: n, 131 second: n 132}; 133const TIME_WITH_SHORT_OFFSET = { 134 hour: n, 135 minute: n, 136 second: n, 137 timeZoneName: s 138}; 139const TIME_WITH_LONG_OFFSET = { 140 hour: n, 141 minute: n, 142 second: n, 143 timeZoneName: l 144}; 145const TIME_24_SIMPLE = { 146 hour: n, 147 minute: n, 148 hourCycle: "h23" 149}; 150const TIME_24_WITH_SECONDS = { 151 hour: n, 152 minute: n, 153 second: n, 154 hourCycle: "h23" 155}; 156const TIME_24_WITH_SHORT_OFFSET = { 157 hour: n, 158 minute: n, 159 second: n, 160 hourCycle: "h23", 161 timeZoneName: s 162}; 163const TIME_24_WITH_LONG_OFFSET = { 164 hour: n, 165 minute: n, 166 second: n, 167 hourCycle: "h23", 168 timeZoneName: l 169}; 170const DATETIME_SHORT = { 171 year: n, 172 month: n, 173 day: n, 174 hour: n, 175 minute: n 176}; 177const DATETIME_SHORT_WITH_SECONDS = { 178 year: n, 179 month: n, 180 day: n, 181 hour: n, 182 minute: n, 183 second: n 184}; 185const DATETIME_MED = { 186 year: n, 187 month: s, 188 day: n, 189 hour: n, 190 minute: n 191}; 192const DATETIME_MED_WITH_SECONDS = { 193 year: n, 194 month: s, 195 day: n, 196 hour: n, 197 minute: n, 198 second: n 199}; 200const DATETIME_MED_WITH_WEEKDAY = { 201 year: n, 202 month: s, 203 day: n, 204 weekday: s, 205 hour: n, 206 minute: n 207}; 208const DATETIME_FULL = { 209 year: n, 210 month: l, 211 day: n, 212 hour: n, 213 minute: n, 214 timeZoneName: s 215}; 216const DATETIME_FULL_WITH_SECONDS = { 217 year: n, 218 month: l, 219 day: n, 220 hour: n, 221 minute: n, 222 second: n, 223 timeZoneName: s 224}; 225const DATETIME_HUGE = { 226 year: n, 227 month: l, 228 day: n, 229 weekday: l, 230 hour: n, 231 minute: n, 232 timeZoneName: l 233}; 234const DATETIME_HUGE_WITH_SECONDS = { 235 year: n, 236 month: l, 237 day: n, 238 weekday: l, 239 hour: n, 240 minute: n, 241 second: n, 242 timeZoneName: l 243}; 244 245/** 246 * @interface 247 */ 248class Zone { 249 /** 250 * The type of zone 251 * @abstract 252 * @type {string} 253 */ 254 get type() { 255 throw new ZoneIsAbstractError(); 256 } 257 258 /** 259 * The name of this zone. 260 * @abstract 261 * @type {string} 262 */ 263 get name() { 264 throw new ZoneIsAbstractError(); 265 } 266 267 /** 268 * The IANA name of this zone. 269 * Defaults to `name` if not overwritten by a subclass. 270 * @abstract 271 * @type {string} 272 */ 273 get ianaName() { 274 return this.name; 275 } 276 277 /** 278 * Returns whether the offset is known to be fixed for the whole year. 279 * @abstract 280 * @type {boolean} 281 */ 282 get isUniversal() { 283 throw new ZoneIsAbstractError(); 284 } 285 286 /** 287 * Returns the offset's common name (such as EST) at the specified timestamp 288 * @abstract 289 * @param {number} ts - Epoch milliseconds for which to get the name 290 * @param {Object} opts - Options to affect the format 291 * @param {string} opts.format - What style of offset to return. Accepts 'long' or 'short'. 292 * @param {string} opts.locale - What locale to return the offset name in. 293 * @return {string} 294 */ 295 offsetName(ts, opts) { 296 throw new ZoneIsAbstractError(); 297 } 298 299 /** 300 * Returns the offset's value as a string 301 * @abstract 302 * @param {number} ts - Epoch milliseconds for which to get the offset 303 * @param {string} format - What style of offset to return. 304 * Accepts 'narrow', 'short', or 'techie'. Returning '+6', '+06:00', or '+0600' respectively 305 * @return {string} 306 */ 307 formatOffset(ts, format) { 308 throw new ZoneIsAbstractError(); 309 } 310 311 /** 312 * Return the offset in minutes for this zone at the specified timestamp. 313 * @abstract 314 * @param {number} ts - Epoch milliseconds for which to compute the offset 315 * @return {number} 316 */ 317 offset(ts) { 318 throw new ZoneIsAbstractError(); 319 } 320 321 /** 322 * Return whether this Zone is equal to another zone 323 * @abstract 324 * @param {Zone} otherZone - the zone to compare 325 * @return {boolean} 326 */ 327 equals(otherZone) { 328 throw new ZoneIsAbstractError(); 329 } 330 331 /** 332 * Return whether this Zone is valid. 333 * @abstract 334 * @type {boolean} 335 */ 336 get isValid() { 337 throw new ZoneIsAbstractError(); 338 } 339} 340 341let singleton$1 = null; 342 343/** 344 * Represents the local zone for this JavaScript environment. 345 * @implements {Zone} 346 */ 347class SystemZone extends Zone { 348 /** 349 * Get a singleton instance of the local zone 350 * @return {SystemZone} 351 */ 352 static get instance() { 353 if (singleton$1 === null) { 354 singleton$1 = new SystemZone(); 355 } 356 return singleton$1; 357 } 358 359 /** @override **/ 360 get type() { 361 return "system"; 362 } 363 364 /** @override **/ 365 get name() { 366 return new Intl.DateTimeFormat().resolvedOptions().timeZone; 367 } 368 369 /** @override **/ 370 get isUniversal() { 371 return false; 372 } 373 374 /** @override **/ 375 offsetName(ts, { 376 format, 377 locale 378 }) { 379 return parseZoneInfo(ts, format, locale); 380 } 381 382 /** @override **/ 383 formatOffset(ts, format) { 384 return formatOffset(this.offset(ts), format); 385 } 386 387 /** @override **/ 388 offset(ts) { 389 return -new Date(ts).getTimezoneOffset(); 390 } 391 392 /** @override **/ 393 equals(otherZone) { 394 return otherZone.type === "system"; 395 } 396 397 /** @override **/ 398 get isValid() { 399 return true; 400 } 401} 402 403const dtfCache = new Map(); 404function makeDTF(zoneName) { 405 let dtf = dtfCache.get(zoneName); 406 if (dtf === undefined) { 407 dtf = new Intl.DateTimeFormat("en-US", { 408 hour12: false, 409 timeZone: zoneName, 410 year: "numeric", 411 month: "2-digit", 412 day: "2-digit", 413 hour: "2-digit", 414 minute: "2-digit", 415 second: "2-digit", 416 era: "short" 417 }); 418 dtfCache.set(zoneName, dtf); 419 } 420 return dtf; 421} 422const typeToPos = { 423 year: 0, 424 month: 1, 425 day: 2, 426 era: 3, 427 hour: 4, 428 minute: 5, 429 second: 6 430}; 431function hackyOffset(dtf, date) { 432 const formatted = dtf.format(date).replace(/\u200E/g, ""), 433 parsed = /(\d+)\/(\d+)\/(\d+) (AD|BC),? (\d+):(\d+):(\d+)/.exec(formatted), 434 [, fMonth, fDay, fYear, fadOrBc, fHour, fMinute, fSecond] = parsed; 435 return [fYear, fMonth, fDay, fadOrBc, fHour, fMinute, fSecond]; 436} 437function partsOffset(dtf, date) { 438 const formatted = dtf.formatToParts(date); 439 const filled = []; 440 for (let i = 0; i < formatted.length; i++) { 441 const { 442 type, 443 value 444 } = formatted[i]; 445 const pos = typeToPos[type]; 446 if (type === "era") { 447 filled[pos] = value; 448 } else if (!isUndefined(pos)) { 449 filled[pos] = parseInt(value, 10); 450 } 451 } 452 return filled; 453} 454const ianaZoneCache = new Map(); 455/** 456 * A zone identified by an IANA identifier, like America/New_York 457 * @implements {Zone} 458 */ 459class IANAZone extends Zone { 460 /** 461 * @param {string} name - Zone name 462 * @return {IANAZone} 463 */ 464 static create(name) { 465 let zone = ianaZoneCache.get(name); 466 if (zone === undefined) { 467 ianaZoneCache.set(name, zone = new IANAZone(name)); 468 } 469 return zone; 470 } 471 472 /** 473 * Reset local caches. Should only be necessary in testing scenarios. 474 * @return {void} 475 */ 476 static resetCache() { 477 ianaZoneCache.clear(); 478 dtfCache.clear(); 479 } 480 481 /** 482 * Returns whether the provided string is a valid specifier. This only checks the string's format, not that the specifier identifies a known zone; see isValidZone for that. 483 * @param {string} s - The string to check validity on 484 * @example IANAZone.isValidSpecifier("America/New_York") //=> true 485 * @example IANAZone.isValidSpecifier("Sport~~blorp") //=> false 486 * @deprecated For backward compatibility, this forwards to isValidZone, better use `isValidZone()` directly instead. 487 * @return {boolean} 488 */ 489 static isValidSpecifier(s) { 490 return this.isValidZone(s); 491 } 492 493 /** 494 * Returns whether the provided string identifies a real zone 495 * @param {string} zone - The string to check 496 * @example IANAZone.isValidZone("America/New_York") //=> true 497 * @example IANAZone.isValidZone("Fantasia/Castle") //=> false 498 * @example IANAZone.isValidZone("Sport~~blorp") //=> false 499 * @return {boolean} 500 */ 501 static isValidZone(zone) { 502 if (!zone) { 503 return false; 504 } 505 try { 506 new Intl.DateTimeFormat("en-US", { 507 timeZone: zone 508 }).format(); 509 return true; 510 } catch (e) { 511 return false; 512 } 513 } 514 constructor(name) { 515 super(); 516 /** @private **/ 517 this.zoneName = name; 518 /** @private **/ 519 this.valid = IANAZone.isValidZone(name); 520 } 521 522 /** 523 * The type of zone. `iana` for all instances of `IANAZone`. 524 * @override 525 * @type {string} 526 */ 527 get type() { 528 return "iana"; 529 } 530 531 /** 532 * The name of this zone (i.e. the IANA zone name). 533 * @override 534 * @type {string} 535 */ 536 get name() { 537 return this.zoneName; 538 } 539 540 /** 541 * Returns whether the offset is known to be fixed for the whole year: 542 * Always returns false for all IANA zones. 543 * @override 544 * @type {boolean} 545 */ 546 get isUniversal() { 547 return false; 548 } 549 550 /** 551 * Returns the offset's common name (such as EST) at the specified timestamp 552 * @override 553 * @param {number} ts - Epoch milliseconds for which to get the name 554 * @param {Object} opts - Options to affect the format 555 * @param {string} opts.format - What style of offset to return. Accepts 'long' or 'short'. 556 * @param {string} opts.locale - What locale to return the offset name in. 557 * @return {string} 558 */ 559 offsetName(ts, { 560 format, 561 locale 562 }) { 563 return parseZoneInfo(ts, format, locale, this.name); 564 } 565 566 /** 567 * Returns the offset's value as a string 568 * @override 569 * @param {number} ts - Epoch milliseconds for which to get the offset 570 * @param {string} format - What style of offset to return. 571 * Accepts 'narrow', 'short', or 'techie'. Returning '+6', '+06:00', or '+0600' respectively 572 * @return {string} 573 */ 574 formatOffset(ts, format) { 575 return formatOffset(this.offset(ts), format); 576 } 577 578 /** 579 * Return the offset in minutes for this zone at the specified timestamp. 580 * @override 581 * @param {number} ts - Epoch milliseconds for which to compute the offset 582 * @return {number} 583 */ 584 offset(ts) { 585 if (!this.valid) return NaN; 586 const date = new Date(ts); 587 if (isNaN(date)) return NaN; 588 const dtf = makeDTF(this.name); 589 let [year, month, day, adOrBc, hour, minute, second] = dtf.formatToParts ? partsOffset(dtf, date) : hackyOffset(dtf, date); 590 if (adOrBc === "BC") { 591 year = -Math.abs(year) + 1; 592 } 593 594 // because we're using hour12 and https://bugs.chromium.org/p/chromium/issues/detail?id=1025564&can=2&q=%2224%3A00%22%20datetimeformat 595 const adjustedHour = hour === 24 ? 0 : hour; 596 const asUTC = objToLocalTS({ 597 year, 598 month, 599 day, 600 hour: adjustedHour, 601 minute, 602 second, 603 millisecond: 0 604 }); 605 let asTS = +date; 606 const over = asTS % 1000; 607 asTS -= over >= 0 ? over : 1000 + over; 608 return (asUTC - asTS) / (60 * 1000); 609 } 610 611 /** 612 * Return whether this Zone is equal to another zone 613 * @override 614 * @param {Zone} otherZone - the zone to compare 615 * @return {boolean} 616 */ 617 equals(otherZone) { 618 return otherZone.type === "iana" && otherZone.name === this.name; 619 } 620 621 /** 622 * Return whether this Zone is valid. 623 * @override 624 * @type {boolean} 625 */ 626 get isValid() { 627 return this.valid; 628 } 629} 630 631// todo - remap caching 632 633let intlLFCache = {}; 634function getCachedLF(locString, opts = {}) { 635 const key = JSON.stringify([locString, opts]); 636 let dtf = intlLFCache[key]; 637 if (!dtf) { 638 dtf = new Intl.ListFormat(locString, opts); 639 intlLFCache[key] = dtf; 640 } 641 return dtf; 642} 643const intlDTCache = new Map(); 644function getCachedDTF(locString, opts = {}) { 645 const key = JSON.stringify([locString, opts]); 646 let dtf = intlDTCache.get(key); 647 if (dtf === undefined) { 648 dtf = new Intl.DateTimeFormat(locString, opts); 649 intlDTCache.set(key, dtf); 650 } 651 return dtf; 652} 653const intlNumCache = new Map(); 654function getCachedINF(locString, opts = {}) { 655 const key = JSON.stringify([locString, opts]); 656 let inf = intlNumCache.get(key); 657 if (inf === undefined) { 658 inf = new Intl.NumberFormat(locString, opts); 659 intlNumCache.set(key, inf); 660 } 661 return inf; 662} 663const intlRelCache = new Map(); 664function getCachedRTF(locString, opts = {}) { 665 const { 666 base, 667 ...cacheKeyOpts 668 } = opts; // exclude `base` from the options 669 const key = JSON.stringify([locString, cacheKeyOpts]); 670 let inf = intlRelCache.get(key); 671 if (inf === undefined) { 672 inf = new Intl.RelativeTimeFormat(locString, opts); 673 intlRelCache.set(key, inf); 674 } 675 return inf; 676} 677let sysLocaleCache = null; 678function systemLocale() { 679 if (sysLocaleCache) { 680 return sysLocaleCache; 681 } else { 682 sysLocaleCache = new Intl.DateTimeFormat().resolvedOptions().locale; 683 return sysLocaleCache; 684 } 685} 686const intlResolvedOptionsCache = new Map(); 687function getCachedIntResolvedOptions(locString) { 688 let opts = intlResolvedOptionsCache.get(locString); 689 if (opts === undefined) { 690 opts = new Intl.DateTimeFormat(locString).resolvedOptions(); 691 intlResolvedOptionsCache.set(locString, opts); 692 } 693 return opts; 694} 695const weekInfoCache = new Map(); 696function getCachedWeekInfo(locString) { 697 let data = weekInfoCache.get(locString); 698 if (!data) { 699 const locale = new Intl.Locale(locString); 700 // browsers currently implement this as a property, but spec says it should be a getter function 701 data = "getWeekInfo" in locale ? locale.getWeekInfo() : locale.weekInfo; 702 // minimalDays was removed from WeekInfo: https://github.com/tc39/proposal-intl-locale-info/issues/86 703 if (!("minimalDays" in data)) { 704 data = { 705 ...fallbackWeekSettings, 706 ...data 707 }; 708 } 709 weekInfoCache.set(locString, data); 710 } 711 return data; 712} 713function parseLocaleString(localeStr) { 714 // I really want to avoid writing a BCP 47 parser 715 // see, e.g. https://github.com/wooorm/bcp-47 716 // Instead, we'll do this: 717 718 // a) if the string has no -u extensions, just leave it alone 719 // b) if it does, use Intl to resolve everything 720 // c) if Intl fails, try again without the -u 721 722 // private subtags and unicode subtags have ordering requirements, 723 // and we're not properly parsing this, so just strip out the 724 // private ones if they exist. 725 const xIndex = localeStr.indexOf("-x-"); 726 if (xIndex !== -1) { 727 localeStr = localeStr.substring(0, xIndex); 728 } 729 const uIndex = localeStr.indexOf("-u-"); 730 if (uIndex === -1) { 731 return [localeStr]; 732 } else { 733 let options; 734 let selectedStr; 735 try { 736 options = getCachedDTF(localeStr).resolvedOptions(); 737 selectedStr = localeStr; 738 } catch (e) { 739 const smaller = localeStr.substring(0, uIndex); 740 options = getCachedDTF(smaller).resolvedOptions(); 741 selectedStr = smaller; 742 } 743 const { 744 numberingSystem, 745 calendar 746 } = options; 747 return [selectedStr, numberingSystem, calendar]; 748 } 749} 750function intlConfigString(localeStr, numberingSystem, outputCalendar) { 751 if (outputCalendar || numberingSystem) { 752 if (!localeStr.includes("-u-")) { 753 localeStr += "-u"; 754 } 755 if (outputCalendar) { 756 localeStr += `-ca-${outputCalendar}`; 757 } 758 if (numberingSystem) { 759 localeStr += `-nu-${numberingSystem}`; 760 } 761 return localeStr; 762 } else { 763 return localeStr; 764 } 765} 766function mapMonths(f) { 767 const ms = []; 768 for (let i = 1; i <= 12; i++) { 769 const dt = DateTime.utc(2009, i, 1); 770 ms.push(f(dt)); 771 } 772 return ms; 773} 774function mapWeekdays(f) { 775 const ms = []; 776 for (let i = 1; i <= 7; i++) { 777 const dt = DateTime.utc(2016, 11, 13 + i); 778 ms.push(f(dt)); 779 } 780 return ms; 781} 782function listStuff(loc, length, englishFn, intlFn) { 783 const mode = loc.listingMode(); 784 if (mode === "error") { 785 return null; 786 } else if (mode === "en") { 787 return englishFn(length); 788 } else { 789 return intlFn(length); 790 } 791} 792function supportsFastNumbers(loc) { 793 if (loc.numberingSystem && loc.numberingSystem !== "latn") { 794 return false; 795 } else { 796 return loc.numberingSystem === "latn" || !loc.locale || loc.locale.startsWith("en") || getCachedIntResolvedOptions(loc.locale).numberingSystem === "latn"; 797 } 798} 799 800/** 801 * @private 802 */ 803 804class PolyNumberFormatter { 805 constructor(intl, forceSimple, opts) { 806 this.padTo = opts.padTo || 0; 807 this.floor = opts.floor || false; 808 const { 809 padTo, 810 floor, 811 ...otherOpts 812 } = opts; 813 if (!forceSimple || Object.keys(otherOpts).length > 0) { 814 const intlOpts = { 815 useGrouping: false, 816 ...opts 817 }; 818 if (opts.padTo > 0) intlOpts.minimumIntegerDigits = opts.padTo; 819 this.inf = getCachedINF(intl, intlOpts); 820 } 821 } 822 format(i) { 823 if (this.inf) { 824 const fixed = this.floor ? Math.floor(i) : i; 825 return this.inf.format(fixed); 826 } else { 827 // to match the browser's numberformatter defaults 828 const fixed = this.floor ? Math.floor(i) : roundTo(i, 3); 829 return padStart(fixed, this.padTo); 830 } 831 } 832} 833 834/** 835 * @private 836 */ 837 838class PolyDateFormatter { 839 constructor(dt, intl, opts) { 840 this.opts = opts; 841 this.originalZone = undefined; 842 let z = undefined; 843 if (this.opts.timeZone) { 844 // Don't apply any workarounds if a timeZone is explicitly provided in opts 845 this.dt = dt; 846 } else if (dt.zone.type === "fixed") { 847 // UTC-8 or Etc/UTC-8 are not part of tzdata, only Etc/GMT+8 and the like. 848 // That is why fixed-offset TZ is set to that unless it is: 849 // 1. Representing offset 0 when UTC is used to maintain previous behavior and does not become GMT. 850 // 2. Unsupported by the browser: 851 // - some do not support Etc/ 852 // - < Etc/GMT-14, > Etc/GMT+12, and 30-minute or 45-minute offsets are not part of tzdata 853 const gmtOffset = -1 * (dt.offset / 60); 854 const offsetZ = gmtOffset >= 0 ? `Etc/GMT+${gmtOffset}` : `Etc/GMT${gmtOffset}`; 855 if (dt.offset !== 0 && IANAZone.create(offsetZ).valid) { 856 z = offsetZ; 857 this.dt = dt; 858 } else { 859 // Not all fixed-offset zones like Etc/+4:30 are present in tzdata so 860 // we manually apply the offset and substitute the zone as needed. 861 z = "UTC"; 862 this.dt = dt.offset === 0 ? dt : dt.setZone("UTC").plus({ 863 minutes: dt.offset 864 }); 865 this.originalZone = dt.zone; 866 } 867 } else if (dt.zone.type === "system") { 868 this.dt = dt; 869 } else if (dt.zone.type === "iana") { 870 this.dt = dt; 871 z = dt.zone.name; 872 } else { 873 // Custom zones can have any offset / offsetName so we just manually 874 // apply the offset and substitute the zone as needed. 875 z = "UTC"; 876 this.dt = dt.setZone("UTC").plus({ 877 minutes: dt.offset 878 }); 879 this.originalZone = dt.zone; 880 } 881 const intlOpts = { 882 ...this.opts 883 }; 884 intlOpts.timeZone = intlOpts.timeZone || z; 885 this.dtf = getCachedDTF(intl, intlOpts); 886 } 887 format() { 888 if (this.originalZone) { 889 // If we have to substitute in the actual zone name, we have to use 890 // formatToParts so that the timezone can be replaced. 891 return this.formatToParts().map(({ 892 value 893 }) => value).join(""); 894 } 895 return this.dtf.format(this.dt.toJSDate()); 896 } 897 formatToParts() { 898 const parts = this.dtf.formatToParts(this.dt.toJSDate()); 899 if (this.originalZone) { 900 return parts.map(part => { 901 if (part.type === "timeZoneName") { 902 const offsetName = this.originalZone.offsetName(this.dt.ts, { 903 locale: this.dt.locale, 904 format: this.opts.timeZoneName 905 }); 906 return { 907 ...part, 908 value: offsetName 909 }; 910 } else { 911 return part; 912 } 913 }); 914 } 915 return parts; 916 } 917 resolvedOptions() { 918 return this.dtf.resolvedOptions(); 919 } 920} 921 922/** 923 * @private 924 */ 925class PolyRelFormatter { 926 constructor(intl, isEnglish, opts) { 927 this.opts = { 928 style: "long", 929 ...opts 930 }; 931 if (!isEnglish && hasRelative()) { 932 this.rtf = getCachedRTF(intl, opts); 933 } 934 } 935 format(count, unit) { 936 if (this.rtf) { 937 return this.rtf.format(count, unit); 938 } else { 939 return formatRelativeTime(unit, count, this.opts.numeric, this.opts.style !== "long"); 940 } 941 } 942 formatToParts(count, unit) { 943 if (this.rtf) { 944 return this.rtf.formatToParts(count, unit); 945 } else { 946 return []; 947 } 948 } 949} 950const fallbackWeekSettings = { 951 firstDay: 1, 952 minimalDays: 4, 953 weekend: [6, 7] 954}; 955 956/** 957 * @private 958 */ 959class Locale { 960 static fromOpts(opts) { 961 return Locale.create(opts.locale, opts.numberingSystem, opts.outputCalendar, opts.weekSettings, opts.defaultToEN); 962 } 963 static create(locale, numberingSystem, outputCalendar, weekSettings, defaultToEN = false) { 964 const specifiedLocale = locale || Settings.defaultLocale; 965 // the system locale is useful for human-readable strings but annoying for parsing/formatting known formats 966 const localeR = specifiedLocale || (defaultToEN ? "en-US" : systemLocale()); 967 const numberingSystemR = numberingSystem || Settings.defaultNumberingSystem; 968 const outputCalendarR = outputCalendar || Settings.defaultOutputCalendar; 969 const weekSettingsR = validateWeekSettings(weekSettings) || Settings.defaultWeekSettings; 970 return new Locale(localeR, numberingSystemR, outputCalendarR, weekSettingsR, specifiedLocale); 971 } 972 static resetCache() { 973 sysLocaleCache = null; 974 intlDTCache.clear(); 975 intlNumCache.clear(); 976 intlRelCache.clear(); 977 intlResolvedOptionsCache.clear(); 978 weekInfoCache.clear(); 979 } 980 static fromObject({ 981 locale, 982 numberingSystem, 983 outputCalendar, 984 weekSettings 985 } = {}) { 986 return Locale.create(locale, numberingSystem, outputCalendar, weekSettings); 987 } 988 constructor(locale, numbering, outputCalendar, weekSettings, specifiedLocale) { 989 const [parsedLocale, parsedNumberingSystem, parsedOutputCalendar] = parseLocaleString(locale); 990 this.locale = parsedLocale; 991 this.numberingSystem = numbering || parsedNumberingSystem || null; 992 this.outputCalendar = outputCalendar || parsedOutputCalendar || null; 993 this.weekSettings = weekSettings; 994 this.intl = intlConfigString(this.locale, this.numberingSystem, this.outputCalendar); 995 this.weekdaysCache = { 996 format: {}, 997 standalone: {} 998 }; 999 this.monthsCache = { 1000 format: {}, 1001 standalone: {} 1002 }; 1003 this.meridiemCache = null; 1004 this.eraCache = {}; 1005 this.specifiedLocale = specifiedLocale; 1006 this.fastNumbersCached = null; 1007 } 1008 get fastNumbers() { 1009 if (this.fastNumbersCached == null) { 1010 this.fastNumbersCached = supportsFastNumbers(this); 1011 } 1012 return this.fastNumbersCached; 1013 } 1014 listingMode() { 1015 const isActuallyEn = this.isEnglish(); 1016 const hasNoWeirdness = (this.numberingSystem === null || this.numberingSystem === "latn") && (this.outputCalendar === null || this.outputCalendar === "gregory"); 1017 return isActuallyEn && hasNoWeirdness ? "en" : "intl"; 1018 } 1019 clone(alts) { 1020 if (!alts || Object.getOwnPropertyNames(alts).length === 0) { 1021 return this; 1022 } else { 1023 return Locale.create(alts.locale || this.specifiedLocale, alts.numberingSystem || this.numberingSystem, alts.outputCalendar || this.outputCalendar, validateWeekSettings(alts.weekSettings) || this.weekSettings, alts.defaultToEN || false); 1024 } 1025 } 1026 redefaultToEN(alts = {}) { 1027 return this.clone({ 1028 ...alts, 1029 defaultToEN: true 1030 }); 1031 } 1032 redefaultToSystem(alts = {}) { 1033 return this.clone({ 1034 ...alts, 1035 defaultToEN: false 1036 }); 1037 } 1038 months(length, format = false) { 1039 return listStuff(this, length, months, () => { 1040 // Workaround for "ja" locale: formatToParts does not label all parts of the month 1041 // as "month" and for this locale there is no difference between "format" and "non-format". 1042 // As such, just use format() instead of formatToParts() and take the whole string 1043 const monthSpecialCase = this.intl === "ja" || this.intl.startsWith("ja-"); 1044 format &= !monthSpecialCase; 1045 const intl = format ? { 1046 month: length, 1047 day: "numeric" 1048 } : { 1049 month: length 1050 }, 1051 formatStr = format ? "format" : "standalone"; 1052 if (!this.monthsCache[formatStr][length]) { 1053 const mapper = !monthSpecialCase ? dt => this.extract(dt, intl, "month") : dt => this.dtFormatter(dt, intl).format(); 1054 this.monthsCache[formatStr][length] = mapMonths(mapper); 1055 } 1056 return this.monthsCache[formatStr][length]; 1057 }); 1058 } 1059 weekdays(length, format = false) { 1060 return listStuff(this, length, weekdays, () => { 1061 const intl = format ? { 1062 weekday: length, 1063 year: "numeric", 1064 month: "long", 1065 day: "numeric" 1066 } : { 1067 weekday: length 1068 }, 1069 formatStr = format ? "format" : "standalone"; 1070 if (!this.weekdaysCache[formatStr][length]) { 1071 this.weekdaysCache[formatStr][length] = mapWeekdays(dt => this.extract(dt, intl, "weekday")); 1072 } 1073 return this.weekdaysCache[formatStr][length]; 1074 }); 1075 } 1076 meridiems() { 1077 return listStuff(this, undefined, () => meridiems, () => { 1078 // In theory there could be aribitrary day periods. We're gonna assume there are exactly two 1079 // for AM and PM. This is probably wrong, but it's makes parsing way easier. 1080 if (!this.meridiemCache) { 1081 const intl = { 1082 hour: "numeric", 1083 hourCycle: "h12" 1084 }; 1085 this.meridiemCache = [DateTime.utc(2016, 11, 13, 9), DateTime.utc(2016, 11, 13, 19)].map(dt => this.extract(dt, intl, "dayperiod")); 1086 } 1087 return this.meridiemCache; 1088 }); 1089 } 1090 eras(length) { 1091 return listStuff(this, length, eras, () => { 1092 const intl = { 1093 era: length 1094 }; 1095 1096 // This is problematic. Different calendars are going to define eras totally differently. What I need is the minimum set of dates 1097 // to definitely enumerate them. 1098 if (!this.eraCache[length]) { 1099 this.eraCache[length] = [DateTime.utc(-40, 1, 1), DateTime.utc(2017, 1, 1)].map(dt => this.extract(dt, intl, "era")); 1100 } 1101 return this.eraCache[length]; 1102 }); 1103 } 1104 extract(dt, intlOpts, field) { 1105 const df = this.dtFormatter(dt, intlOpts), 1106 results = df.formatToParts(), 1107 matching = results.find(m => m.type.toLowerCase() === field); 1108 return matching ? matching.value : null; 1109 } 1110 numberFormatter(opts = {}) { 1111 // this forcesimple option is never used (the only caller short-circuits on it, but it seems safer to leave) 1112 // (in contrast, the rest of the condition is used heavily) 1113 return new PolyNumberFormatter(this.intl, opts.forceSimple || this.fastNumbers, opts); 1114 } 1115 dtFormatter(dt, intlOpts = {}) { 1116 return new PolyDateFormatter(dt, this.intl, intlOpts); 1117 } 1118 relFormatter(opts = {}) { 1119 return new PolyRelFormatter(this.intl, this.isEnglish(), opts); 1120 } 1121 listFormatter(opts = {}) { 1122 return getCachedLF(this.intl, opts); 1123 } 1124 isEnglish() { 1125 return this.locale === "en" || this.locale.toLowerCase() === "en-us" || getCachedIntResolvedOptions(this.intl).locale.startsWith("en-us"); 1126 } 1127 getWeekSettings() { 1128 if (this.weekSettings) { 1129 return this.weekSettings; 1130 } else if (!hasLocaleWeekInfo()) { 1131 return fallbackWeekSettings; 1132 } else { 1133 return getCachedWeekInfo(this.locale); 1134 } 1135 } 1136 getStartOfWeek() { 1137 return this.getWeekSettings().firstDay; 1138 } 1139 getMinDaysInFirstWeek() { 1140 return this.getWeekSettings().minimalDays; 1141 } 1142 getWeekendDays() { 1143 return this.getWeekSettings().weekend; 1144 } 1145 equals(other) { 1146 return this.locale === other.locale && this.numberingSystem === other.numberingSystem && this.outputCalendar === other.outputCalendar; 1147 } 1148 toString() { 1149 return `Locale(${this.locale}, ${this.numberingSystem}, ${this.outputCalendar})`; 1150 } 1151} 1152 1153let singleton = null; 1154 1155/** 1156 * A zone with a fixed offset (meaning no DST) 1157 * @implements {Zone} 1158 */ 1159class FixedOffsetZone extends Zone { 1160 /** 1161 * Get a singleton instance of UTC 1162 * @return {FixedOffsetZone} 1163 */ 1164 static get utcInstance() { 1165 if (singleton === null) { 1166 singleton = new FixedOffsetZone(0); 1167 } 1168 return singleton; 1169 } 1170 1171 /** 1172 * Get an instance with a specified offset 1173 * @param {number} offset - The offset in minutes 1174 * @return {FixedOffsetZone} 1175 */ 1176 static instance(offset) { 1177 return offset === 0 ? FixedOffsetZone.utcInstance : new FixedOffsetZone(offset); 1178 } 1179 1180 /** 1181 * Get an instance of FixedOffsetZone from a UTC offset string, like "UTC+6" 1182 * @param {string} s - The offset string to parse 1183 * @example FixedOffsetZone.parseSpecifier("UTC+6") 1184 * @example FixedOffsetZone.parseSpecifier("UTC+06") 1185 * @example FixedOffsetZone.parseSpecifier("UTC-6:00") 1186 * @return {FixedOffsetZone} 1187 */ 1188 static parseSpecifier(s) { 1189 if (s) { 1190 const r = s.match(/^utc(?:([+-]\d{1,2})(?::(\d{2}))?)?$/i); 1191 if (r) { 1192 return new FixedOffsetZone(signedOffset(r[1], r[2])); 1193 } 1194 } 1195 return null; 1196 } 1197 constructor(offset) { 1198 super(); 1199 /** @private **/ 1200 this.fixed = offset; 1201 } 1202 1203 /** 1204 * The type of zone. `fixed` for all instances of `FixedOffsetZone`. 1205 * @override 1206 * @type {string} 1207 */ 1208 get type() { 1209 return "fixed"; 1210 } 1211 1212 /** 1213 * The name of this zone. 1214 * All fixed zones' names always start with "UTC" (plus optional offset) 1215 * @override 1216 * @type {string} 1217 */ 1218 get name() { 1219 return this.fixed === 0 ? "UTC" : `UTC${formatOffset(this.fixed, "narrow")}`; 1220 } 1221 1222 /** 1223 * The IANA name of this zone, i.e. `Etc/UTC` or `Etc/GMT+/-nn` 1224 * 1225 * @override 1226 * @type {string} 1227 */ 1228 get ianaName() { 1229 if (this.fixed === 0) { 1230 return "Etc/UTC"; 1231 } else { 1232 return `Etc/GMT${formatOffset(-this.fixed, "narrow")}`; 1233 } 1234 } 1235 1236 /** 1237 * Returns the offset's common name at the specified timestamp. 1238 * 1239 * For fixed offset zones this equals to the zone name. 1240 * @override 1241 */ 1242 offsetName() { 1243 return this.name; 1244 } 1245 1246 /** 1247 * Returns the offset's value as a string 1248 * @override 1249 * @param {number} ts - Epoch milliseconds for which to get the offset 1250 * @param {string} format - What style of offset to return. 1251 * Accepts 'narrow', 'short', or 'techie'. Returning '+6', '+06:00', or '+0600' respectively 1252 * @return {string} 1253 */ 1254 formatOffset(ts, format) { 1255 return formatOffset(this.fixed, format); 1256 } 1257 1258 /** 1259 * Returns whether the offset is known to be fixed for the whole year: 1260 * Always returns true for all fixed offset zones. 1261 * @override 1262 * @type {boolean} 1263 */ 1264 get isUniversal() { 1265 return true; 1266 } 1267 1268 /** 1269 * Return the offset in minutes for this zone at the specified timestamp. 1270 * 1271 * For fixed offset zones, this is constant and does not depend on a timestamp. 1272 * @override 1273 * @return {number} 1274 */ 1275 offset() { 1276 return this.fixed; 1277 } 1278 1279 /** 1280 * Return whether this Zone is equal to another zone (i.e. also fixed and same offset) 1281 * @override 1282 * @param {Zone} otherZone - the zone to compare 1283 * @return {boolean} 1284 */ 1285 equals(otherZone) { 1286 return otherZone.type === "fixed" && otherZone.fixed === this.fixed; 1287 } 1288 1289 /** 1290 * Return whether this Zone is valid: 1291 * All fixed offset zones are valid. 1292 * @override 1293 * @type {boolean} 1294 */ 1295 get isValid() { 1296 return true; 1297 } 1298} 1299 1300/** 1301 * A zone that failed to parse. You should never need to instantiate this. 1302 * @implements {Zone} 1303 */ 1304class InvalidZone extends Zone { 1305 constructor(zoneName) { 1306 super(); 1307 /** @private */ 1308 this.zoneName = zoneName; 1309 } 1310 1311 /** @override **/ 1312 get type() { 1313 return "invalid"; 1314 } 1315 1316 /** @override **/ 1317 get name() { 1318 return this.zoneName; 1319 } 1320 1321 /** @override **/ 1322 get isUniversal() { 1323 return false; 1324 } 1325 1326 /** @override **/ 1327 offsetName() { 1328 return null; 1329 } 1330 1331 /** @override **/ 1332 formatOffset() { 1333 return ""; 1334 } 1335 1336 /** @override **/ 1337 offset() { 1338 return NaN; 1339 } 1340 1341 /** @override **/ 1342 equals() { 1343 return false; 1344 } 1345 1346 /** @override **/ 1347 get isValid() { 1348 return false; 1349 } 1350} 1351 1352/** 1353 * @private 1354 */ 1355function normalizeZone(input, defaultZone) { 1356 if (isUndefined(input) || input === null) { 1357 return defaultZone; 1358 } else if (input instanceof Zone) { 1359 return input; 1360 } else if (isString(input)) { 1361 const lowered = input.toLowerCase(); 1362 if (lowered === "default") return defaultZone;else if (lowered === "local" || lowered === "system") return SystemZone.instance;else if (lowered === "utc" || lowered === "gmt") return FixedOffsetZone.utcInstance;else return FixedOffsetZone.parseSpecifier(lowered) || IANAZone.create(input); 1363 } else if (isNumber(input)) { 1364 return FixedOffsetZone.instance(input); 1365 } else if (typeof input === "object" && "offset" in input && typeof input.offset === "function") { 1366 // This is dumb, but the instanceof check above doesn't seem to really work 1367 // so we're duck checking it 1368 return input; 1369 } else { 1370 return new InvalidZone(input); 1371 } 1372} 1373 1374const numberingSystems = { 1375 arab: "[\u0660-\u0669]", 1376 arabext: "[\u06F0-\u06F9]", 1377 bali: "[\u1B50-\u1B59]", 1378 beng: "[\u09E6-\u09EF]", 1379 deva: "[\u0966-\u096F]", 1380 fullwide: "[\uFF10-\uFF19]", 1381 gujr: "[\u0AE6-\u0AEF]", 1382 hanidec: "[ã|ä¸|äº|ä¸|å|äº|å |ä¸|å «|ä¹]", 1383 khmr: "[\u17E0-\u17E9]", 1384 knda: "[\u0CE6-\u0CEF]", 1385 laoo: "[\u0ED0-\u0ED9]", 1386 limb: "[\u1946-\u194F]", 1387 mlym: "[\u0D66-\u0D6F]", 1388 mong: "[\u1810-\u1819]", 1389 mymr: "[\u1040-\u1049]", 1390 orya: "[\u0B66-\u0B6F]", 1391 tamldec: "[\u0BE6-\u0BEF]", 1392 telu: "[\u0C66-\u0C6F]", 1393 thai: "[\u0E50-\u0E59]", 1394 tibt: "[\u0F20-\u0F29]", 1395 latn: "\\d" 1396}; 1397const numberingSystemsUTF16 = { 1398 arab: [1632, 1641], 1399 arabext: [1776, 1785], 1400 bali: [6992, 7001], 1401 beng: [2534, 2543], 1402 deva: [2406, 2415], 1403 fullwide: [65296, 65303], 1404 gujr: [2790, 2799], 1405 khmr: [6112, 6121], 1406 knda: [3302, 3311], 1407 laoo: [3792, 3801], 1408 limb: [6470, 6479], 1409 mlym: [3430, 3439], 1410 mong: [6160, 6169], 1411 mymr: [4160, 4169], 1412 orya: [2918, 2927], 1413 tamldec: [3046, 3055], 1414 telu: [3174, 3183], 1415 thai: [3664, 3673], 1416 tibt: [3872, 3881] 1417}; 1418const hanidecChars = numberingSystems.hanidec.replace(/[\[|\]]/g, "").split(""); 1419function parseDigits(str) { 1420 let value = parseInt(str, 10); 1421 if (isNaN(value)) { 1422 value = ""; 1423 for (let i = 0; i < str.length; i++) { 1424 const code = str.charCodeAt(i); 1425 if (str[i].search(numberingSystems.hanidec) !== -1) { 1426 value += hanidecChars.indexOf(str[i]); 1427 } else { 1428 for (const key in numberingSystemsUTF16) { 1429 const [min, max] = numberingSystemsUTF16[key]; 1430 if (code >= min && code <= max) { 1431 value += code - min; 1432 } 1433 } 1434 } 1435 } 1436 return parseInt(value, 10); 1437 } else { 1438 return value; 1439 } 1440} 1441 1442// cache of {numberingSystem: {append: regex}} 1443const digitRegexCache = new Map(); 1444function resetDigitRegexCache() { 1445 digitRegexCache.clear(); 1446} 1447function digitRegex({ 1448 numberingSystem 1449}, append = "") { 1450 const ns = numberingSystem || "latn"; 1451 let appendCache = digitRegexCache.get(ns); 1452 if (appendCache === undefined) { 1453 appendCache = new Map(); 1454 digitRegexCache.set(ns, appendCache); 1455 } 1456 let regex = appendCache.get(append); 1457 if (regex === undefined) { 1458 regex = new RegExp(`${numberingSystems[ns]}${append}`); 1459 appendCache.set(append, regex); 1460 } 1461 return regex; 1462} 1463 1464let now = () => Date.now(), 1465 defaultZone = "system", 1466 defaultLocale = null, 1467 defaultNumberingSystem = null, 1468 defaultOutputCalendar = null, 1469 twoDigitCutoffYear = 60,
vendor: 2,348 bytes, lines 1470-1549
1470 throwOnInvalid, 1471 defaultWeekSettings = null; 1472 1473/** 1474 * Settings contains static getters and setters that control Luxon's overall behavior. Luxon is a simple library with few options, but the ones it does have live here. 1475 */ 1476class Settings { 1477 /** 1478 * Get the callback for returning the current timestamp. 1479 * @type {function} 1480 */ 1481 static get now() { 1482 return now; 1483 } 1484 1485 /** 1486 * Set the callback for returning the current timestamp. 1487 * The function should return a number, which will be interpreted as an Epoch millisecond count 1488 * @type {function} 1489 * @example Settings.now = () => Date.now() + 3000 // pretend it is 3 seconds in the future 1490 * @example Settings.now = () => 0 // always pretend it's Jan 1, 1970 at midnight in UTC time 1491 */ 1492 static set now(n) { 1493 now = n; 1494 } 1495 1496 /** 1497 * Set the default time zone to create DateTimes in. Does not affect existing instances. 1498 * Use the value "system" to reset this value to the system's time zone. 1499 * @type {string} 1500 */ 1501 static set defaultZone(zone) { 1502 defaultZone = zone; 1503 } 1504 1505 /** 1506 * Get the default time zone object currently used to create DateTimes. Does not affect existing instances. 1507 * The default value is the system's time zone (the one set on the machine that runs this code). 1508 * @type {Zone} 1509 */ 1510 static get defaultZone() { 1511 return normalizeZone(defaultZone, SystemZone.instance); 1512 } 1513 1514 /** 1515 * Get the default locale to create DateTimes with. Does not affect existing instances. 1516 * @type {string} 1517 */ 1518 static get defaultLocale() { 1519 return defaultLocale; 1520 } 1521 1522 /** 1523 * Set the default locale to create DateTimes with. Does not affect existing instances. 1524 * @type {string} 1525 */ 1526 static set defaultLocale(locale) { 1527 defaultLocale = locale; 1528 } 1529 1530 /** 1531 * Get the default numbering system to create DateTimes with. Does not affect existing instances. 1532 * @type {string} 1533 */ 1534 static get defaultNumberingSystem() { 1535 return defaultNumberingSystem; 1536 } 1537 1538 /** 1539 * Set the default numbering system to create DateTimes with. Does not affect existing instances. 1540 * @type {string} 1541 */ 1542 static set defaultNumberingSystem(numberingSystem) { 1543 defaultNumberingSystem = numberingSystem; 1544 } 1545 1546 /** 1547 * Get the default output calendar to create DateTimes with. Does not affect existing instances. 1548 * @type {string} 1549 */
vendor: 222,960 bytes, lines 1550-7815
1550 static get defaultOutputCalendar() { 1551 return defaultOutputCalendar; 1552 } 1553 1554 /** 1555 * Set the default output calendar to create DateTimes with. Does not affect existing instances. 1556 * @type {string} 1557 */ 1558 static set defaultOutputCalendar(outputCalendar) { 1559 defaultOutputCalendar = outputCalendar; 1560 } 1561 1562 /** 1563 * @typedef {Object} WeekSettings 1564 * @property {number} firstDay 1565 * @property {number} minimalDays 1566 * @property {number[]} weekend 1567 */ 1568 1569 /** 1570 * @return {WeekSettings|null} 1571 */ 1572 static get defaultWeekSettings() { 1573 return defaultWeekSettings; 1574 } 1575 1576 /** 1577 * Allows overriding the default locale week settings, i.e. the start of the week, the weekend and 1578 * how many days are required in the first week of a year. 1579 * Does not affect existing instances. 1580 * 1581 * @param {WeekSettings|null} weekSettings 1582 */ 1583 static set defaultWeekSettings(weekSettings) { 1584 defaultWeekSettings = validateWeekSettings(weekSettings); 1585 } 1586 1587 /** 1588 * Get the cutoff year for whether a 2-digit year string is interpreted in the current or previous century. Numbers higher than the cutoff will be considered to mean 19xx and numbers lower or equal to the cutoff will be considered 20xx. 1589 * @type {number} 1590 */ 1591 static get twoDigitCutoffYear() { 1592 return twoDigitCutoffYear; 1593 } 1594 1595 /** 1596 * Set the cutoff year for whether a 2-digit year string is interpreted in the current or previous century. Numbers higher than the cutoff will be considered to mean 19xx and numbers lower or equal to the cutoff will be considered 20xx. 1597 * @type {number} 1598 * @example Settings.twoDigitCutoffYear = 0 // all 'yy' are interpreted as 20th century 1599 * @example Settings.twoDigitCutoffYear = 99 // all 'yy' are interpreted as 21st century 1600 * @example Settings.twoDigitCutoffYear = 50 // '49' -> 2049; '50' -> 1950 1601 * @example Settings.twoDigitCutoffYear = 1950 // interpreted as 50 1602 * @example Settings.twoDigitCutoffYear = 2050 // ALSO interpreted as 50 1603 */ 1604 static set twoDigitCutoffYear(cutoffYear) { 1605 twoDigitCutoffYear = cutoffYear % 100; 1606 } 1607 1608 /** 1609 * Get whether Luxon will throw when it encounters invalid DateTimes, Durations, or Intervals 1610 * @type {boolean} 1611 */ 1612 static get throwOnInvalid() { 1613 return throwOnInvalid; 1614 } 1615 1616 /** 1617 * Set whether Luxon will throw when it encounters invalid DateTimes, Durations, or Intervals 1618 * @type {boolean} 1619 */ 1620 static set throwOnInvalid(t) { 1621 throwOnInvalid = t; 1622 } 1623 1624 /** 1625 * Reset Luxon's global caches. Should only be necessary in testing scenarios. 1626 * @return {void} 1627 */ 1628 static resetCaches() { 1629 Locale.resetCache(); 1630 IANAZone.resetCache(); 1631 DateTime.resetCache(); 1632 resetDigitRegexCache(); 1633 } 1634} 1635 1636class Invalid { 1637 constructor(reason, explanation) { 1638 this.reason = reason; 1639 this.explanation = explanation; 1640 } 1641 toMessage() { 1642 if (this.explanation) { 1643 return `${this.reason}: ${this.explanation}`; 1644 } else { 1645 return this.reason; 1646 } 1647 } 1648} 1649 1650const nonLeapLadder = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334], 1651 leapLadder = [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335]; 1652function unitOutOfRange(unit, value) { 1653 return new Invalid("unit out of range", `you specified ${value} (of type ${typeof value}) as a ${unit}, which is invalid`); 1654} 1655function dayOfWeek(year, month, day) { 1656 const d = new Date(Date.UTC(year, month - 1, day)); 1657 if (year < 100 && year >= 0) { 1658 d.setUTCFullYear(d.getUTCFullYear() - 1900); 1659 } 1660 const js = d.getUTCDay(); 1661 return js === 0 ? 7 : js; 1662} 1663function computeOrdinal(year, month, day) { 1664 return day + (isLeapYear(year) ? leapLadder : nonLeapLadder)[month - 1]; 1665} 1666function uncomputeOrdinal(year, ordinal) { 1667 const table = isLeapYear(year) ? leapLadder : nonLeapLadder, 1668 month0 = table.findIndex(i => i < ordinal), 1669 day = ordinal - table[month0]; 1670 return { 1671 month: month0 + 1, 1672 day 1673 }; 1674} 1675function isoWeekdayToLocal(isoWeekday, startOfWeek) { 1676 return (isoWeekday - startOfWeek + 7) % 7 + 1; 1677} 1678 1679/** 1680 * @private 1681 */ 1682 1683function gregorianToWeek(gregObj, minDaysInFirstWeek = 4, startOfWeek = 1) { 1684 const { 1685 year, 1686 month, 1687 day 1688 } = gregObj, 1689 ordinal = computeOrdinal(year, month, day), 1690 weekday = isoWeekdayToLocal(dayOfWeek(year, month, day), startOfWeek); 1691 let weekNumber = Math.floor((ordinal - weekday + 14 - minDaysInFirstWeek) / 7), 1692 weekYear; 1693 if (weekNumber < 1) { 1694 weekYear = year - 1; 1695 weekNumber = weeksInWeekYear(weekYear, minDaysInFirstWeek, startOfWeek); 1696 } else if (weekNumber > weeksInWeekYear(year, minDaysInFirstWeek, startOfWeek)) { 1697 weekYear = year + 1; 1698 weekNumber = 1; 1699 } else { 1700 weekYear = year; 1701 } 1702 return { 1703 weekYear, 1704 weekNumber, 1705 weekday, 1706 ...timeObject(gregObj) 1707 }; 1708} 1709function weekToGregorian(weekData, minDaysInFirstWeek = 4, startOfWeek = 1) { 1710 const { 1711 weekYear, 1712 weekNumber, 1713 weekday 1714 } = weekData, 1715 weekdayOfJan4 = isoWeekdayToLocal(dayOfWeek(weekYear, 1, minDaysInFirstWeek), startOfWeek), 1716 yearInDays = daysInYear(weekYear); 1717 let ordinal = weekNumber * 7 + weekday - weekdayOfJan4 - 7 + minDaysInFirstWeek, 1718 year; 1719 if (ordinal < 1) { 1720 year = weekYear - 1; 1721 ordinal += daysInYear(year); 1722 } else if (ordinal > yearInDays) { 1723 year = weekYear + 1; 1724 ordinal -= daysInYear(weekYear); 1725 } else { 1726 year = weekYear; 1727 } 1728 const { 1729 month, 1730 day 1731 } = uncomputeOrdinal(year, ordinal); 1732 return { 1733 year, 1734 month, 1735 day, 1736 ...timeObject(weekData) 1737 }; 1738} 1739function gregorianToOrdinal(gregData) { 1740 const { 1741 year, 1742 month, 1743 day 1744 } = gregData; 1745 const ordinal = computeOrdinal(year, month, day); 1746 return { 1747 year, 1748 ordinal, 1749 ...timeObject(gregData) 1750 }; 1751} 1752function ordinalToGregorian(ordinalData) { 1753 const { 1754 year, 1755 ordinal 1756 } = ordinalData; 1757 const { 1758 month, 1759 day 1760 } = uncomputeOrdinal(year, ordinal); 1761 return { 1762 year, 1763 month, 1764 day, 1765 ...timeObject(ordinalData) 1766 }; 1767} 1768 1769/** 1770 * Check if local week units like localWeekday are used in obj. 1771 * If so, validates that they are not mixed with ISO week units and then copies them to the normal week unit properties. 1772 * Modifies obj in-place! 1773 * @param obj the object values 1774 */ 1775function usesLocalWeekValues(obj, loc) { 1776 const hasLocaleWeekData = !isUndefined(obj.localWeekday) || !isUndefined(obj.localWeekNumber) || !isUndefined(obj.localWeekYear); 1777 if (hasLocaleWeekData) { 1778 const hasIsoWeekData = !isUndefined(obj.weekday) || !isUndefined(obj.weekNumber) || !isUndefined(obj.weekYear); 1779 if (hasIsoWeekData) { 1780 throw new ConflictingSpecificationError("Cannot mix locale-based week fields with ISO-based week fields"); 1781 } 1782 if (!isUndefined(obj.localWeekday)) obj.weekday = obj.localWeekday; 1783 if (!isUndefined(obj.localWeekNumber)) obj.weekNumber = obj.localWeekNumber; 1784 if (!isUndefined(obj.localWeekYear)) obj.weekYear = obj.localWeekYear; 1785 delete obj.localWeekday; 1786 delete obj.localWeekNumber; 1787 delete obj.localWeekYear; 1788 return { 1789 minDaysInFirstWeek: loc.getMinDaysInFirstWeek(), 1790 startOfWeek: loc.getStartOfWeek() 1791 }; 1792 } else { 1793 return { 1794 minDaysInFirstWeek: 4, 1795 startOfWeek: 1 1796 }; 1797 } 1798} 1799function hasInvalidWeekData(obj, minDaysInFirstWeek = 4, startOfWeek = 1) { 1800 const validYear = isInteger(obj.weekYear), 1801 validWeek = integerBetween(obj.weekNumber, 1, weeksInWeekYear(obj.weekYear, minDaysInFirstWeek, startOfWeek)), 1802 validWeekday = integerBetween(obj.weekday, 1, 7); 1803 if (!validYear) { 1804 return unitOutOfRange("weekYear", obj.weekYear); 1805 } else if (!validWeek) { 1806 return unitOutOfRange("week", obj.weekNumber); 1807 } else if (!validWeekday) { 1808 return unitOutOfRange("weekday", obj.weekday); 1809 } else return false; 1810} 1811function hasInvalidOrdinalData(obj) { 1812 const validYear = isInteger(obj.year), 1813 validOrdinal = integerBetween(obj.ordinal, 1, daysInYear(obj.year)); 1814 if (!validYear) { 1815 return unitOutOfRange("year", obj.year); 1816 } else if (!validOrdinal) { 1817 return unitOutOfRange("ordinal", obj.ordinal); 1818 } else return false; 1819} 1820function hasInvalidGregorianData(obj) { 1821 const validYear = isInteger(obj.year), 1822 validMonth = integerBetween(obj.month, 1, 12), 1823 validDay = integerBetween(obj.day, 1, daysInMonth(obj.year, obj.month)); 1824 if (!validYear) { 1825 return unitOutOfRange("year", obj.year); 1826 } else if (!validMonth) { 1827 return unitOutOfRange("month", obj.month); 1828 } else if (!validDay) { 1829 return unitOutOfRange("day", obj.day); 1830 } else return false; 1831} 1832function hasInvalidTimeData(obj) { 1833 const { 1834 hour, 1835 minute, 1836 second, 1837 millisecond 1838 } = obj; 1839 const validHour = integerBetween(hour, 0, 23) || hour === 24 && minute === 0 && second === 0 && millisecond === 0, 1840 validMinute = integerBetween(minute, 0, 59), 1841 validSecond = integerBetween(second, 0, 59), 1842 validMillisecond = integerBetween(millisecond, 0, 999); 1843 if (!validHour) { 1844 return unitOutOfRange("hour", hour); 1845 } else if (!validMinute) { 1846 return unitOutOfRange("minute", minute); 1847 } else if (!validSecond) { 1848 return unitOutOfRange("second", second); 1849 } else if (!validMillisecond) { 1850 return unitOutOfRange("millisecond", millisecond); 1851 } else return false; 1852} 1853 1854/* 1855 This is just a junk drawer, containing anything used across multiple classes. 1856 Because Luxon is small(ish), this should stay small and we won't worry about splitting 1857 it up into, say, parsingUtil.js and basicUtil.js and so on. But they are divided up by feature area. 1858*/ 1859 1860/** 1861 * @private 1862 */ 1863 1864// TYPES 1865 1866function isUndefined(o) { 1867 return typeof o === "undefined"; 1868} 1869function isNumber(o) { 1870 return typeof o === "number"; 1871} 1872function isInteger(o) { 1873 return typeof o === "number" && o % 1 === 0; 1874} 1875function isString(o) { 1876 return typeof o === "string"; 1877} 1878function isDate(o) { 1879 return Object.prototype.toString.call(o) === "[object Date]"; 1880} 1881 1882// CAPABILITIES 1883 1884function hasRelative() { 1885 try { 1886 return typeof Intl !== "undefined" && !!Intl.RelativeTimeFormat; 1887 } catch (e) { 1888 return false; 1889 } 1890} 1891function hasLocaleWeekInfo() { 1892 try { 1893 return typeof Intl !== "undefined" && !!Intl.Locale && ("weekInfo" in Intl.Locale.prototype || "getWeekInfo" in Intl.Locale.prototype); 1894 } catch (e) { 1895 return false; 1896 } 1897} 1898 1899// OBJECTS AND ARRAYS 1900 1901function maybeArray(thing) { 1902 return Array.isArray(thing) ? thing : [thing]; 1903} 1904function bestBy(arr, by, compare) { 1905 if (arr.length === 0) { 1906 return undefined; 1907 } 1908 return arr.reduce((best, next) => { 1909 const pair = [by(next), next]; 1910 if (!best) { 1911 return pair; 1912 } else if (compare(best[0], pair[0]) === best[0]) { 1913 return best; 1914 } else { 1915 return pair; 1916 } 1917 }, null)[1]; 1918} 1919function pick(obj, keys) { 1920 return keys.reduce((a, k) => { 1921 a[k] = obj[k]; 1922 return a; 1923 }, {}); 1924} 1925function hasOwnProperty(obj, prop) { 1926 return Object.prototype.hasOwnProperty.call(obj, prop); 1927} 1928function validateWeekSettings(settings) { 1929 if (settings == null) { 1930 return null; 1931 } else if (typeof settings !== "object") { 1932 throw new InvalidArgumentError("Week settings must be an object"); 1933 } else { 1934 if (!integerBetween(settings.firstDay, 1, 7) || !integerBetween(settings.minimalDays, 1, 7) || !Array.isArray(settings.weekend) || settings.weekend.some(v => !integerBetween(v, 1, 7))) { 1935 throw new InvalidArgumentError("Invalid week settings"); 1936 } 1937 return { 1938 firstDay: settings.firstDay, 1939 minimalDays: settings.minimalDays, 1940 weekend: Array.from(settings.weekend) 1941 }; 1942 } 1943} 1944 1945// NUMBERS AND STRINGS 1946 1947function integerBetween(thing, bottom, top) { 1948 return isInteger(thing) && thing >= bottom && thing <= top; 1949} 1950 1951// x % n but takes the sign of n instead of x 1952function floorMod(x, n) { 1953 return x - n * Math.floor(x / n); 1954} 1955function padStart(input, n = 2) { 1956 const isNeg = input < 0; 1957 let padded; 1958 if (isNeg) { 1959 padded = "-" + ("" + -input).padStart(n, "0"); 1960 } else { 1961 padded = ("" + input).padStart(n, "0"); 1962 } 1963 return padded; 1964} 1965function parseInteger(string) { 1966 if (isUndefined(string) || string === null || string === "") { 1967 return undefined; 1968 } else { 1969 return parseInt(string, 10); 1970 } 1971} 1972function parseFloating(string) { 1973 if (isUndefined(string) || string === null || string === "") { 1974 return undefined; 1975 } else { 1976 return parseFloat(string); 1977 } 1978} 1979function parseMillis(fraction) { 1980 // Return undefined (instead of 0) in these cases, where fraction is not set 1981 if (isUndefined(fraction) || fraction === null || fraction === "") { 1982 return undefined; 1983 } else { 1984 const f = parseFloat("0." + fraction) * 1000; 1985 return Math.floor(f); 1986 } 1987} 1988function roundTo(number, digits, rounding = "round") { 1989 const factor = 10 ** digits; 1990 switch (rounding) { 1991 case "expand": 1992 return number > 0 ? Math.ceil(number * factor) / factor : Math.floor(number * factor) / factor; 1993 case "trunc": 1994 return Math.trunc(number * factor) / factor; 1995 case "round": 1996 return Math.round(number * factor) / factor; 1997 case "floor": 1998 return Math.floor(number * factor) / factor; 1999 case "ceil": 2000 return Math.ceil(number * factor) / factor; 2001 default: 2002 throw new RangeError(`Value rounding ${rounding} is out of range`); 2003 } 2004} 2005 2006// DATE BASICS 2007 2008function isLeapYear(year) { 2009 return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0); 2010} 2011function daysInYear(year) { 2012 return isLeapYear(year) ? 366 : 365; 2013} 2014function daysInMonth(year, month) { 2015 const modMonth = floorMod(month - 1, 12) + 1, 2016 modYear = year + (month - modMonth) / 12; 2017 if (modMonth === 2) { 2018 return isLeapYear(modYear) ? 29 : 28; 2019 } else { 2020 return [31, null, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][modMonth - 1]; 2021 } 2022} 2023 2024// convert a calendar object to a local timestamp (epoch, but with the offset baked in) 2025function objToLocalTS(obj) { 2026 let d = Date.UTC(obj.year, obj.month - 1, obj.day, obj.hour, obj.minute, obj.second, obj.millisecond); 2027 2028 // for legacy reasons, years between 0 and 99 are interpreted as 19XX; revert that 2029 if (obj.year < 100 && obj.year >= 0) { 2030 d = new Date(d); 2031 // set the month and day again, this is necessary because year 2000 is a leap year, but year 100 is not 2032 // so if obj.year is in 99, but obj.day makes it roll over into year 100, 2033 // the calculations done by Date.UTC are using year 2000 - which is incorrect 2034 d.setUTCFullYear(obj.year, obj.month - 1, obj.day); 2035 } 2036 return +d; 2037} 2038 2039// adapted from moment.js: https://github.com/moment/moment/blob/000ac1800e620f770f4eb31b5ae908f6167b0ab2/src/lib/units/week-calendar-utils.js 2040function firstWeekOffset(year, minDaysInFirstWeek, startOfWeek) { 2041 const fwdlw = isoWeekdayToLocal(dayOfWeek(year, 1, minDaysInFirstWeek), startOfWeek); 2042 return -fwdlw + minDaysInFirstWeek - 1; 2043} 2044function weeksInWeekYear(weekYear, minDaysInFirstWeek = 4, startOfWeek = 1) { 2045 const weekOffset = firstWeekOffset(weekYear, minDaysInFirstWeek, startOfWeek); 2046 const weekOffsetNext = firstWeekOffset(weekYear + 1, minDaysInFirstWeek, startOfWeek); 2047 return (daysInYear(weekYear) - weekOffset + weekOffsetNext) / 7; 2048} 2049function untruncateYear(year) { 2050 if (year > 99) { 2051 return year; 2052 } else return year > Settings.twoDigitCutoffYear ? 1900 + year : 2000 + year; 2053} 2054 2055// PARSING 2056 2057function parseZoneInfo(ts, offsetFormat, locale, timeZone = null) { 2058 const date = new Date(ts), 2059 intlOpts = { 2060 hourCycle: "h23", 2061 year: "numeric", 2062 month: "2-digit", 2063 day: "2-digit", 2064 hour: "2-digit", 2065 minute: "2-digit" 2066 }; 2067 if (timeZone) { 2068 intlOpts.timeZone = timeZone; 2069 } 2070 const modified = { 2071 timeZoneName: offsetFormat, 2072 ...intlOpts 2073 }; 2074 const parsed = new Intl.DateTimeFormat(locale, modified).formatToParts(date).find(m => m.type.toLowerCase() === "timezonename"); 2075 return parsed ? parsed.value : null; 2076} 2077 2078// signedOffset('-5', '30') -> -330 2079function signedOffset(offHourStr, offMinuteStr) { 2080 let offHour = parseInt(offHourStr, 10); 2081 2082 // don't || this because we want to preserve -0 2083 if (Number.isNaN(offHour)) { 2084 offHour = 0; 2085 } 2086 const offMin = parseInt(offMinuteStr, 10) || 0, 2087 offMinSigned = offHour < 0 || Object.is(offHour, -0) ? -offMin : offMin; 2088 return offHour * 60 + offMinSigned; 2089} 2090 2091// COERCION 2092 2093function asNumber(value) { 2094 const numericValue = Number(value); 2095 if (typeof value === "boolean" || value === "" || !Number.isFinite(numericValue)) throw new InvalidArgumentError(`Invalid unit value ${value}`); 2096 return numericValue; 2097} 2098function normalizeObject(obj, normalizer) { 2099 const normalized = {}; 2100 for (const u in obj) { 2101 if (hasOwnProperty(obj, u)) { 2102 const v = obj[u]; 2103 if (v === undefined || v === null) continue; 2104 normalized[normalizer(u)] = asNumber(v); 2105 } 2106 } 2107 return normalized; 2108} 2109 2110/** 2111 * Returns the offset's value as a string 2112 * @param {number} ts - Epoch milliseconds for which to get the offset 2113 * @param {string} format - What style of offset to return. 2114 * Accepts 'narrow', 'short', or 'techie'. Returning '+6', '+06:00', or '+0600' respectively 2115 * @return {string} 2116 */ 2117function formatOffset(offset, format) { 2118 const hours = Math.trunc(Math.abs(offset / 60)), 2119 minutes = Math.trunc(Math.abs(offset % 60)), 2120 sign = offset >= 0 ? "+" : "-"; 2121 switch (format) { 2122 case "short": 2123 return `${sign}${padStart(hours, 2)}:${padStart(minutes, 2)}`; 2124 case "narrow": 2125 return `${sign}${hours}${minutes > 0 ? `:${minutes}` : ""}`; 2126 case "techie": 2127 return `${sign}${padStart(hours, 2)}${padStart(minutes, 2)}`; 2128 default: 2129 throw new RangeError(`Value format ${format} is out of range for property format`); 2130 } 2131} 2132function timeObject(obj) { 2133 return pick(obj, ["hour", "minute", "second", "millisecond"]); 2134} 2135 2136/** 2137 * @private 2138 */ 2139 2140const monthsLong = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]; 2141const monthsShort = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; 2142const monthsNarrow = ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"]; 2143function months(length) { 2144 switch (length) { 2145 case "narrow": 2146 return [...monthsNarrow]; 2147 case "short": 2148 return [...monthsShort]; 2149 case "long": 2150 return [...monthsLong]; 2151 case "numeric": 2152 return ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"]; 2153 case "2-digit": 2154 return ["01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12"]; 2155 default: 2156 return null; 2157 } 2158} 2159const weekdaysLong = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]; 2160const weekdaysShort = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]; 2161const weekdaysNarrow = ["M", "T", "W", "T", "F", "S", "S"]; 2162function weekdays(length) { 2163 switch (length) { 2164 case "narrow": 2165 return [...weekdaysNarrow]; 2166 case "short": 2167 return [...weekdaysShort]; 2168 case "long": 2169 return [...weekdaysLong]; 2170 case "numeric": 2171 return ["1", "2", "3", "4", "5", "6", "7"]; 2172 default: 2173 return null; 2174 } 2175} 2176const meridiems = ["AM", "PM"]; 2177const erasLong = ["Before Christ", "Anno Domini"]; 2178const erasShort = ["BC", "AD"]; 2179const erasNarrow = ["B", "A"]; 2180function eras(length) { 2181 switch (length) { 2182 case "narrow": 2183 return [...erasNarrow]; 2184 case "short": 2185 return [...erasShort]; 2186 case "long": 2187 return [...erasLong]; 2188 default: 2189 return null; 2190 } 2191} 2192function meridiemForDateTime(dt) { 2193 return meridiems[dt.hour < 12 ? 0 : 1]; 2194} 2195function weekdayForDateTime(dt, length) { 2196 return weekdays(length)[dt.weekday - 1]; 2197} 2198function monthForDateTime(dt, length) { 2199 return months(length)[dt.month - 1]; 2200} 2201function eraForDateTime(dt, length) { 2202 return eras(length)[dt.year < 0 ? 0 : 1]; 2203} 2204function formatRelativeTime(unit, count, numeric = "always", narrow = false) { 2205 const units = { 2206 years: ["year", "yr."], 2207 quarters: ["quarter", "qtr."], 2208 months: ["month", "mo."], 2209 weeks: ["week", "wk."], 2210 days: ["day", "day", "days"], 2211 hours: ["hour", "hr."], 2212 minutes: ["minute", "min."], 2213 seconds: ["second", "sec."] 2214 }; 2215 const lastable = ["hours", "minutes", "seconds"].indexOf(unit) === -1; 2216 if (numeric === "auto" && lastable) { 2217 const isDay = unit === "days"; 2218 switch (count) { 2219 case 1: 2220 return isDay ? "tomorrow" : `next ${units[unit][0]}`; 2221 case -1: 2222 return isDay ? "yesterday" : `last ${units[unit][0]}`; 2223 case 0: 2224 return isDay ? "today" : `this ${units[unit][0]}`; 2225 } 2226 } 2227 2228 const isInPast = Object.is(count, -0) || count < 0, 2229 fmtValue = Math.abs(count), 2230 singular = fmtValue === 1, 2231 lilUnits = units[unit], 2232 fmtUnit = narrow ? singular ? lilUnits[1] : lilUnits[2] || lilUnits[1] : singular ? units[unit][0] : unit; 2233 return isInPast ? `${fmtValue} ${fmtUnit} ago` : `in ${fmtValue} ${fmtUnit}`; 2234} 2235 2236function stringifyTokens(splits, tokenToString) { 2237 let s = ""; 2238 for (const token of splits) { 2239 if (token.literal) { 2240 s += token.val; 2241 } else { 2242 s += tokenToString(token.val); 2243 } 2244 } 2245 return s; 2246} 2247const macroTokenToFormatOpts = { 2248 D: DATE_SHORT, 2249 DD: DATE_MED, 2250 DDD: DATE_FULL, 2251 DDDD: DATE_HUGE, 2252 t: TIME_SIMPLE, 2253 tt: TIME_WITH_SECONDS, 2254 ttt: TIME_WITH_SHORT_OFFSET, 2255 tttt: TIME_WITH_LONG_OFFSET, 2256 T: TIME_24_SIMPLE, 2257 TT: TIME_24_WITH_SECONDS, 2258 TTT: TIME_24_WITH_SHORT_OFFSET, 2259 TTTT: TIME_24_WITH_LONG_OFFSET, 2260 f: DATETIME_SHORT, 2261 ff: DATETIME_MED, 2262 fff: DATETIME_FULL, 2263 ffff: DATETIME_HUGE, 2264 F: DATETIME_SHORT_WITH_SECONDS, 2265 FF: DATETIME_MED_WITH_SECONDS, 2266 FFF: DATETIME_FULL_WITH_SECONDS, 2267 FFFF: DATETIME_HUGE_WITH_SECONDS 2268}; 2269 2270/** 2271 * @private 2272 */ 2273 2274class Formatter { 2275 static create(locale, opts = {}) { 2276 return new Formatter(locale, opts); 2277 } 2278 static parseFormat(fmt) { 2279 // white-space is always considered a literal in user-provided formats 2280 // the " " token has a special meaning (see unitForToken) 2281 2282 let current = null, 2283 currentFull = "", 2284 bracketed = false; 2285 const splits = []; 2286 for (let i = 0; i < fmt.length; i++) { 2287 const c = fmt.charAt(i); 2288 if (c === "'") { 2289 // turn '' into a literal signal quote instead of just skipping the empty literal 2290 if (currentFull.length > 0 || bracketed) { 2291 splits.push({ 2292 literal: bracketed || /^\s+$/.test(currentFull), 2293 val: currentFull === "" ? "'" : currentFull 2294 }); 2295 } 2296 current = null; 2297 currentFull = ""; 2298 bracketed = !bracketed; 2299 } else if (bracketed) { 2300 currentFull += c; 2301 } else if (c === current) { 2302 currentFull += c; 2303 } else { 2304 if (currentFull.length > 0) { 2305 splits.push({ 2306 literal: /^\s+$/.test(currentFull), 2307 val: currentFull 2308 }); 2309 } 2310 currentFull = c; 2311 current = c; 2312 } 2313 } 2314 if (currentFull.length > 0) { 2315 splits.push({ 2316 literal: bracketed || /^\s+$/.test(currentFull), 2317 val: currentFull 2318 }); 2319 } 2320 return splits; 2321 } 2322 static macroTokenToFormatOpts(token) { 2323 return macroTokenToFormatOpts[token]; 2324 } 2325 constructor(locale, formatOpts) { 2326 this.opts = formatOpts; 2327 this.loc = locale; 2328 this.systemLoc = null; 2329 } 2330 formatWithSystemDefault(dt, opts) { 2331 if (this.systemLoc === null) { 2332 this.systemLoc = this.loc.redefaultToSystem(); 2333 } 2334 const df = this.systemLoc.dtFormatter(dt, { 2335 ...this.opts, 2336 ...opts 2337 }); 2338 return df.format(); 2339 } 2340 dtFormatter(dt, opts = {}) { 2341 return this.loc.dtFormatter(dt, { 2342 ...this.opts, 2343 ...opts 2344 }); 2345 } 2346 formatDateTime(dt, opts) { 2347 return this.dtFormatter(dt, opts).format(); 2348 } 2349 formatDateTimeParts(dt, opts) { 2350 return this.dtFormatter(dt, opts).formatToParts(); 2351 } 2352 formatInterval(interval, opts) { 2353 const df = this.dtFormatter(interval.start, opts); 2354 return df.dtf.formatRange(interval.start.toJSDate(), interval.end.toJSDate()); 2355 } 2356 resolvedOptions(dt, opts) { 2357 return this.dtFormatter(dt, opts).resolvedOptions(); 2358 } 2359 num(n, p = 0, signDisplay = undefined) { 2360 // we get some perf out of doing this here, annoyingly 2361 if (this.opts.forceSimple) { 2362 return padStart(n, p); 2363 } 2364 const opts = { 2365 ...this.opts 2366 }; 2367 if (p > 0) { 2368 opts.padTo = p; 2369 } 2370 if (signDisplay) { 2371 opts.signDisplay = signDisplay; 2372 } 2373 return this.loc.numberFormatter(opts).format(n); 2374 } 2375 formatDateTimeFromString(dt, fmt) { 2376 const knownEnglish = this.loc.listingMode() === "en", 2377 useDateTimeFormatter = this.loc.outputCalendar && this.loc.outputCalendar !== "gregory", 2378 string = (opts, extract) => this.loc.extract(dt, opts, extract), 2379 formatOffset = opts => { 2380 if (dt.isOffsetFixed && dt.offset === 0 && opts.allowZ) { 2381 return "Z"; 2382 } 2383 return dt.isValid ? dt.zone.formatOffset(dt.ts, opts.format) : ""; 2384 }, 2385 meridiem = () => knownEnglish ? meridiemForDateTime(dt) : string({ 2386 hour: "numeric", 2387 hourCycle: "h12" 2388 }, "dayperiod"), 2389 month = (length, standalone) => knownEnglish ? monthForDateTime(dt, length) : string(standalone ? { 2390 month: length 2391 } : { 2392 month: length, 2393 day: "numeric" 2394 }, "month"), 2395 weekday = (length, standalone) => knownEnglish ? weekdayForDateTime(dt, length) : string(standalone ? { 2396 weekday: length 2397 } : { 2398 weekday: length, 2399 month: "long", 2400 day: "numeric" 2401 }, "weekday"), 2402 maybeMacro = token => { 2403 const formatOpts = Formatter.macroTokenToFormatOpts(token); 2404 if (formatOpts) { 2405 return this.formatWithSystemDefault(dt, formatOpts); 2406 } else { 2407 return token; 2408 } 2409 }, 2410 era = length => knownEnglish ? eraForDateTime(dt, length) : string({ 2411 era: length 2412 }, "era"), 2413 tokenToString = token => { 2414 // Where possible: https://cldr.unicode.org/translation/date-time/date-time-symbols 2415 switch (token) { 2416 // ms 2417 case "S": 2418 return this.num(dt.millisecond); 2419 case "u": 2420 // falls through 2421 case "SSS": 2422 return this.num(dt.millisecond, 3); 2423 // seconds 2424 case "s": 2425 return this.num(dt.second); 2426 case "ss": 2427 return this.num(dt.second, 2); 2428 // fractional seconds 2429 case "uu": 2430 return this.num(Math.floor(dt.millisecond / 10), 2); 2431 case "uuu": 2432 return this.num(Math.floor(dt.millisecond / 100)); 2433 // minutes 2434 case "m": 2435 return this.num(dt.minute); 2436 case "mm": 2437 return this.num(dt.minute, 2); 2438 // hours 2439 case "h": 2440 return this.num(dt.hour % 12 === 0 ? 12 : dt.hour % 12); 2441 case "hh": 2442 return this.num(dt.hour % 12 === 0 ? 12 : dt.hour % 12, 2); 2443 case "H": 2444 return this.num(dt.hour); 2445 case "HH": 2446 return this.num(dt.hour, 2); 2447 // offset 2448 case "Z": 2449 // like +6 2450 return formatOffset({ 2451 format: "narrow", 2452 allowZ: this.opts.allowZ 2453 }); 2454 case "ZZ": 2455 // like +06:00 2456 return formatOffset({ 2457 format: "short", 2458 allowZ: this.opts.allowZ 2459 }); 2460 case "ZZZ": 2461 // like +0600 2462 return formatOffset({ 2463 format: "techie", 2464 allowZ: this.opts.allowZ 2465 }); 2466 case "ZZZZ": 2467 // like EST 2468 return dt.zone.offsetName(dt.ts, { 2469 format: "short", 2470 locale: this.loc.locale 2471 }); 2472 case "ZZZZZ": 2473 // like Eastern Standard Time 2474 return dt.zone.offsetName(dt.ts, { 2475 format: "long", 2476 locale: this.loc.locale 2477 }); 2478 // zone 2479 case "z": 2480 // like America/New_York 2481 return dt.zoneName; 2482 // meridiems 2483 case "a": 2484 return meridiem(); 2485 // dates 2486 case "d": 2487 return useDateTimeFormatter ? string({ 2488 day: "numeric" 2489 }, "day") : this.num(dt.day); 2490 case "dd": 2491 return useDateTimeFormatter ? string({ 2492 day: "2-digit" 2493 }, "day") : this.num(dt.day, 2); 2494 // weekdays - standalone 2495 case "c": 2496 // like 1 2497 return this.num(dt.weekday); 2498 case "ccc": 2499 // like 'Tues' 2500 return weekday("short", true); 2501 case "cccc": 2502 // like 'Tuesday' 2503 return weekday("long", true); 2504 case "ccccc": 2505 // like 'T' 2506 return weekday("narrow", true); 2507 // weekdays - format 2508 case "E": 2509 // like 1 2510 return this.num(dt.weekday); 2511 case "EEE": 2512 // like 'Tues' 2513 return weekday("short", false); 2514 case "EEEE": 2515 // like 'Tuesday' 2516 return weekday("long", false); 2517 case "EEEEE": 2518 // like 'T' 2519 return weekday("narrow", false); 2520 // months - standalone 2521 case "L": 2522 // like 1 2523 return useDateTimeFormatter ? string({ 2524 month: "numeric", 2525 day: "numeric" 2526 }, "month") : this.num(dt.month); 2527 case "LL": 2528 // like 01, doesn't seem to work 2529 return useDateTimeFormatter ? string({ 2530 month: "2-digit", 2531 day: "numeric" 2532 }, "month") : this.num(dt.month, 2); 2533 case "LLL": 2534 // like Jan 2535 return month("short", true); 2536 case "LLLL": 2537 // like January 2538 return month("long", true); 2539 case "LLLLL": 2540 // like J 2541 return month("narrow", true); 2542 // months - format 2543 case "M": 2544 // like 1 2545 return useDateTimeFormatter ? string({ 2546 month: "numeric" 2547 }, "month") : this.num(dt.month); 2548 case "MM": 2549 // like 01 2550 return useDateTimeFormatter ? string({ 2551 month: "2-digit" 2552 }, "month") : this.num(dt.month, 2); 2553 case "MMM": 2554 // like Jan 2555 return month("short", false); 2556 case "MMMM": 2557 // like January 2558 return month("long", false); 2559 case "MMMMM": 2560 // like J 2561 return month("narrow", false); 2562 // years 2563 case "y": 2564 // like 2014 2565 return useDateTimeFormatter ? string({ 2566 year: "numeric" 2567 }, "year") : this.num(dt.year); 2568 case "yy": 2569 // like 14 2570 return useDateTimeFormatter ? string({ 2571 year: "2-digit" 2572 }, "year") : this.num(dt.year.toString().slice(-2), 2); 2573 case "yyyy": 2574 // like 0012 2575 return useDateTimeFormatter ? string({ 2576 year: "numeric" 2577 }, "year") : this.num(dt.year, 4); 2578 case "yyyyyy": 2579 // like 000012 2580 return useDateTimeFormatter ? string({ 2581 year: "numeric" 2582 }, "year") : this.num(dt.year, 6); 2583 // eras 2584 case "G": 2585 // like AD 2586 return era("short"); 2587 case "GG": 2588 // like Anno Domini 2589 return era("long"); 2590 case "GGGGG": 2591 return era("narrow"); 2592 case "kk": 2593 return this.num(dt.weekYear.toString().slice(-2), 2); 2594 case "kkkk": 2595 return this.num(dt.weekYear, 4); 2596 case "W": 2597 return this.num(dt.weekNumber); 2598 case "WW": 2599 return this.num(dt.weekNumber, 2); 2600 case "n": 2601 return this.num(dt.localWeekNumber); 2602 case "nn": 2603 return this.num(dt.localWeekNumber, 2); 2604 case "ii": 2605 return this.num(dt.localWeekYear.toString().slice(-2), 2); 2606 case "iiii": 2607 return this.num(dt.localWeekYear, 4); 2608 case "o": 2609 return this.num(dt.ordinal); 2610 case "ooo": 2611 return this.num(dt.ordinal, 3); 2612 case "q": 2613 // like 1 2614 return this.num(dt.quarter); 2615 case "qq": 2616 // like 01 2617 return this.num(dt.quarter, 2); 2618 case "X": 2619 return this.num(Math.floor(dt.ts / 1000)); 2620 case "x": 2621 return this.num(dt.ts); 2622 default: 2623 return maybeMacro(token); 2624 } 2625 }; 2626 return stringifyTokens(Formatter.parseFormat(fmt), tokenToString); 2627 } 2628 formatDurationFromString(dur, fmt) { 2629 const invertLargest = this.opts.signMode === "negativeLargestOnly" ? -1 : 1; 2630 const tokenToField = token => { 2631 switch (token[0]) { 2632 case "S": 2633 return "milliseconds"; 2634 case "s": 2635 return "seconds"; 2636 case "m": 2637 return "minutes"; 2638 case "h": 2639 return "hours"; 2640 case "d": 2641 return "days"; 2642 case "w": 2643 return "weeks"; 2644 case "M": 2645 return "months"; 2646 case "y": 2647 return "years"; 2648 default: 2649 return null; 2650 } 2651 }, 2652 tokenToString = (lildur, info) => token => { 2653 const mapped = tokenToField(token); 2654 if (mapped) { 2655 const inversionFactor = info.isNegativeDuration && mapped !== info.largestUnit ? invertLargest : 1; 2656 let signDisplay; 2657 if (this.opts.signMode === "negativeLargestOnly" && mapped !== info.largestUnit) { 2658 signDisplay = "never"; 2659 } else if (this.opts.signMode === "all") { 2660 signDisplay = "always"; 2661 } else { 2662 // "auto" and "negative" are the same, but "auto" has better support 2663 signDisplay = "auto"; 2664 } 2665 return this.num(lildur.get(mapped) * inversionFactor, token.length, signDisplay); 2666 } else { 2667 return token; 2668 } 2669 }, 2670 tokens = Formatter.parseFormat(fmt), 2671 realTokens = tokens.reduce((found, { 2672 literal, 2673 val 2674 }) => literal ? found : found.concat(val), []), 2675 collapsed = dur.shiftTo(...realTokens.map(tokenToField).filter(t => t)), 2676 durationInfo = { 2677 isNegativeDuration: collapsed < 0, 2678 // this relies on "collapsed" being based on "shiftTo", which builds up the object 2679 // in order 2680 largestUnit: Object.keys(collapsed.values)[0] 2681 }; 2682 return stringifyTokens(tokens, tokenToString(collapsed, durationInfo)); 2683 } 2684} 2685 2686/* 2687 * This file handles parsing for well-specified formats. Here's how it works: 2688 * Two things go into parsing: a regex to match with and an extractor to take apart the groups in the match. 2689 * An extractor is just a function that takes a regex match array and returns a { year: ..., month: ... } object 2690 * parse() does the work of executing the regex and applying the extractor. It takes multiple regex/extractor pairs to try in sequence. 2691 * Extractors can take a "cursor" representing the offset in the match to look at. This makes it easy to combine extractors. 2692 * combineExtractors() does the work of combining them, keeping track of the cursor through multiple extractions. 2693 * Some extractions are super dumb and simpleParse and fromStrings help DRY them. 2694 */ 2695 2696const ianaRegex = /[A-Za-z_+-]{1,256}(?::?\/[A-Za-z0-9_+-]{1,256}(?:\/[A-Za-z0-9_+-]{1,256})?)?/; 2697function combineRegexes(...regexes) { 2698 const full = regexes.reduce((f, r) => f + r.source, ""); 2699 return RegExp(`^${full}$`); 2700} 2701function combineExtractors(...extractors) { 2702 return m => extractors.reduce(([mergedVals, mergedZone, cursor], ex) => { 2703 const [val, zone, next] = ex(m, cursor); 2704 return [{ 2705 ...mergedVals, 2706 ...val 2707 }, zone || mergedZone, next]; 2708 }, [{}, null, 1]).slice(0, 2); 2709} 2710function parse(s, ...patterns) { 2711 if (s == null) { 2712 return [null, null]; 2713 } 2714 for (const [regex, extractor] of patterns) { 2715 const m = regex.exec(s); 2716 if (m) { 2717 return extractor(m); 2718 } 2719 } 2720 return [null, null]; 2721} 2722function simpleParse(...keys) { 2723 return (match, cursor) => { 2724 const ret = {}; 2725 let i; 2726 for (i = 0; i < keys.length; i++) { 2727 ret[keys[i]] = parseInteger(match[cursor + i]); 2728 } 2729 return [ret, null, cursor + i]; 2730 }; 2731} 2732 2733// ISO and SQL parsing 2734const offsetRegex = /(?:([Zz])|([+-]\d\d)(?::?(\d\d))?)/; 2735const isoExtendedZone = `(?:${offsetRegex.source}?(?:\\[(${ianaRegex.source})\\])?)?`; 2736const isoTimeBaseRegex = /(\d\d)(?::?(\d\d)(?::?(\d\d)(?:[.,](\d{1,30}))?)?)?/; 2737const isoTimeRegex = RegExp(`${isoTimeBaseRegex.source}${isoExtendedZone}`); 2738const isoTimeExtensionRegex = RegExp(`(?:[Tt]${isoTimeRegex.source})?`); 2739const isoYmdRegex = /([+-]\d{6}|\d{4})(?:-?(\d\d)(?:-?(\d\d))?)?/; 2740const isoWeekRegex = /(\d{4})-?W(\d\d)(?:-?(\d))?/; 2741const isoOrdinalRegex = /(\d{4})-?(\d{3})/; 2742const extractISOWeekData = simpleParse("weekYear", "weekNumber", "weekDay"); 2743const extractISOOrdinalData = simpleParse("year", "ordinal"); 2744const sqlYmdRegex = /(\d{4})-(\d\d)-(\d\d)/; // dumbed-down version of the ISO one 2745const sqlTimeRegex = RegExp(`${isoTimeBaseRegex.source} ?(?:${offsetRegex.source}|(${ianaRegex.source}))?`); 2746const sqlTimeExtensionRegex = RegExp(`(?: ${sqlTimeRegex.source})?`); 2747function int(match, pos, fallback) { 2748 const m = match[pos]; 2749 return isUndefined(m) ? fallback : parseInteger(m); 2750} 2751function extractISOYmd(match, cursor) { 2752 const item = { 2753 year: int(match, cursor), 2754 month: int(match, cursor + 1, 1), 2755 day: int(match, cursor + 2, 1) 2756 }; 2757 return [item, null, cursor + 3]; 2758} 2759function extractISOTime(match, cursor) { 2760 const item = { 2761 hours: int(match, cursor, 0), 2762 minutes: int(match, cursor + 1, 0), 2763 seconds: int(match, cursor + 2, 0), 2764 milliseconds: parseMillis(match[cursor + 3]) 2765 }; 2766 return [item, null, cursor + 4]; 2767} 2768function extractISOOffset(match, cursor) { 2769 const local = !match[cursor] && !match[cursor + 1], 2770 fullOffset = signedOffset(match[cursor + 1], match[cursor + 2]), 2771 zone = local ? null : FixedOffsetZone.instance(fullOffset); 2772 return [{}, zone, cursor + 3]; 2773} 2774function extractIANAZone(match, cursor) { 2775 const zone = match[cursor] ? IANAZone.create(match[cursor]) : null; 2776 return [{}, zone, cursor + 1]; 2777} 2778 2779// ISO time parsing 2780 2781const isoTimeOnly = RegExp(`^T?${isoTimeBaseRegex.source}$`); 2782 2783// ISO duration parsing 2784 2785const isoDuration = /^-?P(?:(?:(-?\d{1,20}(?:\.\d{1,20})?)Y)?(?:(-?\d{1,20}(?:\.\d{1,20})?)M)?(?:(-?\d{1,20}(?:\.\d{1,20})?)W)?(?:(-?\d{1,20}(?:\.\d{1,20})?)D)?(?:T(?:(-?\d{1,20}(?:\.\d{1,20})?)H)?(?:(-?\d{1,20}(?:\.\d{1,20})?)M)?(?:(-?\d{1,20})(?:[.,](-?\d{1,20}))?S)?)?)$/; 2786function extractISODuration(match) { 2787 const [s, yearStr, monthStr, weekStr, dayStr, hourStr, minuteStr, secondStr, millisecondsStr] = match; 2788 const hasNegativePrefix = s[0] === "-"; 2789 const negativeSeconds = secondStr && secondStr[0] === "-"; 2790 const maybeNegate = (num, force = false) => num !== undefined && (force || num && hasNegativePrefix) ? -num : num; 2791 return [{ 2792 years: maybeNegate(parseFloating(yearStr)), 2793 months: maybeNegate(parseFloating(monthStr)), 2794 weeks: maybeNegate(parseFloating(weekStr)), 2795 days: maybeNegate(parseFloating(dayStr)), 2796 hours: maybeNegate(parseFloating(hourStr)), 2797 minutes: maybeNegate(parseFloating(minuteStr)), 2798 seconds: maybeNegate(parseFloating(secondStr), secondStr === "-0"), 2799 milliseconds: maybeNegate(parseMillis(millisecondsStr), negativeSeconds) 2800 }]; 2801} 2802 2803// These are a little braindead. EDT *should* tell us that we're in, say, America/New_York 2804// and not just that we're in -240 *right now*. But since I don't think these are used that often 2805// I'm just going to ignore that 2806const obsOffsets = { 2807 GMT: 0, 2808 EDT: -4 * 60, 2809 EST: -5 * 60, 2810 CDT: -5 * 60, 2811 CST: -6 * 60, 2812 MDT: -6 * 60, 2813 MST: -7 * 60, 2814 PDT: -7 * 60, 2815 PST: -8 * 60 2816}; 2817function fromStrings(weekdayStr, yearStr, monthStr, dayStr, hourStr, minuteStr, secondStr) { 2818 const result = { 2819 year: yearStr.length === 2 ? untruncateYear(parseInteger(yearStr)) : parseInteger(yearStr), 2820 month: monthsShort.indexOf(monthStr) + 1, 2821 day: parseInteger(dayStr), 2822 hour: parseInteger(hourStr), 2823 minute: parseInteger(minuteStr) 2824 }; 2825 if (secondStr) result.second = parseInteger(secondStr); 2826 if (weekdayStr) { 2827 result.weekday = weekdayStr.length > 3 ? weekdaysLong.indexOf(weekdayStr) + 1 : weekdaysShort.indexOf(weekdayStr) + 1; 2828 } 2829 return result; 2830} 2831 2832// RFC 2822/5322 2833const rfc2822 = /^(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun),\s)?(\d{1,2})\s(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s(\d{2,4})\s(\d\d):(\d\d)(?::(\d\d))?\s(?:(UT|GMT|[ECMP][SD]T)|([Zz])|(?:([+-]\d\d)(\d\d)))$/; 2834function extractRFC2822(match) { 2835 const [, weekdayStr, dayStr, monthStr, yearStr, hourStr, minuteStr, secondStr, obsOffset, milOffset, offHourStr, offMinuteStr] = match, 2836 result = fromStrings(weekdayStr, yearStr, monthStr, dayStr, hourStr, minuteStr, secondStr); 2837 let offset; 2838 if (obsOffset) { 2839 offset = obsOffsets[obsOffset]; 2840 } else if (milOffset) { 2841 offset = 0; 2842 } else { 2843 offset = signedOffset(offHourStr, offMinuteStr); 2844 } 2845 return [result, new FixedOffsetZone(offset)]; 2846} 2847function preprocessRFC2822(s) { 2848 // Remove comments and folding whitespace and replace multiple-spaces with a single space 2849 return s.replace(/\([^()]*\)|[\n\t]/g, " ").replace(/(\s\s+)/g, " ").trim(); 2850} 2851 2852// http date 2853 2854const rfc1123 = /^(Mon|Tue|Wed|Thu|Fri|Sat|Sun), (\d\d) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) (\d{4}) (\d\d):(\d\d):(\d\d) GMT$/, 2855 rfc850 = /^(Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday), (\d\d)-(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)-(\d\d) (\d\d):(\d\d):(\d\d) GMT$/, 2856 ascii = /^(Mon|Tue|Wed|Thu|Fri|Sat|Sun) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) ( \d|\d\d) (\d\d):(\d\d):(\d\d) (\d{4})$/; 2857function extractRFC1123Or850(match) { 2858 const [, weekdayStr, dayStr, monthStr, yearStr, hourStr, minuteStr, secondStr] = match, 2859 result = fromStrings(weekdayStr, yearStr, monthStr, dayStr, hourStr, minuteStr, secondStr); 2860 return [result, FixedOffsetZone.utcInstance]; 2861} 2862function extractASCII(match) { 2863 const [, weekdayStr, monthStr, dayStr, hourStr, minuteStr, secondStr, yearStr] = match, 2864 result = fromStrings(weekdayStr, yearStr, monthStr, dayStr, hourStr, minuteStr, secondStr); 2865 return [result, FixedOffsetZone.utcInstance]; 2866} 2867const isoYmdWithTimeExtensionRegex = combineRegexes(isoYmdRegex, isoTimeExtensionRegex); 2868const isoWeekWithTimeExtensionRegex = combineRegexes(isoWeekRegex, isoTimeExtensionRegex); 2869const isoOrdinalWithTimeExtensionRegex = combineRegexes(isoOrdinalRegex, isoTimeExtensionRegex); 2870const isoTimeCombinedRegex = combineRegexes(isoTimeRegex); 2871const extractISOYmdTimeAndOffset = combineExtractors(extractISOYmd, extractISOTime, extractISOOffset, extractIANAZone); 2872const extractISOWeekTimeAndOffset = combineExtractors(extractISOWeekData, extractISOTime, extractISOOffset, extractIANAZone); 2873const extractISOOrdinalDateAndTime = combineExtractors(extractISOOrdinalData, extractISOTime, extractISOOffset, extractIANAZone); 2874const extractISOTimeAndOffset = combineExtractors(extractISOTime, extractISOOffset, extractIANAZone); 2875 2876/* 2877 * @private 2878 */ 2879 2880function parseISODate(s) { 2881 return parse(s, [isoYmdWithTimeExtensionRegex, extractISOYmdTimeAndOffset], [isoWeekWithTimeExtensionRegex, extractISOWeekTimeAndOffset], [isoOrdinalWithTimeExtensionRegex, extractISOOrdinalDateAndTime], [isoTimeCombinedRegex, extractISOTimeAndOffset]); 2882} 2883function parseRFC2822Date(s) { 2884 return parse(preprocessRFC2822(s), [rfc2822, extractRFC2822]); 2885} 2886function parseHTTPDate(s) { 2887 return parse(s, [rfc1123, extractRFC1123Or850], [rfc850, extractRFC1123Or850], [ascii, extractASCII]); 2888} 2889function parseISODuration(s) { 2890 return parse(s, [isoDuration, extractISODuration]); 2891} 2892const extractISOTimeOnly = combineExtractors(extractISOTime); 2893function parseISOTimeOnly(s) { 2894 return parse(s, [isoTimeOnly, extractISOTimeOnly]); 2895} 2896const sqlYmdWithTimeExtensionRegex = combineRegexes(sqlYmdRegex, sqlTimeExtensionRegex); 2897const sqlTimeCombinedRegex = combineRegexes(sqlTimeRegex); 2898const extractISOTimeOffsetAndIANAZone = combineExtractors(extractISOTime, extractISOOffset, extractIANAZone); 2899function parseSQL(s) { 2900 return parse(s, [sqlYmdWithTimeExtensionRegex, extractISOYmdTimeAndOffset], [sqlTimeCombinedRegex, extractISOTimeOffsetAndIANAZone]); 2901} 2902 2903const INVALID$2 = "Invalid Duration"; 2904 2905// unit conversion constants 2906const lowOrderMatrix = { 2907 weeks: { 2908 days: 7, 2909 hours: 7 * 24, 2910 minutes: 7 * 24 * 60, 2911 seconds: 7 * 24 * 60 * 60, 2912 milliseconds: 7 * 24 * 60 * 60 * 1000 2913 }, 2914 days: { 2915 hours: 24, 2916 minutes: 24 * 60, 2917 seconds: 24 * 60 * 60, 2918 milliseconds: 24 * 60 * 60 * 1000 2919 }, 2920 hours: { 2921 minutes: 60, 2922 seconds: 60 * 60, 2923 milliseconds: 60 * 60 * 1000 2924 }, 2925 minutes: { 2926 seconds: 60, 2927 milliseconds: 60 * 1000 2928 }, 2929 seconds: { 2930 milliseconds: 1000 2931 } 2932 }, 2933 casualMatrix = { 2934 years: { 2935 quarters: 4, 2936 months: 12, 2937 weeks: 52, 2938 days: 365, 2939 hours: 365 * 24, 2940 minutes: 365 * 24 * 60, 2941 seconds: 365 * 24 * 60 * 60, 2942 milliseconds: 365 * 24 * 60 * 60 * 1000 2943 }, 2944 quarters: { 2945 months: 3, 2946 weeks: 13, 2947 days: 91, 2948 hours: 91 * 24, 2949 minutes: 91 * 24 * 60, 2950 seconds: 91 * 24 * 60 * 60, 2951 milliseconds: 91 * 24 * 60 * 60 * 1000 2952 }, 2953 months: { 2954 weeks: 4, 2955 days: 30, 2956 hours: 30 * 24, 2957 minutes: 30 * 24 * 60, 2958 seconds: 30 * 24 * 60 * 60, 2959 milliseconds: 30 * 24 * 60 * 60 * 1000 2960 }, 2961 ...lowOrderMatrix 2962 }, 2963 daysInYearAccurate = 146097.0 / 400, 2964 daysInMonthAccurate = 146097.0 / 4800, 2965 accurateMatrix = { 2966 years: { 2967 quarters: 4, 2968 months: 12, 2969 weeks: daysInYearAccurate / 7, 2970 days: daysInYearAccurate, 2971 hours: daysInYearAccurate * 24, 2972 minutes: daysInYearAccurate * 24 * 60, 2973 seconds: daysInYearAccurate * 24 * 60 * 60, 2974 milliseconds: daysInYearAccurate * 24 * 60 * 60 * 1000 2975 }, 2976 quarters: { 2977 months: 3, 2978 weeks: daysInYearAccurate / 28, 2979 days: daysInYearAccurate / 4, 2980 hours: daysInYearAccurate * 24 / 4, 2981 minutes: daysInYearAccurate * 24 * 60 / 4, 2982 seconds: daysInYearAccurate * 24 * 60 * 60 / 4, 2983 milliseconds: daysInYearAccurate * 24 * 60 * 60 * 1000 / 4 2984 }, 2985 months: { 2986 weeks: daysInMonthAccurate / 7, 2987 days: daysInMonthAccurate, 2988 hours: daysInMonthAccurate * 24, 2989 minutes: daysInMonthAccurate * 24 * 60, 2990 seconds: daysInMonthAccurate * 24 * 60 * 60, 2991 milliseconds: daysInMonthAccurate * 24 * 60 * 60 * 1000 2992 }, 2993 ...lowOrderMatrix 2994 }; 2995 2996// units ordered by size 2997const orderedUnits$1 = ["years", "quarters", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds"]; 2998const reverseUnits = orderedUnits$1.slice(0).reverse(); 2999 3000// clone really means "create another instance just like this one, but with these changes" 3001function clone$1(dur, alts, clear = false) { 3002 // deep merge for vals 3003 const conf = { 3004 values: clear ? alts.values : { 3005 ...dur.values, 3006 ...(alts.values || {}) 3007 }, 3008 loc: dur.loc.clone(alts.loc), 3009 conversionAccuracy: alts.conversionAccuracy || dur.conversionAccuracy, 3010 matrix: alts.matrix || dur.matrix 3011 }; 3012 return new Duration(conf); 3013} 3014function durationToMillis(matrix, vals) { 3015 var _vals$milliseconds; 3016 let sum = (_vals$milliseconds = vals.milliseconds) != null ? _vals$milliseconds : 0; 3017 for (const unit of reverseUnits.slice(1)) { 3018 if (vals[unit]) { 3019 sum += vals[unit] * matrix[unit]["milliseconds"]; 3020 } 3021 } 3022 return sum; 3023} 3024 3025// NB: mutates parameters 3026function normalizeValues(matrix, vals) { 3027 // the logic below assumes the overall value of the duration is positive 3028 // if this is not the case, factor is used to make it so 3029 const factor = durationToMillis(matrix, vals) < 0 ? -1 : 1; 3030 orderedUnits$1.reduceRight((previous, current) => { 3031 if (!isUndefined(vals[current])) { 3032 if (previous) { 3033 const previousVal = vals[previous] * factor; 3034 const conv = matrix[current][previous]; 3035 3036 // if (previousVal < 0): 3037 // lower order unit is negative (e.g. { years: 2, days: -2 }) 3038 // normalize this by reducing the higher order unit by the appropriate amount 3039 // and increasing the lower order unit 3040 // this can never make the higher order unit negative, because this function only operates 3041 // on positive durations, so the amount of time represented by the lower order unit cannot 3042 // be larger than the higher order unit 3043 // else: 3044 // lower order unit is positive (e.g. { years: 2, days: 450 } or { years: -2, days: 450 }) 3045 // in this case we attempt to convert as much as possible from the lower order unit into 3046 // the higher order one 3047 // 3048 // Math.floor takes care of both of these cases, rounding away from 0 3049 // if previousVal < 0 it makes the absolute value larger 3050 // if previousVal >= it makes the absolute value smaller 3051 const rollUp = Math.floor(previousVal / conv); 3052 vals[current] += rollUp * factor; 3053 vals[previous] -= rollUp * conv * factor; 3054 } 3055 return current; 3056 } else { 3057 return previous; 3058 } 3059 }, null); 3060 3061 // try to convert any decimals into smaller units if possible 3062 // for example for { years: 2.5, days: 0, seconds: 0 } we want to get { years: 2, days: 182, hours: 12 } 3063 orderedUnits$1.reduce((previous, current) => { 3064 if (!isUndefined(vals[current])) { 3065 if (previous) { 3066 const fraction = vals[previous] % 1; 3067 vals[previous] -= fraction; 3068 vals[current] += fraction * matrix[previous][current]; 3069 } 3070 return current; 3071 } else { 3072 return previous; 3073 } 3074 }, null); 3075} 3076 3077// Remove all properties with a value of 0 from an object 3078function removeZeroes(vals) { 3079 const newVals = {}; 3080 for (const [key, value] of Object.entries(vals)) { 3081 if (value !== 0) { 3082 newVals[key] = value; 3083 } 3084 } 3085 return newVals; 3086} 3087 3088/** 3089 * A Duration object represents a period of time, like "2 months" or "1 day, 1 hour". Conceptually, it's just a map of units to their quantities, accompanied by some additional configuration and methods for creating, parsing, interrogating, transforming, and formatting them. They can be used on their own or in conjunction with other Luxon types; for example, you can use {@link DateTime#plus} to add a Duration object to a DateTime, producing another DateTime. 3090 * 3091 * Here is a brief overview of commonly used methods and getters in Duration: 3092 * 3093 * * **Creation** To create a Duration, use {@link Duration.fromMillis}, {@link Duration.fromObject}, or {@link Duration.fromISO}. 3094 * * **Unit values** See the {@link Duration#years}, {@link Duration#months}, {@link Duration#weeks}, {@link Duration#days}, {@link Duration#hours}, {@link Duration#minutes}, {@link Duration#seconds}, {@link Duration#milliseconds} accessors. 3095 * * **Configuration** See {@link Duration#locale} and {@link Duration#numberingSystem} accessors. 3096 * * **Transformation** To create new Durations out of old ones use {@link Duration#plus}, {@link Duration#minus}, {@link Duration#normalize}, {@link Duration#set}, {@link Duration#reconfigure}, {@link Duration#shiftTo}, and {@link Duration#negate}. 3097 * * **Output** To convert the Duration into other representations, see {@link Duration#as}, {@link Duration#toISO}, {@link Duration#toFormat}, and {@link Duration#toJSON} 3098 * 3099 * There's are more methods documented below. In addition, for more information on subtler topics like internationalization and validity, see the external documentation. 3100 */ 3101class Duration { 3102 /** 3103 * @private 3104 */ 3105 constructor(config) { 3106 const accurate = config.conversionAccuracy === "longterm" || false; 3107 let matrix = accurate ? accurateMatrix : casualMatrix; 3108 if (config.matrix) { 3109 matrix = config.matrix; 3110 } 3111 3112 /** 3113 * @access private 3114 */ 3115 this.values = config.values; 3116 /** 3117 * @access private 3118 */ 3119 this.loc = config.loc || Locale.create(); 3120 /** 3121 * @access private 3122 */ 3123 this.conversionAccuracy = accurate ? "longterm" : "casual"; 3124 /** 3125 * @access private 3126 */ 3127 this.invalid = config.invalid || null; 3128 /** 3129 * @access private 3130 */ 3131 this.matrix = matrix; 3132 /** 3133 * @access private 3134 */ 3135 this.isLuxonDuration = true; 3136 } 3137 3138 /** 3139 * Create Duration from a number of milliseconds. 3140 * @param {number} count of milliseconds 3141 * @param {Object} opts - options for parsing 3142 * @param {string} [opts.locale='en-US'] - the locale to use 3143 * @param {string} opts.numberingSystem - the numbering system to use 3144 * @param {string} [opts.conversionAccuracy='casual'] - the conversion system to use 3145 * @return {Duration} 3146 */ 3147 static fromMillis(count, opts) { 3148 return Duration.fromObject({ 3149 milliseconds: count 3150 }, opts); 3151 } 3152 3153 /** 3154 * Create a Duration from a JavaScript object with keys like 'years' and 'hours'. 3155 * If this object is empty then a zero milliseconds duration is returned. 3156 * @param {Object} obj - the object to create the DateTime from 3157 * @param {number} obj.years 3158 * @param {number} obj.quarters 3159 * @param {number} obj.months 3160 * @param {number} obj.weeks 3161 * @param {number} obj.days 3162 * @param {number} obj.hours 3163 * @param {number} obj.minutes 3164 * @param {number} obj.seconds 3165 * @param {number} obj.milliseconds 3166 * @param {Object} [opts=[]] - options for creating this Duration 3167 * @param {string} [opts.locale='en-US'] - the locale to use 3168 * @param {string} opts.numberingSystem - the numbering system to use 3169 * @param {string} [opts.conversionAccuracy='casual'] - the preset conversion system to use 3170 * @param {string} [opts.matrix=Object] - the custom conversion system to use 3171 * @return {Duration} 3172 */ 3173 static fromObject(obj, opts = {}) { 3174 if (obj == null || typeof obj !== "object") { 3175 throw new InvalidArgumentError(`Duration.fromObject: argument expected to be an object, got ${obj === null ? "null" : typeof obj}`); 3176 } 3177 return new Duration({ 3178 values: normalizeObject(obj, Duration.normalizeUnit), 3179 loc: Locale.fromObject(opts), 3180 conversionAccuracy: opts.conversionAccuracy, 3181 matrix: opts.matrix 3182 }); 3183 } 3184 3185 /** 3186 * Create a Duration from DurationLike. 3187 * 3188 * @param {Object | number | Duration} durationLike 3189 * One of: 3190 * - object with keys like 'years' and 'hours'. 3191 * - number representing milliseconds 3192 * - Duration instance 3193 * @return {Duration} 3194 */ 3195 static fromDurationLike(durationLike) { 3196 if (isNumber(durationLike)) { 3197 return Duration.fromMillis(durationLike); 3198 } else if (Duration.isDuration(durationLike)) { 3199 return durationLike; 3200 } else if (typeof durationLike === "object") { 3201 return Duration.fromObject(durationLike); 3202 } else { 3203 throw new InvalidArgumentError(`Unknown duration argument ${durationLike} of type ${typeof durationLike}`); 3204 } 3205 } 3206 3207 /** 3208 * Create a Duration from an ISO 8601 duration string. 3209 * @param {string} text - text to parse 3210 * @param {Object} opts - options for parsing 3211 * @param {string} [opts.locale='en-US'] - the locale to use 3212 * @param {string} opts.numberingSystem - the numbering system to use 3213 * @param {string} [opts.conversionAccuracy='casual'] - the preset conversion system to use 3214 * @param {string} [opts.matrix=Object] - the preset conversion system to use 3215 * @see https://en.wikipedia.org/wiki/ISO_8601#Durations 3216 * @example Duration.fromISO('P3Y6M1W4DT12H30M5S').toObject() //=> { years: 3, months: 6, weeks: 1, days: 4, hours: 12, minutes: 30, seconds: 5 } 3217 * @example Duration.fromISO('PT23H').toObject() //=> { hours: 23 } 3218 * @example Duration.fromISO('P5Y3M').toObject() //=> { years: 5, months: 3 } 3219 * @return {Duration} 3220 */ 3221 static fromISO(text, opts) { 3222 const [parsed] = parseISODuration(text); 3223 if (parsed) { 3224 return Duration.fromObject(parsed, opts); 3225 } else { 3226 return Duration.invalid("unparsable", `the input "${text}" can't be parsed as ISO 8601`); 3227 } 3228 } 3229 3230 /** 3231 * Create a Duration from an ISO 8601 time string. 3232 * @param {string} text - text to parse 3233 * @param {Object} opts - options for parsing 3234 * @param {string} [opts.locale='en-US'] - the locale to use 3235 * @param {string} opts.numberingSystem - the numbering system to use 3236 * @param {string} [opts.conversionAccuracy='casual'] - the preset conversion system to use 3237 * @param {string} [opts.matrix=Object] - the conversion system to use 3238 * @see https://en.wikipedia.org/wiki/ISO_8601#Times 3239 * @example Duration.fromISOTime('11:22:33.444').toObject() //=> { hours: 11, minutes: 22, seconds: 33, milliseconds: 444 } 3240 * @example Duration.fromISOTime('11:00').toObject() //=> { hours: 11, minutes: 0, seconds: 0 } 3241 * @example Duration.fromISOTime('T11:00').toObject() //=> { hours: 11, minutes: 0, seconds: 0 } 3242 * @example Duration.fromISOTime('1100').toObject() //=> { hours: 11, minutes: 0, seconds: 0 } 3243 * @example Duration.fromISOTime('T1100').toObject() //=> { hours: 11, minutes: 0, seconds: 0 } 3244 * @return {Duration} 3245 */ 3246 static fromISOTime(text, opts) { 3247 const [parsed] = parseISOTimeOnly(text); 3248 if (parsed) { 3249 return Duration.fromObject(parsed, opts); 3250 } else { 3251 return Duration.invalid("unparsable", `the input "${text}" can't be parsed as ISO 8601`); 3252 } 3253 } 3254 3255 /** 3256 * Create an invalid Duration. 3257 * @param {string} reason - simple string of why this datetime is invalid. Should not contain parameters or anything else data-dependent 3258 * @param {string} [explanation=null] - longer explanation, may include parameters and other useful debugging information 3259 * @return {Duration} 3260 */ 3261 static invalid(reason, explanation = null) { 3262 if (!reason) { 3263 throw new InvalidArgumentError("need to specify a reason the Duration is invalid"); 3264 } 3265 const invalid = reason instanceof Invalid ? reason : new Invalid(reason, explanation); 3266 if (Settings.throwOnInvalid) { 3267 throw new InvalidDurationError(invalid); 3268 } else { 3269 return new Duration({ 3270 invalid 3271 }); 3272 } 3273 } 3274 3275 /** 3276 * @private 3277 */ 3278 static normalizeUnit(unit) { 3279 const normalized = { 3280 year: "years", 3281 years: "years", 3282 quarter: "quarters", 3283 quarters: "quarters", 3284 month: "months", 3285 months: "months", 3286 week: "weeks", 3287 weeks: "weeks", 3288 day: "days", 3289 days: "days", 3290 hour: "hours", 3291 hours: "hours", 3292 minute: "minutes", 3293 minutes: "minutes", 3294 second: "seconds", 3295 seconds: "seconds", 3296 millisecond: "milliseconds", 3297 milliseconds: "milliseconds" 3298 }[unit ? unit.toLowerCase() : unit]; 3299 if (!normalized) throw new InvalidUnitError(unit); 3300 return normalized; 3301 } 3302 3303 /** 3304 * Check if an object is a Duration. Works across context boundaries 3305 * @param {object} o 3306 * @return {boolean} 3307 */ 3308 static isDuration(o) { 3309 return o && o.isLuxonDuration || false; 3310 } 3311 3312 /** 3313 * Get the locale of a Duration, such 'en-GB' 3314 * @type {string} 3315 */ 3316 get locale() { 3317 return this.isValid ? this.loc.locale : null; 3318 } 3319 3320 /** 3321 * Get the numbering system of a Duration, such 'beng'. The numbering system is used when formatting the Duration 3322 * 3323 * @type {string} 3324 */ 3325 get numberingSystem() { 3326 return this.isValid ? this.loc.numberingSystem : null; 3327 } 3328 3329 /** 3330 * Returns a string representation of this Duration formatted according to the specified format string. You may use these tokens: 3331 * * `S` for milliseconds 3332 * * `s` for seconds 3333 * * `m` for minutes 3334 * * `h` for hours 3335 * * `d` for days 3336 * * `w` for weeks 3337 * * `M` for months 3338 * * `y` for years 3339 * Notes: 3340 * * Add padding by repeating the token, e.g. "yy" pads the years to two digits, "hhhh" pads the hours out to four digits 3341 * * Tokens can be escaped by wrapping with single quotes. 3342 * * The duration will be converted to the set of units in the format string using {@link Duration#shiftTo} and the Durations's conversion accuracy setting. 3343 * @param {string} fmt - the format string 3344 * @param {Object} opts - options 3345 * @param {boolean} [opts.floor=true] - floor numerical values 3346 * @param {'negative'|'all'|'negativeLargestOnly'} [opts.signMode=negative] - How to handle signs 3347 * @example Duration.fromObject({ years: 1, days: 6, seconds: 2 }).toFormat("y d s") //=> "1 6 2" 3348 * @example Duration.fromObject({ years: 1, days: 6, seconds: 2 }).toFormat("yy dd sss") //=> "01 06 002" 3349 * @example Duration.fromObject({ years: 1, days: 6, seconds: 2 }).toFormat("M S") //=> "12 518402000" 3350 * @example Duration.fromObject({ days: 6, seconds: 2 }).toFormat("d s", { signMode: "all" }) //=> "+6 +2" 3351 * @example Duration.fromObject({ days: -6, seconds: -2 }).toFormat("d s", { signMode: "all" }) //=> "-6 -2" 3352 * @example Duration.fromObject({ days: -6, seconds: -2 }).toFormat("d s", { signMode: "negativeLargestOnly" }) //=> "-6 2" 3353 * @return {string} 3354 */ 3355 toFormat(fmt, opts = {}) { 3356 // reverse-compat since 1.2; we always round down now, never up, and we do it by default 3357 const fmtOpts = { 3358 ...opts, 3359 floor: opts.round !== false && opts.floor !== false 3360 }; 3361 return this.isValid ? Formatter.create(this.loc, fmtOpts).formatDurationFromString(this, fmt) : INVALID$2; 3362 } 3363 3364 /** 3365 * Returns a string representation of a Duration with all units included. 3366 * To modify its behavior, use `listStyle` and any Intl.NumberFormat option, though `unitDisplay` is especially relevant. 3367 * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat#options 3368 * @param {Object} opts - Formatting options. Accepts the same keys as the options parameter of the native `Intl.NumberFormat` constructor, as well as `listStyle`. 3369 * @param {string} [opts.listStyle='narrow'] - How to format the merged list. Corresponds to the `style` property of the options parameter of the native `Intl.ListFormat` constructor. 3370 * @param {boolean} [opts.showZeros=true] - Show all units previously used by the duration even if they are zero 3371 * @example 3372 * ```js 3373 * var dur = Duration.fromObject({ months: 1, weeks: 0, hours: 5, minutes: 6 }) 3374 * dur.toHuman() //=> '1 month, 0 weeks, 5 hours, 6 minutes' 3375 * dur.toHuman({ listStyle: "long" }) //=> '1 month, 0 weeks, 5 hours, and 6 minutes' 3376 * dur.toHuman({ unitDisplay: "short" }) //=> '1 mth, 0 wks, 5 hr, 6 min' 3377 * dur.toHuman({ showZeros: false }) //=> '1 month, 5 hours, 6 minutes' 3378 * ``` 3379 */ 3380 toHuman(opts = {}) { 3381 if (!this.isValid) return INVALID$2; 3382 const showZeros = opts.showZeros !== false; 3383 const l = orderedUnits$1.map(unit => { 3384 const val = this.values[unit]; 3385 if (isUndefined(val) || val === 0 && !showZeros) { 3386 return null; 3387 } 3388 return this.loc.numberFormatter({ 3389 style: "unit", 3390 unitDisplay: "long", 3391 ...opts, 3392 unit: unit.slice(0, -1) 3393 }).format(val); 3394 }).filter(n => n); 3395 return this.loc.listFormatter({ 3396 type: "conjunction", 3397 style: opts.listStyle || "narrow", 3398 ...opts 3399 }).format(l); 3400 } 3401 3402 /** 3403 * Returns a JavaScript object with this Duration's values. 3404 * @example Duration.fromObject({ years: 1, days: 6, seconds: 2 }).toObject() //=> { years: 1, days: 6, seconds: 2 } 3405 * @return {Object} 3406 */ 3407 toObject() { 3408 if (!this.isValid) return {}; 3409 return { 3410 ...this.values 3411 }; 3412 } 3413 3414 /** 3415 * Returns an ISO 8601-compliant string representation of this Duration. 3416 * @see https://en.wikipedia.org/wiki/ISO_8601#Durations 3417 * @example Duration.fromObject({ years: 3, seconds: 45 }).toISO() //=> 'P3YT45S' 3418 * @example Duration.fromObject({ months: 4, seconds: 45 }).toISO() //=> 'P4MT45S' 3419 * @example Duration.fromObject({ months: 5 }).toISO() //=> 'P5M' 3420 * @example Duration.fromObject({ minutes: 5 }).toISO() //=> 'PT5M' 3421 * @example Duration.fromObject({ milliseconds: 6 }).toISO() //=> 'PT0.006S' 3422 * @return {string} 3423 */ 3424 toISO() { 3425 // we could use the formatter, but this is an easier way to get the minimum string 3426 if (!this.isValid) return null; 3427 let s = "P"; 3428 if (this.years !== 0) s += this.years + "Y"; 3429 if (this.months !== 0 || this.quarters !== 0) s += this.months + this.quarters * 3 + "M"; 3430 if (this.weeks !== 0) s += this.weeks + "W"; 3431 if (this.days !== 0) s += this.days + "D"; 3432 if (this.hours !== 0 || this.minutes !== 0 || this.seconds !== 0 || this.milliseconds !== 0) s += "T"; 3433 if (this.hours !== 0) s += this.hours + "H"; 3434 if (this.minutes !== 0) s += this.minutes + "M"; 3435 if (this.seconds !== 0 || this.milliseconds !== 0) 3436 // this will handle "floating point madness" by removing extra decimal places 3437 // https://stackoverflow.com/questions/588004/is-floating-point-math-broken 3438 s += roundTo(this.seconds + this.milliseconds / 1000, 3) + "S"; 3439 if (s === "P") s += "T0S"; 3440 return s; 3441 } 3442 3443 /** 3444 * Returns an ISO 8601-compliant string representation of this Duration, formatted as a time of day. 3445 * Note that this will return null if the duration is invalid, negative, or equal to or greater than 24 hours. 3446 * @see https://en.wikipedia.org/wiki/ISO_8601#Times 3447 * @param {Object} opts - options 3448 * @param {boolean} [opts.suppressMilliseconds=false] - exclude milliseconds from the format if they're 0 3449 * @param {boolean} [opts.suppressSeconds=false] - exclude seconds from the format if they're 0 3450 * @param {boolean} [opts.includePrefix=false] - include the `T` prefix 3451 * @param {string} [opts.format='extended'] - choose between the basic and extended format 3452 * @example Duration.fromObject({ hours: 11 }).toISOTime() //=> '11:00:00.000' 3453 * @example Duration.fromObject({ hours: 11 }).toISOTime({ suppressMilliseconds: true }) //=> '11:00:00' 3454 * @example Duration.fromObject({ hours: 11 }).toISOTime({ suppressSeconds: true }) //=> '11:00' 3455 * @example Duration.fromObject({ hours: 11 }).toISOTime({ includePrefix: true }) //=> 'T11:00:00.000' 3456 * @example Duration.fromObject({ hours: 11 }).toISOTime({ format: 'basic' }) //=> '110000.000' 3457 * @return {string} 3458 */ 3459 toISOTime(opts = {}) { 3460 if (!this.isValid) return null; 3461 const millis = this.toMillis(); 3462 if (millis < 0 || millis >= 86400000) return null; 3463 opts = { 3464 suppressMilliseconds: false, 3465 suppressSeconds: false, 3466 includePrefix: false, 3467 format: "extended", 3468 ...opts, 3469 includeOffset: false 3470 }; 3471 const dateTime = DateTime.fromMillis(millis, { 3472 zone: "UTC" 3473 }); 3474 return dateTime.toISOTime(opts); 3475 } 3476 3477 /** 3478 * Returns an ISO 8601 representation of this Duration appropriate for use in JSON. 3479 * @return {string} 3480 */ 3481 toJSON() { 3482 return this.toISO(); 3483 } 3484 3485 /** 3486 * Returns an ISO 8601 representation of this Duration appropriate for use in debugging. 3487 * @return {string} 3488 */ 3489 toString() { 3490 return this.toISO(); 3491 } 3492 3493 /** 3494 * Returns a string representation of this Duration appropriate for the REPL. 3495 * @return {string} 3496 */ 3497 [Symbol.for("nodejs.util.inspect.custom")]() { 3498 if (this.isValid) { 3499 return `Duration { values: ${JSON.stringify(this.values)} }`; 3500 } else { 3501 return `Duration { Invalid, reason: ${this.invalidReason} }`; 3502 } 3503 } 3504 3505 /** 3506 * Returns an milliseconds value of this Duration. 3507 * @return {number} 3508 */ 3509 toMillis() { 3510 if (!this.isValid) return NaN; 3511 return durationToMillis(this.matrix, this.values); 3512 } 3513 3514 /** 3515 * Returns an milliseconds value of this Duration. Alias of {@link toMillis} 3516 * @return {number} 3517 */ 3518 valueOf() { 3519 return this.toMillis(); 3520 } 3521 3522 /** 3523 * Make this Duration longer by the specified amount. Return a newly-constructed Duration. 3524 * @param {Duration|Object|number} duration - The amount to add. Either a Luxon Duration, a number of milliseconds, the object argument to Duration.fromObject() 3525 * @return {Duration} 3526 */ 3527 plus(duration) { 3528 if (!this.isValid) return this; 3529 const dur = Duration.fromDurationLike(duration), 3530 result = {}; 3531 for (const k of orderedUnits$1) { 3532 if (hasOwnProperty(dur.values, k) || hasOwnProperty(this.values, k)) { 3533 result[k] = dur.get(k) + this.get(k); 3534 } 3535 } 3536 return clone$1(this, { 3537 values: result 3538 }, true); 3539 } 3540 3541 /** 3542 * Make this Duration shorter by the specified amount. Return a newly-constructed Duration. 3543 * @param {Duration|Object|number} duration - The amount to subtract. Either a Luxon Duration, a number of milliseconds, the object argument to Duration.fromObject() 3544 * @return {Duration} 3545 */ 3546 minus(duration) { 3547 if (!this.isValid) return this; 3548 const dur = Duration.fromDurationLike(duration); 3549 return this.plus(dur.negate()); 3550 } 3551 3552 /** 3553 * Scale this Duration by the specified amount. Return a newly-constructed Duration. 3554 * @param {function} fn - The function to apply to each unit. Arity is 1 or 2: the value of the unit and, optionally, the unit name. Must return a number. 3555 * @example Duration.fromObject({ hours: 1, minutes: 30 }).mapUnits(x => x * 2) //=> { hours: 2, minutes: 60 } 3556 * @example Duration.fromObject({ hours: 1, minutes: 30 }).mapUnits((x, u) => u === "hours" ? x * 2 : x) //=> { hours: 2, minutes: 30 } 3557 * @return {Duration} 3558 */ 3559 mapUnits(fn) { 3560 if (!this.isValid) return this; 3561 const result = {}; 3562 for (const k of Object.keys(this.values)) { 3563 result[k] = asNumber(fn(this.values[k], k)); 3564 } 3565 return clone$1(this, { 3566 values: result 3567 }, true); 3568 } 3569 3570 /** 3571 * Get the value of unit. 3572 * @param {string} unit - a unit such as 'minute' or 'day' 3573 * @example Duration.fromObject({years: 2, days: 3}).get('years') //=> 2 3574 * @example Duration.fromObject({years: 2, days: 3}).get('months') //=> 0 3575 * @example Duration.fromObject({years: 2, days: 3}).get('days') //=> 3 3576 * @return {number} 3577 */ 3578 get(unit) { 3579 return this[Duration.normalizeUnit(unit)]; 3580 } 3581 3582 /** 3583 * "Set" the values of specified units. Return a newly-constructed Duration. 3584 * @param {Object} values - a mapping of units to numbers 3585 * @example dur.set({ years: 2017 }) 3586 * @example dur.set({ hours: 8, minutes: 30 }) 3587 * @return {Duration} 3588 */ 3589 set(values) { 3590 if (!this.isValid) return this; 3591 const mixed = { 3592 ...this.values, 3593 ...normalizeObject(values, Duration.normalizeUnit) 3594 }; 3595 return clone$1(this, { 3596 values: mixed 3597 }); 3598 } 3599 3600 /** 3601 * "Set" the locale and/or numberingSystem. Returns a newly-constructed Duration. 3602 * @example dur.reconfigure({ locale: 'en-GB' }) 3603 * @return {Duration} 3604 */ 3605 reconfigure({ 3606 locale, 3607 numberingSystem, 3608 conversionAccuracy, 3609 matrix 3610 } = {}) { 3611 const loc = this.loc.clone({ 3612 locale, 3613 numberingSystem 3614 }); 3615 const opts = { 3616 loc, 3617 matrix, 3618 conversionAccuracy 3619 }; 3620 return clone$1(this, opts); 3621 } 3622 3623 /** 3624 * Return the length of the duration in the specified unit. 3625 * @param {string} unit - a unit such as 'minutes' or 'days' 3626 * @example Duration.fromObject({years: 1}).as('days') //=> 365 3627 * @example Duration.fromObject({years: 1}).as('months') //=> 12 3628 * @example Duration.fromObject({hours: 60}).as('days') //=> 2.5 3629 * @return {number} 3630 */ 3631 as(unit) { 3632 return this.isValid ? this.shiftTo(unit).get(unit) : NaN; 3633 } 3634 3635 /** 3636 * Reduce this Duration to its canonical representation in its current units. 3637 * Assuming the overall value of the Duration is positive, this means: 3638 * - excessive values for lower-order units are converted to higher-order units (if possible, see first and second example) 3639 * - negative lower-order units are converted to higher order units (there must be such a higher order unit, otherwise 3640 * the overall value would be negative, see third example) 3641 * - fractional values for higher-order units are converted to lower-order units (if possible, see fourth example) 3642 * 3643 * If the overall value is negative, the result of this method is equivalent to `this.negate().normalize().negate()`. 3644 * @example Duration.fromObject({ years: 2, days: 5000 }).normalize().toObject() //=> { years: 15, days: 255 } 3645 * @example Duration.fromObject({ days: 5000 }).normalize().toObject() //=> { days: 5000 } 3646 * @example Duration.fromObject({ hours: 12, minutes: -45 }).normalize().toObject() //=> { hours: 11, minutes: 15 } 3647 * @example Duration.fromObject({ years: 2.5, days: 0, hours: 0 }).normalize().toObject() //=> { years: 2, days: 182, hours: 12 } 3648 * @return {Duration} 3649 */ 3650 normalize() { 3651 if (!this.isValid) return this; 3652 const vals = this.toObject(); 3653 normalizeValues(this.matrix, vals); 3654 return clone$1(this, { 3655 values: vals 3656 }, true); 3657 } 3658 3659 /** 3660 * Rescale units to its largest representation 3661 * @example Duration.fromObject({ milliseconds: 90000 }).rescale().toObject() //=> { minutes: 1, seconds: 30 } 3662 * @return {Duration} 3663 */ 3664 rescale() { 3665 if (!this.isValid) return this; 3666 const vals = removeZeroes(this.normalize().shiftToAll().toObject()); 3667 return clone$1(this, { 3668 values: vals 3669 }, true); 3670 } 3671 3672 /** 3673 * Convert this Duration into its representation in a different set of units. 3674 * @example Duration.fromObject({ hours: 1, seconds: 30 }).shiftTo('minutes', 'milliseconds').toObject() //=> { minutes: 60, milliseconds: 30000 } 3675 * @return {Duration} 3676 */ 3677 shiftTo(...units) { 3678 if (!this.isValid) return this; 3679 if (units.length === 0) { 3680 return this; 3681 } 3682 units = units.map(u => Duration.normalizeUnit(u)); 3683 const built = {}, 3684 accumulated = {}, 3685 vals = this.toObject(); 3686 let lastUnit; 3687 for (const k of orderedUnits$1) { 3688 if (units.indexOf(k) >= 0) { 3689 lastUnit = k; 3690 let own = 0; 3691 3692 // anything we haven't boiled down yet should get boiled to this unit 3693 for (const ak in accumulated) { 3694 own += this.matrix[ak][k] * accumulated[ak]; 3695 accumulated[ak] = 0; 3696 } 3697 3698 // plus anything that's already in this unit 3699 if (isNumber(vals[k])) { 3700 own += vals[k]; 3701 } 3702 3703 // only keep the integer part for now in the hopes of putting any decimal part 3704 // into a smaller unit later 3705 const i = Math.trunc(own); 3706 built[k] = i; 3707 accumulated[k] = (own * 1000 - i * 1000) / 1000; 3708 3709 // otherwise, keep it in the wings to boil it later 3710 } else if (isNumber(vals[k])) { 3711 accumulated[k] = vals[k]; 3712 } 3713 } 3714 3715 // anything leftover becomes the decimal for the last unit 3716 // lastUnit must be defined since units is not empty 3717 for (const key in accumulated) { 3718 if (accumulated[key] !== 0) { 3719 built[lastUnit] += key === lastUnit ? accumulated[key] : accumulated[key] / this.matrix[lastUnit][key]; 3720 } 3721 } 3722 normalizeValues(this.matrix, built); 3723 return clone$1(this, { 3724 values: built 3725 }, true); 3726 } 3727 3728 /** 3729 * Shift this Duration to all available units. 3730 * Same as shiftTo("years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds") 3731 * @return {Duration} 3732 */ 3733 shiftToAll() { 3734 if (!this.isValid) return this; 3735 return this.shiftTo("years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds"); 3736 } 3737 3738 /** 3739 * Return the negative of this Duration. 3740 * @example Duration.fromObject({ hours: 1, seconds: 30 }).negate().toObject() //=> { hours: -1, seconds: -30 } 3741 * @return {Duration} 3742 */ 3743 negate() { 3744 if (!this.isValid) return this; 3745 const negated = {}; 3746 for (const k of Object.keys(this.values)) { 3747 negated[k] = this.values[k] === 0 ? 0 : -this.values[k]; 3748 } 3749 return clone$1(this, { 3750 values: negated 3751 }, true); 3752 } 3753 3754 /** 3755 * Removes all units with values equal to 0 from this Duration. 3756 * @example Duration.fromObject({ years: 2, days: 0, hours: 0, minutes: 0 }).removeZeros().toObject() //=> { years: 2 } 3757 * @return {Duration} 3758 */ 3759 removeZeros() { 3760 if (!this.isValid) return this; 3761 const vals = removeZeroes(this.values); 3762 return clone$1(this, { 3763 values: vals 3764 }, true); 3765 } 3766 3767 /** 3768 * Get the years. 3769 * @type {number} 3770 */ 3771 get years() { 3772 return this.isValid ? this.values.years || 0 : NaN; 3773 } 3774 3775 /** 3776 * Get the quarters. 3777 * @type {number} 3778 */ 3779 get quarters() { 3780 return this.isValid ? this.values.quarters || 0 : NaN; 3781 } 3782 3783 /** 3784 * Get the months. 3785 * @type {number} 3786 */ 3787 get months() { 3788 return this.isValid ? this.values.months || 0 : NaN; 3789 } 3790 3791 /** 3792 * Get the weeks 3793 * @type {number} 3794 */ 3795 get weeks() { 3796 return this.isValid ? this.values.weeks || 0 : NaN; 3797 } 3798 3799 /** 3800 * Get the days. 3801 * @type {number} 3802 */ 3803 get days() { 3804 return this.isValid ? this.values.days || 0 : NaN; 3805 } 3806 3807 /** 3808 * Get the hours. 3809 * @type {number} 3810 */ 3811 get hours() { 3812 return this.isValid ? this.values.hours || 0 : NaN; 3813 } 3814 3815 /** 3816 * Get the minutes. 3817 * @type {number} 3818 */ 3819 get minutes() { 3820 return this.isValid ? this.values.minutes || 0 : NaN; 3821 } 3822 3823 /** 3824 * Get the seconds. 3825 * @return {number} 3826 */ 3827 get seconds() { 3828 return this.isValid ? this.values.seconds || 0 : NaN; 3829 } 3830 3831 /** 3832 * Get the milliseconds. 3833 * @return {number} 3834 */ 3835 get milliseconds() { 3836 return this.isValid ? this.values.milliseconds || 0 : NaN; 3837 } 3838 3839 /** 3840 * Returns whether the Duration is invalid. Invalid durations are returned by diff operations 3841 * on invalid DateTimes or Intervals. 3842 * @return {boolean} 3843 */ 3844 get isValid() { 3845 return this.invalid === null; 3846 } 3847 3848 /** 3849 * Returns an error code if this Duration became invalid, or null if the Duration is valid 3850 * @return {string} 3851 */ 3852 get invalidReason() { 3853 return this.invalid ? this.invalid.reason : null; 3854 } 3855 3856 /** 3857 * Returns an explanation of why this Duration became invalid, or null if the Duration is valid 3858 * @type {string} 3859 */ 3860 get invalidExplanation() { 3861 return this.invalid ? this.invalid.explanation : null; 3862 } 3863 3864 /** 3865 * Equality check 3866 * Two Durations are equal iff they have the same units and the same values for each unit. 3867 * @param {Duration} other 3868 * @return {boolean} 3869 */ 3870 equals(other) { 3871 if (!this.isValid || !other.isValid) { 3872 return false; 3873 } 3874 if (!this.loc.equals(other.loc)) { 3875 return false; 3876 } 3877 function eq(v1, v2) { 3878 // Consider 0 and undefined as equal 3879 if (v1 === undefined || v1 === 0) return v2 === undefined || v2 === 0; 3880 return v1 === v2; 3881 } 3882 for (const u of orderedUnits$1) { 3883 if (!eq(this.values[u], other.values[u])) { 3884 return false; 3885 } 3886 } 3887 return true; 3888 } 3889} 3890 3891const INVALID$1 = "Invalid Interval"; 3892 3893// checks if the start is equal to or before the end 3894function validateStartEnd(start, end) { 3895 if (!start || !start.isValid) { 3896 return Interval.invalid("missing or invalid start"); 3897 } else if (!end || !end.isValid) { 3898 return Interval.invalid("missing or invalid end"); 3899 } else if (end < start) { 3900 return Interval.invalid("end before start", `The end of an interval must be after its start, but you had start=${start.toISO()} and end=${end.toISO()}`); 3901 } else { 3902 return null; 3903 } 3904} 3905 3906/** 3907 * An Interval object represents a half-open interval of time, where each endpoint is a {@link DateTime}. Conceptually, it's a container for those two endpoints, accompanied by methods for creating, parsing, interrogating, comparing, transforming, and formatting them. 3908 * 3909 * Here is a brief overview of the most commonly used methods and getters in Interval: 3910 * 3911 * * **Creation** To create an Interval, use {@link Interval.fromDateTimes}, {@link Interval.after}, {@link Interval.before}, or {@link Interval.fromISO}. 3912 * * **Accessors** Use {@link Interval#start} and {@link Interval#end} to get the start and end. 3913 * * **Interrogation** To analyze the Interval, use {@link Interval#count}, {@link Interval#length}, {@link Interval#hasSame}, {@link Interval#contains}, {@link Interval#isAfter}, or {@link Interval#isBefore}. 3914 * * **Transformation** To create other Intervals out of this one, use {@link Interval#set}, {@link Interval#splitAt}, {@link Interval#splitBy}, {@link Interval#divideEqually}, {@link Interval.merge}, {@link Interval.xor}, {@link Interval#union}, {@link Interval#intersection}, or {@link Interval#difference}. 3915 * * **Comparison** To compare this Interval to another one, use {@link Interval#equals}, {@link Interval#overlaps}, {@link Interval#abutsStart}, {@link Interval#abutsEnd}, {@link Interval#engulfs} 3916 * * **Output** To convert the Interval into other representations, see {@link Interval#toString}, {@link Interval#toLocaleString}, {@link Interval#toISO}, {@link Interval#toISODate}, {@link Interval#toISOTime}, {@link Interval#toFormat}, and {@link Interval#toDuration}. 3917 */ 3918class Interval { 3919 /** 3920 * @private 3921 */ 3922 constructor(config) { 3923 /** 3924 * @access private 3925 */ 3926 this.s = config.start; 3927 /** 3928 * @access private 3929 */ 3930 this.e = config.end; 3931 /** 3932 * @access private 3933 */ 3934 this.invalid = config.invalid || null; 3935 /** 3936 * @access private 3937 */ 3938 this.isLuxonInterval = true; 3939 } 3940 3941 /** 3942 * Create an invalid Interval. 3943 * @param {string} reason - simple string of why this Interval is invalid. Should not contain parameters or anything else data-dependent 3944 * @param {string} [explanation=null] - longer explanation, may include parameters and other useful debugging information 3945 * @return {Interval} 3946 */ 3947 static invalid(reason, explanation = null) { 3948 if (!reason) { 3949 throw new InvalidArgumentError("need to specify a reason the Interval is invalid"); 3950 } 3951 const invalid = reason instanceof Invalid ? reason : new Invalid(reason, explanation); 3952 if (Settings.throwOnInvalid) { 3953 throw new InvalidIntervalError(invalid); 3954 } else { 3955 return new Interval({ 3956 invalid 3957 }); 3958 } 3959 } 3960 3961 /** 3962 * Create an Interval from a start DateTime and an end DateTime. Inclusive of the start but not the end. 3963 * @param {DateTime|Date|Object} start 3964 * @param {DateTime|Date|Object} end 3965 * @return {Interval} 3966 */ 3967 static fromDateTimes(start, end) { 3968 const builtStart = friendlyDateTime(start), 3969 builtEnd = friendlyDateTime(end); 3970 const validateError = validateStartEnd(builtStart, builtEnd); 3971 if (validateError == null) { 3972 return new Interval({ 3973 start: builtStart, 3974 end: builtEnd 3975 }); 3976 } else { 3977 return validateError; 3978 } 3979 } 3980 3981 /** 3982 * Create an Interval from a start DateTime and a Duration to extend to. 3983 * @param {DateTime|Date|Object} start 3984 * @param {Duration|Object|number} duration - the length of the Interval. 3985 * @return {Interval} 3986 */ 3987 static after(start, duration) { 3988 const dur = Duration.fromDurationLike(duration), 3989 dt = friendlyDateTime(start); 3990 return Interval.fromDateTimes(dt, dt.plus(dur)); 3991 } 3992 3993 /** 3994 * Create an Interval from an end DateTime and a Duration to extend backwards to. 3995 * @param {DateTime|Date|Object} end 3996 * @param {Duration|Object|number} duration - the length of the Interval. 3997 * @return {Interval} 3998 */ 3999 static before(end, duration) { 4000 const dur = Duration.fromDurationLike(duration), 4001 dt = friendlyDateTime(end); 4002 return Interval.fromDateTimes(dt.minus(dur), dt); 4003 } 4004 4005 /** 4006 * Create an Interval from an ISO 8601 string. 4007 * Accepts `<start>/<end>`, `<start>/<duration>`, and `<duration>/<end>` formats. 4008 * @param {string} text - the ISO string to parse 4009 * @param {Object} [opts] - options to pass {@link DateTime#fromISO} and optionally {@link Duration#fromISO} 4010 * @see https://en.wikipedia.org/wiki/ISO_8601#Time_intervals 4011 * @return {Interval} 4012 */ 4013 static fromISO(text, opts) { 4014 const [s, e] = (text || "").split("/", 2); 4015 if (s && e) { 4016 let start, startIsValid; 4017 try { 4018 start = DateTime.fromISO(s, opts); 4019 startIsValid = start.isValid; 4020 } catch (e) { 4021 startIsValid = false; 4022 } 4023 let end, endIsValid; 4024 try { 4025 end = DateTime.fromISO(e, opts); 4026 endIsValid = end.isValid; 4027 } catch (e) { 4028 endIsValid = false; 4029 } 4030 if (startIsValid && endIsValid) { 4031 return Interval.fromDateTimes(start, end); 4032 } 4033 if (startIsValid) { 4034 const dur = Duration.fromISO(e, opts); 4035 if (dur.isValid) { 4036 return Interval.after(start, dur); 4037 } 4038 } else if (endIsValid) { 4039 const dur = Duration.fromISO(s, opts); 4040 if (dur.isValid) { 4041 return Interval.before(end, dur); 4042 } 4043 } 4044 } 4045 return Interval.invalid("unparsable", `the input "${text}" can't be parsed as ISO 8601`); 4046 } 4047 4048 /** 4049 * Check if an object is an Interval. Works across context boundaries 4050 * @param {object} o 4051 * @return {boolean} 4052 */ 4053 static isInterval(o) { 4054 return o && o.isLuxonInterval || false; 4055 } 4056 4057 /** 4058 * Returns the start of the Interval 4059 * @type {DateTime} 4060 */ 4061 get start() { 4062 return this.isValid ? this.s : null; 4063 } 4064 4065 /** 4066 * Returns the end of the Interval. This is the first instant which is not part of the interval 4067 * (Interval is half-open). 4068 * @type {DateTime} 4069 */ 4070 get end() { 4071 return this.isValid ? this.e : null; 4072 } 4073 4074 /** 4075 * Returns the last DateTime included in the interval (since end is not part of the interval) 4076 * @type {DateTime} 4077 */ 4078 get lastDateTime() { 4079 return this.isValid ? this.e ? this.e.minus(1) : null : null; 4080 } 4081 4082 /** 4083 * Returns whether this Interval's end is at least its start, meaning that the Interval isn't 'backwards'. 4084 * @type {boolean} 4085 */ 4086 get isValid() { 4087 return this.invalidReason === null; 4088 } 4089 4090 /** 4091 * Returns an error code if this Interval is invalid, or null if the Interval is valid 4092 * @type {string} 4093 */ 4094 get invalidReason() { 4095 return this.invalid ? this.invalid.reason : null; 4096 } 4097 4098 /** 4099 * Returns an explanation of why this Interval became invalid, or null if the Interval is valid 4100 * @type {string} 4101 */ 4102 get invalidExplanation() { 4103 return this.invalid ? this.invalid.explanation : null; 4104 } 4105 4106 /** 4107 * Returns the length of the Interval in the specified unit. 4108 * @param {string} unit - the unit (such as 'hours' or 'days') to return the length in. 4109 * @return {number} 4110 */ 4111 length(unit = "milliseconds") { 4112 return this.isValid ? this.toDuration(...[unit]).get(unit) : NaN; 4113 } 4114 4115 /** 4116 * Returns the count of minutes, hours, days, months, or years included in the Interval, even in part. 4117 * Unlike {@link Interval#length} this counts sections of the calendar, not periods of time, e.g. specifying 'day' 4118 * asks 'what dates are included in this interval?', not 'how many days long is this interval?' 4119 * @param {string} [unit='milliseconds'] - the unit of time to count. 4120 * @param {Object} opts - options 4121 * @param {boolean} [opts.useLocaleWeeks=false] - If true, use weeks based on the locale, i.e. use the locale-dependent start of the week; this operation will always use the locale of the start DateTime 4122 * @return {number} 4123 */ 4124 count(unit = "milliseconds", opts) { 4125 if (!this.isValid) return NaN; 4126 const start = this.start.startOf(unit, opts); 4127 let end; 4128 if (opts != null && opts.useLocaleWeeks) { 4129 end = this.end.reconfigure({ 4130 locale: start.locale 4131 }); 4132 } else { 4133 end = this.end; 4134 } 4135 end = end.startOf(unit, opts); 4136 return Math.floor(end.diff(start, unit).get(unit)) + (end.valueOf() !== this.end.valueOf()); 4137 } 4138 4139 /** 4140 * Returns whether this Interval's start and end are both in the same unit of time 4141 * @param {string} unit - the unit of time to check sameness on 4142 * @return {boolean} 4143 */ 4144 hasSame(unit) { 4145 return this.isValid ? this.isEmpty() || this.e.minus(1).hasSame(this.s, unit) : false; 4146 } 4147 4148 /** 4149 * Return whether this Interval has the same start and end DateTimes. 4150 * @return {boolean} 4151 */ 4152 isEmpty() { 4153 return this.s.valueOf() === this.e.valueOf(); 4154 } 4155 4156 /** 4157 * Return whether this Interval's start is after the specified DateTime. 4158 * @param {DateTime} dateTime 4159 * @return {boolean} 4160 */ 4161 isAfter(dateTime) { 4162 if (!this.isValid) return false; 4163 return this.s > dateTime; 4164 } 4165 4166 /** 4167 * Return whether this Interval's end is before the specified DateTime. 4168 * @param {DateTime} dateTime 4169 * @return {boolean} 4170 */ 4171 isBefore(dateTime) { 4172 if (!this.isValid) return false; 4173 return this.e <= dateTime; 4174 } 4175 4176 /** 4177 * Return whether this Interval contains the specified DateTime. 4178 * @param {DateTime} dateTime 4179 * @return {boolean} 4180 */ 4181 contains(dateTime) { 4182 if (!this.isValid) return false; 4183 return this.s <= dateTime && this.e > dateTime; 4184 } 4185 4186 /** 4187 * "Sets" the start and/or end dates. Returns a newly-constructed Interval. 4188 * @param {Object} values - the values to set 4189 * @param {DateTime} values.start - the starting DateTime 4190 * @param {DateTime} values.end - the ending DateTime 4191 * @return {Interval} 4192 */ 4193 set({ 4194 start, 4195 end 4196 } = {}) { 4197 if (!this.isValid) return this; 4198 return Interval.fromDateTimes(start || this.s, end || this.e); 4199 } 4200 4201 /** 4202 * Split this Interval at each of the specified DateTimes 4203 * @param {...DateTime} dateTimes - the unit of time to count. 4204 * @return {Array} 4205 */ 4206 splitAt(...dateTimes) { 4207 if (!this.isValid) return []; 4208 const sorted = dateTimes.map(friendlyDateTime).filter(d => this.contains(d)).sort((a, b) => a.toMillis() - b.toMillis()), 4209 results = []; 4210 let { 4211 s 4212 } = this, 4213 i = 0; 4214 while (s < this.e) { 4215 const added = sorted[i] || this.e, 4216 next = +added > +this.e ? this.e : added; 4217 results.push(Interval.fromDateTimes(s, next)); 4218 s = next; 4219 i += 1; 4220 } 4221 return results; 4222 } 4223 4224 /** 4225 * Split this Interval into smaller Intervals, each of the specified length. 4226 * Left over time is grouped into a smaller interval 4227 * @param {Duration|Object|number} duration - The length of each resulting interval. 4228 * @return {Array} 4229 */ 4230 splitBy(duration) { 4231 const dur = Duration.fromDurationLike(duration); 4232 if (!this.isValid || !dur.isValid || dur.as("milliseconds") === 0) { 4233 return []; 4234 } 4235 let { 4236 s 4237 } = this, 4238 idx = 1, 4239 next; 4240 const results = []; 4241 while (s < this.e) { 4242 const added = this.start.plus(dur.mapUnits(x => x * idx)); 4243 next = +added > +this.e ? this.e : added; 4244 results.push(Interval.fromDateTimes(s, next)); 4245 s = next; 4246 idx += 1; 4247 } 4248 return results; 4249 } 4250 4251 /** 4252 * Split this Interval into the specified number of smaller intervals. 4253 * @param {number} numberOfParts - The number of Intervals to divide the Interval into. 4254 * @return {Array} 4255 */ 4256 divideEqually(numberOfParts) { 4257 if (!this.isValid) return []; 4258 return this.splitBy(this.length() / numberOfParts).slice(0, numberOfParts); 4259 } 4260 4261 /** 4262 * Return whether this Interval overlaps with the specified Interval 4263 * @param {Interval} other 4264 * @return {boolean} 4265 */ 4266 overlaps(other) { 4267 return this.e > other.s && this.s < other.e; 4268 } 4269 4270 /** 4271 * Return whether this Interval's end is adjacent to the specified Interval's start. 4272 * @param {Interval} other 4273 * @return {boolean} 4274 */ 4275 abutsStart(other) { 4276 if (!this.isValid) return false; 4277 return +this.e === +other.s; 4278 } 4279 4280 /** 4281 * Return whether this Interval's start is adjacent to the specified Interval's end. 4282 * @param {Interval} other 4283 * @return {boolean} 4284 */ 4285 abutsEnd(other) { 4286 if (!this.isValid) return false; 4287 return +other.e === +this.s; 4288 } 4289 4290 /** 4291 * Returns true if this Interval fully contains the specified Interval, specifically if the intersect (of this Interval and the other Interval) is equal to the other Interval; false otherwise. 4292 * @param {Interval} other 4293 * @return {boolean} 4294 */ 4295 engulfs(other) { 4296 if (!this.isValid) return false; 4297 return this.s <= other.s && this.e >= other.e; 4298 } 4299 4300 /** 4301 * Return whether this Interval has the same start and end as the specified Interval. 4302 * @param {Interval} other 4303 * @return {boolean} 4304 */ 4305 equals(other) { 4306 if (!this.isValid || !other.isValid) { 4307 return false; 4308 } 4309 return this.s.equals(other.s) && this.e.equals(other.e); 4310 } 4311 4312 /** 4313 * Return an Interval representing the intersection of this Interval and the specified Interval. 4314 * Specifically, the resulting Interval has the maximum start time and the minimum end time of the two Intervals. 4315 * Returns null if the intersection is empty, meaning, the intervals don't intersect. 4316 * @param {Interval} other 4317 * @return {Interval} 4318 */ 4319 intersection(other) { 4320 if (!this.isValid) return this; 4321 const s = this.s > other.s ? this.s : other.s, 4322 e = this.e < other.e ? this.e : other.e; 4323 if (s >= e) { 4324 return null; 4325 } else { 4326 return Interval.fromDateTimes(s, e); 4327 } 4328 } 4329 4330 /** 4331 * Return an Interval representing the union of this Interval and the specified Interval. 4332 * Specifically, the resulting Interval has the minimum start time and the maximum end time of the two Intervals. 4333 * @param {Interval} other 4334 * @return {Interval} 4335 */ 4336 union(other) { 4337 if (!this.isValid) return this; 4338 const s = this.s < other.s ? this.s : other.s, 4339 e = this.e > other.e ? this.e : other.e; 4340 return Interval.fromDateTimes(s, e); 4341 } 4342 4343 /** 4344 * Merge an array of Intervals into an equivalent minimal set of Intervals. 4345 * Combines overlapping and adjacent Intervals. 4346 * The resulting array will contain the Intervals in ascending order, that is, starting with the earliest Interval 4347 * and ending with the latest. 4348 * 4349 * @param {Array} intervals 4350 * @return {Array} 4351 */ 4352 static merge(intervals) { 4353 const [found, final] = intervals.sort((a, b) => a.s - b.s).reduce(([sofar, current], item) => { 4354 if (!current) { 4355 return [sofar, item]; 4356 } else if (current.overlaps(item) || current.abutsStart(item)) { 4357 return [sofar, current.union(item)]; 4358 } else { 4359 return [sofar.concat([current]), item]; 4360 } 4361 }, [[], null]); 4362 if (final) { 4363 found.push(final); 4364 } 4365 return found; 4366 } 4367 4368 /** 4369 * Return an array of Intervals representing the spans of time that only appear in one of the specified Intervals. 4370 * @param {Array} intervals 4371 * @return {Array} 4372 */ 4373 static xor(intervals) { 4374 let start = null, 4375 currentCount = 0; 4376 const results = [], 4377 ends = intervals.map(i => [{ 4378 time: i.s, 4379 type: "s" 4380 }, { 4381 time: i.e, 4382 type: "e" 4383 }]), 4384 flattened = Array.prototype.concat(...ends), 4385 arr = flattened.sort((a, b) => a.time - b.time); 4386 for (const i of arr) { 4387 currentCount += i.type === "s" ? 1 : -1; 4388 if (currentCount === 1) { 4389 start = i.time; 4390 } else { 4391 if (start && +start !== +i.time) { 4392 results.push(Interval.fromDateTimes(start, i.time)); 4393 } 4394 start = null; 4395 } 4396 } 4397 return Interval.merge(results); 4398 } 4399 4400 /** 4401 * Return an Interval representing the span of time in this Interval that doesn't overlap with any of the specified Intervals. 4402 * @param {...Interval} intervals 4403 * @return {Array} 4404 */ 4405 difference(...intervals) { 4406 return Interval.xor([this].concat(intervals)).map(i => this.intersection(i)).filter(i => i && !i.isEmpty()); 4407 } 4408 4409 /** 4410 * Returns a string representation of this Interval appropriate for debugging. 4411 * @return {string} 4412 */ 4413 toString() { 4414 if (!this.isValid) return INVALID$1; 4415 return `[${this.s.toISO()} â ${this.e.toISO()})`; 4416 } 4417 4418 /** 4419 * Returns a string representation of this Interval appropriate for the REPL. 4420 * @return {string} 4421 */ 4422 [Symbol.for("nodejs.util.inspect.custom")]() { 4423 if (this.isValid) { 4424 return `Interval { start: ${this.s.toISO()}, end: ${this.e.toISO()} }`; 4425 } else { 4426 return `Interval { Invalid, reason: ${this.invalidReason} }`; 4427 } 4428 } 4429 4430 /** 4431 * Returns a localized string representing this Interval. Accepts the same options as the 4432 * Intl.DateTimeFormat constructor and any presets defined by Luxon, such as 4433 * {@link DateTime.DATE_FULL} or {@link DateTime.TIME_SIMPLE}. The exact behavior of this method 4434 * is browser-specific, but in general it will return an appropriate representation of the 4435 * Interval in the assigned locale. Defaults to the system's locale if no locale has been 4436 * specified. 4437 * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat 4438 * @param {Object} [formatOpts=DateTime.DATE_SHORT] - Either a DateTime preset or 4439 * Intl.DateTimeFormat constructor options. 4440 * @param {Object} opts - Options to override the configuration of the start DateTime. 4441 * @example Interval.fromISO('2022-11-07T09:00Z/2022-11-08T09:00Z').toLocaleString(); //=> 11/7/2022 â 11/8/2022 4442 * @example Interval.fromISO('2022-11-07T09:00Z/2022-11-08T09:00Z').toLocaleString(DateTime.DATE_FULL); //=> November 7 â 8, 2022 4443 * @example Interval.fromISO('2022-11-07T09:00Z/2022-11-08T09:00Z').toLocaleString(DateTime.DATE_FULL, { locale: 'fr-FR' }); //=> 7â8 novembre 2022 4444 * @example Interval.fromISO('2022-11-07T17:00Z/2022-11-07T19:00Z').toLocaleString(DateTime.TIME_SIMPLE); //=> 6:00 â 8:00 PM 4445 * @example Interval.fromISO('2022-11-07T17:00Z/2022-11-07T19:00Z').toLocaleString({ weekday: 'short', month: 'short', day: '2-digit', hour: '2-digit', minute: '2-digit' }); //=> Mon, Nov 07, 6:00 â 8:00 p 4446 * @return {string} 4447 */ 4448 toLocaleString(formatOpts = DATE_SHORT, opts = {}) { 4449 return this.isValid ? Formatter.create(this.s.loc.clone(opts), formatOpts).formatInterval(this) : INVALID$1; 4450 } 4451 4452 /** 4453 * Returns an ISO 8601-compliant string representation of this Interval. 4454 * @see https://en.wikipedia.org/wiki/ISO_8601#Time_intervals 4455 * @param {Object} opts - The same options as {@link DateTime#toISO} 4456 * @return {string} 4457 */ 4458 toISO(opts) { 4459 if (!this.isValid) return INVALID$1; 4460 return `${this.s.toISO(opts)}/${this.e.toISO(opts)}`; 4461 } 4462 4463 /** 4464 * Returns an ISO 8601-compliant string representation of date of this Interval. 4465 * The time components are ignored. 4466 * @see https://en.wikipedia.org/wiki/ISO_8601#Time_intervals 4467 * @return {string} 4468 */ 4469 toISODate() { 4470 if (!this.isValid) return INVALID$1; 4471 return `${this.s.toISODate()}/${this.e.toISODate()}`; 4472 } 4473 4474 /** 4475 * Returns an ISO 8601-compliant string representation of time of this Interval. 4476 * The date components are ignored. 4477 * @see https://en.wikipedia.org/wiki/ISO_8601#Time_intervals 4478 * @param {Object} opts - The same options as {@link DateTime#toISO} 4479 * @return {string} 4480 */ 4481 toISOTime(opts) { 4482 if (!this.isValid) return INVALID$1; 4483 return `${this.s.toISOTime(opts)}/${this.e.toISOTime(opts)}`; 4484 } 4485 4486 /** 4487 * Returns a string representation of this Interval formatted according to the specified format 4488 * string. **You may not want this.** See {@link Interval#toLocaleString} for a more flexible 4489 * formatting tool. 4490 * @param {string} dateFormat - The format string. This string formats the start and end time. 4491 * See {@link DateTime#toFormat} for details. 4492 * @param {Object} opts - Options. 4493 * @param {string} [opts.separator = ' â '] - A separator to place between the start and end 4494 * representations. 4495 * @return {string} 4496 */ 4497 toFormat(dateFormat, { 4498 separator = " â " 4499 } = {}) { 4500 if (!this.isValid) return INVALID$1; 4501 return `${this.s.toFormat(dateFormat)}${separator}${this.e.toFormat(dateFormat)}`; 4502 } 4503 4504 /** 4505 * Return a Duration representing the time spanned by this interval. 4506 * @param {string|string[]} [unit=['milliseconds']] - the unit or units (such as 'hours' or 'days') to include in the duration. 4507 * @param {Object} opts - options that affect the creation of the Duration 4508 * @param {string} [opts.conversionAccuracy='casual'] - the conversion system to use 4509 * @example Interval.fromDateTimes(dt1, dt2).toDuration().toObject() //=> { milliseconds: 88489257 } 4510 * @example Interval.fromDateTimes(dt1, dt2).toDuration('days').toObject() //=> { days: 1.0241812152777778 } 4511 * @example Interval.fromDateTimes(dt1, dt2).toDuration(['hours', 'minutes']).toObject() //=> { hours: 24, minutes: 34.82095 } 4512 * @example Interval.fromDateTimes(dt1, dt2).toDuration(['hours', 'minutes', 'seconds']).toObject() //=> { hours: 24, minutes: 34, seconds: 49.257 } 4513 * @example Interval.fromDateTimes(dt1, dt2).toDuration('seconds').toObject() //=> { seconds: 88489.257 } 4514 * @return {Duration} 4515 */ 4516 toDuration(unit, opts) { 4517 if (!this.isValid) { 4518 return Duration.invalid(this.invalidReason); 4519 } 4520 return this.e.diff(this.s, unit, opts); 4521 } 4522 4523 /** 4524 * Run mapFn on the interval start and end, returning a new Interval from the resulting DateTimes 4525 * @param {function} mapFn 4526 * @return {Interval} 4527 * @example Interval.fromDateTimes(dt1, dt2).mapEndpoints(endpoint => endpoint.toUTC()) 4528 * @example Interval.fromDateTimes(dt1, dt2).mapEndpoints(endpoint => endpoint.plus({ hours: 2 })) 4529 */ 4530 mapEndpoints(mapFn) { 4531 return Interval.fromDateTimes(mapFn(this.s), mapFn(this.e)); 4532 } 4533} 4534 4535/** 4536 * The Info class contains static methods for retrieving general time and date related data. For example, it has methods for finding out if a time zone has a DST, for listing the months in any supported locale, and for discovering which of Luxon features are available in the current environment. 4537 */ 4538class Info { 4539 /** 4540 * Return whether the specified zone contains a DST. 4541 * @param {string|Zone} [zone='local'] - Zone to check. Defaults to the environment's local zone. 4542 * @return {boolean} 4543 */ 4544 static hasDST(zone = Settings.defaultZone) { 4545 const proto = DateTime.now().setZone(zone).set({ 4546 month: 12 4547 }); 4548 return !zone.isUniversal && proto.offset !== proto.set({ 4549 month: 6 4550 }).offset; 4551 } 4552 4553 /** 4554 * Return whether the specified zone is a valid IANA specifier. 4555 * @param {string} zone - Zone to check 4556 * @return {boolean} 4557 */ 4558 static isValidIANAZone(zone) { 4559 return IANAZone.isValidZone(zone); 4560 } 4561 4562 /** 4563 * Converts the input into a {@link Zone} instance. 4564 * 4565 * * If `input` is already a Zone instance, it is returned unchanged. 4566 * * If `input` is a string containing a valid time zone name, a Zone instance 4567 * with that name is returned. 4568 * * If `input` is a string that doesn't refer to a known time zone, a Zone 4569 * instance with {@link Zone#isValid} == false is returned. 4570 * * If `input is a number, a Zone instance with the specified fixed offset 4571 * in minutes is returned. 4572 * * If `input` is `null` or `undefined`, the default zone is returned. 4573 * @param {string|Zone|number} [input] - the value to be converted 4574 * @return {Zone} 4575 */ 4576 static normalizeZone(input) { 4577 return normalizeZone(input, Settings.defaultZone); 4578 } 4579 4580 /** 4581 * Get the weekday on which the week starts according to the given locale. 4582 * @param {Object} opts - options 4583 * @param {string} [opts.locale] - the locale code 4584 * @param {string} [opts.locObj=null] - an existing locale object to use 4585 * @returns {number} the start of the week, 1 for Monday through 7 for Sunday 4586 */ 4587 static getStartOfWeek({ 4588 locale = null, 4589 locObj = null 4590 } = {}) { 4591 return (locObj || Locale.create(locale)).getStartOfWeek(); 4592 } 4593 4594 /** 4595 * Get the minimum number of days necessary in a week before it is considered part of the next year according 4596 * to the given locale. 4597 * @param {Object} opts - options 4598 * @param {string} [opts.locale] - the locale code 4599 * @param {string} [opts.locObj=null] - an existing locale object to use 4600 * @returns {number} 4601 */ 4602 static getMinimumDaysInFirstWeek({ 4603 locale = null, 4604 locObj = null 4605 } = {}) { 4606 return (locObj || Locale.create(locale)).getMinDaysInFirstWeek(); 4607 } 4608 4609 /** 4610 * Get the weekdays, which are considered the weekend according to the given locale 4611 * @param {Object} opts - options 4612 * @param {string} [opts.locale] - the locale code 4613 * @param {string} [opts.locObj=null] - an existing locale object to use 4614 * @returns {number[]} an array of weekdays, 1 for Monday through 7 for Sunday 4615 */ 4616 static getWeekendWeekdays({ 4617 locale = null, 4618 locObj = null 4619 } = {}) { 4620 // copy the array, because we cache it internally 4621 return (locObj || Locale.create(locale)).getWeekendDays().slice(); 4622 } 4623 4624 /** 4625 * Return an array of standalone month names. 4626 * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat 4627 * @param {string} [length='long'] - the length of the month representation, such as "numeric", "2-digit", "narrow", "short", "long" 4628 * @param {Object} opts - options 4629 * @param {string} [opts.locale] - the locale code 4630 * @param {string} [opts.numberingSystem=null] - the numbering system 4631 * @param {string} [opts.locObj=null] - an existing locale object to use 4632 * @param {string} [opts.outputCalendar='gregory'] - the calendar 4633 * @example Info.months()[0] //=> 'January' 4634 * @example Info.months('short')[0] //=> 'Jan' 4635 * @example Info.months('numeric')[0] //=> '1' 4636 * @example Info.months('short', { locale: 'fr-CA' } )[0] //=> 'janv.' 4637 * @example Info.months('numeric', { locale: 'ar' })[0] //=> 'Ù¡' 4638 * @example Info.months('long', { outputCalendar: 'islamic' })[0] //=> 'RabiÊ» I' 4639 * @return {Array} 4640 */ 4641 static months(length = "long", { 4642 locale = null, 4643 numberingSystem = null, 4644 locObj = null, 4645 outputCalendar = "gregory" 4646 } = {}) { 4647 return (locObj || Locale.create(locale, numberingSystem, outputCalendar)).months(length); 4648 } 4649 4650 /** 4651 * Return an array of format month names. 4652 * Format months differ from standalone months in that they're meant to appear next to the day of the month. In some languages, that 4653 * changes the string. 4654 * See {@link Info#months} 4655 * @param {string} [length='long'] - the length of the month representation, such as "numeric", "2-digit", "narrow", "short", "long" 4656 * @param {Object} opts - options 4657 * @param {string} [opts.locale] - the locale code 4658 * @param {string} [opts.numberingSystem=null] - the numbering system 4659 * @param {string} [opts.locObj=null] - an existing locale object to use 4660 * @param {string} [opts.outputCalendar='gregory'] - the calendar 4661 * @return {Array} 4662 */ 4663 static monthsFormat(length = "long", { 4664 locale = null, 4665 numberingSystem = null, 4666 locObj = null, 4667 outputCalendar = "gregory" 4668 } = {}) { 4669 return (locObj || Locale.create(locale, numberingSystem, outputCalendar)).months(length, true); 4670 } 4671 4672 /** 4673 * Return an array of standalone week names. 4674 * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat 4675 * @param {string} [length='long'] - the length of the weekday representation, such as "narrow", "short", "long". 4676 * @param {Object} opts - options 4677 * @param {string} [opts.locale] - the locale code 4678 * @param {string} [opts.numberingSystem=null] - the numbering system 4679 * @param {string} [opts.locObj=null] - an existing locale object to use 4680 * @example Info.weekdays()[0] //=> 'Monday' 4681 * @example Info.weekdays('short')[0] //=> 'Mon' 4682 * @example Info.weekdays('short', { locale: 'fr-CA' })[0] //=> 'lun.' 4683 * @example Info.weekdays('short', { locale: 'ar' })[0] //=> 'Ø§ÙØ§Ø«ÙÙÙ' 4684 * @return {Array} 4685 */ 4686 static weekdays(length = "long", { 4687 locale = null, 4688 numberingSystem = null, 4689 locObj = null 4690 } = {}) { 4691 return (locObj || Locale.create(locale, numberingSystem, null)).weekdays(length); 4692 } 4693 4694 /** 4695 * Return an array of format week names. 4696 * Format weekdays differ from standalone weekdays in that they're meant to appear next to more date information. In some languages, that 4697 * changes the string. 4698 * See {@link Info#weekdays} 4699 * @param {string} [length='long'] - the length of the month representation, such as "narrow", "short", "long". 4700 * @param {Object} opts - options 4701 * @param {string} [opts.locale=null] - the locale code 4702 * @param {string} [opts.numberingSystem=null] - the numbering system 4703 * @param {string} [opts.locObj=null] - an existing locale object to use 4704 * @return {Array} 4705 */ 4706 static weekdaysFormat(length = "long", { 4707 locale = null, 4708 numberingSystem = null, 4709 locObj = null 4710 } = {}) { 4711 return (locObj || Locale.create(locale, numberingSystem, null)).weekdays(length, true); 4712 } 4713 4714 /** 4715 * Return an array of meridiems. 4716 * @param {Object} opts - options 4717 * @param {string} [opts.locale] - the locale code 4718 * @example Info.meridiems() //=> [ 'AM', 'PM' ] 4719 * @example Info.meridiems({ locale: 'my' }) //=> [ 'áá¶áááº', 'ááá±' ] 4720 * @return {Array} 4721 */ 4722 static meridiems({ 4723 locale = null 4724 } = {}) { 4725 return Locale.create(locale).meridiems(); 4726 } 4727 4728 /** 4729 * Return an array of eras, such as ['BC', 'AD']. The locale can be specified, but the calendar system is always Gregorian. 4730 * @param {string} [length='short'] - the length of the era representation, such as "short" or "long". 4731 * @param {Object} opts - options 4732 * @param {string} [opts.locale] - the locale code 4733 * @example Info.eras() //=> [ 'BC', 'AD' ] 4734 * @example Info.eras('long') //=> [ 'Before Christ', 'Anno Domini' ] 4735 * @example Info.eras('long', { locale: 'fr' }) //=> [ 'avant Jésus-Christ', 'après Jésus-Christ' ] 4736 * @return {Array} 4737 */ 4738 static eras(length = "short", { 4739 locale = null 4740 } = {}) { 4741 return Locale.create(locale, null, "gregory").eras(length); 4742 } 4743 4744 /** 4745 * Return the set of available features in this environment. 4746 * Some features of Luxon are not available in all environments. For example, on older browsers, relative time formatting support is not available. Use this function to figure out if that's the case. 4747 * Keys: 4748 * * `relative`: whether this environment supports relative time formatting 4749 * * `localeWeek`: whether this environment supports different weekdays for the start of the week based on the locale 4750 * @example Info.features() //=> { relative: false, localeWeek: true } 4751 * @return {Object} 4752 */ 4753 static features() { 4754 return { 4755 relative: hasRelative(), 4756 localeWeek: hasLocaleWeekInfo() 4757 }; 4758 } 4759} 4760 4761function dayDiff(earlier, later) { 4762 const utcDayStart = dt => dt.toUTC(0, { 4763 keepLocalTime: true 4764 }).startOf("day").valueOf(), 4765 ms = utcDayStart(later) - utcDayStart(earlier); 4766 return Math.floor(Duration.fromMillis(ms).as("days")); 4767} 4768function highOrderDiffs(cursor, later, units) { 4769 const differs = [["years", (a, b) => b.year - a.year], ["quarters", (a, b) => b.quarter - a.quarter + (b.year - a.year) * 4], ["months", (a, b) => b.month - a.month + (b.year - a.year) * 12], ["weeks", (a, b) => { 4770 const days = dayDiff(a, b); 4771 return (days - days % 7) / 7; 4772 }], ["days", dayDiff]]; 4773 const results = {}; 4774 const earlier = cursor; 4775 let lowestOrder, highWater; 4776 4777 /* This loop tries to diff using larger units first. 4778 If we overshoot, we backtrack and try the next smaller unit. 4779 "cursor" starts out at the earlier timestamp and moves closer and closer to "later" 4780 as we use smaller and smaller units. 4781 highWater keeps track of where we would be if we added one more of the smallest unit, 4782 this is used later to potentially convert any difference smaller than the smallest higher order unit 4783 into a fraction of that smallest higher order unit 4784 */ 4785 for (const [unit, differ] of differs) { 4786 if (units.indexOf(unit) >= 0) { 4787 lowestOrder = unit; 4788 results[unit] = differ(cursor, later); 4789 highWater = earlier.plus(results); 4790 if (highWater > later) { 4791 // we overshot the end point, backtrack cursor by 1 4792 results[unit]--; 4793 cursor = earlier.plus(results); 4794 4795 // if we are still overshooting now, we need to backtrack again 4796 // this happens in certain situations when diffing times in different zones, 4797 // because this calculation ignores time zones 4798 if (cursor > later) { 4799 // keep the "overshot by 1" around as highWater 4800 highWater = cursor; 4801 // backtrack cursor by 1 4802 results[unit]--; 4803 cursor = earlier.plus(results); 4804 } 4805 } else { 4806 cursor = highWater; 4807 } 4808 } 4809 } 4810 return [cursor, results, highWater, lowestOrder]; 4811} 4812function diff (earlier, later, units, opts) { 4813 let [cursor, results, highWater, lowestOrder] = highOrderDiffs(earlier, later, units); 4814 const remainingMillis = later - cursor; 4815 const lowerOrderUnits = units.filter(u => ["hours", "minutes", "seconds", "milliseconds"].indexOf(u) >= 0); 4816 if (lowerOrderUnits.length === 0) { 4817 if (highWater < later) { 4818 highWater = cursor.plus({ 4819 [lowestOrder]: 1 4820 }); 4821 } 4822 if (highWater !== cursor) { 4823 results[lowestOrder] = (results[lowestOrder] || 0) + remainingMillis / (highWater - cursor); 4824 } 4825 } 4826 const duration = Duration.fromObject(results, opts); 4827 if (lowerOrderUnits.length > 0) { 4828 return Duration.fromMillis(remainingMillis, opts).shiftTo(...lowerOrderUnits).plus(duration); 4829 } else { 4830 return duration; 4831 } 4832} 4833 4834const MISSING_FTP = "missing Intl.DateTimeFormat.formatToParts support"; 4835function intUnit(regex, post = i => i) { 4836 return { 4837 regex, 4838 deser: ([s]) => post(parseDigits(s)) 4839 }; 4840} 4841const NBSP = String.fromCharCode(160); 4842const spaceOrNBSP = `[ ${NBSP}]`; 4843const spaceOrNBSPRegExp = new RegExp(spaceOrNBSP, "g"); 4844function fixListRegex(s) { 4845 // make dots optional and also make them literal 4846 // make space and non breakable space characters interchangeable 4847 return s.replace(/\./g, "\\.?").replace(spaceOrNBSPRegExp, spaceOrNBSP); 4848} 4849function stripInsensitivities(s) { 4850 return s.replace(/\./g, "") // ignore dots that were made optional 4851 .replace(spaceOrNBSPRegExp, " ") // interchange space and nbsp 4852 .toLowerCase(); 4853} 4854function oneOf(strings, startIndex) { 4855 if (strings === null) { 4856 return null; 4857 } else { 4858 return { 4859 regex: RegExp(strings.map(fixListRegex).join("|")), 4860 deser: ([s]) => strings.findIndex(i => stripInsensitivities(s) === stripInsensitivities(i)) + startIndex 4861 }; 4862 } 4863} 4864function offset(regex, groups) { 4865 return { 4866 regex, 4867 deser: ([, h, m]) => signedOffset(h, m), 4868 groups 4869 }; 4870} 4871function simple(regex) { 4872 return { 4873 regex, 4874 deser: ([s]) => s 4875 }; 4876} 4877function escapeToken(value) { 4878 return value.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&"); 4879} 4880 4881/** 4882 * @param token 4883 * @param {Locale} loc 4884 */ 4885function unitForToken(token, loc) { 4886 const one = digitRegex(loc), 4887 two = digitRegex(loc, "{2}"), 4888 three = digitRegex(loc, "{3}"), 4889 four = digitRegex(loc, "{4}"), 4890 six = digitRegex(loc, "{6}"), 4891 oneOrTwo = digitRegex(loc, "{1,2}"), 4892 oneToThree = digitRegex(loc, "{1,3}"), 4893 oneToSix = digitRegex(loc, "{1,6}"), 4894 oneToNine = digitRegex(loc, "{1,9}"), 4895 twoToFour = digitRegex(loc, "{2,4}"), 4896 fourToSix = digitRegex(loc, "{4,6}"), 4897 literal = t => ({ 4898 regex: RegExp(escapeToken(t.val)), 4899 deser: ([s]) => s, 4900 literal: true 4901 }), 4902 unitate = t => { 4903 if (token.literal) { 4904 return literal(t); 4905 } 4906 switch (t.val) { 4907 // era 4908 case "G": 4909 return oneOf(loc.eras("short"), 0); 4910 case "GG": 4911 return oneOf(loc.eras("long"), 0); 4912 // years 4913 case "y": 4914 return intUnit(oneToSix); 4915 case "yy": 4916 return intUnit(twoToFour, untruncateYear); 4917 case "yyyy": 4918 return intUnit(four); 4919 case "yyyyy": 4920 return intUnit(fourToSix); 4921 case "yyyyyy": 4922 return intUnit(six); 4923 // months 4924 case "M": 4925 return intUnit(oneOrTwo); 4926 case "MM": 4927 return intUnit(two); 4928 case "MMM": 4929 return oneOf(loc.months("short", true), 1); 4930 case "MMMM": 4931 return oneOf(loc.months("long", true), 1); 4932 case "L": 4933 return intUnit(oneOrTwo); 4934 case "LL": 4935 return intUnit(two); 4936 case "LLL": 4937 return oneOf(loc.months("short", false), 1); 4938 case "LLLL": 4939 return oneOf(loc.months("long", false), 1); 4940 // dates 4941 case "d": 4942 return intUnit(oneOrTwo); 4943 case "dd": 4944 return intUnit(two); 4945 // ordinals 4946 case "o": 4947 return intUnit(oneToThree); 4948 case "ooo": 4949 return intUnit(three); 4950 // time 4951 case "HH": 4952 return intUnit(two); 4953 case "H": 4954 return intUnit(oneOrTwo); 4955 case "hh": 4956 return intUnit(two); 4957 case "h": 4958 return intUnit(oneOrTwo); 4959 case "mm": 4960 return intUnit(two); 4961 case "m": 4962 return intUnit(oneOrTwo); 4963 case "q": 4964 return intUnit(oneOrTwo); 4965 case "qq": 4966 return intUnit(two); 4967 case "s": 4968 return intUnit(oneOrTwo); 4969 case "ss": 4970 return intUnit(two); 4971 case "S": 4972 return intUnit(oneToThree); 4973 case "SSS": 4974 return intUnit(three); 4975 case "u": 4976 return simple(oneToNine); 4977 case "uu": 4978 return simple(oneOrTwo); 4979 case "uuu": 4980 return intUnit(one); 4981 // meridiem 4982 case "a": 4983 return oneOf(loc.meridiems(), 0); 4984 // weekYear (k) 4985 case "kkkk": 4986 return intUnit(four); 4987 case "kk": 4988 return intUnit(twoToFour, untruncateYear); 4989 // weekNumber (W) 4990 case "W": 4991 return intUnit(oneOrTwo); 4992 case "WW": 4993 return intUnit(two); 4994 // weekdays 4995 case "E": 4996 case "c": 4997 return intUnit(one); 4998 case "EEE": 4999 return oneOf(loc.weekdays("short", false), 1); 5000 case "EEEE": 5001 return oneOf(loc.weekdays("long", false), 1); 5002 case "ccc": 5003 return oneOf(loc.weekdays("short", true), 1); 5004 case "cccc": 5005 return oneOf(loc.weekdays("long", true), 1); 5006 // offset/zone 5007 case "Z": 5008 case "ZZ": 5009 return offset(new RegExp(`([+-]${oneOrTwo.source})(?::(${two.source}))?`), 2); 5010 case "ZZZ": 5011 return offset(new RegExp(`([+-]${oneOrTwo.source})(${two.source})?`), 2); 5012 // we don't support ZZZZ (PST) or ZZZZZ (Pacific Standard Time) in parsing 5013 // because we don't have any way to figure out what they are 5014 case "z": 5015 return simple(/[a-z_+-/]{1,256}?/i); 5016 // this special-case "token" represents a place where a macro-token expanded into a white-space literal 5017 // in this case we accept any non-newline white-space 5018 case " ": 5019 return simple(/[^\S\n\r]/); 5020 default: 5021 return literal(t); 5022 } 5023 }; 5024 const unit = unitate(token) || { 5025 invalidReason: MISSING_FTP 5026 }; 5027 unit.token = token; 5028 return unit; 5029} 5030const partTypeStyleToTokenVal = { 5031 year: { 5032 "2-digit": "yy", 5033 numeric: "yyyyy" 5034 }, 5035 month: { 5036 numeric: "M", 5037 "2-digit": "MM", 5038 short: "MMM", 5039 long: "MMMM" 5040 }, 5041 day: { 5042 numeric: "d", 5043 "2-digit": "dd" 5044 }, 5045 weekday: { 5046 short: "EEE", 5047 long: "EEEE" 5048 }, 5049 dayperiod: "a", 5050 dayPeriod: "a", 5051 hour12: { 5052 numeric: "h", 5053 "2-digit": "hh" 5054 }, 5055 hour24: { 5056 numeric: "H", 5057 "2-digit": "HH" 5058 }, 5059 minute: { 5060 numeric: "m", 5061 "2-digit": "mm" 5062 }, 5063 second: { 5064 numeric: "s", 5065 "2-digit": "ss" 5066 }, 5067 timeZoneName: { 5068 long: "ZZZZZ", 5069 short: "ZZZ" 5070 } 5071}; 5072function tokenForPart(part, formatOpts, resolvedOpts) { 5073 const { 5074 type, 5075 value 5076 } = part; 5077 if (type === "literal") { 5078 const isSpace = /^\s+$/.test(value); 5079 return { 5080 literal: !isSpace, 5081 val: isSpace ? " " : value 5082 }; 5083 } 5084 const style = formatOpts[type]; 5085 5086 // The user might have explicitly specified hour12 or hourCycle 5087 // if so, respect their decision 5088 // if not, refer back to the resolvedOpts, which are based on the locale 5089 let actualType = type; 5090 if (type === "hour") { 5091 if (formatOpts.hour12 != null) { 5092 actualType = formatOpts.hour12 ? "hour12" : "hour24"; 5093 } else if (formatOpts.hourCycle != null) { 5094 if (formatOpts.hourCycle === "h11" || formatOpts.hourCycle === "h12") { 5095 actualType = "hour12"; 5096 } else { 5097 actualType = "hour24"; 5098 } 5099 } else { 5100 // tokens only differentiate between 24 hours or not, 5101 // so we do not need to check hourCycle here, which is less supported anyways 5102 actualType = resolvedOpts.hour12 ? "hour12" : "hour24"; 5103 } 5104 } 5105 let val = partTypeStyleToTokenVal[actualType]; 5106 if (typeof val === "object") { 5107 val = val[style]; 5108 } 5109 if (val) { 5110 return { 5111 literal: false, 5112 val 5113 }; 5114 } 5115 return undefined; 5116} 5117function buildRegex(units) { 5118 const re = units.map(u => u.regex).reduce((f, r) => `${f}(${r.source})`, ""); 5119 return [`^${re}$`, units]; 5120} 5121function match(input, regex, handlers) { 5122 const matches = input.match(regex); 5123 if (matches) { 5124 const all = {}; 5125 let matchIndex = 1; 5126 for (const i in handlers) { 5127 if (hasOwnProperty(handlers, i)) { 5128 const h = handlers[i], 5129 groups = h.groups ? h.groups + 1 : 1; 5130 if (!h.literal && h.token) { 5131 all[h.token.val[0]] = h.deser(matches.slice(matchIndex, matchIndex + groups)); 5132 } 5133 matchIndex += groups; 5134 } 5135 } 5136 return [matches, all]; 5137 } else { 5138 return [matches, {}]; 5139 } 5140} 5141function dateTimeFromMatches(matches) { 5142 const toField = token => { 5143 switch (token) { 5144 case "S": 5145 return "millisecond"; 5146 case "s": 5147 return "second"; 5148 case "m": 5149 return "minute"; 5150 case "h": 5151 case "H": 5152 return "hour"; 5153 case "d": 5154 return "day"; 5155 case "o": 5156 return "ordinal"; 5157 case "L": 5158 case "M": 5159 return "month"; 5160 case "y": 5161 return "year"; 5162 case "E": 5163 case "c": 5164 return "weekday"; 5165 case "W": 5166 return "weekNumber"; 5167 case "k": 5168 return "weekYear"; 5169 case "q": 5170 return "quarter"; 5171 default: 5172 return null; 5173 } 5174 }; 5175 let zone = null; 5176 let specificOffset; 5177 if (!isUndefined(matches.z)) { 5178 zone = IANAZone.create(matches.z); 5179 } 5180 if (!isUndefined(matches.Z)) { 5181 if (!zone) { 5182 zone = new FixedOffsetZone(matches.Z); 5183 } 5184 specificOffset = matches.Z; 5185 } 5186 if (!isUndefined(matches.q)) { 5187 matches.M = (matches.q - 1) * 3 + 1; 5188 } 5189 if (!isUndefined(matches.h)) { 5190 if (matches.h < 12 && matches.a === 1) { 5191 matches.h += 12; 5192 } else if (matches.h === 12 && matches.a === 0) { 5193 matches.h = 0; 5194 } 5195 } 5196 if (matches.G === 0 && matches.y) { 5197 matches.y = -matches.y; 5198 } 5199 if (!isUndefined(matches.u)) { 5200 matches.S = parseMillis(matches.u); 5201 } 5202 const vals = Object.keys(matches).reduce((r, k) => { 5203 const f = toField(k); 5204 if (f) { 5205 r[f] = matches[k]; 5206 } 5207 return r; 5208 }, {}); 5209 return [vals, zone, specificOffset]; 5210} 5211let dummyDateTimeCache = null; 5212function getDummyDateTime() { 5213 if (!dummyDateTimeCache) { 5214 dummyDateTimeCache = DateTime.fromMillis(1555555555555); 5215 } 5216 return dummyDateTimeCache; 5217} 5218function maybeExpandMacroToken(token, locale) { 5219 if (token.literal) { 5220 return token; 5221 } 5222 const formatOpts = Formatter.macroTokenToFormatOpts(token.val); 5223 const tokens = formatOptsToTokens(formatOpts, locale); 5224 if (tokens == null || tokens.includes(undefined)) { 5225 return token; 5226 } 5227 return tokens; 5228} 5229function expandMacroTokens(tokens, locale) { 5230 return Array.prototype.concat(...tokens.map(t => maybeExpandMacroToken(t, locale))); 5231} 5232 5233/** 5234 * @private 5235 */ 5236 5237class TokenParser { 5238 constructor(locale, format) { 5239 this.locale = locale; 5240 this.format = format; 5241 this.tokens = expandMacroTokens(Formatter.parseFormat(format), locale); 5242 this.units = this.tokens.map(t => unitForToken(t, locale)); 5243 this.disqualifyingUnit = this.units.find(t => t.invalidReason); 5244 if (!this.disqualifyingUnit) { 5245 const [regexString, handlers] = buildRegex(this.units); 5246 this.regex = RegExp(regexString, "i"); 5247 this.handlers = handlers; 5248 } 5249 } 5250 explainFromTokens(input) { 5251 if (!this.isValid) { 5252 return { 5253 input, 5254 tokens: this.tokens, 5255 invalidReason: this.invalidReason 5256 }; 5257 } else { 5258 const [rawMatches, matches] = match(input, this.regex, this.handlers), 5259 [result, zone, specificOffset] = matches ? dateTimeFromMatches(matches) : [null, null, undefined]; 5260 if (hasOwnProperty(matches, "a") && hasOwnProperty(matches, "H")) { 5261 throw new ConflictingSpecificationError("Can't include meridiem when specifying 24-hour format"); 5262 } 5263 return { 5264 input, 5265 tokens: this.tokens, 5266 regex: this.regex, 5267 rawMatches, 5268 matches, 5269 result, 5270 zone, 5271 specificOffset 5272 }; 5273 } 5274 } 5275 get isValid() { 5276 return !this.disqualifyingUnit; 5277 } 5278 get invalidReason() { 5279 return this.disqualifyingUnit ? this.disqualifyingUnit.invalidReason : null; 5280 } 5281} 5282function explainFromTokens(locale, input, format) { 5283 const parser = new TokenParser(locale, format); 5284 return parser.explainFromTokens(input); 5285} 5286function parseFromTokens(locale, input, format) { 5287 const { 5288 result, 5289 zone, 5290 specificOffset, 5291 invalidReason 5292 } = explainFromTokens(locale, input, format); 5293 return [result, zone, specificOffset, invalidReason]; 5294} 5295function formatOptsToTokens(formatOpts, locale) { 5296 if (!formatOpts) { 5297 return null; 5298 } 5299 const formatter = Formatter.create(locale, formatOpts); 5300 const df = formatter.dtFormatter(getDummyDateTime()); 5301 const parts = df.formatToParts(); 5302 const resolvedOpts = df.resolvedOptions(); 5303 return parts.map(p => tokenForPart(p, formatOpts, resolvedOpts)); 5304} 5305 5306const INVALID = "Invalid DateTime"; 5307const MAX_DATE = 8.64e15; 5308function unsupportedZone(zone) { 5309 return new Invalid("unsupported zone", `the zone "${zone.name}" is not supported`); 5310} 5311 5312// we cache week data on the DT object and this intermediates the cache 5313/** 5314 * @param {DateTime} dt 5315 */ 5316function possiblyCachedWeekData(dt) { 5317 if (dt.weekData === null) { 5318 dt.weekData = gregorianToWeek(dt.c); 5319 } 5320 return dt.weekData; 5321} 5322 5323/** 5324 * @param {DateTime} dt 5325 */ 5326function possiblyCachedLocalWeekData(dt) { 5327 if (dt.localWeekData === null) { 5328 dt.localWeekData = gregorianToWeek(dt.c, dt.loc.getMinDaysInFirstWeek(), dt.loc.getStartOfWeek()); 5329 } 5330 return dt.localWeekData; 5331} 5332 5333// clone really means, "make a new object with these modifications". all "setters" really use this 5334// to create a new object while only changing some of the properties 5335function clone(inst, alts) { 5336 const current = { 5337 ts: inst.ts, 5338 zone: inst.zone, 5339 c: inst.c, 5340 o: inst.o, 5341 loc: inst.loc, 5342 invalid: inst.invalid 5343 }; 5344 return new DateTime({ 5345 ...current, 5346 ...alts, 5347 old: current 5348 }); 5349} 5350 5351// find the right offset a given local time. The o input is our guess, which determines which 5352// offset we'll pick in ambiguous cases (e.g. there are two 3 AMs b/c Fallback DST) 5353function fixOffset(localTS, o, tz) { 5354 // Our UTC time is just a guess because our offset is just a guess 5355 let utcGuess = localTS - o * 60 * 1000; 5356 5357 // Test whether the zone matches the offset for this ts 5358 const o2 = tz.offset(utcGuess); 5359 5360 // If so, offset didn't change and we're done 5361 if (o === o2) { 5362 return [utcGuess, o]; 5363 } 5364 5365 // If not, change the ts by the difference in the offset 5366 utcGuess -= (o2 - o) * 60 * 1000; 5367 5368 // If that gives us the local time we want, we're done 5369 const o3 = tz.offset(utcGuess); 5370 if (o2 === o3) { 5371 return [utcGuess, o2]; 5372 } 5373 5374 // If it's different, we're in a hole time. The offset has changed, but the we don't adjust the time 5375 return [localTS - Math.min(o2, o3) * 60 * 1000, Math.max(o2, o3)]; 5376} 5377 5378// convert an epoch timestamp into a calendar object with the given offset 5379function tsToObj(ts, offset) { 5380 ts += offset * 60 * 1000; 5381 const d = new Date(ts); 5382 return { 5383 year: d.getUTCFullYear(), 5384 month: d.getUTCMonth() + 1, 5385 day: d.getUTCDate(), 5386 hour: d.getUTCHours(), 5387 minute: d.getUTCMinutes(), 5388 second: d.getUTCSeconds(), 5389 millisecond: d.getUTCMilliseconds() 5390 }; 5391} 5392 5393// convert a calendar object to a epoch timestamp 5394function objToTS(obj, offset, zone) { 5395 return fixOffset(objToLocalTS(obj), offset, zone); 5396} 5397 5398// create a new DT instance by adding a duration, adjusting for DSTs 5399function adjustTime(inst, dur) { 5400 const oPre = inst.o, 5401 year = inst.c.year + Math.trunc(dur.years), 5402 month = inst.c.month + Math.trunc(dur.months) + Math.trunc(dur.quarters) * 3, 5403 c = { 5404 ...inst.c, 5405 year, 5406 month, 5407 day: Math.min(inst.c.day, daysInMonth(year, month)) + Math.trunc(dur.days) + Math.trunc(dur.weeks) * 7 5408 }, 5409 millisToAdd = Duration.fromObject({ 5410 years: dur.years - Math.trunc(dur.years), 5411 quarters: dur.quarters - Math.trunc(dur.quarters), 5412 months: dur.months - Math.trunc(dur.months), 5413 weeks: dur.weeks - Math.trunc(dur.weeks), 5414 days: dur.days - Math.trunc(dur.days), 5415 hours: dur.hours, 5416 minutes: dur.minutes, 5417 seconds: dur.seconds, 5418 milliseconds: dur.milliseconds 5419 }).as("milliseconds"), 5420 localTS = objToLocalTS(c); 5421 let [ts, o] = fixOffset(localTS, oPre, inst.zone); 5422 if (millisToAdd !== 0) { 5423 ts += millisToAdd; 5424 // that could have changed the offset by going over a DST, but we want to keep the ts the same 5425 o = inst.zone.offset(ts); 5426 } 5427 return { 5428 ts, 5429 o 5430 }; 5431} 5432 5433// helper useful in turning the results of parsing into real dates 5434// by handling the zone options 5435function parseDataToDateTime(parsed, parsedZone, opts, format, text, specificOffset) { 5436 const { 5437 setZone, 5438 zone 5439 } = opts; 5440 if (parsed && Object.keys(parsed).length !== 0 || parsedZone) { 5441 const interpretationZone = parsedZone || zone, 5442 inst = DateTime.fromObject(parsed, { 5443 ...opts, 5444 zone: interpretationZone, 5445 specificOffset 5446 }); 5447 return setZone ? inst : inst.setZone(zone); 5448 } else { 5449 return DateTime.invalid(new Invalid("unparsable", `the input "${text}" can't be parsed as ${format}`)); 5450 } 5451} 5452 5453// if you want to output a technical format (e.g. RFC 2822), this helper 5454// helps handle the details 5455function toTechFormat(dt, format, allowZ = true) { 5456 return dt.isValid ? Formatter.create(Locale.create("en-US"), { 5457 allowZ, 5458 forceSimple: true 5459 }).formatDateTimeFromString(dt, format) : null; 5460} 5461function toISODate(o, extended, precision) { 5462 const longFormat = o.c.year > 9999 || o.c.year < 0; 5463 let c = ""; 5464 if (longFormat && o.c.year >= 0) c += "+"; 5465 c += padStart(o.c.year, longFormat ? 6 : 4); 5466 if (precision === "year") return c; 5467 if (extended) { 5468 c += "-"; 5469 c += padStart(o.c.month); 5470 if (precision === "month") return c; 5471 c += "-"; 5472 } else { 5473 c += padStart(o.c.month); 5474 if (precision === "month") return c; 5475 } 5476 c += padStart(o.c.day); 5477 return c; 5478} 5479function toISOTime(o, extended, suppressSeconds, suppressMilliseconds, includeOffset, extendedZone, precision) { 5480 let showSeconds = !suppressSeconds || o.c.millisecond !== 0 || o.c.second !== 0, 5481 c = ""; 5482 switch (precision) { 5483 case "day": 5484 case "month": 5485 case "year": 5486 break; 5487 default: 5488 c += padStart(o.c.hour); 5489 if (precision === "hour") break; 5490 if (extended) { 5491 c += ":"; 5492 c += padStart(o.c.minute); 5493 if (precision === "minute") break; 5494 if (showSeconds) { 5495 c += ":"; 5496 c += padStart(o.c.second); 5497 } 5498 } else { 5499 c += padStart(o.c.minute); 5500 if (precision === "minute") break; 5501 if (showSeconds) { 5502 c += padStart(o.c.second); 5503 } 5504 } 5505 if (precision === "second") break; 5506 if (showSeconds && (!suppressMilliseconds || o.c.millisecond !== 0)) { 5507 c += "."; 5508 c += padStart(o.c.millisecond, 3); 5509 } 5510 } 5511 if (includeOffset) { 5512 if (o.isOffsetFixed && o.offset === 0 && !extendedZone) { 5513 c += "Z"; 5514 } else if (o.o < 0) { 5515 c += "-"; 5516 c += padStart(Math.trunc(-o.o / 60)); 5517 c += ":"; 5518 c += padStart(Math.trunc(-o.o % 60)); 5519 } else { 5520 c += "+"; 5521 c += padStart(Math.trunc(o.o / 60)); 5522 c += ":"; 5523 c += padStart(Math.trunc(o.o % 60)); 5524 } 5525 } 5526 if (extendedZone) { 5527 c += "[" + o.zone.ianaName + "]"; 5528 } 5529 return c; 5530} 5531 5532// defaults for unspecified units in the supported calendars 5533const defaultUnitValues = { 5534 month: 1, 5535 day: 1, 5536 hour: 0, 5537 minute: 0, 5538 second: 0, 5539 millisecond: 0 5540 }, 5541 defaultWeekUnitValues = { 5542 weekNumber: 1, 5543 weekday: 1, 5544 hour: 0, 5545 minute: 0, 5546 second: 0, 5547 millisecond: 0 5548 }, 5549 defaultOrdinalUnitValues = { 5550 ordinal: 1, 5551 hour: 0, 5552 minute: 0, 5553 second: 0, 5554 millisecond: 0 5555 }; 5556 5557// Units in the supported calendars, sorted by bigness 5558const orderedUnits = ["year", "month", "day", "hour", "minute", "second", "millisecond"], 5559 orderedWeekUnits = ["weekYear", "weekNumber", "weekday", "hour", "minute", "second", "millisecond"], 5560 orderedOrdinalUnits = ["year", "ordinal", "hour", "minute", "second", "millisecond"]; 5561 5562// standardize case and plurality in units 5563function normalizeUnit(unit) { 5564 const normalized = { 5565 year: "year", 5566 years: "year", 5567 month: "month", 5568 months: "month", 5569 day: "day", 5570 days: "day", 5571 hour: "hour", 5572 hours: "hour", 5573 minute: "minute", 5574 minutes: "minute", 5575 quarter: "quarter", 5576 quarters: "quarter", 5577 second: "second", 5578 seconds: "second", 5579 millisecond: "millisecond", 5580 milliseconds: "millisecond", 5581 weekday: "weekday", 5582 weekdays: "weekday", 5583 weeknumber: "weekNumber", 5584 weeksnumber: "weekNumber", 5585 weeknumbers: "weekNumber", 5586 weekyear: "weekYear", 5587 weekyears: "weekYear", 5588 ordinal: "ordinal" 5589 }[unit.toLowerCase()]; 5590 if (!normalized) throw new InvalidUnitError(unit); 5591 return normalized; 5592} 5593function normalizeUnitWithLocalWeeks(unit) { 5594 switch (unit.toLowerCase()) { 5595 case "localweekday": 5596 case "localweekdays": 5597 return "localWeekday"; 5598 case "localweeknumber": 5599 case "localweeknumbers": 5600 return "localWeekNumber"; 5601 case "localweekyear": 5602 case "localweekyears": 5603 return "localWeekYear"; 5604 default: 5605 return normalizeUnit(unit); 5606 } 5607} 5608 5609// cache offsets for zones based on the current timestamp when this function is 5610// first called. When we are handling a datetime from components like (year, 5611// month, day, hour) in a time zone, we need a guess about what the timezone 5612// offset is so that we can convert into a UTC timestamp. One way is to find the 5613// offset of now in the zone. The actual date may have a different offset (for 5614// example, if we handle a date in June while we're in December in a zone that 5615// observes DST), but we can check and adjust that. 5616// 5617// When handling many dates, calculating the offset for now every time is 5618// expensive. It's just a guess, so we can cache the offset to use even if we 5619// are right on a time change boundary (we'll just correct in the other 5620// direction). Using a timestamp from first read is a slight optimization for 5621// handling dates close to the current date, since those dates will usually be 5622// in the same offset (we could set the timestamp statically, instead). We use a 5623// single timestamp for all zones to make things a bit more predictable. 5624// 5625// This is safe for quickDT (used by local() and utc()) because we don't fill in 5626// higher-order units from tsNow (as we do in fromObject, this requires that 5627// offset is calculated from tsNow). 5628/** 5629 * @param {Zone} zone 5630 * @return {number} 5631 */ 5632function guessOffsetForZone(zone) { 5633 if (zoneOffsetTs === undefined) { 5634 zoneOffsetTs = Settings.now(); 5635 } 5636 5637 // Do not cache anything but IANA zones, because it is not safe to do so. 5638 // Guessing an offset which is not present in the zone can cause wrong results from fixOffset 5639 if (zone.type !== "iana") { 5640 return zone.offset(zoneOffsetTs); 5641 } 5642 const zoneName = zone.name; 5643 let offsetGuess = zoneOffsetGuessCache.get(zoneName); 5644 if (offsetGuess === undefined) { 5645 offsetGuess = zone.offset(zoneOffsetTs); 5646 zoneOffsetGuessCache.set(zoneName, offsetGuess); 5647 } 5648 return offsetGuess; 5649} 5650 5651// this is a dumbed down version of fromObject() that runs about 60% faster 5652// but doesn't do any validation, makes a bunch of assumptions about what units 5653// are present, and so on. 5654function quickDT(obj, opts) { 5655 const zone = normalizeZone(opts.zone, Settings.defaultZone); 5656 if (!zone.isValid) { 5657 return DateTime.invalid(unsupportedZone(zone)); 5658 } 5659 const loc = Locale.fromObject(opts); 5660 let ts, o; 5661 5662 // assume we have the higher-order units 5663 if (!isUndefined(obj.year)) { 5664 for (const u of orderedUnits) { 5665 if (isUndefined(obj[u])) { 5666 obj[u] = defaultUnitValues[u]; 5667 } 5668 } 5669 const invalid = hasInvalidGregorianData(obj) || hasInvalidTimeData(obj); 5670 if (invalid) { 5671 return DateTime.invalid(invalid); 5672 } 5673 const offsetProvis = guessOffsetForZone(zone); 5674 [ts, o] = objToTS(obj, offsetProvis, zone); 5675 } else { 5676 ts = Settings.now(); 5677 } 5678 return new DateTime({ 5679 ts, 5680 zone, 5681 loc, 5682 o 5683 }); 5684} 5685function diffRelative(start, end, opts) { 5686 const round = isUndefined(opts.round) ? true : opts.round, 5687 rounding = isUndefined(opts.rounding) ? "trunc" : opts.rounding, 5688 format = (c, unit) => { 5689 c = roundTo(c, round || opts.calendary ? 0 : 2, opts.calendary ? "round" : rounding); 5690 const formatter = end.loc.clone(opts).relFormatter(opts); 5691 return formatter.format(c, unit); 5692 }, 5693 differ = unit => { 5694 if (opts.calendary) { 5695 if (!end.hasSame(start, unit)) { 5696 return end.startOf(unit).diff(start.startOf(unit), unit).get(unit); 5697 } else return 0; 5698 } else { 5699 return end.diff(start, unit).get(unit); 5700 } 5701 }; 5702 if (opts.unit) { 5703 return format(differ(opts.unit), opts.unit); 5704 } 5705 for (const unit of opts.units) { 5706 const count = differ(unit); 5707 if (Math.abs(count) >= 1) { 5708 return format(count, unit); 5709 } 5710 } 5711 return format(start > end ? -0 : 0, opts.units[opts.units.length - 1]); 5712} 5713function lastOpts(argList) { 5714 let opts = {}, 5715 args; 5716 if (argList.length > 0 && typeof argList[argList.length - 1] === "object") { 5717 opts = argList[argList.length - 1]; 5718 args = Array.from(argList).slice(0, argList.length - 1); 5719 } else { 5720 args = Array.from(argList); 5721 } 5722 return [opts, args]; 5723} 5724 5725/** 5726 * Timestamp to use for cached zone offset guesses (exposed for test) 5727 */ 5728let zoneOffsetTs; 5729/** 5730 * Cache for zone offset guesses (exposed for test). 5731 * 5732 * This optimizes quickDT via guessOffsetForZone to avoid repeated calls of 5733 * zone.offset(). 5734 */ 5735const zoneOffsetGuessCache = new Map(); 5736 5737/** 5738 * A DateTime is an immutable data structure representing a specific date and time and accompanying methods. It contains class and instance methods for creating, parsing, interrogating, transforming, and formatting them. 5739 * 5740 * A DateTime comprises of: 5741 * * A timestamp. Each DateTime instance refers to a specific millisecond of the Unix epoch. 5742 * * A time zone. Each instance is considered in the context of a specific zone (by default the local system's zone). 5743 * * Configuration properties that effect how output strings are formatted, such as `locale`, `numberingSystem`, and `outputCalendar`. 5744 * 5745 * Here is a brief overview of the most commonly used functionality it provides: 5746 * 5747 * * **Creation**: To create a DateTime from its components, use one of its factory class methods: {@link DateTime.local}, {@link DateTime.utc}, and (most flexibly) {@link DateTime.fromObject}. To create one from a standard string format, use {@link DateTime.fromISO}, {@link DateTime.fromHTTP}, and {@link DateTime.fromRFC2822}. To create one from a custom string format, use {@link DateTime.fromFormat}. To create one from a native JS date, use {@link DateTime.fromJSDate}. 5748 * * **Gregorian calendar and time**: To examine the Gregorian properties of a DateTime individually (i.e as opposed to collectively through {@link DateTime#toObject}), use the {@link DateTime#year}, {@link DateTime#month}, 5749 * {@link DateTime#day}, {@link DateTime#hour}, {@link DateTime#minute}, {@link DateTime#second}, {@link DateTime#millisecond} accessors. 5750 * * **Week calendar**: For ISO week calendar attributes, see the {@link DateTime#weekYear}, {@link DateTime#weekNumber}, and {@link DateTime#weekday} accessors. 5751 * * **Configuration** See the {@link DateTime#locale} and {@link DateTime#numberingSystem} accessors. 5752 * * **Transformation**: To transform the DateTime into other DateTimes, use {@link DateTime#set}, {@link DateTime#reconfigure}, {@link DateTime#setZone}, {@link DateTime#setLocale}, {@link DateTime.plus}, {@link DateTime#minus}, {@link DateTime#endOf}, {@link DateTime#startOf}, {@link DateTime#toUTC}, and {@link DateTime#toLocal}. 5753 * * **Output**: To convert the DateTime to other representations, use the {@link DateTime#toRelative}, {@link DateTime#toRelativeCalendar}, {@link DateTime#toJSON}, {@link DateTime#toISO}, {@link DateTime#toHTTP}, {@link DateTime#toObject}, {@link DateTime#toRFC2822}, {@link DateTime#toString}, {@link DateTime#toLocaleString}, {@link DateTime#toFormat}, {@link DateTime#toMillis} and {@link DateTime#toJSDate}. 5754 * 5755 * There's plenty others documented below. In addition, for more information on subtler topics like internationalization, time zones, alternative calendars, validity, and so on, see the external documentation. 5756 */ 5757class DateTime { 5758 /** 5759 * @access private 5760 */ 5761 constructor(config) { 5762 const zone = config.zone || Settings.defaultZone; 5763 let invalid = config.invalid || (Number.isNaN(config.ts) ? new Invalid("invalid input") : null) || (!zone.isValid ? unsupportedZone(zone) : null); 5764 /** 5765 * @access private 5766 */ 5767 this.ts = isUndefined(config.ts) ? Settings.now() : config.ts; 5768 let c = null, 5769 o = null; 5770 if (!invalid) { 5771 const unchanged = config.old && config.old.ts === this.ts && config.old.zone.equals(zone); 5772 if (unchanged) { 5773 [c, o] = [config.old.c, config.old.o]; 5774 } else { 5775 // If an offset has been passed and we have not been called from 5776 // clone(), we can trust it and avoid the offset calculation. 5777 const ot = isNumber(config.o) && !config.old ? config.o : zone.offset(this.ts); 5778 c = tsToObj(this.ts, ot); 5779 invalid = Number.isNaN(c.year) ? new Invalid("invalid input") : null; 5780 c = invalid ? null : c; 5781 o = invalid ? null : ot; 5782 } 5783 } 5784 5785 /** 5786 * @access private 5787 */ 5788 this._zone = zone; 5789 /** 5790 * @access private 5791 */ 5792 this.loc = config.loc || Locale.create(); 5793 /** 5794 * @access private 5795 */ 5796 this.invalid = invalid; 5797 /** 5798 * @access private 5799 */ 5800 this.weekData = null; 5801 /** 5802 * @access private 5803 */ 5804 this.localWeekData = null; 5805 /** 5806 * @access private 5807 */ 5808 this.c = c; 5809 /** 5810 * @access private 5811 */ 5812 this.o = o; 5813 /** 5814 * @access private 5815 */ 5816 this.isLuxonDateTime = true; 5817 } 5818 5819 // CONSTRUCT 5820 5821 /** 5822 * Create a DateTime for the current instant, in the system's time zone. 5823 * 5824 * Use Settings to override these default values if needed. 5825 * @example DateTime.now().toISO() //~> now in the ISO format 5826 * @return {DateTime} 5827 */ 5828 static now() { 5829 return new DateTime({}); 5830 } 5831 5832 /** 5833 * Create a local DateTime 5834 * @param {number} [year] - The calendar year. If omitted (as in, call `local()` with no arguments), the current time will be used 5835 * @param {number} [month=1] - The month, 1-indexed 5836 * @param {number} [day=1] - The day of the month, 1-indexed 5837 * @param {number} [hour=0] - The hour of the day, in 24-hour time 5838 * @param {number} [minute=0] - The minute of the hour, meaning a number between 0 and 59 5839 * @param {number} [second=0] - The second of the minute, meaning a number between 0 and 59 5840 * @param {number} [millisecond=0] - The millisecond of the second, meaning a number between 0 and 999 5841 * @example DateTime.local() //~> now 5842 * @example DateTime.local({ zone: "America/New_York" }) //~> now, in US east coast time 5843 * @example DateTime.local(2017) //~> 2017-01-01T00:00:00 5844 * @example DateTime.local(2017, 3) //~> 2017-03-01T00:00:00 5845 * @example DateTime.local(2017, 3, 12, { locale: "fr" }) //~> 2017-03-12T00:00:00, with a French locale 5846 * @example DateTime.local(2017, 3, 12, 5) //~> 2017-03-12T05:00:00 5847 * @example DateTime.local(2017, 3, 12, 5, { zone: "utc" }) //~> 2017-03-12T05:00:00, in UTC 5848 * @example DateTime.local(2017, 3, 12, 5, 45) //~> 2017-03-12T05:45:00 5849 * @example DateTime.local(2017, 3, 12, 5, 45, 10) //~> 2017-03-12T05:45:10 5850 * @example DateTime.local(2017, 3, 12, 5, 45, 10, 765) //~> 2017-03-12T05:45:10.765 5851 * @return {DateTime} 5852 */ 5853 static local() { 5854 const [opts, args] = lastOpts(arguments), 5855 [year, month, day, hour, minute, second, millisecond] = args; 5856 return quickDT({ 5857 year, 5858 month, 5859 day, 5860 hour, 5861 minute, 5862 second, 5863 millisecond 5864 }, opts); 5865 } 5866 5867 /** 5868 * Create a DateTime in UTC 5869 * @param {number} [year] - The calendar year. If omitted (as in, call `utc()` with no arguments), the current time will be used 5870 * @param {number} [month=1] - The month, 1-indexed 5871 * @param {number} [day=1] - The day of the month 5872 * @param {number} [hour=0] - The hour of the day, in 24-hour time 5873 * @param {number} [minute=0] - The minute of the hour, meaning a number between 0 and 59 5874 * @param {number} [second=0] - The second of the minute, meaning a number between 0 and 59 5875 * @param {number} [millisecond=0] - The millisecond of the second, meaning a number between 0 and 999 5876 * @param {Object} options - configuration options for the DateTime 5877 * @param {string} [options.locale] - a locale to set on the resulting DateTime instance 5878 * @param {string} [options.outputCalendar] - the output calendar to set on the resulting DateTime instance 5879 * @param {string} [options.numberingSystem] - the numbering system to set on the resulting DateTime instance 5880 * @param {string} [options.weekSettings] - the week settings to set on the resulting DateTime instance 5881 * @example DateTime.utc() //~> now 5882 * @example DateTime.utc(2017) //~> 2017-01-01T00:00:00Z 5883 * @example DateTime.utc(2017, 3) //~> 2017-03-01T00:00:00Z 5884 * @example DateTime.utc(2017, 3, 12) //~> 2017-03-12T00:00:00Z 5885 * @example DateTime.utc(2017, 3, 12, 5) //~> 2017-03-12T05:00:00Z 5886 * @example DateTime.utc(2017, 3, 12, 5, 45) //~> 2017-03-12T05:45:00Z 5887 * @example DateTime.utc(2017, 3, 12, 5, 45, { locale: "fr" }) //~> 2017-03-12T05:45:00Z with a French locale 5888 * @example DateTime.utc(2017, 3, 12, 5, 45, 10) //~> 2017-03-12T05:45:10Z 5889 * @example DateTime.utc(2017, 3, 12, 5, 45, 10, 765, { locale: "fr" }) //~> 2017-03-12T05:45:10.765Z with a French locale 5890 * @return {DateTime} 5891 */ 5892 static utc() { 5893 const [opts, args] = lastOpts(arguments), 5894 [year, month, day, hour, minute, second, millisecond] = args; 5895 opts.zone = FixedOffsetZone.utcInstance; 5896 return quickDT({ 5897 year, 5898 month, 5899 day, 5900 hour, 5901 minute, 5902 second, 5903 millisecond 5904 }, opts); 5905 } 5906 5907 /** 5908 * Create a DateTime from a JavaScript Date object. Uses the default zone. 5909 * @param {Date} date - a JavaScript Date object 5910 * @param {Object} options - configuration options for the DateTime 5911 * @param {string|Zone} [options.zone='local'] - the zone to place the DateTime into 5912 * @return {DateTime} 5913 */ 5914 static fromJSDate(date, options = {}) { 5915 const ts = isDate(date) ? date.valueOf() : NaN; 5916 if (Number.isNaN(ts)) { 5917 return DateTime.invalid("invalid input"); 5918 } 5919 const zoneToUse = normalizeZone(options.zone, Settings.defaultZone); 5920 if (!zoneToUse.isValid) { 5921 return DateTime.invalid(unsupportedZone(zoneToUse)); 5922 } 5923 return new DateTime({ 5924 ts: ts, 5925 zone: zoneToUse, 5926 loc: Locale.fromObject(options) 5927 }); 5928 } 5929 5930 /** 5931 * Create a DateTime from a number of milliseconds since the epoch (meaning since 1 January 1970 00:00:00 UTC). Uses the default zone. 5932 * @param {number} milliseconds - a number of milliseconds since 1970 UTC 5933 * @param {Object} options - configuration options for the DateTime 5934 * @param {string|Zone} [options.zone='local'] - the zone to place the DateTime into 5935 * @param {string} [options.locale] - a locale to set on the resulting DateTime instance 5936 * @param {string} options.outputCalendar - the output calendar to set on the resulting DateTime instance 5937 * @param {string} options.numberingSystem - the numbering system to set on the resulting DateTime instance 5938 * @param {string} options.weekSettings - the week settings to set on the resulting DateTime instance 5939 * @return {DateTime} 5940 */ 5941 static fromMillis(milliseconds, options = {}) { 5942 if (!isNumber(milliseconds)) { 5943 throw new InvalidArgumentError(`fromMillis requires a numerical input, but received a ${typeof milliseconds} with value ${milliseconds}`); 5944 } else if (milliseconds < -MAX_DATE || milliseconds > MAX_DATE) { 5945 // this isn't perfect because we can still end up out of range because of additional shifting, but it's a start 5946 return DateTime.invalid("Timestamp out of range"); 5947 } else { 5948 return new DateTime({ 5949 ts: milliseconds, 5950 zone: normalizeZone(options.zone, Settings.defaultZone), 5951 loc: Locale.fromObject(options) 5952 }); 5953 } 5954 } 5955 5956 /** 5957 * Create a DateTime from a number of seconds since the epoch (meaning since 1 January 1970 00:00:00 UTC). Uses the default zone. 5958 * @param {number} seconds - a number of seconds since 1970 UTC 5959 * @param {Object} options - configuration options for the DateTime 5960 * @param {string|Zone} [options.zone='local'] - the zone to place the DateTime into 5961 * @param {string} [options.locale] - a locale to set on the resulting DateTime instance 5962 * @param {string} options.outputCalendar - the output calendar to set on the resulting DateTime instance 5963 * @param {string} options.numberingSystem - the numbering system to set on the resulting DateTime instance 5964 * @param {string} options.weekSettings - the week settings to set on the resulting DateTime instance 5965 * @return {DateTime} 5966 */ 5967 static fromSeconds(seconds, options = {}) { 5968 if (!isNumber(seconds)) { 5969 throw new InvalidArgumentError("fromSeconds requires a numerical input"); 5970 } else { 5971 return new DateTime({ 5972 ts: seconds * 1000, 5973 zone: normalizeZone(options.zone, Settings.defaultZone), 5974 loc: Locale.fromObject(options) 5975 }); 5976 } 5977 } 5978 5979 /** 5980 * Create a DateTime from a JavaScript object with keys like 'year' and 'hour' with reasonable defaults. 5981 * @param {Object} obj - the object to create the DateTime from 5982 * @param {number} obj.year - a year, such as 1987 5983 * @param {number} obj.month - a month, 1-12 5984 * @param {number} obj.day - a day of the month, 1-31, depending on the month 5985 * @param {number} obj.ordinal - day of the year, 1-365 or 366 5986 * @param {number} obj.weekYear - an ISO week year 5987 * @param {number} obj.weekNumber - an ISO week number, between 1 and 52 or 53, depending on the year 5988 * @param {number} obj.weekday - an ISO weekday, 1-7, where 1 is Monday and 7 is Sunday 5989 * @param {number} obj.localWeekYear - a week year, according to the locale 5990 * @param {number} obj.localWeekNumber - a week number, between 1 and 52 or 53, depending on the year, according to the locale 5991 * @param {number} obj.localWeekday - a weekday, 1-7, where 1 is the first and 7 is the last day of the week, according to the locale 5992 * @param {number} obj.hour - hour of the day, 0-23 5993 * @param {number} obj.minute - minute of the hour, 0-59 5994 * @param {number} obj.second - second of the minute, 0-59 5995 * @param {number} obj.millisecond - millisecond of the second, 0-999 5996 * @param {Object} opts - options for creating this DateTime 5997 * @param {string|Zone} [opts.zone='local'] - interpret the numbers in the context of a particular zone. Can take any value taken as the first argument to setZone() 5998 * @param {string} [opts.locale='system\'s locale'] - a locale to set on the resulting DateTime instance 5999 * @param {string} opts.outputCalendar - the output calendar to set on the resulting DateTime instance 6000 * @param {string} opts.numberingSystem - the numbering system to set on the resulting DateTime instance 6001 * @param {string} opts.weekSettings - the week settings to set on the resulting DateTime instance 6002 * @example DateTime.fromObject({ year: 1982, month: 5, day: 25}).toISODate() //=> '1982-05-25' 6003 * @example DateTime.fromObject({ year: 1982 }).toISODate() //=> '1982-01-01' 6004 * @example DateTime.fromObject({ hour: 10, minute: 26, second: 6 }) //~> today at 10:26:06 6005 * @example DateTime.fromObject({ hour: 10, minute: 26, second: 6 }, { zone: 'utc' }), 6006 * @example DateTime.fromObject({ hour: 10, minute: 26, second: 6 }, { zone: 'local' }) 6007 * @example DateTime.fromObject({ hour: 10, minute: 26, second: 6 }, { zone: 'America/New_York' }) 6008 * @example DateTime.fromObject({ weekYear: 2016, weekNumber: 2, weekday: 3 }).toISODate() //=> '2016-01-13' 6009 * @example DateTime.fromObject({ localWeekYear: 2022, localWeekNumber: 1, localWeekday: 1 }, { locale: "en-US" }).toISODate() //=> '2021-12-26' 6010 * @return {DateTime} 6011 */ 6012 static fromObject(obj, opts = {}) { 6013 obj = obj || {}; 6014 const zoneToUse = normalizeZone(opts.zone, Settings.defaultZone); 6015 if (!zoneToUse.isValid) { 6016 return DateTime.invalid(unsupportedZone(zoneToUse)); 6017 } 6018 const loc = Locale.fromObject(opts); 6019 const normalized = normalizeObject(obj, normalizeUnitWithLocalWeeks); 6020 const { 6021 minDaysInFirstWeek, 6022 startOfWeek 6023 } = usesLocalWeekValues(normalized, loc); 6024 const tsNow = Settings.now(), 6025 offsetProvis = !isUndefined(opts.specificOffset) ? opts.specificOffset : zoneToUse.offset(tsNow), 6026 containsOrdinal = !isUndefined(normalized.ordinal), 6027 containsGregorYear = !isUndefined(normalized.year), 6028 containsGregorMD = !isUndefined(normalized.month) || !isUndefined(normalized.day), 6029 containsGregor = containsGregorYear || containsGregorMD, 6030 definiteWeekDef = normalized.weekYear || normalized.weekNumber; 6031 6032 // cases: 6033 // just a weekday -> this week's instance of that weekday, no worries 6034 // (gregorian data or ordinal) + (weekYear or weekNumber) -> error 6035 // (gregorian month or day) + ordinal -> error 6036 // otherwise just use weeks or ordinals or gregorian, depending on what's specified 6037 6038 if ((containsGregor || containsOrdinal) && definiteWeekDef) { 6039 throw new ConflictingSpecificationError("Can't mix weekYear/weekNumber units with year/month/day or ordinals"); 6040 } 6041 if (containsGregorMD && containsOrdinal) { 6042 throw new ConflictingSpecificationError("Can't mix ordinal dates with month/day"); 6043 } 6044 const useWeekData = definiteWeekDef || normalized.weekday && !containsGregor; 6045 6046 // configure ourselves to deal with gregorian dates or week stuff 6047 let units, 6048 defaultValues, 6049 objNow = tsToObj(tsNow, offsetProvis); 6050 if (useWeekData) { 6051 units = orderedWeekUnits; 6052 defaultValues = defaultWeekUnitValues; 6053 objNow = gregorianToWeek(objNow, minDaysInFirstWeek, startOfWeek); 6054 } else if (containsOrdinal) { 6055 units = orderedOrdinalUnits; 6056 defaultValues = defaultOrdinalUnitValues; 6057 objNow = gregorianToOrdinal(objNow); 6058 } else { 6059 units = orderedUnits; 6060 defaultValues = defaultUnitValues; 6061 } 6062 6063 // set default values for missing stuff 6064 let foundFirst = false; 6065 for (const u of units) { 6066 const v = normalized[u]; 6067 if (!isUndefined(v)) { 6068 foundFirst = true; 6069 } else if (foundFirst) { 6070 normalized[u] = defaultValues[u]; 6071 } else { 6072 normalized[u] = objNow[u]; 6073 } 6074 } 6075 6076 // make sure the values we have are in range 6077 const higherOrderInvalid = useWeekData ? hasInvalidWeekData(normalized, minDaysInFirstWeek, startOfWeek) : containsOrdinal ? hasInvalidOrdinalData(normalized) : hasInvalidGregorianData(normalized), 6078 invalid = higherOrderInvalid || hasInvalidTimeData(normalized); 6079 if (invalid) { 6080 return DateTime.invalid(invalid); 6081 } 6082 6083 // compute the actual time 6084 const gregorian = useWeekData ? weekToGregorian(normalized, minDaysInFirstWeek, startOfWeek) : containsOrdinal ? ordinalToGregorian(normalized) : normalized, 6085 [tsFinal, offsetFinal] = objToTS(gregorian, offsetProvis, zoneToUse), 6086 inst = new DateTime({ 6087 ts: tsFinal, 6088 zone: zoneToUse, 6089 o: offsetFinal, 6090 loc 6091 }); 6092 6093 // gregorian data + weekday serves only to validate 6094 if (normalized.weekday && containsGregor && obj.weekday !== inst.weekday) { 6095 return DateTime.invalid("mismatched weekday", `you can't specify both a weekday of ${normalized.weekday} and a date of ${inst.toISO()}`); 6096 } 6097 if (!inst.isValid) { 6098 return DateTime.invalid(inst.invalid); 6099 } 6100 return inst; 6101 } 6102 6103 /** 6104 * Create a DateTime from an ISO 8601 string 6105 * @param {string} text - the ISO string 6106 * @param {Object} opts - options to affect the creation 6107 * @param {string|Zone} [opts.zone='local'] - use this zone if no offset is specified in the input string itself. Will also convert the time to this zone 6108 * @param {boolean} [opts.setZone=false] - override the zone with a fixed-offset zone specified in the string itself, if it specifies one 6109 * @param {string} [opts.locale='system's locale'] - a locale to set on the resulting DateTime instance 6110 * @param {string} [opts.outputCalendar] - the output calendar to set on the resulting DateTime instance 6111 * @param {string} [opts.numberingSystem] - the numbering system to set on the resulting DateTime instance 6112 * @param {string} [opts.weekSettings] - the week settings to set on the resulting DateTime instance 6113 * @example DateTime.fromISO('2016-05-25T09:08:34.123') 6114 * @example DateTime.fromISO('2016-05-25T09:08:34.123+06:00') 6115 * @example DateTime.fromISO('2016-05-25T09:08:34.123+06:00', {setZone: true}) 6116 * @example DateTime.fromISO('2016-05-25T09:08:34.123', {zone: 'utc'}) 6117 * @example DateTime.fromISO('2016-W05-4') 6118 * @return {DateTime} 6119 */ 6120 static fromISO(text, opts = {}) { 6121 const [vals, parsedZone] = parseISODate(text); 6122 return parseDataToDateTime(vals, parsedZone, opts, "ISO 8601", text); 6123 } 6124 6125 /** 6126 * Create a DateTime from an RFC 2822 string 6127 * @param {string} text - the RFC 2822 string 6128 * @param {Object} opts - options to affect the creation 6129 * @param {string|Zone} [opts.zone='local'] - convert the time to this zone. Since the offset is always specified in the string itself, this has no effect on the interpretation of string, merely the zone the resulting DateTime is expressed in. 6130 * @param {boolean} [opts.setZone=false] - override the zone with a fixed-offset zone specified in the string itself, if it specifies one 6131 * @param {string} [opts.locale='system's locale'] - a locale to set on the resulting DateTime instance 6132 * @param {string} opts.outputCalendar - the output calendar to set on the resulting DateTime instance 6133 * @param {string} opts.numberingSystem - the numbering system to set on the resulting DateTime instance 6134 * @param {string} opts.weekSettings - the week settings to set on the resulting DateTime instance 6135 * @example DateTime.fromRFC2822('25 Nov 2016 13:23:12 GMT') 6136 * @example DateTime.fromRFC2822('Fri, 25 Nov 2016 13:23:12 +0600') 6137 * @example DateTime.fromRFC2822('25 Nov 2016 13:23 Z') 6138 * @return {DateTime} 6139 */ 6140 static fromRFC2822(text, opts = {}) { 6141 const [vals, parsedZone] = parseRFC2822Date(text); 6142 return parseDataToDateTime(vals, parsedZone, opts, "RFC 2822", text); 6143 } 6144 6145 /** 6146 * Create a DateTime from an HTTP header date 6147 * @see https://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1 6148 * @param {string} text - the HTTP header date 6149 * @param {Object} opts - options to affect the creation 6150 * @param {string|Zone} [opts.zone='local'] - convert the time to this zone. Since HTTP dates are always in UTC, this has no effect on the interpretation of string, merely the zone the resulting DateTime is expressed in. 6151 * @param {boolean} [opts.setZone=false] - override the zone with the fixed-offset zone specified in the string. For HTTP dates, this is always UTC, so this option is equivalent to setting the `zone` option to 'utc', but this option is included for consistency with similar methods. 6152 * @param {string} [opts.locale='system's locale'] - a locale to set on the resulting DateTime instance 6153 * @param {string} opts.outputCalendar - the output calendar to set on the resulting DateTime instance 6154 * @param {string} opts.numberingSystem - the numbering system to set on the resulting DateTime instance 6155 * @param {string} opts.weekSettings - the week settings to set on the resulting DateTime instance 6156 * @example DateTime.fromHTTP('Sun, 06 Nov 1994 08:49:37 GMT') 6157 * @example DateTime.fromHTTP('Sunday, 06-Nov-94 08:49:37 GMT') 6158 * @example DateTime.fromHTTP('Sun Nov 6 08:49:37 1994') 6159 * @return {DateTime} 6160 */ 6161 static fromHTTP(text, opts = {}) { 6162 const [vals, parsedZone] = parseHTTPDate(text); 6163 return parseDataToDateTime(vals, parsedZone, opts, "HTTP", opts); 6164 } 6165 6166 /** 6167 * Create a DateTime from an input string and format string. 6168 * Defaults to en-US if no locale has been specified, regardless of the system's locale. For a table of tokens and their interpretations, see [here](https://moment.github.io/luxon/#/parsing?id=table-of-tokens). 6169 * @param {string} text - the string to parse 6170 * @param {string} fmt - the format the string is expected to be in (see the link below for the formats) 6171 * @param {Object} opts - options to affect the creation 6172 * @param {string|Zone} [opts.zone='local'] - use this zone if no offset is specified in the input string itself. Will also convert the DateTime to this zone 6173 * @param {boolean} [opts.setZone=false] - override the zone with a zone specified in the string itself, if it specifies one 6174 * @param {string} [opts.locale='en-US'] - a locale string to use when parsing. Will also set the DateTime to this locale 6175 * @param {string} opts.numberingSystem - the numbering system to use when parsing. Will also set the resulting DateTime to this numbering system 6176 * @param {string} opts.weekSettings - the week settings to set on the resulting DateTime instance 6177 * @param {string} opts.outputCalendar - the output calendar to set on the resulting DateTime instance 6178 * @return {DateTime} 6179 */ 6180 static fromFormat(text, fmt, opts = {}) { 6181 if (isUndefined(text) || isUndefined(fmt)) { 6182 throw new InvalidArgumentError("fromFormat requires an input string and a format"); 6183 } 6184 const { 6185 locale = null, 6186 numberingSystem = null 6187 } = opts, 6188 localeToUse = Locale.fromOpts({ 6189 locale, 6190 numberingSystem, 6191 defaultToEN: true 6192 }), 6193 [vals, parsedZone, specificOffset, invalid] = parseFromTokens(localeToUse, text, fmt); 6194 if (invalid) { 6195 return DateTime.invalid(invalid); 6196 } else { 6197 return parseDataToDateTime(vals, parsedZone, opts, `format ${fmt}`, text, specificOffset); 6198 } 6199 } 6200 6201 /** 6202 * @deprecated use fromFormat instead 6203 */ 6204 static fromString(text, fmt, opts = {}) { 6205 return DateTime.fromFormat(text, fmt, opts); 6206 } 6207 6208 /** 6209 * Create a DateTime from a SQL date, time, or datetime 6210 * Defaults to en-US if no locale has been specified, regardless of the system's locale 6211 * @param {string} text - the string to parse 6212 * @param {Object} opts - options to affect the creation 6213 * @param {string|Zone} [opts.zone='local'] - use this zone if no offset is specified in the input string itself. Will also convert the DateTime to this zone 6214 * @param {boolean} [opts.setZone=false] - override the zone with a zone specified in the string itself, if it specifies one 6215 * @param {string} [opts.locale='en-US'] - a locale string to use when parsing. Will also set the DateTime to this locale 6216 * @param {string} opts.numberingSystem - the numbering system to use when parsing. Will also set the resulting DateTime to this numbering system 6217 * @param {string} opts.weekSettings - the week settings to set on the resulting DateTime instance 6218 * @param {string} opts.outputCalendar - the output calendar to set on the resulting DateTime instance 6219 * @example DateTime.fromSQL('2017-05-15') 6220 * @example DateTime.fromSQL('2017-05-15 09:12:34') 6221 * @example DateTime.fromSQL('2017-05-15 09:12:34.342') 6222 * @example DateTime.fromSQL('2017-05-15 09:12:34.342+06:00') 6223 * @example DateTime.fromSQL('2017-05-15 09:12:34.342 America/Los_Angeles') 6224 * @example DateTime.fromSQL('2017-05-15 09:12:34.342 America/Los_Angeles', { setZone: true }) 6225 * @example DateTime.fromSQL('2017-05-15 09:12:34.342', { zone: 'America/Los_Angeles' }) 6226 * @example DateTime.fromSQL('09:12:34.342') 6227 * @return {DateTime} 6228 */ 6229 static fromSQL(text, opts = {}) { 6230 const [vals, parsedZone] = parseSQL(text); 6231 return parseDataToDateTime(vals, parsedZone, opts, "SQL", text); 6232 } 6233 6234 /** 6235 * Create an invalid DateTime. 6236 * @param {string} reason - simple string of why this DateTime is invalid. Should not contain parameters or anything else data-dependent. 6237 * @param {string} [explanation=null] - longer explanation, may include parameters and other useful debugging information 6238 * @return {DateTime} 6239 */ 6240 static invalid(reason, explanation = null) { 6241 if (!reason) { 6242 throw new InvalidArgumentError("need to specify a reason the DateTime is invalid"); 6243 } 6244 const invalid = reason instanceof Invalid ? reason : new Invalid(reason, explanation); 6245 if (Settings.throwOnInvalid) { 6246 throw new InvalidDateTimeError(invalid); 6247 } else { 6248 return new DateTime({ 6249 invalid 6250 }); 6251 } 6252 } 6253 6254 /** 6255 * Check if an object is an instance of DateTime. Works across context boundaries 6256 * @param {object} o 6257 * @return {boolean} 6258 */ 6259 static isDateTime(o) { 6260 return o && o.isLuxonDateTime || false; 6261 } 6262 6263 /** 6264 * Produce the format string for a set of options 6265 * @param formatOpts 6266 * @param localeOpts 6267 * @returns {string} 6268 */ 6269 static parseFormatForOpts(formatOpts, localeOpts = {}) { 6270 const tokenList = formatOptsToTokens(formatOpts, Locale.fromObject(localeOpts)); 6271 return !tokenList ? null : tokenList.map(t => t ? t.val : null).join(""); 6272 } 6273 6274 /** 6275 * Produce the the fully expanded format token for the locale 6276 * Does NOT quote characters, so quoted tokens will not round trip correctly 6277 * @param fmt 6278 * @param localeOpts 6279 * @returns {string} 6280 */ 6281 static expandFormat(fmt, localeOpts = {}) { 6282 const expanded = expandMacroTokens(Formatter.parseFormat(fmt), Locale.fromObject(localeOpts)); 6283 return expanded.map(t => t.val).join(""); 6284 } 6285 static resetCache() { 6286 zoneOffsetTs = undefined; 6287 zoneOffsetGuessCache.clear(); 6288 } 6289 6290 // INFO 6291 6292 /** 6293 * Get the value of unit. 6294 * @param {string} unit - a unit such as 'minute' or 'day' 6295 * @example DateTime.local(2017, 7, 4).get('month'); //=> 7 6296 * @example DateTime.local(2017, 7, 4).get('day'); //=> 4 6297 * @return {number} 6298 */ 6299 get(unit) { 6300 return this[unit]; 6301 } 6302 6303 /** 6304 * Returns whether the DateTime is valid. Invalid DateTimes occur when: 6305 * * The DateTime was created from invalid calendar information, such as the 13th month or February 30 6306 * * The DateTime was created by an operation on another invalid date 6307 * @type {boolean} 6308 */ 6309 get isValid() { 6310 return this.invalid === null; 6311 } 6312 6313 /** 6314 * Returns an error code if this DateTime is invalid, or null if the DateTime is valid 6315 * @type {string} 6316 */ 6317 get invalidReason() { 6318 return this.invalid ? this.invalid.reason : null; 6319 } 6320 6321 /** 6322 * Returns an explanation of why this DateTime became invalid, or null if the DateTime is valid 6323 * @type {string} 6324 */ 6325 get invalidExplanation() { 6326 return this.invalid ? this.invalid.explanation : null; 6327 } 6328 6329 /** 6330 * Get the locale of a DateTime, such 'en-GB'. The locale is used when formatting the DateTime 6331 * 6332 * @type {string} 6333 */ 6334 get locale() { 6335 return this.isValid ? this.loc.locale : null; 6336 } 6337 6338 /** 6339 * Get the numbering system of a DateTime, such 'beng'. The numbering system is used when formatting the DateTime 6340 * 6341 * @type {string} 6342 */ 6343 get numberingSystem() { 6344 return this.isValid ? this.loc.numberingSystem : null; 6345 } 6346 6347 /** 6348 * Get the output calendar of a DateTime, such 'islamic'. The output calendar is used when formatting the DateTime 6349 * 6350 * @type {string} 6351 */ 6352 get outputCalendar() { 6353 return this.isValid ? this.loc.outputCalendar : null; 6354 } 6355 6356 /** 6357 * Get the time zone associated with this DateTime. 6358 * @type {Zone} 6359 */ 6360 get zone() { 6361 return this._zone; 6362 } 6363 6364 /** 6365 * Get the name of the time zone. 6366 * @type {string} 6367 */ 6368 get zoneName() { 6369 return this.isValid ? this.zone.name : null; 6370 } 6371 6372 /** 6373 * Get the year 6374 * @example DateTime.local(2017, 5, 25).year //=> 2017 6375 * @type {number} 6376 */ 6377 get year() { 6378 return this.isValid ? this.c.year : NaN; 6379 } 6380 6381 /** 6382 * Get the quarter 6383 * @example DateTime.local(2017, 5, 25).quarter //=> 2 6384 * @type {number} 6385 */ 6386 get quarter() { 6387 return this.isValid ? Math.ceil(this.c.month / 3) : NaN; 6388 } 6389 6390 /** 6391 * Get the month (1-12). 6392 * @example DateTime.local(2017, 5, 25).month //=> 5 6393 * @type {number} 6394 */ 6395 get month() { 6396 return this.isValid ? this.c.month : NaN; 6397 } 6398 6399 /** 6400 * Get the day of the month (1-30ish). 6401 * @example DateTime.local(2017, 5, 25).day //=> 25 6402 * @type {number} 6403 */ 6404 get day() { 6405 return this.isValid ? this.c.day : NaN; 6406 } 6407 6408 /** 6409 * Get the hour of the day (0-23). 6410 * @example DateTime.local(2017, 5, 25, 9).hour //=> 9 6411 * @type {number} 6412 */ 6413 get hour() { 6414 return this.isValid ? this.c.hour : NaN; 6415 } 6416 6417 /** 6418 * Get the minute of the hour (0-59). 6419 * @example DateTime.local(2017, 5, 25, 9, 30).minute //=> 30 6420 * @type {number} 6421 */ 6422 get minute() { 6423 return this.isValid ? this.c.minute : NaN; 6424 } 6425 6426 /** 6427 * Get the second of the minute (0-59). 6428 * @example DateTime.local(2017, 5, 25, 9, 30, 52).second //=> 52 6429 * @type {number} 6430 */ 6431 get second() { 6432 return this.isValid ? this.c.second : NaN; 6433 } 6434 6435 /** 6436 * Get the millisecond of the second (0-999). 6437 * @example DateTime.local(2017, 5, 25, 9, 30, 52, 654).millisecond //=> 654 6438 * @type {number} 6439 */ 6440 get millisecond() { 6441 return this.isValid ? this.c.millisecond : NaN; 6442 } 6443 6444 /** 6445 * Get the week year 6446 * @see https://en.wikipedia.org/wiki/ISO_week_date 6447 * @example DateTime.local(2014, 12, 31).weekYear //=> 2015 6448 * @type {number} 6449 */ 6450 get weekYear() { 6451 return this.isValid ? possiblyCachedWeekData(this).weekYear : NaN; 6452 } 6453 6454 /** 6455 * Get the week number of the week year (1-52ish). 6456 * @see https://en.wikipedia.org/wiki/ISO_week_date 6457 * @example DateTime.local(2017, 5, 25).weekNumber //=> 21 6458 * @type {number} 6459 */ 6460 get weekNumber() { 6461 return this.isValid ? possiblyCachedWeekData(this).weekNumber : NaN; 6462 } 6463 6464 /** 6465 * Get the day of the week. 6466 * 1 is Monday and 7 is Sunday 6467 * @see https://en.wikipedia.org/wiki/ISO_week_date 6468 * @example DateTime.local(2014, 11, 31).weekday //=> 4 6469 * @type {number} 6470 */ 6471 get weekday() { 6472 return this.isValid ? possiblyCachedWeekData(this).weekday : NaN; 6473 } 6474 6475 /** 6476 * Returns true if this date is on a weekend according to the locale, false otherwise 6477 * @returns {boolean} 6478 */ 6479 get isWeekend() { 6480 return this.isValid && this.loc.getWeekendDays().includes(this.weekday); 6481 } 6482 6483 /** 6484 * Get the day of the week according to the locale. 6485 * 1 is the first day of the week and 7 is the last day of the week. 6486 * If the locale assigns Sunday as the first day of the week, then a date which is a Sunday will return 1, 6487 * @returns {number} 6488 */ 6489 get localWeekday() { 6490 return this.isValid ? possiblyCachedLocalWeekData(this).weekday : NaN; 6491 } 6492 6493 /** 6494 * Get the week number of the week year according to the locale. Different locales assign week numbers differently, 6495 * because the week can start on different days of the week (see localWeekday) and because a different number of days 6496 * is required for a week to count as the first week of a year. 6497 * @returns {number} 6498 */ 6499 get localWeekNumber() { 6500 return this.isValid ? possiblyCachedLocalWeekData(this).weekNumber : NaN; 6501 } 6502 6503 /** 6504 * Get the week year according to the locale. Different locales assign week numbers (and therefor week years) 6505 * differently, see localWeekNumber. 6506 * @returns {number} 6507 */ 6508 get localWeekYear() { 6509 return this.isValid ? possiblyCachedLocalWeekData(this).weekYear : NaN; 6510 } 6511 6512 /** 6513 * Get the ordinal (meaning the day of the year) 6514 * @example DateTime.local(2017, 5, 25).ordinal //=> 145 6515 * @type {number|DateTime} 6516 */ 6517 get ordinal() { 6518 return this.isValid ? gregorianToOrdinal(this.c).ordinal : NaN; 6519 } 6520 6521 /** 6522 * Get the human readable short month name, such as 'Oct'. 6523 * Defaults to the system's locale if no locale has been specified 6524 * @example DateTime.local(2017, 10, 30).monthShort //=> Oct 6525 * @type {string} 6526 */ 6527 get monthShort() { 6528 return this.isValid ? Info.months("short", { 6529 locObj: this.loc 6530 })[this.month - 1] : null; 6531 } 6532 6533 /** 6534 * Get the human readable long month name, such as 'October'. 6535 * Defaults to the system's locale if no locale has been specified 6536 * @example DateTime.local(2017, 10, 30).monthLong //=> October 6537 * @type {string} 6538 */ 6539 get monthLong() { 6540 return this.isValid ? Info.months("long", { 6541 locObj: this.loc 6542 })[this.month - 1] : null; 6543 } 6544 6545 /** 6546 * Get the human readable short weekday, such as 'Mon'. 6547 * Defaults to the system's locale if no locale has been specified 6548 * @example DateTime.local(2017, 10, 30).weekdayShort //=> Mon 6549 * @type {string} 6550 */ 6551 get weekdayShort() { 6552 return this.isValid ? Info.weekdays("short", { 6553 locObj: this.loc 6554 })[this.weekday - 1] : null; 6555 } 6556 6557 /** 6558 * Get the human readable long weekday, such as 'Monday'. 6559 * Defaults to the system's locale if no locale has been specified 6560 * @example DateTime.local(2017, 10, 30).weekdayLong //=> Monday 6561 * @type {string} 6562 */ 6563 get weekdayLong() { 6564 return this.isValid ? Info.weekdays("long", { 6565 locObj: this.loc 6566 })[this.weekday - 1] : null; 6567 } 6568 6569 /** 6570 * Get the UTC offset of this DateTime in minutes 6571 * @example DateTime.now().offset //=> -240 6572 * @example DateTime.utc().offset //=> 0 6573 * @type {number} 6574 */ 6575 get offset() { 6576 return this.isValid ? +this.o : NaN; 6577 } 6578 6579 /** 6580 * Get the short human name for the zone's current offset, for example "EST" or "EDT". 6581 * Defaults to the system's locale if no locale has been specified 6582 * @type {string} 6583 */ 6584 get offsetNameShort() { 6585 if (this.isValid) { 6586 return this.zone.offsetName(this.ts, { 6587 format: "short", 6588 locale: this.locale 6589 }); 6590 } else { 6591 return null; 6592 } 6593 } 6594 6595 /** 6596 * Get the long human name for the zone's current offset, for example "Eastern Standard Time" or "Eastern Daylight Time". 6597 * Defaults to the system's locale if no locale has been specified 6598 * @type {string} 6599 */ 6600 get offsetNameLong() { 6601 if (this.isValid) { 6602 return this.zone.offsetName(this.ts, { 6603 format: "long", 6604 locale: this.locale 6605 }); 6606 } else { 6607 return null; 6608 } 6609 } 6610 6611 /** 6612 * Get whether this zone's offset ever changes, as in a DST. 6613 * @type {boolean} 6614 */ 6615 get isOffsetFixed() { 6616 return this.isValid ? this.zone.isUniversal : null; 6617 } 6618 6619 /** 6620 * Get whether the DateTime is in a DST. 6621 * @type {boolean} 6622 */ 6623 get isInDST() { 6624 if (this.isOffsetFixed) { 6625 return false; 6626 } else { 6627 return this.offset > this.set({ 6628 month: 1, 6629 day: 1 6630 }).offset || this.offset > this.set({ 6631 month: 5 6632 }).offset; 6633 } 6634 } 6635 6636 /** 6637 * Get those DateTimes which have the same local time as this DateTime, but a different offset from UTC 6638 * in this DateTime's zone. During DST changes local time can be ambiguous, for example 6639 * `2023-10-29T02:30:00` in `Europe/Berlin` can have offset `+01:00` or `+02:00`. 6640 * This method will return both possible DateTimes if this DateTime's local time is ambiguous. 6641 * @returns {DateTime[]} 6642 */ 6643 getPossibleOffsets() { 6644 if (!this.isValid || this.isOffsetFixed) { 6645 return [this]; 6646 } 6647 const dayMs = 86400000; 6648 const minuteMs = 60000; 6649 const localTS = objToLocalTS(this.c); 6650 const oEarlier = this.zone.offset(localTS - dayMs); 6651 const oLater = this.zone.offset(localTS + dayMs); 6652 const o1 = this.zone.offset(localTS - oEarlier * minuteMs); 6653 const o2 = this.zone.offset(localTS - oLater * minuteMs); 6654 if (o1 === o2) { 6655 return [this]; 6656 } 6657 const ts1 = localTS - o1 * minuteMs; 6658 const ts2 = localTS - o2 * minuteMs; 6659 const c1 = tsToObj(ts1, o1); 6660 const c2 = tsToObj(ts2, o2); 6661 if (c1.hour === c2.hour && c1.minute === c2.minute && c1.second === c2.second && c1.millisecond === c2.millisecond) { 6662 return [clone(this, { 6663 ts: ts1 6664 }), clone(this, { 6665 ts: ts2 6666 })]; 6667 } 6668 return [this]; 6669 } 6670 6671 /** 6672 * Returns true if this DateTime is in a leap year, false otherwise 6673 * @example DateTime.local(2016).isInLeapYear //=> true 6674 * @example DateTime.local(2013).isInLeapYear //=> false 6675 * @type {boolean} 6676 */ 6677 get isInLeapYear() { 6678 return isLeapYear(this.year); 6679 } 6680 6681 /** 6682 * Returns the number of days in this DateTime's month 6683 * @example DateTime.local(2016, 2).daysInMonth //=> 29 6684 * @example DateTime.local(2016, 3).daysInMonth //=> 31 6685 * @type {number} 6686 */ 6687 get daysInMonth() { 6688 return daysInMonth(this.year, this.month); 6689 } 6690 6691 /** 6692 * Returns the number of days in this DateTime's year 6693 * @example DateTime.local(2016).daysInYear //=> 366 6694 * @example DateTime.local(2013).daysInYear //=> 365 6695 * @type {number} 6696 */ 6697 get daysInYear() { 6698 return this.isValid ? daysInYear(this.year) : NaN; 6699 } 6700 6701 /** 6702 * Returns the number of weeks in this DateTime's year 6703 * @see https://en.wikipedia.org/wiki/ISO_week_date 6704 * @example DateTime.local(2004).weeksInWeekYear //=> 53 6705 * @example DateTime.local(2013).weeksInWeekYear //=> 52 6706 * @type {number} 6707 */ 6708 get weeksInWeekYear() { 6709 return this.isValid ? weeksInWeekYear(this.weekYear) : NaN; 6710 } 6711 6712 /** 6713 * Returns the number of weeks in this DateTime's local week year 6714 * @example DateTime.local(2020, 6, {locale: 'en-US'}).weeksInLocalWeekYear //=> 52 6715 * @example DateTime.local(2020, 6, {locale: 'de-DE'}).weeksInLocalWeekYear //=> 53 6716 * @type {number} 6717 */ 6718 get weeksInLocalWeekYear() { 6719 return this.isValid ? weeksInWeekYear(this.localWeekYear, this.loc.getMinDaysInFirstWeek(), this.loc.getStartOfWeek()) : NaN; 6720 } 6721 6722 /** 6723 * Returns the resolved Intl options for this DateTime. 6724 * This is useful in understanding the behavior of formatting methods 6725 * @param {Object} opts - the same options as toLocaleString 6726 * @return {Object} 6727 */ 6728 resolvedLocaleOptions(opts = {}) { 6729 const { 6730 locale, 6731 numberingSystem, 6732 calendar 6733 } = Formatter.create(this.loc.clone(opts), opts).resolvedOptions(this); 6734 return { 6735 locale, 6736 numberingSystem, 6737 outputCalendar: calendar 6738 }; 6739 } 6740 6741 // TRANSFORM 6742 6743 /** 6744 * "Set" the DateTime's zone to UTC. Returns a newly-constructed DateTime. 6745 * 6746 * Equivalent to {@link DateTime#setZone}('utc') 6747 * @param {number} [offset=0] - optionally, an offset from UTC in minutes 6748 * @param {Object} [opts={}] - options to pass to `setZone()` 6749 * @return {DateTime} 6750 */ 6751 toUTC(offset = 0, opts = {}) { 6752 return this.setZone(FixedOffsetZone.instance(offset), opts); 6753 } 6754 6755 /** 6756 * "Set" the DateTime's zone to the host's local zone. Returns a newly-constructed DateTime. 6757 * 6758 * Equivalent to `setZone('local')` 6759 * @return {DateTime} 6760 */ 6761 toLocal() { 6762 return this.setZone(Settings.defaultZone); 6763 } 6764 6765 /** 6766 * "Set" the DateTime's zone to specified zone. Returns a newly-constructed DateTime. 6767 * 6768 * By default, the setter keeps the underlying time the same (as in, the same timestamp), but the new instance will report different local times and consider DSTs when making computations, as with {@link DateTime#plus}. You may wish to use {@link DateTime#toLocal} and {@link DateTime#toUTC} which provide simple convenience wrappers for commonly used zones. 6769 * @param {string|Zone} [zone='local'] - a zone identifier. As a string, that can be any IANA zone supported by the host environment, or a fixed-offset name of the form 'UTC+3', or the strings 'local' or 'utc'. You may also supply an instance of a {@link DateTime#Zone} class. 6770 * @param {Object} opts - options 6771 * @param {boolean} [opts.keepLocalTime=false] - If true, adjust the underlying time so that the local time stays the same, but in the target zone. You should rarely need this. 6772 * @return {DateTime} 6773 */ 6774 setZone(zone, { 6775 keepLocalTime = false, 6776 keepCalendarTime = false 6777 } = {}) { 6778 zone = normalizeZone(zone, Settings.defaultZone); 6779 if (zone.equals(this.zone)) { 6780 return this; 6781 } else if (!zone.isValid) { 6782 return DateTime.invalid(unsupportedZone(zone)); 6783 } else { 6784 let newTS = this.ts; 6785 if (keepLocalTime || keepCalendarTime) { 6786 const offsetGuess = zone.offset(this.ts); 6787 const asObj = this.toObject(); 6788 [newTS] = objToTS(asObj, offsetGuess, zone); 6789 } 6790 return clone(this, { 6791 ts: newTS, 6792 zone 6793 }); 6794 } 6795 } 6796 6797 /** 6798 * "Set" the locale, numberingSystem, or outputCalendar. Returns a newly-constructed DateTime. 6799 * @param {Object} properties - the properties to set 6800 * @example DateTime.local(2017, 5, 25).reconfigure({ locale: 'en-GB' }) 6801 * @return {DateTime} 6802 */ 6803 reconfigure({ 6804 locale, 6805 numberingSystem, 6806 outputCalendar 6807 } = {}) { 6808 const loc = this.loc.clone({ 6809 locale, 6810 numberingSystem, 6811 outputCalendar 6812 }); 6813 return clone(this, { 6814 loc 6815 }); 6816 } 6817 6818 /** 6819 * "Set" the locale. Returns a newly-constructed DateTime. 6820 * Just a convenient alias for reconfigure({ locale }) 6821 * @example DateTime.local(2017, 5, 25).setLocale('en-GB') 6822 * @return {DateTime} 6823 */ 6824 setLocale(locale) { 6825 return this.reconfigure({ 6826 locale 6827 }); 6828 } 6829 6830 /** 6831 * "Set" the values of specified units. Returns a newly-constructed DateTime. 6832 * You can only set units with this method; for "setting" metadata, see {@link DateTime#reconfigure} and {@link DateTime#setZone}. 6833 * 6834 * This method also supports setting locale-based week units, i.e. `localWeekday`, `localWeekNumber` and `localWeekYear`. 6835 * They cannot be mixed with ISO-week units like `weekday`. 6836 * @param {Object} values - a mapping of units to numbers 6837 * @example dt.set({ year: 2017 }) 6838 * @example dt.set({ hour: 8, minute: 30 }) 6839 * @example dt.set({ weekday: 5 }) 6840 * @example dt.set({ year: 2005, ordinal: 234 }) 6841 * @return {DateTime} 6842 */ 6843 set(values) { 6844 if (!this.isValid) return this; 6845 const normalized = normalizeObject(values, normalizeUnitWithLocalWeeks); 6846 const { 6847 minDaysInFirstWeek, 6848 startOfWeek 6849 } = usesLocalWeekValues(normalized, this.loc); 6850 const settingWeekStuff = !isUndefined(normalized.weekYear) || !isUndefined(normalized.weekNumber) || !isUndefined(normalized.weekday), 6851 containsOrdinal = !isUndefined(normalized.ordinal), 6852 containsGregorYear = !isUndefined(normalized.year), 6853 containsGregorMD = !isUndefined(normalized.month) || !isUndefined(normalized.day), 6854 containsGregor = containsGregorYear || containsGregorMD, 6855 definiteWeekDef = normalized.weekYear || normalized.weekNumber; 6856 if ((containsGregor || containsOrdinal) && definiteWeekDef) { 6857 throw new ConflictingSpecificationError("Can't mix weekYear/weekNumber units with year/month/day or ordinals"); 6858 } 6859 if (containsGregorMD && containsOrdinal) { 6860 throw new ConflictingSpecificationError("Can't mix ordinal dates with month/day"); 6861 } 6862 let mixed; 6863 if (settingWeekStuff) { 6864 mixed = weekToGregorian({ 6865 ...gregorianToWeek(this.c, minDaysInFirstWeek, startOfWeek), 6866 ...normalized 6867 }, minDaysInFirstWeek, startOfWeek); 6868 } else if (!isUndefined(normalized.ordinal)) { 6869 mixed = ordinalToGregorian({ 6870 ...gregorianToOrdinal(this.c), 6871 ...normalized 6872 }); 6873 } else { 6874 mixed = { 6875 ...this.toObject(), 6876 ...normalized 6877 }; 6878 6879 // if we didn't set the day but we ended up on an overflow date, 6880 // use the last day of the right month 6881 if (isUndefined(normalized.day)) { 6882 mixed.day = Math.min(daysInMonth(mixed.year, mixed.month), mixed.day); 6883 } 6884 } 6885 const [ts, o] = objToTS(mixed, this.o, this.zone); 6886 return clone(this, { 6887 ts, 6888 o 6889 }); 6890 } 6891 6892 /** 6893 * Add a period of time to this DateTime and return the resulting DateTime 6894 * 6895 * Adding hours, minutes, seconds, or milliseconds increases the timestamp by the right number of milliseconds. Adding days, months, or years shifts the calendar, accounting for DSTs and leap years along the way. Thus, `dt.plus({ hours: 24 })` may result in a different time than `dt.plus({ days: 1 })` if there's a DST shift in between. 6896 * @param {Duration|Object|number} duration - The amount to add. Either a Luxon Duration, a number of milliseconds, the object argument to Duration.fromObject() 6897 * @example DateTime.now().plus(123) //~> in 123 milliseconds 6898 * @example DateTime.now().plus({ minutes: 15 }) //~> in 15 minutes 6899 * @example DateTime.now().plus({ days: 1 }) //~> this time tomorrow 6900 * @example DateTime.now().plus({ days: -1 }) //~> this time yesterday 6901 * @example DateTime.now().plus({ hours: 3, minutes: 13 }) //~> in 3 hr, 13 min 6902 * @example DateTime.now().plus(Duration.fromObject({ hours: 3, minutes: 13 })) //~> in 3 hr, 13 min 6903 * @return {DateTime} 6904 */ 6905 plus(duration) { 6906 if (!this.isValid) return this; 6907 const dur = Duration.fromDurationLike(duration); 6908 return clone(this, adjustTime(this, dur)); 6909 } 6910 6911 /** 6912 * Subtract a period of time to this DateTime and return the resulting DateTime 6913 * See {@link DateTime#plus} 6914 * @param {Duration|Object|number} duration - The amount to subtract. Either a Luxon Duration, a number of milliseconds, the object argument to Duration.fromObject() 6915 @return {DateTime} 6916 */ 6917 minus(duration) { 6918 if (!this.isValid) return this; 6919 const dur = Duration.fromDurationLike(duration).negate(); 6920 return clone(this, adjustTime(this, dur)); 6921 } 6922 6923 /** 6924 * "Set" this DateTime to the beginning of a unit of time. 6925 * @param {string} unit - The unit to go to the beginning of. Can be 'year', 'quarter', 'month', 'week', 'day', 'hour', 'minute', 'second', or 'millisecond'. 6926 * @param {Object} opts - options 6927 * @param {boolean} [opts.useLocaleWeeks=false] - If true, use weeks based on the locale, i.e. use the locale-dependent start of the week 6928 * @example DateTime.local(2014, 3, 3).startOf('month').toISODate(); //=> '2014-03-01' 6929 * @example DateTime.local(2014, 3, 3).startOf('year').toISODate(); //=> '2014-01-01' 6930 * @example DateTime.local(2014, 3, 3).startOf('week').toISODate(); //=> '2014-03-03', weeks always start on Mondays 6931 * @example DateTime.local(2014, 3, 3, 5, 30).startOf('day').toISOTime(); //=> '00:00.000-05:00' 6932 * @example DateTime.local(2014, 3, 3, 5, 30).startOf('hour').toISOTime(); //=> '05:00:00.000-05:00' 6933 * @return {DateTime} 6934 */ 6935 startOf(unit, { 6936 useLocaleWeeks = false 6937 } = {}) { 6938 if (!this.isValid) return this; 6939 const o = {}, 6940 normalizedUnit = Duration.normalizeUnit(unit); 6941 switch (normalizedUnit) { 6942 case "years": 6943 o.month = 1; 6944 // falls through 6945 case "quarters": 6946 case "months": 6947 o.day = 1; 6948 // falls through 6949 case "weeks": 6950 case "days": 6951 o.hour = 0; 6952 // falls through 6953 case "hours": 6954 o.minute = 0; 6955 // falls through 6956 case "minutes": 6957 o.second = 0; 6958 // falls through 6959 case "seconds": 6960 o.millisecond = 0; 6961 break; 6962 // no default, invalid units throw in normalizeUnit() 6963 } 6964 6965 if (normalizedUnit === "weeks") { 6966 if (useLocaleWeeks) { 6967 const startOfWeek = this.loc.getStartOfWeek(); 6968 const { 6969 weekday 6970 } = this; 6971 if (weekday < startOfWeek) { 6972 o.weekNumber = this.weekNumber - 1; 6973 } 6974 o.weekday = startOfWeek; 6975 } else { 6976 o.weekday = 1; 6977 } 6978 } 6979 if (normalizedUnit === "quarters") { 6980 const q = Math.ceil(this.month / 3); 6981 o.month = (q - 1) * 3 + 1; 6982 } 6983 return this.set(o); 6984 } 6985 6986 /** 6987 * "Set" this DateTime to the end (meaning the last millisecond) of a unit of time 6988 * @param {string} unit - The unit to go to the end of. Can be 'year', 'quarter', 'month', 'week', 'day', 'hour', 'minute', 'second', or 'millisecond'. 6989 * @param {Object} opts - options 6990 * @param {boolean} [opts.useLocaleWeeks=false] - If true, use weeks based on the locale, i.e. use the locale-dependent start of the week 6991 * @example DateTime.local(2014, 3, 3).endOf('month').toISO(); //=> '2014-03-31T23:59:59.999-05:00' 6992 * @example DateTime.local(2014, 3, 3).endOf('year').toISO(); //=> '2014-12-31T23:59:59.999-05:00' 6993 * @example DateTime.local(2014, 3, 3).endOf('week').toISO(); // => '2014-03-09T23:59:59.999-05:00', weeks start on Mondays 6994 * @example DateTime.local(2014, 3, 3, 5, 30).endOf('day').toISO(); //=> '2014-03-03T23:59:59.999-05:00' 6995 * @example DateTime.local(2014, 3, 3, 5, 30).endOf('hour').toISO(); //=> '2014-03-03T05:59:59.999-05:00' 6996 * @return {DateTime} 6997 */ 6998 endOf(unit, opts) { 6999 return this.isValid ? this.plus({ 7000 [unit]: 1 7001 }).startOf(unit, opts).minus(1) : this; 7002 } 7003 7004 // OUTPUT 7005 7006 /** 7007 * Returns a string representation of this DateTime formatted according to the specified format string. 7008 * **You may not want this.** See {@link DateTime#toLocaleString} for a more flexible formatting tool. For a table of tokens and their interpretations, see [here](https://moment.github.io/luxon/#/formatting?id=table-of-tokens). 7009 * Defaults to en-US if no locale has been specified, regardless of the system's locale. 7010 * @param {string} fmt - the format string 7011 * @param {Object} opts - opts to override the configuration options on this DateTime 7012 * @example DateTime.now().toFormat('yyyy LLL dd') //=> '2017 Apr 22' 7013 * @example DateTime.now().setLocale('fr').toFormat('yyyy LLL dd') //=> '2017 avr. 22' 7014 * @example DateTime.now().toFormat('yyyy LLL dd', { locale: "fr" }) //=> '2017 avr. 22' 7015 * @example DateTime.now().toFormat("HH 'hours and' mm 'minutes'") //=> '20 hours and 55 minutes' 7016 * @return {string} 7017 */ 7018 toFormat(fmt, opts = {}) { 7019 return this.isValid ? Formatter.create(this.loc.redefaultToEN(opts)).formatDateTimeFromString(this, fmt) : INVALID; 7020 } 7021 7022 /** 7023 * Returns a localized string representing this date. Accepts the same options as the Intl.DateTimeFormat constructor and any presets defined by Luxon, such as `DateTime.DATE_FULL` or `DateTime.TIME_SIMPLE`. 7024 * The exact behavior of this method is browser-specific, but in general it will return an appropriate representation 7025 * of the DateTime in the assigned locale. 7026 * Defaults to the system's locale if no locale has been specified 7027 * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat 7028 * @param formatOpts {Object} - Intl.DateTimeFormat constructor options and configuration options 7029 * @param {Object} opts - opts to override the configuration options on this DateTime 7030 * @example DateTime.now().toLocaleString(); //=> 4/20/2017 7031 * @example DateTime.now().setLocale('en-gb').toLocaleString(); //=> '20/04/2017' 7032 * @example DateTime.now().toLocaleString(DateTime.DATE_FULL); //=> 'April 20, 2017' 7033 * @example DateTime.now().toLocaleString(DateTime.DATE_FULL, { locale: 'fr' }); //=> '28 août 2022' 7034 * @example DateTime.now().toLocaleString(DateTime.TIME_SIMPLE); //=> '11:32 AM' 7035 * @example DateTime.now().toLocaleString(DateTime.DATETIME_SHORT); //=> '4/20/2017, 11:32 AM' 7036 * @example DateTime.now().toLocaleString({ weekday: 'long', month: 'long', day: '2-digit' }); //=> 'Thursday, April 20' 7037 * @example DateTime.now().toLocaleString({ weekday: 'short', month: 'short', day: '2-digit', hour: '2-digit', minute: '2-digit' }); //=> 'Thu, Apr 20, 11:27 AM' 7038 * @example DateTime.now().toLocaleString({ hour: '2-digit', minute: '2-digit', hourCycle: 'h23' }); //=> '11:32' 7039 * @return {string} 7040 */ 7041 toLocaleString(formatOpts = DATE_SHORT, opts = {}) { 7042 return this.isValid ? Formatter.create(this.loc.clone(opts), formatOpts).formatDateTime(this) : INVALID; 7043 } 7044 7045 /** 7046 * Returns an array of format "parts", meaning individual tokens along with metadata. This is allows callers to post-process individual sections of the formatted output. 7047 * Defaults to the system's locale if no locale has been specified 7048 * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat/formatToParts 7049 * @param opts {Object} - Intl.DateTimeFormat constructor options, same as `toLocaleString`. 7050 * @example DateTime.now().toLocaleParts(); //=> [ 7051 * //=> { type: 'day', value: '25' }, 7052 * //=> { type: 'literal', value: '/' }, 7053 * //=> { type: 'month', value: '05' }, 7054 * //=> { type: 'literal', value: '/' }, 7055 * //=> { type: 'year', value: '1982' } 7056 * //=> ] 7057 */ 7058 toLocaleParts(opts = {}) { 7059 return this.isValid ? Formatter.create(this.loc.clone(opts), opts).formatDateTimeParts(this) : []; 7060 } 7061 7062 /** 7063 * Returns an ISO 8601-compliant string representation of this DateTime 7064 * @param {Object} opts - options 7065 * @param {boolean} [opts.suppressMilliseconds=false] - exclude milliseconds from the format if they're 0 7066 * @param {boolean} [opts.suppressSeconds=false] - exclude seconds from the format if they're 0 7067 * @param {boolean} [opts.includeOffset=true] - include the offset, such as 'Z' or '-04:00' 7068 * @param {boolean} [opts.extendedZone=false] - add the time zone format extension 7069 * @param {string} [opts.format='extended'] - choose between the basic and extended format 7070 * @param {string} [opts.precision='milliseconds'] - truncate output to desired presicion: 'years', 'months', 'days', 'hours', 'minutes', 'seconds' or 'milliseconds'. When precision and suppressSeconds or suppressMilliseconds are used together, precision sets the maximum unit shown in the output, however seconds or milliseconds will still be suppressed if they are 0. 7071 * @example DateTime.utc(1983, 5, 25).toISO() //=> '1982-05-25T00:00:00.000Z' 7072 * @example DateTime.now().toISO() //=> '2017-04-22T20:47:05.335-04:00' 7073 * @example DateTime.now().toISO({ includeOffset: false }) //=> '2017-04-22T20:47:05.335' 7074 * @example DateTime.now().toISO({ format: 'basic' }) //=> '20170422T204705.335-0400' 7075 * @example DateTime.now().toISO({ precision: 'day' }) //=> '2017-04-22Z' 7076 * @example DateTime.now().toISO({ precision: 'minute' }) //=> '2017-04-22T20:47Z' 7077 * @return {string|null} 7078 */ 7079 toISO({ 7080 format = "extended", 7081 suppressSeconds = false, 7082 suppressMilliseconds = false, 7083 includeOffset = true, 7084 extendedZone = false, 7085 precision = "milliseconds" 7086 } = {}) { 7087 if (!this.isValid) { 7088 return null; 7089 } 7090 precision = normalizeUnit(precision); 7091 const ext = format === "extended"; 7092 let c = toISODate(this, ext, precision); 7093 if (orderedUnits.indexOf(precision) >= 3) c += "T"; 7094 c += toISOTime(this, ext, suppressSeconds, suppressMilliseconds, includeOffset, extendedZone, precision); 7095 return c; 7096 } 7097 7098 /** 7099 * Returns an ISO 8601-compliant string representation of this DateTime's date component 7100 * @param {Object} opts - options 7101 * @param {string} [opts.format='extended'] - choose between the basic and extended format 7102 * @param {string} [opts.precision='day'] - truncate output to desired precision: 'years', 'months', or 'days'. 7103 * @example DateTime.utc(1982, 5, 25).toISODate() //=> '1982-05-25' 7104 * @example DateTime.utc(1982, 5, 25).toISODate({ format: 'basic' }) //=> '19820525' 7105 * @example DateTime.utc(1982, 5, 25).toISODate({ precision: 'month' }) //=> '1982-05' 7106 * @return {string|null} 7107 */ 7108 toISODate({ 7109 format = "extended", 7110 precision = "day" 7111 } = {}) { 7112 if (!this.isValid) { 7113 return null; 7114 } 7115 return toISODate(this, format === "extended", normalizeUnit(precision)); 7116 } 7117 7118 /** 7119 * Returns an ISO 8601-compliant string representation of this DateTime's week date 7120 * @example DateTime.utc(1982, 5, 25).toISOWeekDate() //=> '1982-W21-2' 7121 * @return {string} 7122 */ 7123 toISOWeekDate() { 7124 return toTechFormat(this, "kkkk-'W'WW-c"); 7125 } 7126 7127 /** 7128 * Returns an ISO 8601-compliant string representation of this DateTime's time component 7129 * @param {Object} opts - options 7130 * @param {boolean} [opts.suppressMilliseconds=false] - exclude milliseconds from the format if they're 0 7131 * @param {boolean} [opts.suppressSeconds=false] - exclude seconds from the format if they're 0 7132 * @param {boolean} [opts.includeOffset=true] - include the offset, such as 'Z' or '-04:00' 7133 * @param {boolean} [opts.extendedZone=true] - add the time zone format extension 7134 * @param {boolean} [opts.includePrefix=false] - include the `T` prefix 7135 * @param {string} [opts.format='extended'] - choose between the basic and extended format 7136 * @param {string} [opts.precision='milliseconds'] - truncate output to desired presicion: 'hours', 'minutes', 'seconds' or 'milliseconds'. When precision and suppressSeconds or suppressMilliseconds are used together, precision sets the maximum unit shown in the output, however seconds or milliseconds will still be suppressed if they are 0. 7137 * @example DateTime.utc().set({ hour: 7, minute: 34 }).toISOTime() //=> '07:34:19.361Z' 7138 * @example DateTime.utc().set({ hour: 7, minute: 34, seconds: 0, milliseconds: 0 }).toISOTime({ suppressSeconds: true }) //=> '07:34Z' 7139 * @example DateTime.utc().set({ hour: 7, minute: 34 }).toISOTime({ format: 'basic' }) //=> '073419.361Z' 7140 * @example DateTime.utc().set({ hour: 7, minute: 34 }).toISOTime({ includePrefix: true }) //=> 'T07:34:19.361Z' 7141 * @example DateTime.utc().set({ hour: 7, minute: 34, second: 56 }).toISOTime({ precision: 'minute' }) //=> '07:34Z' 7142 * @return {string} 7143 */ 7144 toISOTime({ 7145 suppressMilliseconds = false, 7146 suppressSeconds = false, 7147 includeOffset = true, 7148 includePrefix = false, 7149 extendedZone = false, 7150 format = "extended", 7151 precision = "milliseconds" 7152 } = {}) { 7153 if (!this.isValid) { 7154 return null; 7155 } 7156 precision = normalizeUnit(precision); 7157 let c = includePrefix && orderedUnits.indexOf(precision) >= 3 ? "T" : ""; 7158 return c + toISOTime(this, format === "extended", suppressSeconds, suppressMilliseconds, includeOffset, extendedZone, precision); 7159 } 7160 7161 /** 7162 * Returns an RFC 2822-compatible string representation of this DateTime 7163 * @example DateTime.utc(2014, 7, 13).toRFC2822() //=> 'Sun, 13 Jul 2014 00:00:00 +0000' 7164 * @example DateTime.local(2014, 7, 13).toRFC2822() //=> 'Sun, 13 Jul 2014 00:00:00 -0400' 7165 * @return {string} 7166 */ 7167 toRFC2822() { 7168 return toTechFormat(this, "EEE, dd LLL yyyy HH:mm:ss ZZZ", false); 7169 } 7170 7171 /** 7172 * Returns a string representation of this DateTime appropriate for use in HTTP headers. The output is always expressed in GMT. 7173 * Specifically, the string conforms to RFC 1123. 7174 * @see https://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1 7175 * @example DateTime.utc(2014, 7, 13).toHTTP() //=> 'Sun, 13 Jul 2014 00:00:00 GMT' 7176 * @example DateTime.utc(2014, 7, 13, 19).toHTTP() //=> 'Sun, 13 Jul 2014 19:00:00 GMT' 7177 * @return {string} 7178 */ 7179 toHTTP() { 7180 return toTechFormat(this.toUTC(), "EEE, dd LLL yyyy HH:mm:ss 'GMT'"); 7181 } 7182 7183 /** 7184 * Returns a string representation of this DateTime appropriate for use in SQL Date 7185 * @example DateTime.utc(2014, 7, 13).toSQLDate() //=> '2014-07-13' 7186 * @return {string|null} 7187 */ 7188 toSQLDate() { 7189 if (!this.isValid) { 7190 return null; 7191 } 7192 return toISODate(this, true); 7193 } 7194 7195 /** 7196 * Returns a string representation of this DateTime appropriate for use in SQL Time 7197 * @param {Object} opts - options 7198 * @param {boolean} [opts.includeZone=false] - include the zone, such as 'America/New_York'. Overrides includeOffset. 7199 * @param {boolean} [opts.includeOffset=true] - include the offset, such as 'Z' or '-04:00' 7200 * @param {boolean} [opts.includeOffsetSpace=true] - include the space between the time and the offset, such as '05:15:16.345 -04:00' 7201 * @example DateTime.utc().toSQL() //=> '05:15:16.345' 7202 * @example DateTime.now().toSQL() //=> '05:15:16.345 -04:00' 7203 * @example DateTime.now().toSQL({ includeOffset: false }) //=> '05:15:16.345' 7204 * @example DateTime.now().toSQL({ includeZone: false }) //=> '05:15:16.345 America/New_York' 7205 * @return {string} 7206 */ 7207 toSQLTime({ 7208 includeOffset = true, 7209 includeZone = false, 7210 includeOffsetSpace = true 7211 } = {}) { 7212 let fmt = "HH:mm:ss.SSS"; 7213 if (includeZone || includeOffset) { 7214 if (includeOffsetSpace) { 7215 fmt += " "; 7216 } 7217 if (includeZone) { 7218 fmt += "z"; 7219 } else if (includeOffset) { 7220 fmt += "ZZ"; 7221 } 7222 } 7223 return toTechFormat(this, fmt, true); 7224 } 7225 7226 /** 7227 * Returns a string representation of this DateTime appropriate for use in SQL DateTime 7228 * @param {Object} opts - options 7229 * @param {boolean} [opts.includeZone=false] - include the zone, such as 'America/New_York'. Overrides includeOffset. 7230 * @param {boolean} [opts.includeOffset=true] - include the offset, such as 'Z' or '-04:00' 7231 * @param {boolean} [opts.includeOffsetSpace=true] - include the space between the time and the offset, such as '05:15:16.345 -04:00' 7232 * @example DateTime.utc(2014, 7, 13).toSQL() //=> '2014-07-13 00:00:00.000 Z' 7233 * @example DateTime.local(2014, 7, 13).toSQL() //=> '2014-07-13 00:00:00.000 -04:00' 7234 * @example DateTime.local(2014, 7, 13).toSQL({ includeOffset: false }) //=> '2014-07-13 00:00:00.000' 7235 * @example DateTime.local(2014, 7, 13).toSQL({ includeZone: true }) //=> '2014-07-13 00:00:00.000 America/New_York' 7236 * @return {string} 7237 */ 7238 toSQL(opts = {}) { 7239 if (!this.isValid) { 7240 return null; 7241 } 7242 return `${this.toSQLDate()} ${this.toSQLTime(opts)}`; 7243 } 7244 7245 /** 7246 * Returns a string representation of this DateTime appropriate for debugging 7247 * @return {string} 7248 */ 7249 toString() { 7250 return this.isValid ? this.toISO() : INVALID; 7251 } 7252 7253 /** 7254 * Returns a string representation of this DateTime appropriate for the REPL. 7255 * @return {string} 7256 */ 7257 [Symbol.for("nodejs.util.inspect.custom")]() { 7258 if (this.isValid) { 7259 return `DateTime { ts: ${this.toISO()}, zone: ${this.zone.name}, locale: ${this.locale} }`; 7260 } else { 7261 return `DateTime { Invalid, reason: ${this.invalidReason} }`; 7262 } 7263 } 7264 7265 /** 7266 * Returns the epoch milliseconds of this DateTime. Alias of {@link DateTime#toMillis} 7267 * @return {number} 7268 */ 7269 valueOf() { 7270 return this.toMillis(); 7271 } 7272 7273 /** 7274 * Returns the epoch milliseconds of this DateTime. 7275 * @return {number} 7276 */ 7277 toMillis() { 7278 return this.isValid ? this.ts : NaN; 7279 } 7280 7281 /** 7282 * Returns the epoch seconds (including milliseconds in the fractional part) of this DateTime. 7283 * @return {number} 7284 */ 7285 toSeconds() { 7286 return this.isValid ? this.ts / 1000 : NaN; 7287 } 7288 7289 /** 7290 * Returns the epoch seconds (as a whole number) of this DateTime. 7291 * @return {number} 7292 */ 7293 toUnixInteger() { 7294 return this.isValid ? Math.floor(this.ts / 1000) : NaN; 7295 } 7296 7297 /** 7298 * Returns an ISO 8601 representation of this DateTime appropriate for use in JSON. 7299 * @return {string} 7300 */ 7301 toJSON() { 7302 return this.toISO(); 7303 } 7304 7305 /** 7306 * Returns a BSON serializable equivalent to this DateTime. 7307 * @return {Date} 7308 */ 7309 toBSON() { 7310 return this.toJSDate(); 7311 } 7312 7313 /** 7314 * Returns a JavaScript object with this DateTime's year, month, day, and so on. 7315 * @param opts - options for generating the object 7316 * @param {boolean} [opts.includeConfig=false] - include configuration attributes in the output 7317 * @example DateTime.now().toObject() //=> { year: 2017, month: 4, day: 22, hour: 20, minute: 49, second: 42, millisecond: 268 } 7318 * @return {Object} 7319 */ 7320 toObject(opts = {}) { 7321 if (!this.isValid) return {}; 7322 const base = { 7323 ...this.c 7324 }; 7325 if (opts.includeConfig) { 7326 base.outputCalendar = this.outputCalendar; 7327 base.numberingSystem = this.loc.numberingSystem; 7328 base.locale = this.loc.locale; 7329 } 7330 return base; 7331 } 7332 7333 /** 7334 * Returns a JavaScript Date equivalent to this DateTime. 7335 * @return {Date} 7336 */ 7337 toJSDate() { 7338 return new Date(this.isValid ? this.ts : NaN); 7339 } 7340 7341 // COMPARE 7342 7343 /** 7344 * Return the difference between two DateTimes as a Duration. 7345 * @param {DateTime} otherDateTime - the DateTime to compare this one to 7346 * @param {string|string[]} [unit=['milliseconds']] - the unit or array of units (such as 'hours' or 'days') to include in the duration. 7347 * @param {Object} opts - options that affect the creation of the Duration 7348 * @param {string} [opts.conversionAccuracy='casual'] - the conversion system to use 7349 * @example 7350 * var i1 = DateTime.fromISO('1982-05-25T09:45'), 7351 * i2 = DateTime.fromISO('1983-10-14T10:30'); 7352 * i2.diff(i1).toObject() //=> { milliseconds: 43807500000 } 7353 * i2.diff(i1, 'hours').toObject() //=> { hours: 12168.75 } 7354 * i2.diff(i1, ['months', 'days']).toObject() //=> { months: 16, days: 19.03125 } 7355 * i2.diff(i1, ['months', 'days', 'hours']).toObject() //=> { months: 16, days: 19, hours: 0.75 } 7356 * @return {Duration} 7357 */ 7358 diff(otherDateTime, unit = "milliseconds", opts = {}) { 7359 if (!this.isValid || !otherDateTime.isValid) { 7360 return Duration.invalid("created by diffing an invalid DateTime"); 7361 } 7362 const durOpts = { 7363 locale: this.locale, 7364 numberingSystem: this.numberingSystem, 7365 ...opts 7366 }; 7367 const units = maybeArray(unit).map(Duration.normalizeUnit), 7368 otherIsLater = otherDateTime.valueOf() > this.valueOf(), 7369 earlier = otherIsLater ? this : otherDateTime, 7370 later = otherIsLater ? otherDateTime : this, 7371 diffed = diff(earlier, later, units, durOpts); 7372 return otherIsLater ? diffed.negate() : diffed; 7373 } 7374 7375 /** 7376 * Return the difference between this DateTime and right now. 7377 * See {@link DateTime#diff} 7378 * @param {string|string[]} [unit=['milliseconds']] - the unit or units units (such as 'hours' or 'days') to include in the duration 7379 * @param {Object} opts - options that affect the creation of the Duration 7380 * @param {string} [opts.conversionAccuracy='casual'] - the conversion system to use 7381 * @return {Duration} 7382 */ 7383 diffNow(unit = "milliseconds", opts = {}) { 7384 return this.diff(DateTime.now(), unit, opts); 7385 } 7386 7387 /** 7388 * Return an Interval spanning between this DateTime and another DateTime 7389 * @param {DateTime} otherDateTime - the other end point of the Interval 7390 * @return {Interval|DateTime} 7391 */ 7392 until(otherDateTime) { 7393 return this.isValid ? Interval.fromDateTimes(this, otherDateTime) : this; 7394 } 7395 7396 /** 7397 * Return whether this DateTime is in the same unit of time as another DateTime. 7398 * Higher-order units must also be identical for this function to return `true`. 7399 * Note that time zones are **ignored** in this comparison, which compares the **local** calendar time. Use {@link DateTime#setZone} to convert one of the dates if needed. 7400 * @param {DateTime} otherDateTime - the other DateTime 7401 * @param {string} unit - the unit of time to check sameness on 7402 * @param {Object} opts - options 7403 * @param {boolean} [opts.useLocaleWeeks=false] - If true, use weeks based on the locale, i.e. use the locale-dependent start of the week; only the locale of this DateTime is used 7404 * @example DateTime.now().hasSame(otherDT, 'day'); //~> true if otherDT is in the same current calendar day 7405 * @return {boolean} 7406 */ 7407 hasSame(otherDateTime, unit, opts) { 7408 if (!this.isValid) return false; 7409 const inputMs = otherDateTime.valueOf(); 7410 const adjustedToZone = this.setZone(otherDateTime.zone, { 7411 keepLocalTime: true 7412 }); 7413 return adjustedToZone.startOf(unit, opts) <= inputMs && inputMs <= adjustedToZone.endOf(unit, opts); 7414 } 7415 7416 /** 7417 * Equality check 7418 * Two DateTimes are equal if and only if they represent the same millisecond, have the same zone and location, and are both valid. 7419 * To compare just the millisecond values, use `+dt1 === +dt2`. 7420 * @param {DateTime} other - the other DateTime 7421 * @return {boolean} 7422 */ 7423 equals(other) { 7424 return this.isValid && other.isValid && this.valueOf() === other.valueOf() && this.zone.equals(other.zone) && this.loc.equals(other.loc); 7425 } 7426 7427 /** 7428 * Returns a string representation of a this time relative to now, such as "in two days". Can only internationalize if your 7429 * platform supports Intl.RelativeTimeFormat. Rounds towards zero by default. 7430 * @param {Object} options - options that affect the output 7431 * @param {DateTime} [options.base=DateTime.now()] - the DateTime to use as the basis to which this time is compared. Defaults to now. 7432 * @param {string} [options.style="long"] - the style of units, must be "long", "short", or "narrow" 7433 * @param {string|string[]} options.unit - use a specific unit or array of units; if omitted, or an array, the method will pick the best unit. Use an array or one of "years", "quarters", "months", "weeks", "days", "hours", "minutes", or "seconds" 7434 * @param {boolean} [options.round=true] - whether to round the numbers in the output. 7435 * @param {string} [options.rounding="trunc"] - rounding method to use when rounding the numbers in the output. Can be "trunc" (toward zero), "expand" (away from zero), "round", "floor", or "ceil". 7436 * @param {number} [options.padding=0] - padding in milliseconds. This allows you to round up the result if it fits inside the threshold. Don't use in combination with {round: false} because the decimal output will include the padding. 7437 * @param {string} options.locale - override the locale of this DateTime 7438 * @param {string} options.numberingSystem - override the numberingSystem of this DateTime. The Intl system may choose not to honor this 7439 * @example DateTime.now().plus({ days: 1 }).toRelative() //=> "in 1 day" 7440 * @example DateTime.now().setLocale("es").toRelative({ days: 1 }) //=> "dentro de 1 dÃa" 7441 * @example DateTime.now().plus({ days: 1 }).toRelative({ locale: "fr" }) //=> "dans 23 heures" 7442 * @example DateTime.now().minus({ days: 2 }).toRelative() //=> "2 days ago" 7443 * @example DateTime.now().minus({ days: 2 }).toRelative({ unit: "hours" }) //=> "48 hours ago" 7444 * @example DateTime.now().minus({ hours: 36 }).toRelative({ round: false }) //=> "1.5 days ago" 7445 */ 7446 toRelative(options = {}) { 7447 if (!this.isValid) return null; 7448 const base = options.base || DateTime.fromObject({}, { 7449 zone: this.zone 7450 }), 7451 padding = options.padding ? this < base ? -options.padding : options.padding : 0; 7452 let units = ["years", "months", "days", "hours", "minutes", "seconds"]; 7453 let unit = options.unit; 7454 if (Array.isArray(options.unit)) { 7455 units = options.unit; 7456 unit = undefined; 7457 } 7458 return diffRelative(base, this.plus(padding), { 7459 ...options, 7460 numeric: "always", 7461 units, 7462 unit 7463 }); 7464 } 7465 7466 /** 7467 * Returns a string representation of this date relative to today, such as "yesterday" or "next month". 7468 * Only internationalizes on platforms that supports Intl.RelativeTimeFormat. 7469 * @param {Object} options - options that affect the output 7470 * @param {DateTime} [options.base=DateTime.now()] - the DateTime to use as the basis to which this time is compared. Defaults to now. 7471 * @param {string} options.locale - override the locale of this DateTime 7472 * @param {string} options.unit - use a specific unit; if omitted, the method will pick the unit. Use one of "years", "quarters", "months", "weeks", or "days" 7473 * @param {string} options.numberingSystem - override the numberingSystem of this DateTime. The Intl system may choose not to honor this 7474 * @example DateTime.now().plus({ days: 1 }).toRelativeCalendar() //=> "tomorrow" 7475 * @example DateTime.now().setLocale("es").plus({ days: 1 }).toRelative() //=> ""mañana" 7476 * @example DateTime.now().plus({ days: 1 }).toRelativeCalendar({ locale: "fr" }) //=> "demain" 7477 * @example DateTime.now().minus({ days: 2 }).toRelativeCalendar() //=> "2 days ago" 7478 */ 7479 toRelativeCalendar(options = {}) { 7480 if (!this.isValid) return null; 7481 return diffRelative(options.base || DateTime.fromObject({}, { 7482 zone: this.zone 7483 }), this, { 7484 ...options, 7485 numeric: "auto", 7486 units: ["years", "months", "days"], 7487 calendary: true 7488 }); 7489 } 7490 7491 /** 7492 * Return the min of several date times 7493 * @param {...DateTime} dateTimes - the DateTimes from which to choose the minimum 7494 * @return {DateTime} the min DateTime, or undefined if called with no argument 7495 */ 7496 static min(...dateTimes) { 7497 if (!dateTimes.every(DateTime.isDateTime)) { 7498 throw new InvalidArgumentError("min requires all arguments be DateTimes"); 7499 } 7500 return bestBy(dateTimes, i => i.valueOf(), Math.min); 7501 } 7502 7503 /** 7504 * Return the max of several date times 7505 * @param {...DateTime} dateTimes - the DateTimes from which to choose the maximum 7506 * @return {DateTime} the max DateTime, or undefined if called with no argument 7507 */ 7508 static max(...dateTimes) { 7509 if (!dateTimes.every(DateTime.isDateTime)) { 7510 throw new InvalidArgumentError("max requires all arguments be DateTimes"); 7511 } 7512 return bestBy(dateTimes, i => i.valueOf(), Math.max); 7513 } 7514 7515 // MISC 7516 7517 /** 7518 * Explain how a string would be parsed by fromFormat() 7519 * @param {string} text - the string to parse 7520 * @param {string} fmt - the format the string is expected to be in (see description) 7521 * @param {Object} options - options taken by fromFormat() 7522 * @return {Object} 7523 */ 7524 static fromFormatExplain(text, fmt, options = {}) { 7525 const { 7526 locale = null, 7527 numberingSystem = null 7528 } = options, 7529 localeToUse = Locale.fromOpts({ 7530 locale, 7531 numberingSystem, 7532 defaultToEN: true 7533 }); 7534 return explainFromTokens(localeToUse, text, fmt); 7535 } 7536 7537 /** 7538 * @deprecated use fromFormatExplain instead 7539 */ 7540 static fromStringExplain(text, fmt, options = {}) { 7541 return DateTime.fromFormatExplain(text, fmt, options); 7542 } 7543 7544 /** 7545 * Build a parser for `fmt` using the given locale. This parser can be passed 7546 * to {@link DateTime.fromFormatParser} to a parse a date in this format. This 7547 * can be used to optimize cases where many dates need to be parsed in a 7548 * specific format. 7549 * 7550 * @param {String} fmt - the format the string is expected to be in (see 7551 * description) 7552 * @param {Object} options - options used to set locale and numberingSystem 7553 * for parser 7554 * @returns {TokenParser} - opaque object to be used 7555 */ 7556 static buildFormatParser(fmt, options = {}) { 7557 const { 7558 locale = null, 7559 numberingSystem = null 7560 } = options, 7561 localeToUse = Locale.fromOpts({ 7562 locale, 7563 numberingSystem, 7564 defaultToEN: true 7565 }); 7566 return new TokenParser(localeToUse, fmt); 7567 } 7568 7569 /** 7570 * Create a DateTime from an input string and format parser. 7571 * 7572 * The format parser must have been created with the same locale as this call. 7573 * 7574 * @param {String} text - the string to parse 7575 * @param {TokenParser} formatParser - parser from {@link DateTime.buildFormatParser} 7576 * @param {Object} opts - options taken by fromFormat() 7577 * @returns {DateTime} 7578 */ 7579 static fromFormatParser(text, formatParser, opts = {}) { 7580 if (isUndefined(text) || isUndefined(formatParser)) { 7581 throw new InvalidArgumentError("fromFormatParser requires an input string and a format parser"); 7582 } 7583 const { 7584 locale = null, 7585 numberingSystem = null 7586 } = opts, 7587 localeToUse = Locale.fromOpts({ 7588 locale, 7589 numberingSystem, 7590 defaultToEN: true 7591 }); 7592 if (!localeToUse.equals(formatParser.locale)) { 7593 throw new InvalidArgumentError(`fromFormatParser called with a locale of ${localeToUse}, ` + `but the format parser was created for ${formatParser.locale}`); 7594 } 7595 const { 7596 result, 7597 zone, 7598 specificOffset, 7599 invalidReason 7600 } = formatParser.explainFromTokens(text); 7601 if (invalidReason) { 7602 return DateTime.invalid(invalidReason); 7603 } else { 7604 return parseDataToDateTime(result, zone, opts, `format ${formatParser.format}`, text, specificOffset); 7605 } 7606 } 7607 7608 // FORMAT PRESETS 7609 7610 /** 7611 * {@link DateTime#toLocaleString} format like 10/14/1983 7612 * @type {Object} 7613 */ 7614 static get DATE_SHORT() { 7615 return DATE_SHORT; 7616 } 7617 7618 /** 7619 * {@link DateTime#toLocaleString} format like 'Oct 14, 1983' 7620 * @type {Object} 7621 */ 7622 static get DATE_MED() { 7623 return DATE_MED; 7624 } 7625 7626 /** 7627 * {@link DateTime#toLocaleString} format like 'Fri, Oct 14, 1983' 7628 * @type {Object} 7629 */ 7630 static get DATE_MED_WITH_WEEKDAY() { 7631 return DATE_MED_WITH_WEEKDAY; 7632 } 7633 7634 /** 7635 * {@link DateTime#toLocaleString} format like 'October 14, 1983' 7636 * @type {Object} 7637 */ 7638 static get DATE_FULL() { 7639 return DATE_FULL; 7640 } 7641 7642 /** 7643 * {@link DateTime#toLocaleString} format like 'Tuesday, October 14, 1983' 7644 * @type {Object} 7645 */ 7646 static get DATE_HUGE() { 7647 return DATE_HUGE; 7648 } 7649 7650 /** 7651 * {@link DateTime#toLocaleString} format like '09:30 AM'. Only 12-hour if the locale is. 7652 * @type {Object} 7653 */ 7654 static get TIME_SIMPLE() { 7655 return TIME_SIMPLE; 7656 } 7657 7658 /** 7659 * {@link DateTime#toLocaleString} format like '09:30:23 AM'. Only 12-hour if the locale is. 7660 * @type {Object} 7661 */ 7662 static get TIME_WITH_SECONDS() { 7663 return TIME_WITH_SECONDS; 7664 } 7665 7666 /** 7667 * {@link DateTime#toLocaleString} format like '09:30:23 AM EDT'. Only 12-hour if the locale is. 7668 * @type {Object} 7669 */ 7670 static get TIME_WITH_SHORT_OFFSET() { 7671 return TIME_WITH_SHORT_OFFSET; 7672 } 7673 7674 /** 7675 * {@link DateTime#toLocaleString} format like '09:30:23 AM Eastern Daylight Time'. Only 12-hour if the locale is. 7676 * @type {Object} 7677 */ 7678 static get TIME_WITH_LONG_OFFSET() { 7679 return TIME_WITH_LONG_OFFSET; 7680 } 7681 7682 /** 7683 * {@link DateTime#toLocaleString} format like '09:30', always 24-hour. 7684 * @type {Object} 7685 */ 7686 static get TIME_24_SIMPLE() { 7687 return TIME_24_SIMPLE; 7688 } 7689 7690 /** 7691 * {@link DateTime#toLocaleString} format like '09:30:23', always 24-hour. 7692 * @type {Object} 7693 */ 7694 static get TIME_24_WITH_SECONDS() { 7695 return TIME_24_WITH_SECONDS; 7696 } 7697 7698 /** 7699 * {@link DateTime#toLocaleString} format like '09:30:23 EDT', always 24-hour. 7700 * @type {Object} 7701 */ 7702 static get TIME_24_WITH_SHORT_OFFSET() { 7703 return TIME_24_WITH_SHORT_OFFSET; 7704 } 7705 7706 /** 7707 * {@link DateTime#toLocaleString} format like '09:30:23 Eastern Daylight Time', always 24-hour. 7708 * @type {Object} 7709 */ 7710 static get TIME_24_WITH_LONG_OFFSET() { 7711 return TIME_24_WITH_LONG_OFFSET; 7712 } 7713 7714 /** 7715 * {@link DateTime#toLocaleString} format like '10/14/1983, 9:30 AM'. Only 12-hour if the locale is. 7716 * @type {Object} 7717 */ 7718 static get DATETIME_SHORT() { 7719 return DATETIME_SHORT; 7720 } 7721 7722 /** 7723 * {@link DateTime#toLocaleString} format like '10/14/1983, 9:30:33 AM'. Only 12-hour if the locale is. 7724 * @type {Object} 7725 */ 7726 static get DATETIME_SHORT_WITH_SECONDS() { 7727 return DATETIME_SHORT_WITH_SECONDS; 7728 } 7729 7730 /** 7731 * {@link DateTime#toLocaleString} format like 'Oct 14, 1983, 9:30 AM'. Only 12-hour if the locale is. 7732 * @type {Object} 7733 */ 7734 static get DATETIME_MED() { 7735 return DATETIME_MED; 7736 } 7737 7738 /** 7739 * {@link DateTime#toLocaleString} format like 'Oct 14, 1983, 9:30:33 AM'. Only 12-hour if the locale is. 7740 * @type {Object} 7741 */ 7742 static get DATETIME_MED_WITH_SECONDS() { 7743 return DATETIME_MED_WITH_SECONDS; 7744 } 7745 7746 /** 7747 * {@link DateTime#toLocaleString} format like 'Fri, 14 Oct 1983, 9:30 AM'. Only 12-hour if the locale is. 7748 * @type {Object} 7749 */ 7750 static get DATETIME_MED_WITH_WEEKDAY() { 7751 return DATETIME_MED_WITH_WEEKDAY; 7752 } 7753 7754 /** 7755 * {@link DateTime#toLocaleString} format like 'October 14, 1983, 9:30 AM EDT'. Only 12-hour if the locale is. 7756 * @type {Object} 7757 */ 7758 static get DATETIME_FULL() { 7759 return DATETIME_FULL; 7760 } 7761 7762 /** 7763 * {@link DateTime#toLocaleString} format like 'October 14, 1983, 9:30:33 AM EDT'. Only 12-hour if the locale is. 7764 * @type {Object} 7765 */ 7766 static get DATETIME_FULL_WITH_SECONDS() { 7767 return DATETIME_FULL_WITH_SECONDS; 7768 } 7769 7770 /** 7771 * {@link DateTime#toLocaleString} format like 'Friday, October 14, 1983, 9:30 AM Eastern Daylight Time'. Only 12-hour if the locale is. 7772 * @type {Object} 7773 */ 7774 static get DATETIME_HUGE() { 7775 return DATETIME_HUGE; 7776 } 7777 7778 /** 7779 * {@link DateTime#toLocaleString} format like 'Friday, October 14, 1983, 9:30:33 AM Eastern Daylight Time'. Only 12-hour if the locale is. 7780 * @type {Object} 7781 */ 7782 static get DATETIME_HUGE_WITH_SECONDS() { 7783 return DATETIME_HUGE_WITH_SECONDS; 7784 } 7785} 7786 7787/** 7788 * @private 7789 */ 7790function friendlyDateTime(dateTimeish) { 7791 if (DateTime.isDateTime(dateTimeish)) { 7792 return dateTimeish; 7793 } else if (dateTimeish && dateTimeish.valueOf && isNumber(dateTimeish.valueOf())) { 7794 return DateTime.fromJSDate(dateTimeish); 7795 } else if (dateTimeish && typeof dateTimeish === "object") { 7796 return DateTime.fromObject(dateTimeish); 7797 } else { 7798 throw new InvalidArgumentError(`Unknown datetime argument: ${dateTimeish}, of type ${typeof dateTimeish}`); 7799 } 7800} 7801 7802const VERSION = "3.7.2"; 7803 7804exports.DateTime = DateTime; 7805exports.Duration = Duration; 7806exports.FixedOffsetZone = FixedOffsetZone; 7807exports.IANAZone = IANAZone; 7808exports.Info = Info; 7809exports.Interval = Interval; 7810exports.InvalidZone = InvalidZone; 7811exports.Settings = Settings; 7812exports.SystemZone = SystemZone; 7813exports.VERSION = VERSION; 7814exports.Zone = Zone; 7815//# sourceMappingURL=luxon.js.map
7816 7817 7818/***/ } 7819 7820}]); 7821//# sourceMappingURL=vendors-node_modules_luxon_build_node_luxon_js.95927b80.js.map
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.