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