@nmshd/runtime 2.2.0 → 2.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -466,11 +466,11 @@ const content_1 = __webpack_require__(/*! @nmshd/content */ "@nmshd/content");
466
466
  const crypto_1 = __webpack_require__(/*! @nmshd/crypto */ "@nmshd/crypto");
467
467
  const transport_1 = __webpack_require__(/*! @nmshd/transport */ "@nmshd/transport");
468
468
  exports.buildInformation = {
469
- version: "2.2.0",
470
- build: "133",
471
- date: "2022-12-05T13:59:01+00:00",
472
- commit: "027cf5cd12fd1dc4afe025cf05ffdf1c7e606f46",
473
- dependencies: {"@js-soft/docdb-querytranslator":"1.1.0","@js-soft/logging-abstractions":"1.0.0","@js-soft/ts-serval":"2.0.5","@js-soft/ts-utils":"^2.3.0","@nmshd/consumption":"2.3.0","@nmshd/content":"2.1.2","@nmshd/crypto":"2.0.3","@nmshd/transport":"2.0.0","ajv":"^8.11.2","ajv-errors":"^3.0.0","ajv-formats":"^2.1.1","json-stringify-safe":"^5.0.1","luxon":"^3.1.0","qrcode":"1.5.1","reflect-metadata":"0.1.13","ts-simple-nameof":"1.3.1","typescript-ioc":"3.2.2"},
469
+ version: "2.2.1",
470
+ build: "134",
471
+ date: "2022-12-06T13:09:23+00:00",
472
+ commit: "d7f19253acca6735402d45f41ccc4c30d42ff2b5",
473
+ dependencies: {"@js-soft/docdb-querytranslator":"1.1.0","@js-soft/logging-abstractions":"1.0.0","@js-soft/ts-serval":"2.0.5","@js-soft/ts-utils":"^2.3.0","@nmshd/consumption":"2.3.0","@nmshd/content":"2.1.2","@nmshd/crypto":"2.0.3","@nmshd/transport":"2.0.0","ajv":"^8.11.2","ajv-errors":"^3.0.0","ajv-formats":"^2.1.1","json-stringify-safe":"^5.0.1","luxon":"^3.1.1","qrcode":"1.5.1","reflect-metadata":"0.1.13","ts-simple-nameof":"1.3.1","typescript-ioc":"3.2.2"},
474
474
  libraries: {
475
475
  serval: ts_serval_1.buildInformation,
476
476
  consumption: consumption_1.buildInformation,
@@ -32464,7 +32464,10 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
32464
32464
  };
32465
32465
  Object.defineProperty(exports, "__esModule", ({ value: true }));
32466
32466
  exports.CreateOwnRelationshipTemplateUseCase = void 0;
32467
+ const ts_serval_1 = __webpack_require__(/*! @js-soft/ts-serval */ "@js-soft/ts-serval");
32467
32468
  const ts_utils_1 = __webpack_require__(/*! @js-soft/ts-utils */ "./node_modules/@js-soft/ts-utils/dist/index.js");
32469
+ const consumption_1 = __webpack_require__(/*! @nmshd/consumption */ "@nmshd/consumption");
32470
+ const content_1 = __webpack_require__(/*! @nmshd/content */ "@nmshd/content");
32468
32471
  const transport_1 = __webpack_require__(/*! @nmshd/transport */ "@nmshd/transport");
32469
32472
  const luxon_1 = __webpack_require__(/*! luxon */ "./node_modules/luxon/build/node/luxon.js");
32470
32473
  const ts_simple_nameof_1 = __webpack_require__(/*! ts-simple-nameof */ "./node_modules/ts-simple-nameof/index.js");
@@ -32490,12 +32493,16 @@ Validator = __decorate([
32490
32493
  __metadata("design:paramtypes", [common_1.SchemaRepository])
32491
32494
  ], Validator);
32492
32495
  let CreateOwnRelationshipTemplateUseCase = class CreateOwnRelationshipTemplateUseCase extends common_1.UseCase {
32493
- constructor(templateController, accountController, validator) {
32496
+ constructor(templateController, accountController, outgoingRequestsController, validator) {
32494
32497
  super(validator);
32495
32498
  this.templateController = templateController;
32496
32499
  this.accountController = accountController;
32500
+ this.outgoingRequestsController = outgoingRequestsController;
32497
32501
  }
32498
32502
  async executeInternal(request) {
32503
+ const validationError = await this.validateRelationshipTemplateContent(request.content);
32504
+ if (validationError)
32505
+ return ts_utils_1.Result.fail(validationError);
32499
32506
  const relationshipTemplate = await this.templateController.sendRelationshipTemplate({
32500
32507
  content: request.content,
32501
32508
  expiresAt: transport_1.CoreDate.from(request.expiresAt),
@@ -32504,13 +32511,29 @@ let CreateOwnRelationshipTemplateUseCase = class CreateOwnRelationshipTemplateUs
32504
32511
  await this.accountController.syncDatawallet();
32505
32512
  return ts_utils_1.Result.ok(RelationshipTemplateMapper_1.RelationshipTemplateMapper.toRelationshipTemplateDTO(relationshipTemplate));
32506
32513
  }
32514
+ async validateRelationshipTemplateContent(content) {
32515
+ const transformedContent = ts_serval_1.Serializable.fromUnknown(content);
32516
+ if (!(transformedContent instanceof content_1.RelationshipTemplateContent))
32517
+ return;
32518
+ const validationResult = await this.outgoingRequestsController.canCreate({ content: transformedContent.onNewRelationship });
32519
+ if (validationResult.isError())
32520
+ return validationResult.error;
32521
+ if (transformedContent.onExistingRelationship) {
32522
+ const validationResult = await this.outgoingRequestsController.canCreate({ content: transformedContent.onExistingRelationship });
32523
+ if (validationResult.isError())
32524
+ return validationResult.error;
32525
+ }
32526
+ return;
32527
+ }
32507
32528
  };
32508
32529
  CreateOwnRelationshipTemplateUseCase = __decorate([
32509
32530
  __param(0, typescript_ioc_1.Inject),
32510
32531
  __param(1, typescript_ioc_1.Inject),
32511
32532
  __param(2, typescript_ioc_1.Inject),
32533
+ __param(3, typescript_ioc_1.Inject),
32512
32534
  __metadata("design:paramtypes", [transport_1.RelationshipTemplateController,
32513
32535
  transport_1.AccountController,
32536
+ consumption_1.OutgoingRequestsController,
32514
32537
  Validator])
32515
32538
  ], CreateOwnRelationshipTemplateUseCase);
32516
32539
  exports.CreateOwnRelationshipTemplateUseCase = CreateOwnRelationshipTemplateUseCase;
@@ -45671,1351 +45694,975 @@ const DATETIME_HUGE_WITH_SECONDS = {
45671
45694
  timeZoneName: l
45672
45695
  };
45673
45696
 
45674
- /*
45675
- This is just a junk drawer, containing anything used across multiple classes.
45676
- Because Luxon is small(ish), this should stay small and we won't worry about splitting
45677
- it up into, say, parsingUtil.js and basicUtil.js and so on. But they are divided up by feature area.
45678
- */
45679
45697
  /**
45680
- * @private
45698
+ * @interface
45681
45699
  */
45682
- // TYPES
45683
-
45684
- function isUndefined(o) {
45685
- return typeof o === "undefined";
45686
- }
45687
- function isNumber(o) {
45688
- return typeof o === "number";
45689
- }
45690
- function isInteger(o) {
45691
- return typeof o === "number" && o % 1 === 0;
45692
- }
45693
- function isString(o) {
45694
- return typeof o === "string";
45695
- }
45696
- function isDate(o) {
45697
- return Object.prototype.toString.call(o) === "[object Date]";
45698
- } // CAPABILITIES
45699
-
45700
- function hasRelative() {
45701
- try {
45702
- return typeof Intl !== "undefined" && !!Intl.RelativeTimeFormat;
45703
- } catch (e) {
45704
- return false;
45705
- }
45706
- } // OBJECTS AND ARRAYS
45707
45700
 
45708
- function maybeArray(thing) {
45709
- return Array.isArray(thing) ? thing : [thing];
45710
- }
45711
- function bestBy(arr, by, compare) {
45712
- if (arr.length === 0) {
45713
- return undefined;
45701
+ class Zone {
45702
+ /**
45703
+ * The type of zone
45704
+ * @abstract
45705
+ * @type {string}
45706
+ */
45707
+ get type() {
45708
+ throw new ZoneIsAbstractError();
45714
45709
  }
45710
+ /**
45711
+ * The name of this zone.
45712
+ * @abstract
45713
+ * @type {string}
45714
+ */
45715
45715
 
45716
- return arr.reduce((best, next) => {
45717
- const pair = [by(next), next];
45718
45716
 
45719
- if (!best) {
45720
- return pair;
45721
- } else if (compare(best[0], pair[0]) === best[0]) {
45722
- return best;
45723
- } else {
45724
- return pair;
45725
- }
45726
- }, null)[1];
45727
- }
45728
- function pick(obj, keys) {
45729
- return keys.reduce((a, k) => {
45730
- a[k] = obj[k];
45731
- return a;
45732
- }, {});
45733
- }
45734
- function hasOwnProperty(obj, prop) {
45735
- return Object.prototype.hasOwnProperty.call(obj, prop);
45736
- } // NUMBERS AND STRINGS
45717
+ get name() {
45718
+ throw new ZoneIsAbstractError();
45719
+ }
45737
45720
 
45738
- function integerBetween(thing, bottom, top) {
45739
- return isInteger(thing) && thing >= bottom && thing <= top;
45740
- } // x % n but takes the sign of n instead of x
45721
+ get ianaName() {
45722
+ return this.name;
45723
+ }
45724
+ /**
45725
+ * Returns whether the offset is known to be fixed for the whole year.
45726
+ * @abstract
45727
+ * @type {boolean}
45728
+ */
45741
45729
 
45742
- function floorMod(x, n) {
45743
- return x - n * Math.floor(x / n);
45744
- }
45745
- function padStart(input, n = 2) {
45746
- const isNeg = input < 0;
45747
- let padded;
45748
45730
 
45749
- if (isNeg) {
45750
- padded = "-" + ("" + -input).padStart(n, "0");
45751
- } else {
45752
- padded = ("" + input).padStart(n, "0");
45731
+ get isUniversal() {
45732
+ throw new ZoneIsAbstractError();
45753
45733
  }
45734
+ /**
45735
+ * Returns the offset's common name (such as EST) at the specified timestamp
45736
+ * @abstract
45737
+ * @param {number} ts - Epoch milliseconds for which to get the name
45738
+ * @param {Object} opts - Options to affect the format
45739
+ * @param {string} opts.format - What style of offset to return. Accepts 'long' or 'short'.
45740
+ * @param {string} opts.locale - What locale to return the offset name in.
45741
+ * @return {string}
45742
+ */
45754
45743
 
45755
- return padded;
45756
- }
45757
- function parseInteger(string) {
45758
- if (isUndefined(string) || string === null || string === "") {
45759
- return undefined;
45760
- } else {
45761
- return parseInt(string, 10);
45762
- }
45763
- }
45764
- function parseFloating(string) {
45765
- if (isUndefined(string) || string === null || string === "") {
45766
- return undefined;
45767
- } else {
45768
- return parseFloat(string);
45769
- }
45770
- }
45771
- function parseMillis(fraction) {
45772
- // Return undefined (instead of 0) in these cases, where fraction is not set
45773
- if (isUndefined(fraction) || fraction === null || fraction === "") {
45774
- return undefined;
45775
- } else {
45776
- const f = parseFloat("0." + fraction) * 1000;
45777
- return Math.floor(f);
45744
+
45745
+ offsetName(ts, opts) {
45746
+ throw new ZoneIsAbstractError();
45778
45747
  }
45779
- }
45780
- function roundTo(number, digits, towardZero = false) {
45781
- const factor = 10 ** digits,
45782
- rounder = towardZero ? Math.trunc : Math.round;
45783
- return rounder(number * factor) / factor;
45784
- } // DATE BASICS
45748
+ /**
45749
+ * Returns the offset's value as a string
45750
+ * @abstract
45751
+ * @param {number} ts - Epoch milliseconds for which to get the offset
45752
+ * @param {string} format - What style of offset to return.
45753
+ * Accepts 'narrow', 'short', or 'techie'. Returning '+6', '+06:00', or '+0600' respectively
45754
+ * @return {string}
45755
+ */
45785
45756
 
45786
- function isLeapYear(year) {
45787
- return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
45788
- }
45789
- function daysInYear(year) {
45790
- return isLeapYear(year) ? 366 : 365;
45791
- }
45792
- function daysInMonth(year, month) {
45793
- const modMonth = floorMod(month - 1, 12) + 1,
45794
- modYear = year + (month - modMonth) / 12;
45795
45757
 
45796
- if (modMonth === 2) {
45797
- return isLeapYear(modYear) ? 29 : 28;
45798
- } else {
45799
- return [31, null, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][modMonth - 1];
45758
+ formatOffset(ts, format) {
45759
+ throw new ZoneIsAbstractError();
45800
45760
  }
45801
- } // covert a calendar object to a local timestamp (epoch, but with the offset baked in)
45761
+ /**
45762
+ * Return the offset in minutes for this zone at the specified timestamp.
45763
+ * @abstract
45764
+ * @param {number} ts - Epoch milliseconds for which to compute the offset
45765
+ * @return {number}
45766
+ */
45802
45767
 
45803
- function objToLocalTS(obj) {
45804
- let d = Date.UTC(obj.year, obj.month - 1, obj.day, obj.hour, obj.minute, obj.second, obj.millisecond); // for legacy reasons, years between 0 and 99 are interpreted as 19XX; revert that
45805
45768
 
45806
- if (obj.year < 100 && obj.year >= 0) {
45807
- d = new Date(d);
45808
- d.setUTCFullYear(d.getUTCFullYear() - 1900);
45769
+ offset(ts) {
45770
+ throw new ZoneIsAbstractError();
45809
45771
  }
45772
+ /**
45773
+ * Return whether this Zone is equal to another zone
45774
+ * @abstract
45775
+ * @param {Zone} otherZone - the zone to compare
45776
+ * @return {boolean}
45777
+ */
45810
45778
 
45811
- return +d;
45812
- }
45813
- function weeksInWeekYear(weekYear) {
45814
- const p1 = (weekYear + Math.floor(weekYear / 4) - Math.floor(weekYear / 100) + Math.floor(weekYear / 400)) % 7,
45815
- last = weekYear - 1,
45816
- p2 = (last + Math.floor(last / 4) - Math.floor(last / 100) + Math.floor(last / 400)) % 7;
45817
- return p1 === 4 || p2 === 3 ? 53 : 52;
45818
- }
45819
- function untruncateYear(year) {
45820
- if (year > 99) {
45821
- return year;
45822
- } else return year > 60 ? 1900 + year : 2000 + year;
45823
- } // PARSING
45824
-
45825
- function parseZoneInfo(ts, offsetFormat, locale, timeZone = null) {
45826
- const date = new Date(ts),
45827
- intlOpts = {
45828
- hourCycle: "h23",
45829
- year: "numeric",
45830
- month: "2-digit",
45831
- day: "2-digit",
45832
- hour: "2-digit",
45833
- minute: "2-digit"
45834
- };
45835
45779
 
45836
- if (timeZone) {
45837
- intlOpts.timeZone = timeZone;
45780
+ equals(otherZone) {
45781
+ throw new ZoneIsAbstractError();
45838
45782
  }
45783
+ /**
45784
+ * Return whether this Zone is valid.
45785
+ * @abstract
45786
+ * @type {boolean}
45787
+ */
45839
45788
 
45840
- const modified = {
45841
- timeZoneName: offsetFormat,
45842
- ...intlOpts
45843
- };
45844
- const parsed = new Intl.DateTimeFormat(locale, modified).formatToParts(date).find(m => m.type.toLowerCase() === "timezonename");
45845
- return parsed ? parsed.value : null;
45846
- } // signedOffset('-5', '30') -> -330
45847
-
45848
- function signedOffset(offHourStr, offMinuteStr) {
45849
- let offHour = parseInt(offHourStr, 10); // don't || this because we want to preserve -0
45850
45789
 
45851
- if (Number.isNaN(offHour)) {
45852
- offHour = 0;
45790
+ get isValid() {
45791
+ throw new ZoneIsAbstractError();
45853
45792
  }
45854
45793
 
45855
- const offMin = parseInt(offMinuteStr, 10) || 0,
45856
- offMinSigned = offHour < 0 || Object.is(offHour, -0) ? -offMin : offMin;
45857
- return offHour * 60 + offMinSigned;
45858
- } // COERCION
45859
-
45860
- function asNumber(value) {
45861
- const numericValue = Number(value);
45862
- if (typeof value === "boolean" || value === "" || Number.isNaN(numericValue)) throw new InvalidArgumentError(`Invalid unit value ${value}`);
45863
- return numericValue;
45864
45794
  }
45865
- function normalizeObject(obj, normalizer) {
45866
- const normalized = {};
45867
45795
 
45868
- for (const u in obj) {
45869
- if (hasOwnProperty(obj, u)) {
45870
- const v = obj[u];
45871
- if (v === undefined || v === null) continue;
45872
- normalized[normalizer(u)] = asNumber(v);
45796
+ let singleton$1 = null;
45797
+ /**
45798
+ * Represents the local zone for this JavaScript environment.
45799
+ * @implements {Zone}
45800
+ */
45801
+
45802
+ class SystemZone extends Zone {
45803
+ /**
45804
+ * Get a singleton instance of the local zone
45805
+ * @return {SystemZone}
45806
+ */
45807
+ static get instance() {
45808
+ if (singleton$1 === null) {
45809
+ singleton$1 = new SystemZone();
45873
45810
  }
45874
- }
45875
45811
 
45876
- return normalized;
45877
- }
45878
- function formatOffset(offset, format) {
45879
- const hours = Math.trunc(Math.abs(offset / 60)),
45880
- minutes = Math.trunc(Math.abs(offset % 60)),
45881
- sign = offset >= 0 ? "+" : "-";
45812
+ return singleton$1;
45813
+ }
45814
+ /** @override **/
45882
45815
 
45883
- switch (format) {
45884
- case "short":
45885
- return `${sign}${padStart(hours, 2)}:${padStart(minutes, 2)}`;
45886
45816
 
45887
- case "narrow":
45888
- return `${sign}${hours}${minutes > 0 ? `:${minutes}` : ""}`;
45817
+ get type() {
45818
+ return "system";
45819
+ }
45820
+ /** @override **/
45889
45821
 
45890
- case "techie":
45891
- return `${sign}${padStart(hours, 2)}${padStart(minutes, 2)}`;
45892
45822
 
45893
- default:
45894
- throw new RangeError(`Value format ${format} is out of range for property format`);
45823
+ get name() {
45824
+ return new Intl.DateTimeFormat().resolvedOptions().timeZone;
45895
45825
  }
45896
- }
45897
- function timeObject(obj) {
45898
- return pick(obj, ["hour", "minute", "second", "millisecond"]);
45899
- }
45900
- const ianaRegex = /[A-Za-z_+-]{1,256}(?::?\/[A-Za-z0-9_+-]{1,256}(?:\/[A-Za-z0-9_+-]{1,256})?)?/;
45826
+ /** @override **/
45901
45827
 
45902
- /**
45903
- * @private
45904
- */
45905
45828
 
45829
+ get isUniversal() {
45830
+ return false;
45831
+ }
45832
+ /** @override **/
45906
45833
 
45907
- const monthsLong = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
45908
- const monthsShort = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
45909
- const monthsNarrow = ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"];
45910
- function months(length) {
45911
- switch (length) {
45912
- case "narrow":
45913
- return [...monthsNarrow];
45914
45834
 
45915
- case "short":
45916
- return [...monthsShort];
45835
+ offsetName(ts, {
45836
+ format,
45837
+ locale
45838
+ }) {
45839
+ return parseZoneInfo(ts, format, locale);
45840
+ }
45841
+ /** @override **/
45917
45842
 
45918
- case "long":
45919
- return [...monthsLong];
45920
45843
 
45921
- case "numeric":
45922
- return ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"];
45844
+ formatOffset(ts, format) {
45845
+ return formatOffset(this.offset(ts), format);
45846
+ }
45847
+ /** @override **/
45923
45848
 
45924
- case "2-digit":
45925
- return ["01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12"];
45926
45849
 
45927
- default:
45928
- return null;
45850
+ offset(ts) {
45851
+ return -new Date(ts).getTimezoneOffset();
45929
45852
  }
45930
- }
45931
- const weekdaysLong = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"];
45932
- const weekdaysShort = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
45933
- const weekdaysNarrow = ["M", "T", "W", "T", "F", "S", "S"];
45934
- function weekdays(length) {
45935
- switch (length) {
45936
- case "narrow":
45937
- return [...weekdaysNarrow];
45853
+ /** @override **/
45938
45854
 
45939
- case "short":
45940
- return [...weekdaysShort];
45941
45855
 
45942
- case "long":
45943
- return [...weekdaysLong];
45856
+ equals(otherZone) {
45857
+ return otherZone.type === "system";
45858
+ }
45859
+ /** @override **/
45944
45860
 
45945
- case "numeric":
45946
- return ["1", "2", "3", "4", "5", "6", "7"];
45947
45861
 
45948
- default:
45949
- return null;
45862
+ get isValid() {
45863
+ return true;
45950
45864
  }
45951
- }
45952
- const meridiems = ["AM", "PM"];
45953
- const erasLong = ["Before Christ", "Anno Domini"];
45954
- const erasShort = ["BC", "AD"];
45955
- const erasNarrow = ["B", "A"];
45956
- function eras(length) {
45957
- switch (length) {
45958
- case "narrow":
45959
- return [...erasNarrow];
45960
45865
 
45961
- case "short":
45962
- return [...erasShort];
45866
+ }
45963
45867
 
45964
- case "long":
45965
- return [...erasLong];
45868
+ let dtfCache = {};
45966
45869
 
45967
- default:
45968
- return null;
45870
+ function makeDTF(zone) {
45871
+ if (!dtfCache[zone]) {
45872
+ dtfCache[zone] = new Intl.DateTimeFormat("en-US", {
45873
+ hour12: false,
45874
+ timeZone: zone,
45875
+ year: "numeric",
45876
+ month: "2-digit",
45877
+ day: "2-digit",
45878
+ hour: "2-digit",
45879
+ minute: "2-digit",
45880
+ second: "2-digit",
45881
+ era: "short"
45882
+ });
45969
45883
  }
45884
+
45885
+ return dtfCache[zone];
45970
45886
  }
45971
- function meridiemForDateTime(dt) {
45972
- return meridiems[dt.hour < 12 ? 0 : 1];
45973
- }
45974
- function weekdayForDateTime(dt, length) {
45975
- return weekdays(length)[dt.weekday - 1];
45976
- }
45977
- function monthForDateTime(dt, length) {
45978
- return months(length)[dt.month - 1];
45979
- }
45980
- function eraForDateTime(dt, length) {
45981
- return eras(length)[dt.year < 0 ? 0 : 1];
45982
- }
45983
- function formatRelativeTime(unit, count, numeric = "always", narrow = false) {
45984
- const units = {
45985
- years: ["year", "yr."],
45986
- quarters: ["quarter", "qtr."],
45987
- months: ["month", "mo."],
45988
- weeks: ["week", "wk."],
45989
- days: ["day", "day", "days"],
45990
- hours: ["hour", "hr."],
45991
- minutes: ["minute", "min."],
45992
- seconds: ["second", "sec."]
45993
- };
45994
- const lastable = ["hours", "minutes", "seconds"].indexOf(unit) === -1;
45995
45887
 
45996
- if (numeric === "auto" && lastable) {
45997
- const isDay = unit === "days";
45888
+ const typeToPos = {
45889
+ year: 0,
45890
+ month: 1,
45891
+ day: 2,
45892
+ era: 3,
45893
+ hour: 4,
45894
+ minute: 5,
45895
+ second: 6
45896
+ };
45998
45897
 
45999
- switch (count) {
46000
- case 1:
46001
- return isDay ? "tomorrow" : `next ${units[unit][0]}`;
45898
+ function hackyOffset(dtf, date) {
45899
+ const formatted = dtf.format(date).replace(/\u200E/g, ""),
45900
+ parsed = /(\d+)\/(\d+)\/(\d+) (AD|BC),? (\d+):(\d+):(\d+)/.exec(formatted),
45901
+ [, fMonth, fDay, fYear, fadOrBc, fHour, fMinute, fSecond] = parsed;
45902
+ return [fYear, fMonth, fDay, fadOrBc, fHour, fMinute, fSecond];
45903
+ }
46002
45904
 
46003
- case -1:
46004
- return isDay ? "yesterday" : `last ${units[unit][0]}`;
45905
+ function partsOffset(dtf, date) {
45906
+ const formatted = dtf.formatToParts(date);
45907
+ const filled = [];
46005
45908
 
46006
- case 0:
46007
- return isDay ? "today" : `this ${units[unit][0]}`;
45909
+ for (let i = 0; i < formatted.length; i++) {
45910
+ const {
45911
+ type,
45912
+ value
45913
+ } = formatted[i];
45914
+ const pos = typeToPos[type];
46008
45915
 
45916
+ if (type === "era") {
45917
+ filled[pos] = value;
45918
+ } else if (!isUndefined(pos)) {
45919
+ filled[pos] = parseInt(value, 10);
46009
45920
  }
46010
45921
  }
46011
45922
 
46012
- const isInPast = Object.is(count, -0) || count < 0,
46013
- fmtValue = Math.abs(count),
46014
- singular = fmtValue === 1,
46015
- lilUnits = units[unit],
46016
- fmtUnit = narrow ? singular ? lilUnits[1] : lilUnits[2] || lilUnits[1] : singular ? units[unit][0] : unit;
46017
- return isInPast ? `${fmtValue} ${fmtUnit} ago` : `in ${fmtValue} ${fmtUnit}`;
45923
+ return filled;
46018
45924
  }
46019
45925
 
46020
- function stringifyTokens(splits, tokenToString) {
46021
- let s = "";
45926
+ let ianaZoneCache = {};
45927
+ /**
45928
+ * A zone identified by an IANA identifier, like America/New_York
45929
+ * @implements {Zone}
45930
+ */
46022
45931
 
46023
- for (const token of splits) {
46024
- if (token.literal) {
46025
- s += token.val;
46026
- } else {
46027
- s += tokenToString(token.val);
45932
+ class IANAZone extends Zone {
45933
+ /**
45934
+ * @param {string} name - Zone name
45935
+ * @return {IANAZone}
45936
+ */
45937
+ static create(name) {
45938
+ if (!ianaZoneCache[name]) {
45939
+ ianaZoneCache[name] = new IANAZone(name);
46028
45940
  }
46029
- }
46030
45941
 
46031
- return s;
46032
- }
45942
+ return ianaZoneCache[name];
45943
+ }
45944
+ /**
45945
+ * Reset local caches. Should only be necessary in testing scenarios.
45946
+ * @return {void}
45947
+ */
46033
45948
 
46034
- const macroTokenToFormatOpts = {
46035
- D: DATE_SHORT,
46036
- DD: DATE_MED,
46037
- DDD: DATE_FULL,
46038
- DDDD: DATE_HUGE,
46039
- t: TIME_SIMPLE,
46040
- tt: TIME_WITH_SECONDS,
46041
- ttt: TIME_WITH_SHORT_OFFSET,
46042
- tttt: TIME_WITH_LONG_OFFSET,
46043
- T: TIME_24_SIMPLE,
46044
- TT: TIME_24_WITH_SECONDS,
46045
- TTT: TIME_24_WITH_SHORT_OFFSET,
46046
- TTTT: TIME_24_WITH_LONG_OFFSET,
46047
- f: DATETIME_SHORT,
46048
- ff: DATETIME_MED,
46049
- fff: DATETIME_FULL,
46050
- ffff: DATETIME_HUGE,
46051
- F: DATETIME_SHORT_WITH_SECONDS,
46052
- FF: DATETIME_MED_WITH_SECONDS,
46053
- FFF: DATETIME_FULL_WITH_SECONDS,
46054
- FFFF: DATETIME_HUGE_WITH_SECONDS
46055
- };
46056
- /**
46057
- * @private
46058
- */
46059
45949
 
46060
- class Formatter {
46061
- static create(locale, opts = {}) {
46062
- return new Formatter(locale, opts);
45950
+ static resetCache() {
45951
+ ianaZoneCache = {};
45952
+ dtfCache = {};
46063
45953
  }
45954
+ /**
45955
+ * 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.
45956
+ * @param {string} s - The string to check validity on
45957
+ * @example IANAZone.isValidSpecifier("America/New_York") //=> true
45958
+ * @example IANAZone.isValidSpecifier("Sport~~blorp") //=> false
45959
+ * @deprecated This method returns false for some valid IANA names. Use isValidZone instead.
45960
+ * @return {boolean}
45961
+ */
46064
45962
 
46065
- static parseFormat(fmt) {
46066
- let current = null,
46067
- currentFull = "",
46068
- bracketed = false;
46069
- const splits = [];
46070
-
46071
- for (let i = 0; i < fmt.length; i++) {
46072
- const c = fmt.charAt(i);
46073
45963
 
46074
- if (c === "'") {
46075
- if (currentFull.length > 0) {
46076
- splits.push({
46077
- literal: bracketed,
46078
- val: currentFull
46079
- });
46080
- }
45964
+ static isValidSpecifier(s) {
45965
+ return this.isValidZone(s);
45966
+ }
45967
+ /**
45968
+ * Returns whether the provided string identifies a real zone
45969
+ * @param {string} zone - The string to check
45970
+ * @example IANAZone.isValidZone("America/New_York") //=> true
45971
+ * @example IANAZone.isValidZone("Fantasia/Castle") //=> false
45972
+ * @example IANAZone.isValidZone("Sport~~blorp") //=> false
45973
+ * @return {boolean}
45974
+ */
46081
45975
 
46082
- current = null;
46083
- currentFull = "";
46084
- bracketed = !bracketed;
46085
- } else if (bracketed) {
46086
- currentFull += c;
46087
- } else if (c === current) {
46088
- currentFull += c;
46089
- } else {
46090
- if (currentFull.length > 0) {
46091
- splits.push({
46092
- literal: false,
46093
- val: currentFull
46094
- });
46095
- }
46096
45976
 
46097
- currentFull = c;
46098
- current = c;
46099
- }
45977
+ static isValidZone(zone) {
45978
+ if (!zone) {
45979
+ return false;
46100
45980
  }
46101
45981
 
46102
- if (currentFull.length > 0) {
46103
- splits.push({
46104
- literal: bracketed,
46105
- val: currentFull
46106
- });
45982
+ try {
45983
+ new Intl.DateTimeFormat("en-US", {
45984
+ timeZone: zone
45985
+ }).format();
45986
+ return true;
45987
+ } catch (e) {
45988
+ return false;
46107
45989
  }
46108
-
46109
- return splits;
46110
- }
46111
-
46112
- static macroTokenToFormatOpts(token) {
46113
- return macroTokenToFormatOpts[token];
46114
45990
  }
46115
45991
 
46116
- constructor(locale, formatOpts) {
46117
- this.opts = formatOpts;
46118
- this.loc = locale;
46119
- this.systemLoc = null;
46120
- }
45992
+ constructor(name) {
45993
+ super();
45994
+ /** @private **/
46121
45995
 
46122
- formatWithSystemDefault(dt, opts) {
46123
- if (this.systemLoc === null) {
46124
- this.systemLoc = this.loc.redefaultToSystem();
46125
- }
45996
+ this.zoneName = name;
45997
+ /** @private **/
46126
45998
 
46127
- const df = this.systemLoc.dtFormatter(dt, { ...this.opts,
46128
- ...opts
46129
- });
46130
- return df.format();
45999
+ this.valid = IANAZone.isValidZone(name);
46131
46000
  }
46001
+ /** @override **/
46132
46002
 
46133
- formatDateTime(dt, opts = {}) {
46134
- const df = this.loc.dtFormatter(dt, { ...this.opts,
46135
- ...opts
46136
- });
46137
- return df.format();
46138
- }
46139
46003
 
46140
- formatDateTimeParts(dt, opts = {}) {
46141
- const df = this.loc.dtFormatter(dt, { ...this.opts,
46142
- ...opts
46143
- });
46144
- return df.formatToParts();
46004
+ get type() {
46005
+ return "iana";
46145
46006
  }
46007
+ /** @override **/
46146
46008
 
46147
- resolvedOptions(dt, opts = {}) {
46148
- const df = this.loc.dtFormatter(dt, { ...this.opts,
46149
- ...opts
46150
- });
46151
- return df.resolvedOptions();
46009
+
46010
+ get name() {
46011
+ return this.zoneName;
46152
46012
  }
46013
+ /** @override **/
46153
46014
 
46154
- num(n, p = 0) {
46155
- // we get some perf out of doing this here, annoyingly
46156
- if (this.opts.forceSimple) {
46157
- return padStart(n, p);
46158
- }
46159
46015
 
46160
- const opts = { ...this.opts
46161
- };
46016
+ get isUniversal() {
46017
+ return false;
46018
+ }
46019
+ /** @override **/
46162
46020
 
46163
- if (p > 0) {
46164
- opts.padTo = p;
46165
- }
46166
46021
 
46167
- return this.loc.numberFormatter(opts).format(n);
46022
+ offsetName(ts, {
46023
+ format,
46024
+ locale
46025
+ }) {
46026
+ return parseZoneInfo(ts, format, locale, this.name);
46168
46027
  }
46028
+ /** @override **/
46169
46029
 
46170
- formatDateTimeFromString(dt, fmt) {
46171
- const knownEnglish = this.loc.listingMode() === "en",
46172
- useDateTimeFormatter = this.loc.outputCalendar && this.loc.outputCalendar !== "gregory",
46173
- string = (opts, extract) => this.loc.extract(dt, opts, extract),
46174
- formatOffset = opts => {
46175
- if (dt.isOffsetFixed && dt.offset === 0 && opts.allowZ) {
46176
- return "Z";
46177
- }
46178
46030
 
46179
- return dt.isValid ? dt.zone.formatOffset(dt.ts, opts.format) : "";
46180
- },
46181
- meridiem = () => knownEnglish ? meridiemForDateTime(dt) : string({
46182
- hour: "numeric",
46183
- hourCycle: "h12"
46184
- }, "dayperiod"),
46185
- month = (length, standalone) => knownEnglish ? monthForDateTime(dt, length) : string(standalone ? {
46186
- month: length
46187
- } : {
46188
- month: length,
46189
- day: "numeric"
46190
- }, "month"),
46191
- weekday = (length, standalone) => knownEnglish ? weekdayForDateTime(dt, length) : string(standalone ? {
46192
- weekday: length
46193
- } : {
46194
- weekday: length,
46195
- month: "long",
46196
- day: "numeric"
46197
- }, "weekday"),
46198
- maybeMacro = token => {
46199
- const formatOpts = Formatter.macroTokenToFormatOpts(token);
46031
+ formatOffset(ts, format) {
46032
+ return formatOffset(this.offset(ts), format);
46033
+ }
46034
+ /** @override **/
46200
46035
 
46201
- if (formatOpts) {
46202
- return this.formatWithSystemDefault(dt, formatOpts);
46203
- } else {
46204
- return token;
46205
- }
46206
- },
46207
- era = length => knownEnglish ? eraForDateTime(dt, length) : string({
46208
- era: length
46209
- }, "era"),
46210
- tokenToString = token => {
46211
- // Where possible: http://cldr.unicode.org/translation/date-time-1/date-time#TOC-Standalone-vs.-Format-Styles
46212
- switch (token) {
46213
- // ms
46214
- case "S":
46215
- return this.num(dt.millisecond);
46216
46036
 
46217
- case "u": // falls through
46037
+ offset(ts) {
46038
+ const date = new Date(ts);
46039
+ if (isNaN(date)) return NaN;
46040
+ const dtf = makeDTF(this.name);
46041
+ let [year, month, day, adOrBc, hour, minute, second] = dtf.formatToParts ? partsOffset(dtf, date) : hackyOffset(dtf, date);
46218
46042
 
46219
- case "SSS":
46220
- return this.num(dt.millisecond, 3);
46221
- // seconds
46043
+ if (adOrBc === "BC") {
46044
+ year = -Math.abs(year) + 1;
46045
+ } // because we're using hour12 and https://bugs.chromium.org/p/chromium/issues/detail?id=1025564&can=2&q=%2224%3A00%22%20datetimeformat
46222
46046
 
46223
- case "s":
46224
- return this.num(dt.second);
46225
46047
 
46226
- case "ss":
46227
- return this.num(dt.second, 2);
46228
- // fractional seconds
46048
+ const adjustedHour = hour === 24 ? 0 : hour;
46049
+ const asUTC = objToLocalTS({
46050
+ year,
46051
+ month,
46052
+ day,
46053
+ hour: adjustedHour,
46054
+ minute,
46055
+ second,
46056
+ millisecond: 0
46057
+ });
46058
+ let asTS = +date;
46059
+ const over = asTS % 1000;
46060
+ asTS -= over >= 0 ? over : 1000 + over;
46061
+ return (asUTC - asTS) / (60 * 1000);
46062
+ }
46063
+ /** @override **/
46229
46064
 
46230
- case "uu":
46231
- return this.num(Math.floor(dt.millisecond / 10), 2);
46232
46065
 
46233
- case "uuu":
46234
- return this.num(Math.floor(dt.millisecond / 100));
46235
- // minutes
46066
+ equals(otherZone) {
46067
+ return otherZone.type === "iana" && otherZone.name === this.name;
46068
+ }
46069
+ /** @override **/
46236
46070
 
46237
- case "m":
46238
- return this.num(dt.minute);
46239
46071
 
46240
- case "mm":
46241
- return this.num(dt.minute, 2);
46242
- // hours
46072
+ get isValid() {
46073
+ return this.valid;
46074
+ }
46243
46075
 
46244
- case "h":
46245
- return this.num(dt.hour % 12 === 0 ? 12 : dt.hour % 12);
46076
+ }
46246
46077
 
46247
- case "hh":
46248
- return this.num(dt.hour % 12 === 0 ? 12 : dt.hour % 12, 2);
46078
+ let intlLFCache = {};
46249
46079
 
46250
- case "H":
46251
- return this.num(dt.hour);
46080
+ function getCachedLF(locString, opts = {}) {
46081
+ const key = JSON.stringify([locString, opts]);
46082
+ let dtf = intlLFCache[key];
46252
46083
 
46253
- case "HH":
46254
- return this.num(dt.hour, 2);
46255
- // offset
46084
+ if (!dtf) {
46085
+ dtf = new Intl.ListFormat(locString, opts);
46086
+ intlLFCache[key] = dtf;
46087
+ }
46256
46088
 
46257
- case "Z":
46258
- // like +6
46259
- return formatOffset({
46260
- format: "narrow",
46261
- allowZ: this.opts.allowZ
46262
- });
46089
+ return dtf;
46090
+ }
46263
46091
 
46264
- case "ZZ":
46265
- // like +06:00
46266
- return formatOffset({
46267
- format: "short",
46268
- allowZ: this.opts.allowZ
46269
- });
46092
+ let intlDTCache = {};
46270
46093
 
46271
- case "ZZZ":
46272
- // like +0600
46273
- return formatOffset({
46274
- format: "techie",
46275
- allowZ: this.opts.allowZ
46276
- });
46094
+ function getCachedDTF(locString, opts = {}) {
46095
+ const key = JSON.stringify([locString, opts]);
46096
+ let dtf = intlDTCache[key];
46277
46097
 
46278
- case "ZZZZ":
46279
- // like EST
46280
- return dt.zone.offsetName(dt.ts, {
46281
- format: "short",
46282
- locale: this.loc.locale
46283
- });
46098
+ if (!dtf) {
46099
+ dtf = new Intl.DateTimeFormat(locString, opts);
46100
+ intlDTCache[key] = dtf;
46101
+ }
46284
46102
 
46285
- case "ZZZZZ":
46286
- // like Eastern Standard Time
46287
- return dt.zone.offsetName(dt.ts, {
46288
- format: "long",
46289
- locale: this.loc.locale
46290
- });
46291
- // zone
46103
+ return dtf;
46104
+ }
46292
46105
 
46293
- case "z":
46294
- // like America/New_York
46295
- return dt.zoneName;
46296
- // meridiems
46106
+ let intlNumCache = {};
46297
46107
 
46298
- case "a":
46299
- return meridiem();
46300
- // dates
46108
+ function getCachedINF(locString, opts = {}) {
46109
+ const key = JSON.stringify([locString, opts]);
46110
+ let inf = intlNumCache[key];
46301
46111
 
46302
- case "d":
46303
- return useDateTimeFormatter ? string({
46304
- day: "numeric"
46305
- }, "day") : this.num(dt.day);
46112
+ if (!inf) {
46113
+ inf = new Intl.NumberFormat(locString, opts);
46114
+ intlNumCache[key] = inf;
46115
+ }
46306
46116
 
46307
- case "dd":
46308
- return useDateTimeFormatter ? string({
46309
- day: "2-digit"
46310
- }, "day") : this.num(dt.day, 2);
46311
- // weekdays - standalone
46117
+ return inf;
46118
+ }
46312
46119
 
46313
- case "c":
46314
- // like 1
46315
- return this.num(dt.weekday);
46120
+ let intlRelCache = {};
46316
46121
 
46317
- case "ccc":
46318
- // like 'Tues'
46319
- return weekday("short", true);
46122
+ function getCachedRTF(locString, opts = {}) {
46123
+ const {
46124
+ base,
46125
+ ...cacheKeyOpts
46126
+ } = opts; // exclude `base` from the options
46320
46127
 
46321
- case "cccc":
46322
- // like 'Tuesday'
46323
- return weekday("long", true);
46128
+ const key = JSON.stringify([locString, cacheKeyOpts]);
46129
+ let inf = intlRelCache[key];
46324
46130
 
46325
- case "ccccc":
46326
- // like 'T'
46327
- return weekday("narrow", true);
46328
- // weekdays - format
46131
+ if (!inf) {
46132
+ inf = new Intl.RelativeTimeFormat(locString, opts);
46133
+ intlRelCache[key] = inf;
46134
+ }
46329
46135
 
46330
- case "E":
46331
- // like 1
46332
- return this.num(dt.weekday);
46136
+ return inf;
46137
+ }
46333
46138
 
46334
- case "EEE":
46335
- // like 'Tues'
46336
- return weekday("short", false);
46139
+ let sysLocaleCache = null;
46337
46140
 
46338
- case "EEEE":
46339
- // like 'Tuesday'
46340
- return weekday("long", false);
46141
+ function systemLocale() {
46142
+ if (sysLocaleCache) {
46143
+ return sysLocaleCache;
46144
+ } else {
46145
+ sysLocaleCache = new Intl.DateTimeFormat().resolvedOptions().locale;
46146
+ return sysLocaleCache;
46147
+ }
46148
+ }
46341
46149
 
46342
- case "EEEEE":
46343
- // like 'T'
46344
- return weekday("narrow", false);
46345
- // months - standalone
46150
+ function parseLocaleString(localeStr) {
46151
+ // I really want to avoid writing a BCP 47 parser
46152
+ // see, e.g. https://github.com/wooorm/bcp-47
46153
+ // Instead, we'll do this:
46154
+ // a) if the string has no -u extensions, just leave it alone
46155
+ // b) if it does, use Intl to resolve everything
46156
+ // c) if Intl fails, try again without the -u
46157
+ const uIndex = localeStr.indexOf("-u-");
46346
46158
 
46347
- case "L":
46348
- // like 1
46349
- return useDateTimeFormatter ? string({
46350
- month: "numeric",
46351
- day: "numeric"
46352
- }, "month") : this.num(dt.month);
46159
+ if (uIndex === -1) {
46160
+ return [localeStr];
46161
+ } else {
46162
+ let options;
46163
+ const smaller = localeStr.substring(0, uIndex);
46353
46164
 
46354
- case "LL":
46355
- // like 01, doesn't seem to work
46356
- return useDateTimeFormatter ? string({
46357
- month: "2-digit",
46358
- day: "numeric"
46359
- }, "month") : this.num(dt.month, 2);
46165
+ try {
46166
+ options = getCachedDTF(localeStr).resolvedOptions();
46167
+ } catch (e) {
46168
+ options = getCachedDTF(smaller).resolvedOptions();
46169
+ }
46360
46170
 
46361
- case "LLL":
46362
- // like Jan
46363
- return month("short", true);
46171
+ const {
46172
+ numberingSystem,
46173
+ calendar
46174
+ } = options; // return the smaller one so that we can append the calendar and numbering overrides to it
46364
46175
 
46365
- case "LLLL":
46366
- // like January
46367
- return month("long", true);
46176
+ return [smaller, numberingSystem, calendar];
46177
+ }
46178
+ }
46368
46179
 
46369
- case "LLLLL":
46370
- // like J
46371
- return month("narrow", true);
46372
- // months - format
46180
+ function intlConfigString(localeStr, numberingSystem, outputCalendar) {
46181
+ if (outputCalendar || numberingSystem) {
46182
+ localeStr += "-u";
46373
46183
 
46374
- case "M":
46375
- // like 1
46376
- return useDateTimeFormatter ? string({
46377
- month: "numeric"
46378
- }, "month") : this.num(dt.month);
46184
+ if (outputCalendar) {
46185
+ localeStr += `-ca-${outputCalendar}`;
46186
+ }
46379
46187
 
46380
- case "MM":
46381
- // like 01
46382
- return useDateTimeFormatter ? string({
46383
- month: "2-digit"
46384
- }, "month") : this.num(dt.month, 2);
46188
+ if (numberingSystem) {
46189
+ localeStr += `-nu-${numberingSystem}`;
46190
+ }
46385
46191
 
46386
- case "MMM":
46387
- // like Jan
46388
- return month("short", false);
46192
+ return localeStr;
46193
+ } else {
46194
+ return localeStr;
46195
+ }
46196
+ }
46389
46197
 
46390
- case "MMMM":
46391
- // like January
46392
- return month("long", false);
46198
+ function mapMonths(f) {
46199
+ const ms = [];
46393
46200
 
46394
- case "MMMMM":
46395
- // like J
46396
- return month("narrow", false);
46397
- // years
46201
+ for (let i = 1; i <= 12; i++) {
46202
+ const dt = DateTime.utc(2016, i, 1);
46203
+ ms.push(f(dt));
46204
+ }
46398
46205
 
46399
- case "y":
46400
- // like 2014
46401
- return useDateTimeFormatter ? string({
46402
- year: "numeric"
46403
- }, "year") : this.num(dt.year);
46206
+ return ms;
46207
+ }
46404
46208
 
46405
- case "yy":
46406
- // like 14
46407
- return useDateTimeFormatter ? string({
46408
- year: "2-digit"
46409
- }, "year") : this.num(dt.year.toString().slice(-2), 2);
46209
+ function mapWeekdays(f) {
46210
+ const ms = [];
46410
46211
 
46411
- case "yyyy":
46412
- // like 0012
46413
- return useDateTimeFormatter ? string({
46414
- year: "numeric"
46415
- }, "year") : this.num(dt.year, 4);
46212
+ for (let i = 1; i <= 7; i++) {
46213
+ const dt = DateTime.utc(2016, 11, 13 + i);
46214
+ ms.push(f(dt));
46215
+ }
46416
46216
 
46417
- case "yyyyyy":
46418
- // like 000012
46419
- return useDateTimeFormatter ? string({
46420
- year: "numeric"
46421
- }, "year") : this.num(dt.year, 6);
46422
- // eras
46217
+ return ms;
46218
+ }
46423
46219
 
46424
- case "G":
46425
- // like AD
46426
- return era("short");
46220
+ function listStuff(loc, length, defaultOK, englishFn, intlFn) {
46221
+ const mode = loc.listingMode(defaultOK);
46427
46222
 
46428
- case "GG":
46429
- // like Anno Domini
46430
- return era("long");
46223
+ if (mode === "error") {
46224
+ return null;
46225
+ } else if (mode === "en") {
46226
+ return englishFn(length);
46227
+ } else {
46228
+ return intlFn(length);
46229
+ }
46230
+ }
46431
46231
 
46432
- case "GGGGG":
46433
- return era("narrow");
46434
-
46435
- case "kk":
46436
- return this.num(dt.weekYear.toString().slice(-2), 2);
46232
+ function supportsFastNumbers(loc) {
46233
+ if (loc.numberingSystem && loc.numberingSystem !== "latn") {
46234
+ return false;
46235
+ } else {
46236
+ return loc.numberingSystem === "latn" || !loc.locale || loc.locale.startsWith("en") || new Intl.DateTimeFormat(loc.intl).resolvedOptions().numberingSystem === "latn";
46237
+ }
46238
+ }
46239
+ /**
46240
+ * @private
46241
+ */
46437
46242
 
46438
- case "kkkk":
46439
- return this.num(dt.weekYear, 4);
46440
46243
 
46441
- case "W":
46442
- return this.num(dt.weekNumber);
46244
+ class PolyNumberFormatter {
46245
+ constructor(intl, forceSimple, opts) {
46246
+ this.padTo = opts.padTo || 0;
46247
+ this.floor = opts.floor || false;
46248
+ const {
46249
+ padTo,
46250
+ floor,
46251
+ ...otherOpts
46252
+ } = opts;
46443
46253
 
46444
- case "WW":
46445
- return this.num(dt.weekNumber, 2);
46254
+ if (!forceSimple || Object.keys(otherOpts).length > 0) {
46255
+ const intlOpts = {
46256
+ useGrouping: false,
46257
+ ...opts
46258
+ };
46259
+ if (opts.padTo > 0) intlOpts.minimumIntegerDigits = opts.padTo;
46260
+ this.inf = getCachedINF(intl, intlOpts);
46261
+ }
46262
+ }
46446
46263
 
46447
- case "o":
46448
- return this.num(dt.ordinal);
46264
+ format(i) {
46265
+ if (this.inf) {
46266
+ const fixed = this.floor ? Math.floor(i) : i;
46267
+ return this.inf.format(fixed);
46268
+ } else {
46269
+ // to match the browser's numberformatter defaults
46270
+ const fixed = this.floor ? Math.floor(i) : roundTo(i, 3);
46271
+ return padStart(fixed, this.padTo);
46272
+ }
46273
+ }
46449
46274
 
46450
- case "ooo":
46451
- return this.num(dt.ordinal, 3);
46275
+ }
46276
+ /**
46277
+ * @private
46278
+ */
46452
46279
 
46453
- case "q":
46454
- // like 1
46455
- return this.num(dt.quarter);
46456
46280
 
46457
- case "qq":
46458
- // like 01
46459
- return this.num(dt.quarter, 2);
46281
+ class PolyDateFormatter {
46282
+ constructor(dt, intl, opts) {
46283
+ this.opts = opts;
46284
+ let z;
46460
46285
 
46461
- case "X":
46462
- return this.num(Math.floor(dt.ts / 1000));
46286
+ if (dt.zone.isUniversal) {
46287
+ // UTC-8 or Etc/UTC-8 are not part of tzdata, only Etc/GMT+8 and the like.
46288
+ // That is why fixed-offset TZ is set to that unless it is:
46289
+ // 1. Representing offset 0 when UTC is used to maintain previous behavior and does not become GMT.
46290
+ // 2. Unsupported by the browser:
46291
+ // - some do not support Etc/
46292
+ // - < Etc/GMT-14, > Etc/GMT+12, and 30-minute or 45-minute offsets are not part of tzdata
46293
+ const gmtOffset = -1 * (dt.offset / 60);
46294
+ const offsetZ = gmtOffset >= 0 ? `Etc/GMT+${gmtOffset}` : `Etc/GMT${gmtOffset}`;
46463
46295
 
46464
- case "x":
46465
- return this.num(dt.ts);
46296
+ if (dt.offset !== 0 && IANAZone.create(offsetZ).valid) {
46297
+ z = offsetZ;
46298
+ this.dt = dt;
46299
+ } else {
46300
+ // Not all fixed-offset zones like Etc/+4:30 are present in tzdata.
46301
+ // So we have to make do. Two cases:
46302
+ // 1. The format options tell us to show the zone. We can't do that, so the best
46303
+ // we can do is format the date in UTC.
46304
+ // 2. The format options don't tell us to show the zone. Then we can adjust them
46305
+ // the time and tell the formatter to show it to us in UTC, so that the time is right
46306
+ // and the bad zone doesn't show up.
46307
+ z = "UTC";
46466
46308
 
46467
- default:
46468
- return maybeMacro(token);
46309
+ if (opts.timeZoneName) {
46310
+ this.dt = dt;
46311
+ } else {
46312
+ this.dt = dt.offset === 0 ? dt : DateTime.fromMillis(dt.ts + dt.offset * 60 * 1000);
46313
+ }
46469
46314
  }
46315
+ } else if (dt.zone.type === "system") {
46316
+ this.dt = dt;
46317
+ } else {
46318
+ this.dt = dt;
46319
+ z = dt.zone.name;
46320
+ }
46321
+
46322
+ const intlOpts = { ...this.opts
46470
46323
  };
46471
46324
 
46472
- return stringifyTokens(Formatter.parseFormat(fmt), tokenToString);
46325
+ if (z) {
46326
+ intlOpts.timeZone = z;
46327
+ }
46328
+
46329
+ this.dtf = getCachedDTF(intl, intlOpts);
46473
46330
  }
46474
46331
 
46475
- formatDurationFromString(dur, fmt) {
46476
- const tokenToField = token => {
46477
- switch (token[0]) {
46478
- case "S":
46479
- return "millisecond";
46332
+ format() {
46333
+ return this.dtf.format(this.dt.toJSDate());
46334
+ }
46480
46335
 
46481
- case "s":
46482
- return "second";
46336
+ formatToParts() {
46337
+ return this.dtf.formatToParts(this.dt.toJSDate());
46338
+ }
46483
46339
 
46484
- case "m":
46485
- return "minute";
46340
+ resolvedOptions() {
46341
+ return this.dtf.resolvedOptions();
46342
+ }
46486
46343
 
46487
- case "h":
46488
- return "hour";
46344
+ }
46345
+ /**
46346
+ * @private
46347
+ */
46489
46348
 
46490
- case "d":
46491
- return "day";
46492
46349
 
46493
- case "w":
46494
- return "week";
46350
+ class PolyRelFormatter {
46351
+ constructor(intl, isEnglish, opts) {
46352
+ this.opts = {
46353
+ style: "long",
46354
+ ...opts
46355
+ };
46495
46356
 
46496
- case "M":
46497
- return "month";
46357
+ if (!isEnglish && hasRelative()) {
46358
+ this.rtf = getCachedRTF(intl, opts);
46359
+ }
46360
+ }
46498
46361
 
46499
- case "y":
46500
- return "year";
46362
+ format(count, unit) {
46363
+ if (this.rtf) {
46364
+ return this.rtf.format(count, unit);
46365
+ } else {
46366
+ return formatRelativeTime(unit, count, this.opts.numeric, this.opts.style !== "long");
46367
+ }
46368
+ }
46501
46369
 
46502
- default:
46503
- return null;
46504
- }
46505
- },
46506
- tokenToString = lildur => token => {
46507
- const mapped = tokenToField(token);
46370
+ formatToParts(count, unit) {
46371
+ if (this.rtf) {
46372
+ return this.rtf.formatToParts(count, unit);
46373
+ } else {
46374
+ return [];
46375
+ }
46376
+ }
46508
46377
 
46509
- if (mapped) {
46510
- return this.num(lildur.get(mapped), token.length);
46511
- } else {
46512
- return token;
46513
- }
46514
- },
46515
- tokens = Formatter.parseFormat(fmt),
46516
- realTokens = tokens.reduce((found, {
46517
- literal,
46518
- val
46519
- }) => literal ? found : found.concat(val), []),
46520
- collapsed = dur.shiftTo(...realTokens.map(tokenToField).filter(t => t));
46378
+ }
46379
+ /**
46380
+ * @private
46381
+ */
46521
46382
 
46522
- return stringifyTokens(tokens, tokenToString(collapsed));
46383
+
46384
+ class Locale {
46385
+ static fromOpts(opts) {
46386
+ return Locale.create(opts.locale, opts.numberingSystem, opts.outputCalendar, opts.defaultToEN);
46523
46387
  }
46524
46388
 
46525
- }
46389
+ static create(locale, numberingSystem, outputCalendar, defaultToEN = false) {
46390
+ const specifiedLocale = locale || Settings.defaultLocale; // the system locale is useful for human readable strings but annoying for parsing/formatting known formats
46526
46391
 
46527
- class Invalid {
46528
- constructor(reason, explanation) {
46529
- this.reason = reason;
46530
- this.explanation = explanation;
46392
+ const localeR = specifiedLocale || (defaultToEN ? "en-US" : systemLocale());
46393
+ const numberingSystemR = numberingSystem || Settings.defaultNumberingSystem;
46394
+ const outputCalendarR = outputCalendar || Settings.defaultOutputCalendar;
46395
+ return new Locale(localeR, numberingSystemR, outputCalendarR, specifiedLocale);
46531
46396
  }
46532
46397
 
46533
- toMessage() {
46534
- if (this.explanation) {
46535
- return `${this.reason}: ${this.explanation}`;
46536
- } else {
46537
- return this.reason;
46538
- }
46398
+ static resetCache() {
46399
+ sysLocaleCache = null;
46400
+ intlDTCache = {};
46401
+ intlNumCache = {};
46402
+ intlRelCache = {};
46539
46403
  }
46540
46404
 
46541
- }
46405
+ static fromObject({
46406
+ locale,
46407
+ numberingSystem,
46408
+ outputCalendar
46409
+ } = {}) {
46410
+ return Locale.create(locale, numberingSystem, outputCalendar);
46411
+ }
46542
46412
 
46543
- /**
46544
- * @interface
46545
- */
46413
+ constructor(locale, numbering, outputCalendar, specifiedLocale) {
46414
+ const [parsedLocale, parsedNumberingSystem, parsedOutputCalendar] = parseLocaleString(locale);
46415
+ this.locale = parsedLocale;
46416
+ this.numberingSystem = numbering || parsedNumberingSystem || null;
46417
+ this.outputCalendar = outputCalendar || parsedOutputCalendar || null;
46418
+ this.intl = intlConfigString(this.locale, this.numberingSystem, this.outputCalendar);
46419
+ this.weekdaysCache = {
46420
+ format: {},
46421
+ standalone: {}
46422
+ };
46423
+ this.monthsCache = {
46424
+ format: {},
46425
+ standalone: {}
46426
+ };
46427
+ this.meridiemCache = null;
46428
+ this.eraCache = {};
46429
+ this.specifiedLocale = specifiedLocale;
46430
+ this.fastNumbersCached = null;
46431
+ }
46546
46432
 
46547
- class Zone {
46548
- /**
46549
- * The type of zone
46550
- * @abstract
46551
- * @type {string}
46552
- */
46553
- get type() {
46554
- throw new ZoneIsAbstractError();
46433
+ get fastNumbers() {
46434
+ if (this.fastNumbersCached == null) {
46435
+ this.fastNumbersCached = supportsFastNumbers(this);
46436
+ }
46437
+
46438
+ return this.fastNumbersCached;
46555
46439
  }
46556
- /**
46557
- * The name of this zone.
46558
- * @abstract
46559
- * @type {string}
46560
- */
46561
46440
 
46441
+ listingMode() {
46442
+ const isActuallyEn = this.isEnglish();
46443
+ const hasNoWeirdness = (this.numberingSystem === null || this.numberingSystem === "latn") && (this.outputCalendar === null || this.outputCalendar === "gregory");
46444
+ return isActuallyEn && hasNoWeirdness ? "en" : "intl";
46445
+ }
46562
46446
 
46563
- get name() {
46564
- throw new ZoneIsAbstractError();
46447
+ clone(alts) {
46448
+ if (!alts || Object.getOwnPropertyNames(alts).length === 0) {
46449
+ return this;
46450
+ } else {
46451
+ return Locale.create(alts.locale || this.specifiedLocale, alts.numberingSystem || this.numberingSystem, alts.outputCalendar || this.outputCalendar, alts.defaultToEN || false);
46452
+ }
46565
46453
  }
46566
46454
 
46567
- get ianaName() {
46568
- return this.name;
46455
+ redefaultToEN(alts = {}) {
46456
+ return this.clone({ ...alts,
46457
+ defaultToEN: true
46458
+ });
46569
46459
  }
46570
- /**
46571
- * Returns whether the offset is known to be fixed for the whole year.
46572
- * @abstract
46573
- * @type {boolean}
46574
- */
46575
46460
 
46461
+ redefaultToSystem(alts = {}) {
46462
+ return this.clone({ ...alts,
46463
+ defaultToEN: false
46464
+ });
46465
+ }
46576
46466
 
46577
- get isUniversal() {
46578
- throw new ZoneIsAbstractError();
46467
+ months(length, format = false, defaultOK = true) {
46468
+ return listStuff(this, length, defaultOK, months, () => {
46469
+ const intl = format ? {
46470
+ month: length,
46471
+ day: "numeric"
46472
+ } : {
46473
+ month: length
46474
+ },
46475
+ formatStr = format ? "format" : "standalone";
46476
+
46477
+ if (!this.monthsCache[formatStr][length]) {
46478
+ this.monthsCache[formatStr][length] = mapMonths(dt => this.extract(dt, intl, "month"));
46479
+ }
46480
+
46481
+ return this.monthsCache[formatStr][length];
46482
+ });
46579
46483
  }
46580
- /**
46581
- * Returns the offset's common name (such as EST) at the specified timestamp
46582
- * @abstract
46583
- * @param {number} ts - Epoch milliseconds for which to get the name
46584
- * @param {Object} opts - Options to affect the format
46585
- * @param {string} opts.format - What style of offset to return. Accepts 'long' or 'short'.
46586
- * @param {string} opts.locale - What locale to return the offset name in.
46587
- * @return {string}
46588
- */
46589
46484
 
46485
+ weekdays(length, format = false, defaultOK = true) {
46486
+ return listStuff(this, length, defaultOK, weekdays, () => {
46487
+ const intl = format ? {
46488
+ weekday: length,
46489
+ year: "numeric",
46490
+ month: "long",
46491
+ day: "numeric"
46492
+ } : {
46493
+ weekday: length
46494
+ },
46495
+ formatStr = format ? "format" : "standalone";
46590
46496
 
46591
- offsetName(ts, opts) {
46592
- throw new ZoneIsAbstractError();
46497
+ if (!this.weekdaysCache[formatStr][length]) {
46498
+ this.weekdaysCache[formatStr][length] = mapWeekdays(dt => this.extract(dt, intl, "weekday"));
46499
+ }
46500
+
46501
+ return this.weekdaysCache[formatStr][length];
46502
+ });
46593
46503
  }
46594
- /**
46595
- * Returns the offset's value as a string
46596
- * @abstract
46597
- * @param {number} ts - Epoch milliseconds for which to get the offset
46598
- * @param {string} format - What style of offset to return.
46599
- * Accepts 'narrow', 'short', or 'techie'. Returning '+6', '+06:00', or '+0600' respectively
46600
- * @return {string}
46601
- */
46602
46504
 
46505
+ meridiems(defaultOK = true) {
46506
+ return listStuff(this, undefined, defaultOK, () => meridiems, () => {
46507
+ // In theory there could be aribitrary day periods. We're gonna assume there are exactly two
46508
+ // for AM and PM. This is probably wrong, but it's makes parsing way easier.
46509
+ if (!this.meridiemCache) {
46510
+ const intl = {
46511
+ hour: "numeric",
46512
+ hourCycle: "h12"
46513
+ };
46514
+ this.meridiemCache = [DateTime.utc(2016, 11, 13, 9), DateTime.utc(2016, 11, 13, 19)].map(dt => this.extract(dt, intl, "dayperiod"));
46515
+ }
46603
46516
 
46604
- formatOffset(ts, format) {
46605
- throw new ZoneIsAbstractError();
46517
+ return this.meridiemCache;
46518
+ });
46606
46519
  }
46607
- /**
46608
- * Return the offset in minutes for this zone at the specified timestamp.
46609
- * @abstract
46610
- * @param {number} ts - Epoch milliseconds for which to compute the offset
46611
- * @return {number}
46612
- */
46613
46520
 
46521
+ eras(length, defaultOK = true) {
46522
+ return listStuff(this, length, defaultOK, eras, () => {
46523
+ const intl = {
46524
+ era: length
46525
+ }; // This is problematic. Different calendars are going to define eras totally differently. What I need is the minimum set of dates
46526
+ // to definitely enumerate them.
46614
46527
 
46615
- offset(ts) {
46616
- throw new ZoneIsAbstractError();
46528
+ if (!this.eraCache[length]) {
46529
+ this.eraCache[length] = [DateTime.utc(-40, 1, 1), DateTime.utc(2017, 1, 1)].map(dt => this.extract(dt, intl, "era"));
46530
+ }
46531
+
46532
+ return this.eraCache[length];
46533
+ });
46617
46534
  }
46618
- /**
46619
- * Return whether this Zone is equal to another zone
46620
- * @abstract
46621
- * @param {Zone} otherZone - the zone to compare
46622
- * @return {boolean}
46623
- */
46624
46535
 
46536
+ extract(dt, intlOpts, field) {
46537
+ const df = this.dtFormatter(dt, intlOpts),
46538
+ results = df.formatToParts(),
46539
+ matching = results.find(m => m.type.toLowerCase() === field);
46540
+ return matching ? matching.value : null;
46541
+ }
46625
46542
 
46626
- equals(otherZone) {
46627
- throw new ZoneIsAbstractError();
46543
+ numberFormatter(opts = {}) {
46544
+ // this forcesimple option is never used (the only caller short-circuits on it, but it seems safer to leave)
46545
+ // (in contrast, the rest of the condition is used heavily)
46546
+ return new PolyNumberFormatter(this.intl, opts.forceSimple || this.fastNumbers, opts);
46547
+ }
46548
+
46549
+ dtFormatter(dt, intlOpts = {}) {
46550
+ return new PolyDateFormatter(dt, this.intl, intlOpts);
46628
46551
  }
46629
- /**
46630
- * Return whether this Zone is valid.
46631
- * @abstract
46632
- * @type {boolean}
46633
- */
46634
46552
 
46553
+ relFormatter(opts = {}) {
46554
+ return new PolyRelFormatter(this.intl, this.isEnglish(), opts);
46555
+ }
46635
46556
 
46636
- get isValid() {
46637
- throw new ZoneIsAbstractError();
46557
+ listFormatter(opts = {}) {
46558
+ return getCachedLF(this.intl, opts);
46559
+ }
46560
+
46561
+ isEnglish() {
46562
+ return this.locale === "en" || this.locale.toLowerCase() === "en-us" || new Intl.DateTimeFormat(this.intl).resolvedOptions().locale.startsWith("en-us");
46563
+ }
46564
+
46565
+ equals(other) {
46566
+ return this.locale === other.locale && this.numberingSystem === other.numberingSystem && this.outputCalendar === other.outputCalendar;
46638
46567
  }
46639
46568
 
46640
46569
  }
46641
46570
 
46642
- let singleton$1 = null;
46571
+ let singleton = null;
46643
46572
  /**
46644
- * Represents the local zone for this JavaScript environment.
46573
+ * A zone with a fixed offset (meaning no DST)
46645
46574
  * @implements {Zone}
46646
46575
  */
46647
46576
 
46648
- class SystemZone extends Zone {
46577
+ class FixedOffsetZone extends Zone {
46649
46578
  /**
46650
- * Get a singleton instance of the local zone
46651
- * @return {SystemZone}
46579
+ * Get a singleton instance of UTC
46580
+ * @return {FixedOffsetZone}
46652
46581
  */
46653
- static get instance() {
46654
- if (singleton$1 === null) {
46655
- singleton$1 = new SystemZone();
46582
+ static get utcInstance() {
46583
+ if (singleton === null) {
46584
+ singleton = new FixedOffsetZone(0);
46656
46585
  }
46657
46586
 
46658
- return singleton$1;
46587
+ return singleton;
46659
46588
  }
46660
- /** @override **/
46589
+ /**
46590
+ * Get an instance with a specified offset
46591
+ * @param {number} offset - The offset in minutes
46592
+ * @return {FixedOffsetZone}
46593
+ */
46661
46594
 
46662
46595
 
46663
- get type() {
46664
- return "system";
46596
+ static instance(offset) {
46597
+ return offset === 0 ? FixedOffsetZone.utcInstance : new FixedOffsetZone(offset);
46665
46598
  }
46666
- /** @override **/
46599
+ /**
46600
+ * Get an instance of FixedOffsetZone from a UTC offset string, like "UTC+6"
46601
+ * @param {string} s - The offset string to parse
46602
+ * @example FixedOffsetZone.parseSpecifier("UTC+6")
46603
+ * @example FixedOffsetZone.parseSpecifier("UTC+06")
46604
+ * @example FixedOffsetZone.parseSpecifier("UTC-6:00")
46605
+ * @return {FixedOffsetZone}
46606
+ */
46667
46607
 
46668
46608
 
46669
- get name() {
46670
- return new Intl.DateTimeFormat().resolvedOptions().timeZone;
46609
+ static parseSpecifier(s) {
46610
+ if (s) {
46611
+ const r = s.match(/^utc(?:([+-]\d{1,2})(?::(\d{2}))?)?$/i);
46612
+
46613
+ if (r) {
46614
+ return new FixedOffsetZone(signedOffset(r[1], r[2]));
46615
+ }
46616
+ }
46617
+
46618
+ return null;
46671
46619
  }
46672
- /** @override **/
46673
46620
 
46621
+ constructor(offset) {
46622
+ super();
46623
+ /** @private **/
46674
46624
 
46675
- get isUniversal() {
46676
- return false;
46625
+ this.fixed = offset;
46677
46626
  }
46678
46627
  /** @override **/
46679
46628
 
46680
46629
 
46681
- offsetName(ts, {
46682
- format,
46683
- locale
46684
- }) {
46685
- return parseZoneInfo(ts, format, locale);
46630
+ get type() {
46631
+ return "fixed";
46686
46632
  }
46687
46633
  /** @override **/
46688
46634
 
46689
46635
 
46690
- formatOffset(ts, format) {
46691
- return formatOffset(this.offset(ts), format);
46636
+ get name() {
46637
+ return this.fixed === 0 ? "UTC" : `UTC${formatOffset(this.fixed, "narrow")}`;
46638
+ }
46639
+
46640
+ get ianaName() {
46641
+ if (this.fixed === 0) {
46642
+ return "Etc/UTC";
46643
+ } else {
46644
+ return `Etc/GMT${formatOffset(-this.fixed, "narrow")}`;
46645
+ }
46692
46646
  }
46693
46647
  /** @override **/
46694
46648
 
46695
46649
 
46696
- offset(ts) {
46697
- return -new Date(ts).getTimezoneOffset();
46650
+ offsetName() {
46651
+ return this.name;
46698
46652
  }
46699
46653
  /** @override **/
46700
46654
 
46701
46655
 
46702
- equals(otherZone) {
46703
- return otherZone.type === "system";
46656
+ formatOffset(ts, format) {
46657
+ return formatOffset(this.fixed, format);
46704
46658
  }
46705
46659
  /** @override **/
46706
46660
 
46707
46661
 
46708
- get isValid() {
46662
+ get isUniversal() {
46709
46663
  return true;
46710
46664
  }
46711
-
46712
- }
46713
-
46714
- let dtfCache = {};
46715
-
46716
- function makeDTF(zone) {
46717
- if (!dtfCache[zone]) {
46718
- dtfCache[zone] = new Intl.DateTimeFormat("en-US", {
46719
- hour12: false,
46720
- timeZone: zone,
46721
- year: "numeric",
46722
- month: "2-digit",
46723
- day: "2-digit",
46724
- hour: "2-digit",
46725
- minute: "2-digit",
46726
- second: "2-digit",
46727
- era: "short"
46728
- });
46729
- }
46730
-
46731
- return dtfCache[zone];
46732
- }
46733
-
46734
- const typeToPos = {
46735
- year: 0,
46736
- month: 1,
46737
- day: 2,
46738
- era: 3,
46739
- hour: 4,
46740
- minute: 5,
46741
- second: 6
46742
- };
46743
-
46744
- function hackyOffset(dtf, date) {
46745
- const formatted = dtf.format(date).replace(/\u200E/g, ""),
46746
- parsed = /(\d+)\/(\d+)\/(\d+) (AD|BC),? (\d+):(\d+):(\d+)/.exec(formatted),
46747
- [, fMonth, fDay, fYear, fadOrBc, fHour, fMinute, fSecond] = parsed;
46748
- return [fYear, fMonth, fDay, fadOrBc, fHour, fMinute, fSecond];
46749
- }
46750
-
46751
- function partsOffset(dtf, date) {
46752
- const formatted = dtf.formatToParts(date);
46753
- const filled = [];
46754
-
46755
- for (let i = 0; i < formatted.length; i++) {
46756
- const {
46757
- type,
46758
- value
46759
- } = formatted[i];
46760
- const pos = typeToPos[type];
46761
-
46762
- if (type === "era") {
46763
- filled[pos] = value;
46764
- } else if (!isUndefined(pos)) {
46765
- filled[pos] = parseInt(value, 10);
46766
- }
46767
- }
46768
-
46769
- return filled;
46770
- }
46771
-
46772
- let ianaZoneCache = {};
46773
- /**
46774
- * A zone identified by an IANA identifier, like America/New_York
46775
- * @implements {Zone}
46776
- */
46777
-
46778
- class IANAZone extends Zone {
46779
- /**
46780
- * @param {string} name - Zone name
46781
- * @return {IANAZone}
46782
- */
46783
- static create(name) {
46784
- if (!ianaZoneCache[name]) {
46785
- ianaZoneCache[name] = new IANAZone(name);
46786
- }
46787
-
46788
- return ianaZoneCache[name];
46789
- }
46790
- /**
46791
- * Reset local caches. Should only be necessary in testing scenarios.
46792
- * @return {void}
46793
- */
46794
-
46795
-
46796
- static resetCache() {
46797
- ianaZoneCache = {};
46798
- dtfCache = {};
46799
- }
46800
- /**
46801
- * 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.
46802
- * @param {string} s - The string to check validity on
46803
- * @example IANAZone.isValidSpecifier("America/New_York") //=> true
46804
- * @example IANAZone.isValidSpecifier("Sport~~blorp") //=> false
46805
- * @deprecated This method returns false for some valid IANA names. Use isValidZone instead.
46806
- * @return {boolean}
46807
- */
46808
-
46809
-
46810
- static isValidSpecifier(s) {
46811
- return this.isValidZone(s);
46812
- }
46813
- /**
46814
- * Returns whether the provided string identifies a real zone
46815
- * @param {string} zone - The string to check
46816
- * @example IANAZone.isValidZone("America/New_York") //=> true
46817
- * @example IANAZone.isValidZone("Fantasia/Castle") //=> false
46818
- * @example IANAZone.isValidZone("Sport~~blorp") //=> false
46819
- * @return {boolean}
46820
- */
46821
-
46822
-
46823
- static isValidZone(zone) {
46824
- if (!zone) {
46825
- return false;
46826
- }
46827
-
46828
- try {
46829
- new Intl.DateTimeFormat("en-US", {
46830
- timeZone: zone
46831
- }).format();
46832
- return true;
46833
- } catch (e) {
46834
- return false;
46835
- }
46836
- }
46837
-
46838
- constructor(name) {
46839
- super();
46840
- /** @private **/
46841
-
46842
- this.zoneName = name;
46843
- /** @private **/
46844
-
46845
- this.valid = IANAZone.isValidZone(name);
46846
- }
46847
- /** @override **/
46848
-
46849
-
46850
- get type() {
46851
- return "iana";
46852
- }
46853
- /** @override **/
46854
-
46855
-
46856
- get name() {
46857
- return this.zoneName;
46858
- }
46859
- /** @override **/
46860
-
46861
-
46862
- get isUniversal() {
46863
- return false;
46864
- }
46865
- /** @override **/
46866
-
46867
-
46868
- offsetName(ts, {
46869
- format,
46870
- locale
46871
- }) {
46872
- return parseZoneInfo(ts, format, locale, this.name);
46873
- }
46874
- /** @override **/
46875
-
46876
-
46877
- formatOffset(ts, format) {
46878
- return formatOffset(this.offset(ts), format);
46879
- }
46880
- /** @override **/
46881
-
46882
-
46883
- offset(ts) {
46884
- const date = new Date(ts);
46885
- if (isNaN(date)) return NaN;
46886
- const dtf = makeDTF(this.name);
46887
- let [year, month, day, adOrBc, hour, minute, second] = dtf.formatToParts ? partsOffset(dtf, date) : hackyOffset(dtf, date);
46888
-
46889
- if (adOrBc === "BC") {
46890
- year = -Math.abs(year) + 1;
46891
- } // because we're using hour12 and https://bugs.chromium.org/p/chromium/issues/detail?id=1025564&can=2&q=%2224%3A00%22%20datetimeformat
46892
-
46893
-
46894
- const adjustedHour = hour === 24 ? 0 : hour;
46895
- const asUTC = objToLocalTS({
46896
- year,
46897
- month,
46898
- day,
46899
- hour: adjustedHour,
46900
- minute,
46901
- second,
46902
- millisecond: 0
46903
- });
46904
- let asTS = +date;
46905
- const over = asTS % 1000;
46906
- asTS -= over >= 0 ? over : 1000 + over;
46907
- return (asUTC - asTS) / (60 * 1000);
46908
- }
46909
- /** @override **/
46910
-
46911
-
46912
- equals(otherZone) {
46913
- return otherZone.type === "iana" && otherZone.name === this.name;
46914
- }
46915
- /** @override **/
46916
-
46917
-
46918
- get isValid() {
46919
- return this.valid;
46920
- }
46921
-
46922
- }
46923
-
46924
- let singleton = null;
46925
- /**
46926
- * A zone with a fixed offset (meaning no DST)
46927
- * @implements {Zone}
46928
- */
46929
-
46930
- class FixedOffsetZone extends Zone {
46931
- /**
46932
- * Get a singleton instance of UTC
46933
- * @return {FixedOffsetZone}
46934
- */
46935
- static get utcInstance() {
46936
- if (singleton === null) {
46937
- singleton = new FixedOffsetZone(0);
46938
- }
46939
-
46940
- return singleton;
46941
- }
46942
- /**
46943
- * Get an instance with a specified offset
46944
- * @param {number} offset - The offset in minutes
46945
- * @return {FixedOffsetZone}
46946
- */
46947
-
46948
-
46949
- static instance(offset) {
46950
- return offset === 0 ? FixedOffsetZone.utcInstance : new FixedOffsetZone(offset);
46951
- }
46952
- /**
46953
- * Get an instance of FixedOffsetZone from a UTC offset string, like "UTC+6"
46954
- * @param {string} s - The offset string to parse
46955
- * @example FixedOffsetZone.parseSpecifier("UTC+6")
46956
- * @example FixedOffsetZone.parseSpecifier("UTC+06")
46957
- * @example FixedOffsetZone.parseSpecifier("UTC-6:00")
46958
- * @return {FixedOffsetZone}
46959
- */
46960
-
46961
-
46962
- static parseSpecifier(s) {
46963
- if (s) {
46964
- const r = s.match(/^utc(?:([+-]\d{1,2})(?::(\d{2}))?)?$/i);
46965
-
46966
- if (r) {
46967
- return new FixedOffsetZone(signedOffset(r[1], r[2]));
46968
- }
46969
- }
46970
-
46971
- return null;
46972
- }
46973
-
46974
- constructor(offset) {
46975
- super();
46976
- /** @private **/
46977
-
46978
- this.fixed = offset;
46979
- }
46980
- /** @override **/
46981
-
46982
-
46983
- get type() {
46984
- return "fixed";
46985
- }
46986
- /** @override **/
46987
-
46988
-
46989
- get name() {
46990
- return this.fixed === 0 ? "UTC" : `UTC${formatOffset(this.fixed, "narrow")}`;
46991
- }
46992
-
46993
- get ianaName() {
46994
- if (this.fixed === 0) {
46995
- return "Etc/UTC";
46996
- } else {
46997
- return `Etc/GMT${formatOffset(-this.fixed, "narrow")}`;
46998
- }
46999
- }
47000
- /** @override **/
47001
-
47002
-
47003
- offsetName() {
47004
- return this.name;
47005
- }
47006
- /** @override **/
47007
-
47008
-
47009
- formatOffset(ts, format) {
47010
- return formatOffset(this.fixed, format);
47011
- }
47012
- /** @override **/
47013
-
47014
-
47015
- get isUniversal() {
47016
- return true;
47017
- }
47018
- /** @override **/
46665
+ /** @override **/
47019
46666
 
47020
46667
 
47021
46668
  offset() {
@@ -47127,6 +46774,7 @@ let now = () => Date.now(),
47127
46774
  defaultLocale = null,
47128
46775
  defaultNumberingSystem = null,
47129
46776
  defaultOutputCalendar = null,
46777
+ twoDigitCutoffYear = 60,
47130
46778
  throwOnInvalid;
47131
46779
  /**
47132
46780
  * 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.
@@ -47227,6 +46875,28 @@ class Settings {
47227
46875
  static set defaultOutputCalendar(outputCalendar) {
47228
46876
  defaultOutputCalendar = outputCalendar;
47229
46877
  }
46878
+ /**
46879
+ * Get the cutoff year after which a string encoding a year as two digits is interpreted to occur in the current century.
46880
+ * @type {number}
46881
+ */
46882
+
46883
+
46884
+ static get twoDigitCutoffYear() {
46885
+ return twoDigitCutoffYear;
46886
+ }
46887
+ /**
46888
+ * Set the cutoff year after which a string encoding a year as two digits is interpreted to occur in the current century.
46889
+ * @type {number}
46890
+ * @example Settings.twoDigitCutoffYear = 0 // cut-off year is 0, so all 'yy' are interpretted as current century
46891
+ * @example Settings.twoDigitCutoffYear = 50 // '49' -> 1949; '50' -> 2050
46892
+ * @example Settings.twoDigitCutoffYear = 1950 // interpretted as 50
46893
+ * @example Settings.twoDigitCutoffYear = 2050 // ALSO interpretted as 50
46894
+ */
46895
+
46896
+
46897
+ static set twoDigitCutoffYear(cutoffYear) {
46898
+ twoDigitCutoffYear = cutoffYear % 100;
46899
+ }
47230
46900
  /**
47231
46901
  * Get whether Luxon will throw when it encounters invalid DateTimes, Durations, or Intervals
47232
46902
  * @type {boolean}
@@ -47258,495 +46928,870 @@ class Settings {
47258
46928
 
47259
46929
  }
47260
46930
 
47261
- let intlLFCache = {};
46931
+ /*
46932
+ This is just a junk drawer, containing anything used across multiple classes.
46933
+ Because Luxon is small(ish), this should stay small and we won't worry about splitting
46934
+ it up into, say, parsingUtil.js and basicUtil.js and so on. But they are divided up by feature area.
46935
+ */
46936
+ /**
46937
+ * @private
46938
+ */
46939
+ // TYPES
47262
46940
 
47263
- function getCachedLF(locString, opts = {}) {
47264
- const key = JSON.stringify([locString, opts]);
47265
- let dtf = intlLFCache[key];
46941
+ function isUndefined(o) {
46942
+ return typeof o === "undefined";
46943
+ }
46944
+ function isNumber(o) {
46945
+ return typeof o === "number";
46946
+ }
46947
+ function isInteger(o) {
46948
+ return typeof o === "number" && o % 1 === 0;
46949
+ }
46950
+ function isString(o) {
46951
+ return typeof o === "string";
46952
+ }
46953
+ function isDate(o) {
46954
+ return Object.prototype.toString.call(o) === "[object Date]";
46955
+ } // CAPABILITIES
47266
46956
 
47267
- if (!dtf) {
47268
- dtf = new Intl.ListFormat(locString, opts);
47269
- intlLFCache[key] = dtf;
46957
+ function hasRelative() {
46958
+ try {
46959
+ return typeof Intl !== "undefined" && !!Intl.RelativeTimeFormat;
46960
+ } catch (e) {
46961
+ return false;
47270
46962
  }
46963
+ } // OBJECTS AND ARRAYS
47271
46964
 
47272
- return dtf;
46965
+ function maybeArray(thing) {
46966
+ return Array.isArray(thing) ? thing : [thing];
47273
46967
  }
46968
+ function bestBy(arr, by, compare) {
46969
+ if (arr.length === 0) {
46970
+ return undefined;
46971
+ }
47274
46972
 
47275
- let intlDTCache = {};
46973
+ return arr.reduce((best, next) => {
46974
+ const pair = [by(next), next];
47276
46975
 
47277
- function getCachedDTF(locString, opts = {}) {
47278
- const key = JSON.stringify([locString, opts]);
47279
- let dtf = intlDTCache[key];
46976
+ if (!best) {
46977
+ return pair;
46978
+ } else if (compare(best[0], pair[0]) === best[0]) {
46979
+ return best;
46980
+ } else {
46981
+ return pair;
46982
+ }
46983
+ }, null)[1];
46984
+ }
46985
+ function pick(obj, keys) {
46986
+ return keys.reduce((a, k) => {
46987
+ a[k] = obj[k];
46988
+ return a;
46989
+ }, {});
46990
+ }
46991
+ function hasOwnProperty(obj, prop) {
46992
+ return Object.prototype.hasOwnProperty.call(obj, prop);
46993
+ } // NUMBERS AND STRINGS
47280
46994
 
47281
- if (!dtf) {
47282
- dtf = new Intl.DateTimeFormat(locString, opts);
47283
- intlDTCache[key] = dtf;
46995
+ function integerBetween(thing, bottom, top) {
46996
+ return isInteger(thing) && thing >= bottom && thing <= top;
46997
+ } // x % n but takes the sign of n instead of x
46998
+
46999
+ function floorMod(x, n) {
47000
+ return x - n * Math.floor(x / n);
47001
+ }
47002
+ function padStart(input, n = 2) {
47003
+ const isNeg = input < 0;
47004
+ let padded;
47005
+
47006
+ if (isNeg) {
47007
+ padded = "-" + ("" + -input).padStart(n, "0");
47008
+ } else {
47009
+ padded = ("" + input).padStart(n, "0");
47284
47010
  }
47285
47011
 
47286
- return dtf;
47012
+ return padded;
47287
47013
  }
47014
+ function parseInteger(string) {
47015
+ if (isUndefined(string) || string === null || string === "") {
47016
+ return undefined;
47017
+ } else {
47018
+ return parseInt(string, 10);
47019
+ }
47020
+ }
47021
+ function parseFloating(string) {
47022
+ if (isUndefined(string) || string === null || string === "") {
47023
+ return undefined;
47024
+ } else {
47025
+ return parseFloat(string);
47026
+ }
47027
+ }
47028
+ function parseMillis(fraction) {
47029
+ // Return undefined (instead of 0) in these cases, where fraction is not set
47030
+ if (isUndefined(fraction) || fraction === null || fraction === "") {
47031
+ return undefined;
47032
+ } else {
47033
+ const f = parseFloat("0." + fraction) * 1000;
47034
+ return Math.floor(f);
47035
+ }
47036
+ }
47037
+ function roundTo(number, digits, towardZero = false) {
47038
+ const factor = 10 ** digits,
47039
+ rounder = towardZero ? Math.trunc : Math.round;
47040
+ return rounder(number * factor) / factor;
47041
+ } // DATE BASICS
47288
47042
 
47289
- let intlNumCache = {};
47043
+ function isLeapYear(year) {
47044
+ return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
47045
+ }
47046
+ function daysInYear(year) {
47047
+ return isLeapYear(year) ? 366 : 365;
47048
+ }
47049
+ function daysInMonth(year, month) {
47050
+ const modMonth = floorMod(month - 1, 12) + 1,
47051
+ modYear = year + (month - modMonth) / 12;
47290
47052
 
47291
- function getCachedINF(locString, opts = {}) {
47292
- const key = JSON.stringify([locString, opts]);
47293
- let inf = intlNumCache[key];
47053
+ if (modMonth === 2) {
47054
+ return isLeapYear(modYear) ? 29 : 28;
47055
+ } else {
47056
+ return [31, null, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][modMonth - 1];
47057
+ }
47058
+ } // covert a calendar object to a local timestamp (epoch, but with the offset baked in)
47294
47059
 
47295
- if (!inf) {
47296
- inf = new Intl.NumberFormat(locString, opts);
47297
- intlNumCache[key] = inf;
47060
+ function objToLocalTS(obj) {
47061
+ let d = Date.UTC(obj.year, obj.month - 1, obj.day, obj.hour, obj.minute, obj.second, obj.millisecond); // for legacy reasons, years between 0 and 99 are interpreted as 19XX; revert that
47062
+
47063
+ if (obj.year < 100 && obj.year >= 0) {
47064
+ d = new Date(d);
47065
+ d.setUTCFullYear(d.getUTCFullYear() - 1900);
47298
47066
  }
47299
47067
 
47300
- return inf;
47068
+ return +d;
47069
+ }
47070
+ function weeksInWeekYear(weekYear) {
47071
+ const p1 = (weekYear + Math.floor(weekYear / 4) - Math.floor(weekYear / 100) + Math.floor(weekYear / 400)) % 7,
47072
+ last = weekYear - 1,
47073
+ p2 = (last + Math.floor(last / 4) - Math.floor(last / 100) + Math.floor(last / 400)) % 7;
47074
+ return p1 === 4 || p2 === 3 ? 53 : 52;
47301
47075
  }
47076
+ function untruncateYear(year) {
47077
+ if (year > 99) {
47078
+ return year;
47079
+ } else return year > Settings.twoDigitCutoffYear ? 1900 + year : 2000 + year;
47080
+ } // PARSING
47302
47081
 
47303
- let intlRelCache = {};
47082
+ function parseZoneInfo(ts, offsetFormat, locale, timeZone = null) {
47083
+ const date = new Date(ts),
47084
+ intlOpts = {
47085
+ hourCycle: "h23",
47086
+ year: "numeric",
47087
+ month: "2-digit",
47088
+ day: "2-digit",
47089
+ hour: "2-digit",
47090
+ minute: "2-digit"
47091
+ };
47304
47092
 
47305
- function getCachedRTF(locString, opts = {}) {
47306
- const {
47307
- base,
47308
- ...cacheKeyOpts
47309
- } = opts; // exclude `base` from the options
47093
+ if (timeZone) {
47094
+ intlOpts.timeZone = timeZone;
47095
+ }
47310
47096
 
47311
- const key = JSON.stringify([locString, cacheKeyOpts]);
47312
- let inf = intlRelCache[key];
47097
+ const modified = {
47098
+ timeZoneName: offsetFormat,
47099
+ ...intlOpts
47100
+ };
47101
+ const parsed = new Intl.DateTimeFormat(locale, modified).formatToParts(date).find(m => m.type.toLowerCase() === "timezonename");
47102
+ return parsed ? parsed.value : null;
47103
+ } // signedOffset('-5', '30') -> -330
47313
47104
 
47314
- if (!inf) {
47315
- inf = new Intl.RelativeTimeFormat(locString, opts);
47316
- intlRelCache[key] = inf;
47105
+ function signedOffset(offHourStr, offMinuteStr) {
47106
+ let offHour = parseInt(offHourStr, 10); // don't || this because we want to preserve -0
47107
+
47108
+ if (Number.isNaN(offHour)) {
47109
+ offHour = 0;
47317
47110
  }
47318
47111
 
47319
- return inf;
47112
+ const offMin = parseInt(offMinuteStr, 10) || 0,
47113
+ offMinSigned = offHour < 0 || Object.is(offHour, -0) ? -offMin : offMin;
47114
+ return offHour * 60 + offMinSigned;
47115
+ } // COERCION
47116
+
47117
+ function asNumber(value) {
47118
+ const numericValue = Number(value);
47119
+ if (typeof value === "boolean" || value === "" || Number.isNaN(numericValue)) throw new InvalidArgumentError(`Invalid unit value ${value}`);
47120
+ return numericValue;
47320
47121
  }
47122
+ function normalizeObject(obj, normalizer) {
47123
+ const normalized = {};
47321
47124
 
47322
- let sysLocaleCache = null;
47125
+ for (const u in obj) {
47126
+ if (hasOwnProperty(obj, u)) {
47127
+ const v = obj[u];
47128
+ if (v === undefined || v === null) continue;
47129
+ normalized[normalizer(u)] = asNumber(v);
47130
+ }
47131
+ }
47323
47132
 
47324
- function systemLocale() {
47325
- if (sysLocaleCache) {
47326
- return sysLocaleCache;
47327
- } else {
47328
- sysLocaleCache = new Intl.DateTimeFormat().resolvedOptions().locale;
47329
- return sysLocaleCache;
47133
+ return normalized;
47134
+ }
47135
+ function formatOffset(offset, format) {
47136
+ const hours = Math.trunc(Math.abs(offset / 60)),
47137
+ minutes = Math.trunc(Math.abs(offset % 60)),
47138
+ sign = offset >= 0 ? "+" : "-";
47139
+
47140
+ switch (format) {
47141
+ case "short":
47142
+ return `${sign}${padStart(hours, 2)}:${padStart(minutes, 2)}`;
47143
+
47144
+ case "narrow":
47145
+ return `${sign}${hours}${minutes > 0 ? `:${minutes}` : ""}`;
47146
+
47147
+ case "techie":
47148
+ return `${sign}${padStart(hours, 2)}${padStart(minutes, 2)}`;
47149
+
47150
+ default:
47151
+ throw new RangeError(`Value format ${format} is out of range for property format`);
47330
47152
  }
47331
47153
  }
47154
+ function timeObject(obj) {
47155
+ return pick(obj, ["hour", "minute", "second", "millisecond"]);
47156
+ }
47332
47157
 
47333
- function parseLocaleString(localeStr) {
47334
- // I really want to avoid writing a BCP 47 parser
47335
- // see, e.g. https://github.com/wooorm/bcp-47
47336
- // Instead, we'll do this:
47337
- // a) if the string has no -u extensions, just leave it alone
47338
- // b) if it does, use Intl to resolve everything
47339
- // c) if Intl fails, try again without the -u
47340
- const uIndex = localeStr.indexOf("-u-");
47158
+ /**
47159
+ * @private
47160
+ */
47341
47161
 
47342
- if (uIndex === -1) {
47343
- return [localeStr];
47344
- } else {
47345
- let options;
47346
- const smaller = localeStr.substring(0, uIndex);
47347
47162
 
47348
- try {
47349
- options = getCachedDTF(localeStr).resolvedOptions();
47350
- } catch (e) {
47351
- options = getCachedDTF(smaller).resolvedOptions();
47163
+ const monthsLong = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
47164
+ const monthsShort = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
47165
+ const monthsNarrow = ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"];
47166
+ function months(length) {
47167
+ switch (length) {
47168
+ case "narrow":
47169
+ return [...monthsNarrow];
47170
+
47171
+ case "short":
47172
+ return [...monthsShort];
47173
+
47174
+ case "long":
47175
+ return [...monthsLong];
47176
+
47177
+ case "numeric":
47178
+ return ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"];
47179
+
47180
+ case "2-digit":
47181
+ return ["01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12"];
47182
+
47183
+ default:
47184
+ return null;
47185
+ }
47186
+ }
47187
+ const weekdaysLong = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"];
47188
+ const weekdaysShort = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
47189
+ const weekdaysNarrow = ["M", "T", "W", "T", "F", "S", "S"];
47190
+ function weekdays(length) {
47191
+ switch (length) {
47192
+ case "narrow":
47193
+ return [...weekdaysNarrow];
47194
+
47195
+ case "short":
47196
+ return [...weekdaysShort];
47197
+
47198
+ case "long":
47199
+ return [...weekdaysLong];
47200
+
47201
+ case "numeric":
47202
+ return ["1", "2", "3", "4", "5", "6", "7"];
47203
+
47204
+ default:
47205
+ return null;
47206
+ }
47207
+ }
47208
+ const meridiems = ["AM", "PM"];
47209
+ const erasLong = ["Before Christ", "Anno Domini"];
47210
+ const erasShort = ["BC", "AD"];
47211
+ const erasNarrow = ["B", "A"];
47212
+ function eras(length) {
47213
+ switch (length) {
47214
+ case "narrow":
47215
+ return [...erasNarrow];
47216
+
47217
+ case "short":
47218
+ return [...erasShort];
47219
+
47220
+ case "long":
47221
+ return [...erasLong];
47222
+
47223
+ default:
47224
+ return null;
47225
+ }
47226
+ }
47227
+ function meridiemForDateTime(dt) {
47228
+ return meridiems[dt.hour < 12 ? 0 : 1];
47229
+ }
47230
+ function weekdayForDateTime(dt, length) {
47231
+ return weekdays(length)[dt.weekday - 1];
47232
+ }
47233
+ function monthForDateTime(dt, length) {
47234
+ return months(length)[dt.month - 1];
47235
+ }
47236
+ function eraForDateTime(dt, length) {
47237
+ return eras(length)[dt.year < 0 ? 0 : 1];
47238
+ }
47239
+ function formatRelativeTime(unit, count, numeric = "always", narrow = false) {
47240
+ const units = {
47241
+ years: ["year", "yr."],
47242
+ quarters: ["quarter", "qtr."],
47243
+ months: ["month", "mo."],
47244
+ weeks: ["week", "wk."],
47245
+ days: ["day", "day", "days"],
47246
+ hours: ["hour", "hr."],
47247
+ minutes: ["minute", "min."],
47248
+ seconds: ["second", "sec."]
47249
+ };
47250
+ const lastable = ["hours", "minutes", "seconds"].indexOf(unit) === -1;
47251
+
47252
+ if (numeric === "auto" && lastable) {
47253
+ const isDay = unit === "days";
47254
+
47255
+ switch (count) {
47256
+ case 1:
47257
+ return isDay ? "tomorrow" : `next ${units[unit][0]}`;
47258
+
47259
+ case -1:
47260
+ return isDay ? "yesterday" : `last ${units[unit][0]}`;
47261
+
47262
+ case 0:
47263
+ return isDay ? "today" : `this ${units[unit][0]}`;
47264
+
47352
47265
  }
47266
+ }
47353
47267
 
47354
- const {
47355
- numberingSystem,
47356
- calendar
47357
- } = options; // return the smaller one so that we can append the calendar and numbering overrides to it
47268
+ const isInPast = Object.is(count, -0) || count < 0,
47269
+ fmtValue = Math.abs(count),
47270
+ singular = fmtValue === 1,
47271
+ lilUnits = units[unit],
47272
+ fmtUnit = narrow ? singular ? lilUnits[1] : lilUnits[2] || lilUnits[1] : singular ? units[unit][0] : unit;
47273
+ return isInPast ? `${fmtValue} ${fmtUnit} ago` : `in ${fmtValue} ${fmtUnit}`;
47274
+ }
47358
47275
 
47359
- return [smaller, numberingSystem, calendar];
47276
+ function stringifyTokens(splits, tokenToString) {
47277
+ let s = "";
47278
+
47279
+ for (const token of splits) {
47280
+ if (token.literal) {
47281
+ s += token.val;
47282
+ } else {
47283
+ s += tokenToString(token.val);
47284
+ }
47360
47285
  }
47286
+
47287
+ return s;
47361
47288
  }
47362
47289
 
47363
- function intlConfigString(localeStr, numberingSystem, outputCalendar) {
47364
- if (outputCalendar || numberingSystem) {
47365
- localeStr += "-u";
47290
+ const macroTokenToFormatOpts = {
47291
+ D: DATE_SHORT,
47292
+ DD: DATE_MED,
47293
+ DDD: DATE_FULL,
47294
+ DDDD: DATE_HUGE,
47295
+ t: TIME_SIMPLE,
47296
+ tt: TIME_WITH_SECONDS,
47297
+ ttt: TIME_WITH_SHORT_OFFSET,
47298
+ tttt: TIME_WITH_LONG_OFFSET,
47299
+ T: TIME_24_SIMPLE,
47300
+ TT: TIME_24_WITH_SECONDS,
47301
+ TTT: TIME_24_WITH_SHORT_OFFSET,
47302
+ TTTT: TIME_24_WITH_LONG_OFFSET,
47303
+ f: DATETIME_SHORT,
47304
+ ff: DATETIME_MED,
47305
+ fff: DATETIME_FULL,
47306
+ ffff: DATETIME_HUGE,
47307
+ F: DATETIME_SHORT_WITH_SECONDS,
47308
+ FF: DATETIME_MED_WITH_SECONDS,
47309
+ FFF: DATETIME_FULL_WITH_SECONDS,
47310
+ FFFF: DATETIME_HUGE_WITH_SECONDS
47311
+ };
47312
+ /**
47313
+ * @private
47314
+ */
47366
47315
 
47367
- if (outputCalendar) {
47368
- localeStr += `-ca-${outputCalendar}`;
47316
+ class Formatter {
47317
+ static create(locale, opts = {}) {
47318
+ return new Formatter(locale, opts);
47319
+ }
47320
+
47321
+ static parseFormat(fmt) {
47322
+ let current = null,
47323
+ currentFull = "",
47324
+ bracketed = false;
47325
+ const splits = [];
47326
+
47327
+ for (let i = 0; i < fmt.length; i++) {
47328
+ const c = fmt.charAt(i);
47329
+
47330
+ if (c === "'") {
47331
+ if (currentFull.length > 0) {
47332
+ splits.push({
47333
+ literal: bracketed,
47334
+ val: currentFull
47335
+ });
47336
+ }
47337
+
47338
+ current = null;
47339
+ currentFull = "";
47340
+ bracketed = !bracketed;
47341
+ } else if (bracketed) {
47342
+ currentFull += c;
47343
+ } else if (c === current) {
47344
+ currentFull += c;
47345
+ } else {
47346
+ if (currentFull.length > 0) {
47347
+ splits.push({
47348
+ literal: false,
47349
+ val: currentFull
47350
+ });
47351
+ }
47352
+
47353
+ currentFull = c;
47354
+ current = c;
47355
+ }
47369
47356
  }
47370
47357
 
47371
- if (numberingSystem) {
47372
- localeStr += `-nu-${numberingSystem}`;
47358
+ if (currentFull.length > 0) {
47359
+ splits.push({
47360
+ literal: bracketed,
47361
+ val: currentFull
47362
+ });
47373
47363
  }
47374
47364
 
47375
- return localeStr;
47376
- } else {
47377
- return localeStr;
47365
+ return splits;
47378
47366
  }
47379
- }
47380
47367
 
47381
- function mapMonths(f) {
47382
- const ms = [];
47368
+ static macroTokenToFormatOpts(token) {
47369
+ return macroTokenToFormatOpts[token];
47370
+ }
47383
47371
 
47384
- for (let i = 1; i <= 12; i++) {
47385
- const dt = DateTime.utc(2016, i, 1);
47386
- ms.push(f(dt));
47372
+ constructor(locale, formatOpts) {
47373
+ this.opts = formatOpts;
47374
+ this.loc = locale;
47375
+ this.systemLoc = null;
47387
47376
  }
47388
47377
 
47389
- return ms;
47390
- }
47378
+ formatWithSystemDefault(dt, opts) {
47379
+ if (this.systemLoc === null) {
47380
+ this.systemLoc = this.loc.redefaultToSystem();
47381
+ }
47382
+
47383
+ const df = this.systemLoc.dtFormatter(dt, { ...this.opts,
47384
+ ...opts
47385
+ });
47386
+ return df.format();
47387
+ }
47388
+
47389
+ formatDateTime(dt, opts = {}) {
47390
+ const df = this.loc.dtFormatter(dt, { ...this.opts,
47391
+ ...opts
47392
+ });
47393
+ return df.format();
47394
+ }
47395
+
47396
+ formatDateTimeParts(dt, opts = {}) {
47397
+ const df = this.loc.dtFormatter(dt, { ...this.opts,
47398
+ ...opts
47399
+ });
47400
+ return df.formatToParts();
47401
+ }
47402
+
47403
+ resolvedOptions(dt, opts = {}) {
47404
+ const df = this.loc.dtFormatter(dt, { ...this.opts,
47405
+ ...opts
47406
+ });
47407
+ return df.resolvedOptions();
47408
+ }
47409
+
47410
+ num(n, p = 0) {
47411
+ // we get some perf out of doing this here, annoyingly
47412
+ if (this.opts.forceSimple) {
47413
+ return padStart(n, p);
47414
+ }
47415
+
47416
+ const opts = { ...this.opts
47417
+ };
47418
+
47419
+ if (p > 0) {
47420
+ opts.padTo = p;
47421
+ }
47422
+
47423
+ return this.loc.numberFormatter(opts).format(n);
47424
+ }
47425
+
47426
+ formatDateTimeFromString(dt, fmt) {
47427
+ const knownEnglish = this.loc.listingMode() === "en",
47428
+ useDateTimeFormatter = this.loc.outputCalendar && this.loc.outputCalendar !== "gregory",
47429
+ string = (opts, extract) => this.loc.extract(dt, opts, extract),
47430
+ formatOffset = opts => {
47431
+ if (dt.isOffsetFixed && dt.offset === 0 && opts.allowZ) {
47432
+ return "Z";
47433
+ }
47434
+
47435
+ return dt.isValid ? dt.zone.formatOffset(dt.ts, opts.format) : "";
47436
+ },
47437
+ meridiem = () => knownEnglish ? meridiemForDateTime(dt) : string({
47438
+ hour: "numeric",
47439
+ hourCycle: "h12"
47440
+ }, "dayperiod"),
47441
+ month = (length, standalone) => knownEnglish ? monthForDateTime(dt, length) : string(standalone ? {
47442
+ month: length
47443
+ } : {
47444
+ month: length,
47445
+ day: "numeric"
47446
+ }, "month"),
47447
+ weekday = (length, standalone) => knownEnglish ? weekdayForDateTime(dt, length) : string(standalone ? {
47448
+ weekday: length
47449
+ } : {
47450
+ weekday: length,
47451
+ month: "long",
47452
+ day: "numeric"
47453
+ }, "weekday"),
47454
+ maybeMacro = token => {
47455
+ const formatOpts = Formatter.macroTokenToFormatOpts(token);
47456
+
47457
+ if (formatOpts) {
47458
+ return this.formatWithSystemDefault(dt, formatOpts);
47459
+ } else {
47460
+ return token;
47461
+ }
47462
+ },
47463
+ era = length => knownEnglish ? eraForDateTime(dt, length) : string({
47464
+ era: length
47465
+ }, "era"),
47466
+ tokenToString = token => {
47467
+ // Where possible: http://cldr.unicode.org/translation/date-time-1/date-time#TOC-Standalone-vs.-Format-Styles
47468
+ switch (token) {
47469
+ // ms
47470
+ case "S":
47471
+ return this.num(dt.millisecond);
47472
+
47473
+ case "u": // falls through
47474
+
47475
+ case "SSS":
47476
+ return this.num(dt.millisecond, 3);
47477
+ // seconds
47478
+
47479
+ case "s":
47480
+ return this.num(dt.second);
47481
+
47482
+ case "ss":
47483
+ return this.num(dt.second, 2);
47484
+ // fractional seconds
47485
+
47486
+ case "uu":
47487
+ return this.num(Math.floor(dt.millisecond / 10), 2);
47488
+
47489
+ case "uuu":
47490
+ return this.num(Math.floor(dt.millisecond / 100));
47491
+ // minutes
47492
+
47493
+ case "m":
47494
+ return this.num(dt.minute);
47495
+
47496
+ case "mm":
47497
+ return this.num(dt.minute, 2);
47498
+ // hours
47499
+
47500
+ case "h":
47501
+ return this.num(dt.hour % 12 === 0 ? 12 : dt.hour % 12);
47502
+
47503
+ case "hh":
47504
+ return this.num(dt.hour % 12 === 0 ? 12 : dt.hour % 12, 2);
47505
+
47506
+ case "H":
47507
+ return this.num(dt.hour);
47508
+
47509
+ case "HH":
47510
+ return this.num(dt.hour, 2);
47511
+ // offset
47512
+
47513
+ case "Z":
47514
+ // like +6
47515
+ return formatOffset({
47516
+ format: "narrow",
47517
+ allowZ: this.opts.allowZ
47518
+ });
47391
47519
 
47392
- function mapWeekdays(f) {
47393
- const ms = [];
47520
+ case "ZZ":
47521
+ // like +06:00
47522
+ return formatOffset({
47523
+ format: "short",
47524
+ allowZ: this.opts.allowZ
47525
+ });
47394
47526
 
47395
- for (let i = 1; i <= 7; i++) {
47396
- const dt = DateTime.utc(2016, 11, 13 + i);
47397
- ms.push(f(dt));
47398
- }
47527
+ case "ZZZ":
47528
+ // like +0600
47529
+ return formatOffset({
47530
+ format: "techie",
47531
+ allowZ: this.opts.allowZ
47532
+ });
47399
47533
 
47400
- return ms;
47401
- }
47534
+ case "ZZZZ":
47535
+ // like EST
47536
+ return dt.zone.offsetName(dt.ts, {
47537
+ format: "short",
47538
+ locale: this.loc.locale
47539
+ });
47402
47540
 
47403
- function listStuff(loc, length, defaultOK, englishFn, intlFn) {
47404
- const mode = loc.listingMode(defaultOK);
47541
+ case "ZZZZZ":
47542
+ // like Eastern Standard Time
47543
+ return dt.zone.offsetName(dt.ts, {
47544
+ format: "long",
47545
+ locale: this.loc.locale
47546
+ });
47547
+ // zone
47405
47548
 
47406
- if (mode === "error") {
47407
- return null;
47408
- } else if (mode === "en") {
47409
- return englishFn(length);
47410
- } else {
47411
- return intlFn(length);
47412
- }
47413
- }
47549
+ case "z":
47550
+ // like America/New_York
47551
+ return dt.zoneName;
47552
+ // meridiems
47414
47553
 
47415
- function supportsFastNumbers(loc) {
47416
- if (loc.numberingSystem && loc.numberingSystem !== "latn") {
47417
- return false;
47418
- } else {
47419
- return loc.numberingSystem === "latn" || !loc.locale || loc.locale.startsWith("en") || new Intl.DateTimeFormat(loc.intl).resolvedOptions().numberingSystem === "latn";
47420
- }
47421
- }
47422
- /**
47423
- * @private
47424
- */
47554
+ case "a":
47555
+ return meridiem();
47556
+ // dates
47425
47557
 
47558
+ case "d":
47559
+ return useDateTimeFormatter ? string({
47560
+ day: "numeric"
47561
+ }, "day") : this.num(dt.day);
47426
47562
 
47427
- class PolyNumberFormatter {
47428
- constructor(intl, forceSimple, opts) {
47429
- this.padTo = opts.padTo || 0;
47430
- this.floor = opts.floor || false;
47431
- const {
47432
- padTo,
47433
- floor,
47434
- ...otherOpts
47435
- } = opts;
47563
+ case "dd":
47564
+ return useDateTimeFormatter ? string({
47565
+ day: "2-digit"
47566
+ }, "day") : this.num(dt.day, 2);
47567
+ // weekdays - standalone
47436
47568
 
47437
- if (!forceSimple || Object.keys(otherOpts).length > 0) {
47438
- const intlOpts = {
47439
- useGrouping: false,
47440
- ...opts
47441
- };
47442
- if (opts.padTo > 0) intlOpts.minimumIntegerDigits = opts.padTo;
47443
- this.inf = getCachedINF(intl, intlOpts);
47444
- }
47445
- }
47569
+ case "c":
47570
+ // like 1
47571
+ return this.num(dt.weekday);
47446
47572
 
47447
- format(i) {
47448
- if (this.inf) {
47449
- const fixed = this.floor ? Math.floor(i) : i;
47450
- return this.inf.format(fixed);
47451
- } else {
47452
- // to match the browser's numberformatter defaults
47453
- const fixed = this.floor ? Math.floor(i) : roundTo(i, 3);
47454
- return padStart(fixed, this.padTo);
47455
- }
47456
- }
47573
+ case "ccc":
47574
+ // like 'Tues'
47575
+ return weekday("short", true);
47457
47576
 
47458
- }
47459
- /**
47460
- * @private
47461
- */
47577
+ case "cccc":
47578
+ // like 'Tuesday'
47579
+ return weekday("long", true);
47462
47580
 
47581
+ case "ccccc":
47582
+ // like 'T'
47583
+ return weekday("narrow", true);
47584
+ // weekdays - format
47463
47585
 
47464
- class PolyDateFormatter {
47465
- constructor(dt, intl, opts) {
47466
- this.opts = opts;
47467
- let z;
47586
+ case "E":
47587
+ // like 1
47588
+ return this.num(dt.weekday);
47468
47589
 
47469
- if (dt.zone.isUniversal) {
47470
- // UTC-8 or Etc/UTC-8 are not part of tzdata, only Etc/GMT+8 and the like.
47471
- // That is why fixed-offset TZ is set to that unless it is:
47472
- // 1. Representing offset 0 when UTC is used to maintain previous behavior and does not become GMT.
47473
- // 2. Unsupported by the browser:
47474
- // - some do not support Etc/
47475
- // - < Etc/GMT-14, > Etc/GMT+12, and 30-minute or 45-minute offsets are not part of tzdata
47476
- const gmtOffset = -1 * (dt.offset / 60);
47477
- const offsetZ = gmtOffset >= 0 ? `Etc/GMT+${gmtOffset}` : `Etc/GMT${gmtOffset}`;
47590
+ case "EEE":
47591
+ // like 'Tues'
47592
+ return weekday("short", false);
47478
47593
 
47479
- if (dt.offset !== 0 && IANAZone.create(offsetZ).valid) {
47480
- z = offsetZ;
47481
- this.dt = dt;
47482
- } else {
47483
- // Not all fixed-offset zones like Etc/+4:30 are present in tzdata.
47484
- // So we have to make do. Two cases:
47485
- // 1. The format options tell us to show the zone. We can't do that, so the best
47486
- // we can do is format the date in UTC.
47487
- // 2. The format options don't tell us to show the zone. Then we can adjust them
47488
- // the time and tell the formatter to show it to us in UTC, so that the time is right
47489
- // and the bad zone doesn't show up.
47490
- z = "UTC";
47594
+ case "EEEE":
47595
+ // like 'Tuesday'
47596
+ return weekday("long", false);
47491
47597
 
47492
- if (opts.timeZoneName) {
47493
- this.dt = dt;
47494
- } else {
47495
- this.dt = dt.offset === 0 ? dt : DateTime.fromMillis(dt.ts + dt.offset * 60 * 1000);
47496
- }
47497
- }
47498
- } else if (dt.zone.type === "system") {
47499
- this.dt = dt;
47500
- } else {
47501
- this.dt = dt;
47502
- z = dt.zone.name;
47503
- }
47598
+ case "EEEEE":
47599
+ // like 'T'
47600
+ return weekday("narrow", false);
47601
+ // months - standalone
47504
47602
 
47505
- const intlOpts = { ...this.opts
47506
- };
47603
+ case "L":
47604
+ // like 1
47605
+ return useDateTimeFormatter ? string({
47606
+ month: "numeric",
47607
+ day: "numeric"
47608
+ }, "month") : this.num(dt.month);
47507
47609
 
47508
- if (z) {
47509
- intlOpts.timeZone = z;
47510
- }
47610
+ case "LL":
47611
+ // like 01, doesn't seem to work
47612
+ return useDateTimeFormatter ? string({
47613
+ month: "2-digit",
47614
+ day: "numeric"
47615
+ }, "month") : this.num(dt.month, 2);
47511
47616
 
47512
- this.dtf = getCachedDTF(intl, intlOpts);
47513
- }
47617
+ case "LLL":
47618
+ // like Jan
47619
+ return month("short", true);
47514
47620
 
47515
- format() {
47516
- return this.dtf.format(this.dt.toJSDate());
47517
- }
47621
+ case "LLLL":
47622
+ // like January
47623
+ return month("long", true);
47518
47624
 
47519
- formatToParts() {
47520
- return this.dtf.formatToParts(this.dt.toJSDate());
47521
- }
47625
+ case "LLLLL":
47626
+ // like J
47627
+ return month("narrow", true);
47628
+ // months - format
47522
47629
 
47523
- resolvedOptions() {
47524
- return this.dtf.resolvedOptions();
47525
- }
47630
+ case "M":
47631
+ // like 1
47632
+ return useDateTimeFormatter ? string({
47633
+ month: "numeric"
47634
+ }, "month") : this.num(dt.month);
47526
47635
 
47527
- }
47528
- /**
47529
- * @private
47530
- */
47636
+ case "MM":
47637
+ // like 01
47638
+ return useDateTimeFormatter ? string({
47639
+ month: "2-digit"
47640
+ }, "month") : this.num(dt.month, 2);
47531
47641
 
47642
+ case "MMM":
47643
+ // like Jan
47644
+ return month("short", false);
47532
47645
 
47533
- class PolyRelFormatter {
47534
- constructor(intl, isEnglish, opts) {
47535
- this.opts = {
47536
- style: "long",
47537
- ...opts
47538
- };
47646
+ case "MMMM":
47647
+ // like January
47648
+ return month("long", false);
47539
47649
 
47540
- if (!isEnglish && hasRelative()) {
47541
- this.rtf = getCachedRTF(intl, opts);
47542
- }
47543
- }
47650
+ case "MMMMM":
47651
+ // like J
47652
+ return month("narrow", false);
47653
+ // years
47544
47654
 
47545
- format(count, unit) {
47546
- if (this.rtf) {
47547
- return this.rtf.format(count, unit);
47548
- } else {
47549
- return formatRelativeTime(unit, count, this.opts.numeric, this.opts.style !== "long");
47550
- }
47551
- }
47655
+ case "y":
47656
+ // like 2014
47657
+ return useDateTimeFormatter ? string({
47658
+ year: "numeric"
47659
+ }, "year") : this.num(dt.year);
47552
47660
 
47553
- formatToParts(count, unit) {
47554
- if (this.rtf) {
47555
- return this.rtf.formatToParts(count, unit);
47556
- } else {
47557
- return [];
47558
- }
47559
- }
47661
+ case "yy":
47662
+ // like 14
47663
+ return useDateTimeFormatter ? string({
47664
+ year: "2-digit"
47665
+ }, "year") : this.num(dt.year.toString().slice(-2), 2);
47560
47666
 
47561
- }
47562
- /**
47563
- * @private
47564
- */
47667
+ case "yyyy":
47668
+ // like 0012
47669
+ return useDateTimeFormatter ? string({
47670
+ year: "numeric"
47671
+ }, "year") : this.num(dt.year, 4);
47565
47672
 
47673
+ case "yyyyyy":
47674
+ // like 000012
47675
+ return useDateTimeFormatter ? string({
47676
+ year: "numeric"
47677
+ }, "year") : this.num(dt.year, 6);
47678
+ // eras
47566
47679
 
47567
- class Locale {
47568
- static fromOpts(opts) {
47569
- return Locale.create(opts.locale, opts.numberingSystem, opts.outputCalendar, opts.defaultToEN);
47570
- }
47680
+ case "G":
47681
+ // like AD
47682
+ return era("short");
47571
47683
 
47572
- static create(locale, numberingSystem, outputCalendar, defaultToEN = false) {
47573
- const specifiedLocale = locale || Settings.defaultLocale; // the system locale is useful for human readable strings but annoying for parsing/formatting known formats
47684
+ case "GG":
47685
+ // like Anno Domini
47686
+ return era("long");
47574
47687
 
47575
- const localeR = specifiedLocale || (defaultToEN ? "en-US" : systemLocale());
47576
- const numberingSystemR = numberingSystem || Settings.defaultNumberingSystem;
47577
- const outputCalendarR = outputCalendar || Settings.defaultOutputCalendar;
47578
- return new Locale(localeR, numberingSystemR, outputCalendarR, specifiedLocale);
47579
- }
47688
+ case "GGGGG":
47689
+ return era("narrow");
47580
47690
 
47581
- static resetCache() {
47582
- sysLocaleCache = null;
47583
- intlDTCache = {};
47584
- intlNumCache = {};
47585
- intlRelCache = {};
47586
- }
47691
+ case "kk":
47692
+ return this.num(dt.weekYear.toString().slice(-2), 2);
47587
47693
 
47588
- static fromObject({
47589
- locale,
47590
- numberingSystem,
47591
- outputCalendar
47592
- } = {}) {
47593
- return Locale.create(locale, numberingSystem, outputCalendar);
47594
- }
47694
+ case "kkkk":
47695
+ return this.num(dt.weekYear, 4);
47595
47696
 
47596
- constructor(locale, numbering, outputCalendar, specifiedLocale) {
47597
- const [parsedLocale, parsedNumberingSystem, parsedOutputCalendar] = parseLocaleString(locale);
47598
- this.locale = parsedLocale;
47599
- this.numberingSystem = numbering || parsedNumberingSystem || null;
47600
- this.outputCalendar = outputCalendar || parsedOutputCalendar || null;
47601
- this.intl = intlConfigString(this.locale, this.numberingSystem, this.outputCalendar);
47602
- this.weekdaysCache = {
47603
- format: {},
47604
- standalone: {}
47605
- };
47606
- this.monthsCache = {
47607
- format: {},
47608
- standalone: {}
47609
- };
47610
- this.meridiemCache = null;
47611
- this.eraCache = {};
47612
- this.specifiedLocale = specifiedLocale;
47613
- this.fastNumbersCached = null;
47614
- }
47697
+ case "W":
47698
+ return this.num(dt.weekNumber);
47615
47699
 
47616
- get fastNumbers() {
47617
- if (this.fastNumbersCached == null) {
47618
- this.fastNumbersCached = supportsFastNumbers(this);
47619
- }
47700
+ case "WW":
47701
+ return this.num(dt.weekNumber, 2);
47620
47702
 
47621
- return this.fastNumbersCached;
47622
- }
47703
+ case "o":
47704
+ return this.num(dt.ordinal);
47623
47705
 
47624
- listingMode() {
47625
- const isActuallyEn = this.isEnglish();
47626
- const hasNoWeirdness = (this.numberingSystem === null || this.numberingSystem === "latn") && (this.outputCalendar === null || this.outputCalendar === "gregory");
47627
- return isActuallyEn && hasNoWeirdness ? "en" : "intl";
47628
- }
47706
+ case "ooo":
47707
+ return this.num(dt.ordinal, 3);
47629
47708
 
47630
- clone(alts) {
47631
- if (!alts || Object.getOwnPropertyNames(alts).length === 0) {
47632
- return this;
47633
- } else {
47634
- return Locale.create(alts.locale || this.specifiedLocale, alts.numberingSystem || this.numberingSystem, alts.outputCalendar || this.outputCalendar, alts.defaultToEN || false);
47635
- }
47636
- }
47709
+ case "q":
47710
+ // like 1
47711
+ return this.num(dt.quarter);
47637
47712
 
47638
- redefaultToEN(alts = {}) {
47639
- return this.clone({ ...alts,
47640
- defaultToEN: true
47641
- });
47642
- }
47713
+ case "qq":
47714
+ // like 01
47715
+ return this.num(dt.quarter, 2);
47643
47716
 
47644
- redefaultToSystem(alts = {}) {
47645
- return this.clone({ ...alts,
47646
- defaultToEN: false
47647
- });
47648
- }
47717
+ case "X":
47718
+ return this.num(Math.floor(dt.ts / 1000));
47649
47719
 
47650
- months(length, format = false, defaultOK = true) {
47651
- return listStuff(this, length, defaultOK, months, () => {
47652
- const intl = format ? {
47653
- month: length,
47654
- day: "numeric"
47655
- } : {
47656
- month: length
47657
- },
47658
- formatStr = format ? "format" : "standalone";
47720
+ case "x":
47721
+ return this.num(dt.ts);
47659
47722
 
47660
- if (!this.monthsCache[formatStr][length]) {
47661
- this.monthsCache[formatStr][length] = mapMonths(dt => this.extract(dt, intl, "month"));
47723
+ default:
47724
+ return maybeMacro(token);
47662
47725
  }
47726
+ };
47663
47727
 
47664
- return this.monthsCache[formatStr][length];
47665
- });
47728
+ return stringifyTokens(Formatter.parseFormat(fmt), tokenToString);
47666
47729
  }
47667
47730
 
47668
- weekdays(length, format = false, defaultOK = true) {
47669
- return listStuff(this, length, defaultOK, weekdays, () => {
47670
- const intl = format ? {
47671
- weekday: length,
47672
- year: "numeric",
47673
- month: "long",
47674
- day: "numeric"
47675
- } : {
47676
- weekday: length
47677
- },
47678
- formatStr = format ? "format" : "standalone";
47679
-
47680
- if (!this.weekdaysCache[formatStr][length]) {
47681
- this.weekdaysCache[formatStr][length] = mapWeekdays(dt => this.extract(dt, intl, "weekday"));
47682
- }
47731
+ formatDurationFromString(dur, fmt) {
47732
+ const tokenToField = token => {
47733
+ switch (token[0]) {
47734
+ case "S":
47735
+ return "millisecond";
47683
47736
 
47684
- return this.weekdaysCache[formatStr][length];
47685
- });
47686
- }
47737
+ case "s":
47738
+ return "second";
47687
47739
 
47688
- meridiems(defaultOK = true) {
47689
- return listStuff(this, undefined, defaultOK, () => meridiems, () => {
47690
- // In theory there could be aribitrary day periods. We're gonna assume there are exactly two
47691
- // for AM and PM. This is probably wrong, but it's makes parsing way easier.
47692
- if (!this.meridiemCache) {
47693
- const intl = {
47694
- hour: "numeric",
47695
- hourCycle: "h12"
47696
- };
47697
- this.meridiemCache = [DateTime.utc(2016, 11, 13, 9), DateTime.utc(2016, 11, 13, 19)].map(dt => this.extract(dt, intl, "dayperiod"));
47698
- }
47740
+ case "m":
47741
+ return "minute";
47699
47742
 
47700
- return this.meridiemCache;
47701
- });
47702
- }
47743
+ case "h":
47744
+ return "hour";
47703
47745
 
47704
- eras(length, defaultOK = true) {
47705
- return listStuff(this, length, defaultOK, eras, () => {
47706
- const intl = {
47707
- era: length
47708
- }; // This is problematic. Different calendars are going to define eras totally differently. What I need is the minimum set of dates
47709
- // to definitely enumerate them.
47746
+ case "d":
47747
+ return "day";
47710
47748
 
47711
- if (!this.eraCache[length]) {
47712
- this.eraCache[length] = [DateTime.utc(-40, 1, 1), DateTime.utc(2017, 1, 1)].map(dt => this.extract(dt, intl, "era"));
47713
- }
47749
+ case "w":
47750
+ return "week";
47714
47751
 
47715
- return this.eraCache[length];
47716
- });
47717
- }
47752
+ case "M":
47753
+ return "month";
47718
47754
 
47719
- extract(dt, intlOpts, field) {
47720
- const df = this.dtFormatter(dt, intlOpts),
47721
- results = df.formatToParts(),
47722
- matching = results.find(m => m.type.toLowerCase() === field);
47723
- return matching ? matching.value : null;
47724
- }
47755
+ case "y":
47756
+ return "year";
47725
47757
 
47726
- numberFormatter(opts = {}) {
47727
- // this forcesimple option is never used (the only caller short-circuits on it, but it seems safer to leave)
47728
- // (in contrast, the rest of the condition is used heavily)
47729
- return new PolyNumberFormatter(this.intl, opts.forceSimple || this.fastNumbers, opts);
47730
- }
47758
+ default:
47759
+ return null;
47760
+ }
47761
+ },
47762
+ tokenToString = lildur => token => {
47763
+ const mapped = tokenToField(token);
47731
47764
 
47732
- dtFormatter(dt, intlOpts = {}) {
47733
- return new PolyDateFormatter(dt, this.intl, intlOpts);
47734
- }
47765
+ if (mapped) {
47766
+ return this.num(lildur.get(mapped), token.length);
47767
+ } else {
47768
+ return token;
47769
+ }
47770
+ },
47771
+ tokens = Formatter.parseFormat(fmt),
47772
+ realTokens = tokens.reduce((found, {
47773
+ literal,
47774
+ val
47775
+ }) => literal ? found : found.concat(val), []),
47776
+ collapsed = dur.shiftTo(...realTokens.map(tokenToField).filter(t => t));
47735
47777
 
47736
- relFormatter(opts = {}) {
47737
- return new PolyRelFormatter(this.intl, this.isEnglish(), opts);
47778
+ return stringifyTokens(tokens, tokenToString(collapsed));
47738
47779
  }
47739
47780
 
47740
- listFormatter(opts = {}) {
47741
- return getCachedLF(this.intl, opts);
47742
- }
47781
+ }
47743
47782
 
47744
- isEnglish() {
47745
- return this.locale === "en" || this.locale.toLowerCase() === "en-us" || new Intl.DateTimeFormat(this.intl).resolvedOptions().locale.startsWith("en-us");
47783
+ class Invalid {
47784
+ constructor(reason, explanation) {
47785
+ this.reason = reason;
47786
+ this.explanation = explanation;
47746
47787
  }
47747
47788
 
47748
- equals(other) {
47749
- return this.locale === other.locale && this.numberingSystem === other.numberingSystem && this.outputCalendar === other.outputCalendar;
47789
+ toMessage() {
47790
+ if (this.explanation) {
47791
+ return `${this.reason}: ${this.explanation}`;
47792
+ } else {
47793
+ return this.reason;
47794
+ }
47750
47795
  }
47751
47796
 
47752
47797
  }
@@ -47761,6 +47806,8 @@ class Locale {
47761
47806
  * Some extractions are super dumb and simpleParse and fromStrings help DRY them.
47762
47807
  */
47763
47808
 
47809
+ const ianaRegex = /[A-Za-z_+-]{1,256}(?::?\/[A-Za-z0-9_+-]{1,256}(?:\/[A-Za-z0-9_+-]{1,256})?)?/;
47810
+
47764
47811
  function combineRegexes(...regexes) {
47765
47812
  const full = regexes.reduce((f, r) => f + r.source, "");
47766
47813
  return RegExp(`^${full}$`);
@@ -53026,7 +53073,7 @@ function friendlyDateTime(dateTimeish) {
53026
53073
  }
53027
53074
  }
53028
53075
 
53029
- const VERSION = "3.1.0";
53076
+ const VERSION = "3.1.1";
53030
53077
 
53031
53078
  exports.DateTime = DateTime;
53032
53079
  exports.Duration = Duration;