[
  {
    "path": ".agignore",
    "content": "/build\npackage-lock.json\n"
  },
  {
    "path": ".babelrc",
    "content": "{\n  \"presets\": [\n    \"@babel/preset-env\"\n  ]\n}\n"
  },
  {
    "path": ".editorconfig",
    "content": "root = true\n\n[*]\ncharset = utf-8\nindent_size = 2\nend_of_line = lf\nindent_style = space\ninsert_final_newline = false\ntrim_trailing_whitespace = true\n\n[*.md]\ntrim_trailing_whitespace = false\n"
  },
  {
    "path": ".gitattributes",
    "content": "scripts/* linguist-vendored\ndocker/* linguist-vendored\nsite/** linguist-vendored\n.husky/* linguist-vendored\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: \"\"\nlabels: bug\n---\n\n**Describe the bug**\nA clear and concise description of what the bug is.\n\n**To Reproduce**\nPlease share a minimal code example that triggers the problem:\n\n**Actual vs Expected behavior**\nA clear and concise description of what you expected to happen.\n\n**Desktop (please complete the following information):**\n\n- OS: [e.g. iOS]\n- Browser [e.g. Chrome 84, safari 14.0]\n- Luxon version [e.g. 1.25.0]\n- Your timezone [e.g. \"America/New_York\"]\n\n**Additional context**\nAdd any other context about the problem here.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: \"\"\nlabels: enhancement\n---\n\n**Is your feature request related to a problem? Please describe.**\nA clear and concise description of what the problem is. Ex. I'm always frustrated when [...]\n\n**Describe the solution you'd like**\nA clear and concise description of what you want to happen.\n\n**Describe alternatives you've considered**\nA clear and concise description of any alternative solutions or features you've considered.\n\n**Additional context**\nAdd any other context or screenshots about the feature request here.\n"
  },
  {
    "path": ".github/workflows/test.yml",
    "content": "name: Test\n\non:\n  push:\n    branches: [master]\n  pull_request:\n    branches: [master]\n\njobs:\n  build-and-test:\n    runs-on: ubuntu-latest\n\n    env:\n      LANG: en_US.utf8\n      LIMIT_JEST: yes\n      TZ: America/New_York\n\n    strategy:\n      matrix:\n        node-version:\n          - 18.20.8 # latest 18.x\n          - 20.19.4 # latest 20.x\n          - 22.17.1 # latest 22.x\n          - 24.4.1 # latest 22.x\n\n    steps:\n      - uses: actions/checkout@v3\n      - name: Use Node.js ${{ matrix.node-version }}\n        uses: actions/setup-node@v3\n        with:\n          node-version: ${{ matrix.node-version }}\n          cache: \"npm\"\n      - run: npm ci\n      - run: npm run build\n      - run: npm run format-check\n      - run: npm run test\n      - run: npm run site\n      - run: bash <(curl -s https://codecov.io/bash)"
  },
  {
    "path": ".gitignore",
    "content": "node_modules\n.tern-port\n/build\n#*\n.#*\ncoverage/\n.DS_Store\n.external-ecmascript.js\n.idea\n.vscode\ntest/scratch.test.js\n"
  },
  {
    "path": ".husky/.gitignore",
    "content": "_\n"
  },
  {
    "path": ".husky/pre-commit",
    "content": "#!/bin/sh\n. \"$(dirname \"$0\")/_/husky.sh\"\n\nnpx lint-staged\n"
  },
  {
    "path": ".prettierrc",
    "content": "{ \"printWidth\": 100 }\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Changelog\n\n# 3.7.2 (2025-07-09)\n- Fix ES6 packaging\n\n# 3.7.1 (2025-07-09)\n- Revert change in ES6 packaging\n\n# 3.7.0 (2025-07-09)\n- Added `showZeros` option to `Duration#toHuman`\n- Added `Duration#removeZeros` method.\n- Added `rounding` option to `DateTime#toRelative`\n- Added `precision` option to ISO formatting methods\n- Added `signMode` option to `Duration#toFormat`\n- Allow escaping single quotes in format strings\n- Improve output of `Info.months` and `Info.monthsFormat` for `ja` locale\n- Accept lowercase `t` as a separator in ISO strings\n- Accept lowercase `z` as an offset in ISO strings\n- Reject non-finite numbers where previously only `NaN` was rejected\n- Improve the documentation for Interval\n- Added a dark theme for the documentation site\n\n# 3.6.1 (2025-03-31)\n- Add Fallback for `minimalDays` being removed from Intl.WeekInfo\n- Fix various caches when JS keywords like \"constructor\" are used for names\n\n# 3.6.0 (2025-03-25)\n- Add `Interval.lastDateTime`\n- Fix a bug that could cause wrong timezone calculations when multiple timezones are in use \n\n# 3.5.0 (2024-08-03)\n\n- Various performance improvements\n- throwOnInvalid causes the constructor to throw if the year is invalid\n\n# 3.4.4 (2023-11-12)\n\n- Localized week support (#1454)\n- Added custom inspect for Node (#1526)\n- Fix sorting in `Interval.splitAt` (#1524)\n\n# 3.4.3 (2023-09-05)\n\n- Fixes another regression from 3.4.0 (#1496)\n\n# 3.4.2 (2023-08-26)\n\n- Fixes regression from 3.4.1 (#1493)\n\n# 3.4.1 (2023-08-23)\n\n- Fixes for regressions from 3.4.0 (#1482 and #1488)\n\n# 3.4.0 (2023-08-08)\n\n- Fix type checking on input zones\n- Fix Islamic months listing\n- Fix normalize() for negative inputs\n\n# 3.3.0 (2023-03-03)\n\n- Fix off-by-one in Interval#count (#1308)\n- Support formatting for custom zones (#1377)\n- Fix parsing for narrow spaces (#1369)\n- Handle leap year issue with AD 100 (#1390)\n- Allow parsing of just an offset\n\n# 3.2.1 (2023-01-04)\n\n- Fix for RFC-2822 regex vulnerability\n- Better handling of BCP tags with -x- extensions\n\n# 3.2.0 (2022-12-29)\n\n- Allow timeZone to be specified as an intl option\n- Fix for diff's handling of end-of-month when crossing leap years (#1340)\n- Add Interval.toLocaleString() (#1320)\n\n# 3.1.1 (2022-11-28)\n\n- Add Settings.twoDigitCutoffYear\n\n# 3.1.0 (2022-10-31)\n\n- Add Duration.rescale\n\n# 3.0.4 (2022-09-24)\n\n- Fix quarters in diffs (#1279)\n- Export package.json in package (#1239)\n\n# 3.0.2 (2022-08-28)\n\n- Lots of doc changes\n- Added DateTime.expandFormat\n- Added support for custom conversion matrices in Durations\n\n# 3.0.1 (2022-07-09)\n\n- Add DateTime.parseFormatForOpts\n\n# 3.0.0 (2022-07-09)\n\n- Add \"default\" as an option for specifying a zone, and change \"system\" to really mean the system zone (breaking change)\n\n# 2.5.0 (2022-07-09)\n\n- Support for ESM-style node imports\n- Fix Wednesday parsing for RFC 850 strings\n- Increase number of digits allowed in ISO durations\n\n## 2.4.0 (2022-05-08)\n\n- Add support for parsing the ISO zone extension, like `2022-05-08T20:42:00.000-04:00[America/New_York]`\n- Add an `extendedZone` option to `toISO()` and `toISOTime`\n- Improvements to `DateTime.isInDST()`\n- Fix for parsing in Vietnames (and probably other languages)\n\n## 2.3.2 (2022-04-17)\n\n- Fix timezone calculations for negative years\n- add week formatting token \"w\" for durations\n- fix weekday computation for years 0-100\n\n## 2.3.1 (2022-02-23)\n\n- Added an `includeOffsetSpace` option to `toSQL` and `toSQLTime`\n- Added `toUnixInteger`\n- Don't use `-0` when negating durations with zeros in them\n\n## 2.3.0 (2022-01-02)\n\n- Major perf improvements to `toISO()`, `toISODate()`, `toISOTime()`, and `toSQLDate()`\n- Fixed date padding for negative years in `toISO()`\n- Added Duration#toHuman()\n\n## 2.2.0 (2021-12-10)\n\n- Allow offsets to pick among ambiguous times when both an offset and zone are provided to `fromFormat`\n- Fix a floating point bug in `Duration.shiftTo()`\n\n## 2.1.1 (2021-11-08)\n\n- Fix issue in quirky environments that lack `hourCycle` support and sometimes computed offsets 12 hours off\n\n## 2.1.0 (2021-11-07)\n\n- Stop special casing of `Etc/GMT*` zones\n- export fromDurationLike\n- memoize zone validation\n- Support for fractional elements in duration ISO parsing\n- Added `uu` and `uuu` tokens for fractional millisecond parsing\n\n## 2.0.2 (2021-08-08)\n\nFix locale defaulting\n\n## 2.0.0 (2021-07-3)\n\nSee [Upgrading section](https://moment.github.io/luxon/#/upgrading?id=_1x-to-20)\n\n## 1.28.0 (2021-07-03)\n\n- Fix ISO parsing for offset specifiers in Year-Ordinal formats\n\n## 1.27.0 (2021-05-08)\n\n- Fix GMT zone parsing for older versions of Node\n- Support multiple units in `toRelative`\n- Various documentation updates\n\n## 1.26.0 (2021-02-13)\n\n- Add fromISOTime, toISOTime and toMillis to Duration (#803)\n- Fix padding of negative years in IsoDate (#871)\n- Fix hasSame unit comparison (#798)\n- Export VERSION information (#794)\n- Durations are considered equal with extra zero units. Fixes #809 (#811)\n\n## 1.25.0 (2020-08-23)\n\n- fix fromFormat with Intl formats containing non-breaking spaces\n- Support higher precision in ISO milliseconds\n- Some fixes for 00:30 timezones\n- Fix some throwOnInvalid for invalid Intervals\n- Various doc fixes\n- Fix Interval#isSame for empty intervals\n- Mark package as side effect-free\n- Add support for intervals with a large number of seconds\n\n## 1.24.1 (2020-05-04)\n\n- Remove erroneous `console.log` call\n\n## 1.24.0 (2020-05-03)\n\n- Update polyfills for pollyfilled build\n\n## 1.23.0 (2020-04-02)\n\n- Allow minus sign prefix when creating Duration from ISO\n\n## 1.22.2 (2020-03-25)\n\n- Added more details to error messages for type errors\n\n## 1.22.1 (2020-03-19)\n\n- Added support for ISO basic format to DateTime#toISO\n\n## 1.22.0 (2020-01-26)\n\n- Fix setZone's handling of pre-1970 dates with millisecond components\n- Fix keepLocalTime for large jumps near the target zone's DST\n- Fix cache perf for toRelative()\n\n## 1.21.3 (2019-11-28)\n\n- Fix parsing of meridiems in macro tokens in newer versions of v8\n\n## 1.21.2 (2019-11-18)\n\n- Fix bug in Chrome Canary that threw off time zone calculations\n\n## 1.21.1 (2019-11-03)\n\n- Fix for quarter parsing\n- Some documentation updates\n\n## 1.21.0 (2019-10-30)\n\n- Added quarter support to the parser\n- Fix some rounding issues in ISO formatting\n\n## 1.20.0 (2019-10-29)\n\n- Added Duration#mapUnits\n- added Interval#toISODate and Interval#toISOTime\n- Some documentation fixes\n\n## 1.19.3\n\n- Cache offset values\n- Fix handling of negative sub 1-hour offsets\n\n## 1.19.2\n\n- Speculative fix for Node 6\n\n## 1.19.1\n\n- Fix Intl.DateTimeFormat usage for polyfills\n\n## 1.19.0\n\n- Interval#splitAt now ignores input dates outside the interval\n- Don't allow decimals in DateTime creation\n\n## 1.18.2\n\n- Fix handling of decimals in DateTime#plus and #minus\n\n## 1.18.1\n\n- Fix validity when adding or subtracting time that exceeds Date max/min boundaries\n\n## 1.18.0\n\n- Add support for macro tokens in the parser\n\n## 1.17.2\n\n- Fix issue with `toRelative` using `style: short` with plural days\n\n## 1.17.1\n\n- Reject out-of-range numbers in DateTime.fromMillis\n- Reject 0s in ISO date inputs\n\n## 1.17.0\n\n- DateTime.min and DateTime.max throw if they get the wrong kind of arguments\n- Fixed throwOnInvalid logic for Interval\n- Added `DATETIME_MED_WITH_WEEKDAY` preset\n\n## 1.16.1\n\n- Catch errors trying to use Intl in weird versions of IE 11\n\n## 1.16.0\n\n- Fixed locale default logic for `DateTime#toFormat(\"ZZZZ\")\n\n## 1.15.0\n\n- Added `formatOffset` to Zones\n\n## 1.14.0\n\n- Allow the zone argument to Interval.fromISO with duration components\n- Ignore the zone argument to Duration factory methods\n\n## 1.13.3\n\n- Fix keepLocalTime calculations that span offset changes\n\n## 1.13.2\n\n- Fixed ISO formatting for dates > 999\n\n## 1.13.1\n\n- Performance improvements for regex parsing\n\n## 1.13.0\n\n- Support numberSystem in fromFormat\n- Fix validity for bad initial zone specifiers\n\n## 1.12.1\n\n- Fix cross-month diffs in some scenarios\n- Fix time zone parsing when the time zone isn't at the end\n- Memoize IANA zone creation\n\n## 1.12.0\n\n- Add some explicit CDN support to the NPM package\n- Add week token to duration ISO support\n- Lots of cleanup and test coverage changes\n\n## 1.11.4\n\n- `setZone(\"local\")` now returns the defaultZone if it is set\n- Fixes for the polyfilled build\n\n## 1.11.3\n\n- Allow 24:00 in ISO (and other) strings\n- Fix some bugs with the typecheck functions like `DateTime.isDateTime()`\n\n## 1.11.2\n\n- Fixed handling of some characters in fromFormat literal sections\n- Handle string values in object arguments to DateTime methods\n- Fixed toRelativeCalendar's handling of zones in the base date\n\n## 1.11.1\n\n- Fix DateTime#plus() when spanning across AD 100\n\n## 1.11.0\n\n- Fix low-year handling for IANA zones\n- `DateTime#toLocal()` now uses the default locale\n- Fix zero duration formatting\n- Many documentation fixes\n\n## 1.10.0\n\n- Fix endOf(\"day\") during DSTs (#399)\n- Add `Interval#mapEndpoints (#400)\n- Add `DateTime#zone` and `Info.normalizeZone` (#404)\n\n## 1.9.0\n\n- Add `DateTime#toRelative` and `DateTime#toRelativeCalendar`\n\n## 1.8.3\n\n- Allow \"UTC\" in the zone position of `fromSQL`\n- Force `isDateTime` and `isDuration` to return booleans in all cases\n\n## 1.8.2\n\n- Trim leading \\u200e characters from offset names in Edge 16 and 17\n\n## 1.8.1\n\n- Add `DateTime.fromSeconds` and `DateTime#toSeconds`\n\n## 1.7.1\n\n- Floor the seconds instead of rounding them when outputting the 'X' format\n- Change the options to toLocale to override the configuration (the previous options were essentially ignored)\n\n## 1.6.2\n\n- Fixing merge error that resulted in bad error messages\n\n## 1.6.0\n\n- **midly breaking** Rework negative durations\n- Fix handling\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing to Luxon\n\n## General guidelines\n\nPatches are welcome. Luxon is at this point just a baby and it could use lots of help. But before you dive in...Luxon is one of those tightly-scoped libraries where the default answer to \"should this library do X?\" is likely \"no\". **So ask first!** It might save you some time and energy.\n\nHere are some vague notes on Luxon's design philosophy:\n\n1.  We won't accept patches that can't be internationalized using the JS environment's (e.g. the browser's) native capabilities. This means that most convenient humanization features are out of scope.\n1.  We try hard to have a clear definition of what Luxon does and doesn't do. With few exceptions, this is not a \"do what I mean\" library.\n1.  Luxon shouldn't contain simple conveniences that bloat the library to save callers a couple lines of code. Write those lines in your own code.\n1.  Most of the complexity of JS module loading compatibility is left to the build. If you have a \"this can't be loaded in my bespoke JS module loader\" problems, this isn't something you should be solving with changes to the `src` directory. If it's a common use case and is possible to generate with Rollup, it can get its own build command.\n1.  We prefer documentation clarifications and gotchas to go in the docstrings, not in the guides on the docs page. Obviously, if the guides are wrong, they should be fixed, but we don't want them to turn into troubleshooting pages. On the other hand, making sure the method-level documentation has ample examples and notes is great.\n1.  You'll need to sign a CLA as part of your first pull request to Luxon.\n\n## Building and testing\n\nBuilding and testing is done through npm scripts. The tests run in Node and require Node 18 with full-icu support. This is because some of the features available in Luxon (like internationalization and time zones) need that stuff and we test it all. On any platform, if you have Node 18 installed with full-icu, you're good to go; just run `scripts/test`. But you probably don't have that, so read on.\n\n### OSX\n\nMac is easy:\nOpen the terminal.\n\n```\nbrew install node --with-full-icu\nnpm install\n./scripts/test\n```\n\nIf that's for whatever reason a pain, the Linux instructions should also work, as well as the Docker ones.\n\n### Linux\n\nThere are two ways to get full-icu support in Linux: build it with that support, or provide it as a module. We'll cover the latter. Assuming you've installed Node 10:\n\n```\nnpm install\nnpm install full-icu\n./scripts/test\n```\n\nWhere `scripts/test` is just `NODE_ICU_DATA=\"$(pwd)/node_modules/full-icu\" npm run test`, which is required for making Node load the full-icu module you just installed. You can run all the other npm scripts (e.g. `npm run docs`) directly; they don't require Intl support.\n\n### Windows\n\nIf you have [Bash](https://git-scm.com/downloads) or [WSL](https://docs.microsoft.com/en-us/windows/wsl/install-win10), the Linux instructions seem to work fine.\n\nI would love to add instructions for a non-WSL install of the dev env!\n\n### Docker\n\nIn case messing with your Node environment just to run Luxon's tests is too much to ask, we've provided a Docker container. You'll need a functioning Docker environment, but the rest is easy:\n\n```\n./docker/npm install --ignore-scripts\n./docker/npm test\n```\n\n## Patch basics\n\nOnce you're sure your bugfix or feature makes sense for Luxon, make sure you take these steps:\n\n1.  Be sure to add tests and run them with `scripts/test`\n1.  Be sure you run `npm run format` before you commit. Note this will modify your source files to line up with the style guidelines.\n1.  Make sure you add or ESDoc annotations appropriately. You can run `npm run docs` to generate the HTML for them. They land in the `build/docs` directory. This also builds the markdown files in `/docs` into the guide on the Luxon website.\n1.  To test Luxon in your browser, run `npm run site` and then open `build/demo/global.html`. You can access Luxon classes in the console like `window.luxon.DateTime`.\n1.  To test in Node, run `npm run build` and then run something like `var DateTime = require('./build/cjs-browser/luxon').DateTime`.\n\nLuxon uses [Husky](https://github.com/typicode/husky) to run the formatter on your code as a pre-commit hook. You should still run `npm run format` yourself to catch other issues, but this hook will help prevent you from failing the build with a trivial formatting error.\n\n## npm script reference\n\n| Command                      | Function                                |\n| ---------------------------- | --------------------------------------- |\n| `npm run build`              | Build all the distributable files       |\n| `npm run build-node`         | Build just for Node                     |\n| `npm run test`               | Run the test suite, but see notes above |\n| `npm run format`             | Run the Prettier formatter              |\n| `npm run site`               | Build the Luxon website, including docs                 |\n| `npm run check-doc-coverage` | Check whether there's full doc coverage |\n| `npm run benchmark`          | Run performance benchmarks |\n"
  },
  {
    "path": "LICENSE.md",
    "content": "Copyright 2019 JS Foundation and other contributors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# Luxon\n\n[![MIT License][license-image]][license] [![Build Status][github-action-image]][github-action-url] [![NPM version][npm-version-image]][npm-url] [![Coverage Status][test-coverage-image]][test-coverage-url] [![PRs welcome][contributing-image]][contributing-url]\n\nLuxon is a library for working with dates and times in JavaScript.\n\n```js\nDateTime.now().setZone(\"America/New_York\").minus({ weeks: 1 }).endOf(\"day\").toISO();\n```\n\n## 🚀 The road to Luxon 4.0\n\nPlease [read about and share your feedback](https://github.com/moment/luxon/discussions/1742) on the plans for the next major version of Luxon!\n\n## Upgrading to 3.0\n\n[Guide](https://moment.github.io/luxon/#upgrading)\n\n## Features\n * DateTime, Duration, and Interval types.\n * Immutable, chainable, unambiguous API.\n * Parsing and formatting for common and custom formats.\n * Native time zone and Intl support (no locale or tz files).\n\n## Download/install\n\n[Download/install instructions](https://moment.github.io/luxon/#/install)\n\n## Documentation\n\n* [General documentation](https://moment.github.io/luxon/#/?id=luxon)\n* [API docs](https://moment.github.io/luxon/api-docs/index.html)\n* [Quick tour](https://moment.github.io/luxon/#/tour)\n* [For Moment users](https://moment.github.io/luxon/#/moment)\n* [Why does Luxon exist?](https://moment.github.io/luxon/#/why)\n* [A quick demo](https://moment.github.io/luxon/demo/global.html)\n\n## Development\n\nSee [contributing](CONTRIBUTING.md).\n\n![Phasers to stun][phasers-image]\n\n[license-image]: https://img.shields.io/badge/license-MIT-blue.svg\n[license]: LICENSE.md\n\n[github-action-image]: https://github.com/moment/luxon/actions/workflows/test.yml/badge.svg\n[github-action-url]: https://github.com/moment/luxon/actions/workflows/test.yml\n\n[npm-url]: https://npmjs.org/package/luxon\n[npm-version-image]: https://badge.fury.io/js/luxon.svg\n\n[test-coverage-url]: https://codecov.io/gh/moment/luxon\n[test-coverage-image]: https://codecov.io/gh/moment/luxon/branch/master/graph/badge.svg\n\n[contributing-url]: https://github.com/moment/luxon/blob/master/CONTRIBUTING.md\n[contributing-image]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg\n\n[phasers-image]: https://img.shields.io/badge/phasers-stun-brightgreen.svg\n"
  },
  {
    "path": "babel.config.js",
    "content": "module.exports = { presets: [\"@babel/preset-env\"] };\n"
  },
  {
    "path": "benchmarks/datetime.js",
    "content": "import Benchmark from \"benchmark\";\nimport DateTime from \"../src/datetime.js\";\nimport Settings from \"../src/settings.js\";\n\nfunction runDateTimeSuite() {\n  return new Promise((resolve, reject) => {\n    const suite = new Benchmark.Suite();\n\n    const dt = DateTime.now();\n\n    const formatParser = DateTime.buildFormatParser(\"yyyy/MM/dd HH:mm:ss.SSS\");\n\n    suite\n      .add(\"DateTime.now\", () => {\n        DateTime.now();\n      })\n      .add(\"DateTime.fromObject with locale\", () => {\n        DateTime.fromObject({}, { locale: \"fr\" });\n      })\n      .add(\"DateTime.local with numbers\", () => {\n        DateTime.local(2017, 5, 15);\n      })\n      .add(\"DateTime.local with numbers and zone\", () => {\n        DateTime.local(2017, 5, 15, 11, 7, 35, { zone: \"America/New_York\" });\n      })\n      .add(\"DateTime.fromISO\", () => {\n        DateTime.fromISO(\"1982-05-25T09:10:11.445Z\");\n      })\n      .add(\"DateTime.fromSQL\", () => {\n        DateTime.fromSQL(\"2016-05-14 10:23:54.2346\");\n      })\n      .add(\"DateTime.fromFormat\", () => {\n        DateTime.fromFormat(\"1982/05/25 09:10:11.445\", \"yyyy/MM/dd HH:mm:ss.SSS\");\n      })\n      .add(\"DateTime.fromFormat with zone\", () => {\n        DateTime.fromFormat(\"1982/05/25 09:10:11.445\", \"yyyy/MM/dd HH:mm:ss.SSS\", {\n          zone: \"America/Los_Angeles\",\n        });\n      })\n      .add(\"DateTime.fromFormatParser\", () => {\n        DateTime.fromFormatParser(\"1982/05/25 09:10:11.445\", formatParser);\n      })\n      .add(\"DateTime.fromFormatParser with zone\", () => {\n        DateTime.fromFormatParser(\"1982/05/25 09:10:11.445\", formatParser, {\n          zone: \"America/Los_Angeles\",\n        });\n      })\n      .add(\"DateTime#setZone\", () => {\n        dt.setZone(\"America/Los_Angeles\");\n      })\n      .add(\"DateTime#toFormat\", () => {\n        dt.toFormat(\"yyyy-MM-dd\");\n      })\n      .add(\"DateTime#toFormat with macro\", () => {\n        dt.toFormat(\"T\");\n      })\n      .add(\"DateTime#toFormat with macro no cache\", () => {\n        dt.toFormat(\"T\");\n        Settings.resetCaches();\n      })\n      .add(\"DateTime#format in german\", () => {\n        dt.setLocale(\"de-De\").toFormat(\"d. LLL. HH:mm\");\n      })\n      .add(\"DateTime#format in german and no-cache\", () => {\n        dt.setLocale(\"de-De\").toFormat(\"d. LLL. HH:mm\");\n        Settings.resetCaches();\n      })\n      .add(\"DateTime#add\", () => {\n        dt.plus({ milliseconds: 3434 });\n      })\n      .add(\"DateTime#toISO\", () => {\n        dt.toISO();\n      })\n      .add(\"DateTime#toLocaleString\", () => {\n        dt.toLocaleString();\n      })\n      .add(\"DateTime#toLocaleString in utc\", () => {\n        dt.toUTC().toLocaleString();\n      })\n      .add(\"DateTime#toRelativeCalendar\", () => {\n        dt.toRelativeCalendar({ base: DateTime.now(), locale: \"fi\" });\n      })\n      .on(\"cycle\", (event) => {\n        console.log(String(event.target));\n      })\n      .on(\"complete\", function () {\n        console.log(\"Fastest is \" + this.filter(\"fastest\").map(\"name\"));\n        resolve();\n      })\n      .on(\"error\", function () {\n        reject(this.error);\n      })\n      .run();\n  });\n}\n\nconst allSuites = [runDateTimeSuite];\n\nexport default allSuites;\n"
  },
  {
    "path": "benchmarks/index.js",
    "content": "import dateTimeSuites from \"./datetime.js\";\nimport infoSuites from \"./info.js\";\n\nconst allSuites = [...dateTimeSuites, ...infoSuites];\n\nasync function runAllSuites() {\n  for (const runSuite of allSuites) {\n    await runSuite();\n  }\n}\n\nrunAllSuites();\n"
  },
  {
    "path": "benchmarks/info.js",
    "content": "import Benchmark from \"benchmark\";\nimport Info from \"../src/info.js\";\nimport Locale from \"../src/impl/locale.js\";\n\nfunction runWeekdaysSuite() {\n  return new Promise((resolve, reject) => {\n    const locale = Locale.create(null, null, null);\n\n    new Benchmark.Suite()\n      .add(\"Info.weekdays with existing locale\", () => {\n        Info.weekdays(\"long\", { locObj: locale });\n      })\n      .add(\"Info.weekdays\", () => {\n        Info.weekdays(\"long\");\n      })\n      .on(\"cycle\", (event) => {\n        console.log(String(event.target));\n      })\n      .on(\"complete\", function () {\n        console.log(\"Fastest is \" + this.filter(\"fastest\").map(\"name\"));\n        resolve();\n      })\n      .on(\"error\", function () {\n        reject(this.error);\n      })\n      .run();\n  });\n}\n\nfunction runWeekdaysFormatSuite() {\n  return new Promise((resolve, reject) => {\n    const locale = Locale.create(null, null, null);\n\n    new Benchmark.Suite()\n      .add(\"Info.weekdaysFormat with existing locale\", () => {\n        Info.weekdaysFormat(\"long\", { locObj: locale });\n      })\n      .add(\"Info.weekdaysFormat\", () => {\n        Info.weekdaysFormat(\"long\");\n      })\n      .on(\"cycle\", (event) => {\n        console.log(String(event.target));\n      })\n      .on(\"complete\", function () {\n        console.log(\"Fastest is \" + this.filter(\"fastest\").map(\"name\"));\n        resolve();\n      })\n      .on(\"error\", function () {\n        reject(this.error);\n      })\n      .run();\n  });\n}\n\nfunction runMonthsSuite() {\n  return new Promise((resolve, reject) => {\n    const locale = Locale.create(null, null, null);\n    new Benchmark.Suite()\n      .add(\"Info.months with existing locale\", () => {\n        Info.months(\"long\", { locObj: locale });\n      })\n      .add(\"Info.months\", () => {\n        Info.months(\"long\");\n      })\n      .on(\"cycle\", (event) => {\n        console.log(String(event.target));\n      })\n      .on(\"complete\", function () {\n        console.log(\"Fastest is \" + this.filter(\"fastest\").map(\"name\"));\n        resolve();\n      })\n      .on(\"error\", function () {\n        reject(this.error);\n      })\n      .run();\n  });\n}\n\nfunction runMonthsFormatSuite() {\n  return new Promise((resolve, reject) => {\n    const locale = Locale.create(null, null, null);\n\n    new Benchmark.Suite()\n      .add(\"Info.monthsFormat with existing locale\", () => {\n        Info.monthsFormat(\"long\", { locObj: locale });\n      })\n      .add(\"Info.monthsFormat\", () => {\n        Info.monthsFormat(\"long\");\n      })\n      .on(\"cycle\", (event) => {\n        console.log(String(event.target));\n      })\n      .on(\"complete\", function () {\n        console.log(\"Fastest is \" + this.filter(\"fastest\").map(\"name\"));\n        resolve();\n      })\n      .on(\"error\", function () {\n        reject(this.error);\n      })\n      .run();\n  });\n}\n\nconst allSuites = [runMonthsSuite, runMonthsFormatSuite, runWeekdaysSuite, runWeekdaysFormatSuite];\n\nexport default allSuites;\n"
  },
  {
    "path": "benchmarks/package.json",
    "content": "{\n  \"type\": \"module\"\n}\n"
  },
  {
    "path": "codecov.yml",
    "content": "comment: false\ncoverage:\n  status:\n    project:\n      default:\n        target: auto\n        threshold: null\n        base: auto\n"
  },
  {
    "path": "docker/Dockerfile",
    "content": "FROM library/node:15-slim\n\nENV HUSKY_SKIP_INSTALL=1\n\nENV LANG=en_US.utf8\nENV LIMIT_JEST=yes\nENV CI=yes\nENV TZ=America/New_York\n"
  },
  {
    "path": "docker/build",
    "content": "#!/usr/bin/env bash\ndocker build docker -t icambron/luxon\n"
  },
  {
    "path": "docker/npm",
    "content": "#!/usr/bin/env bash\ndocker run -it --rm -v $(pwd):/luxon -w /luxon icambron/luxon npm $@\n"
  },
  {
    "path": "docker/push",
    "content": "#!/usr/bin/env bash\ndocker push icambron/luxon\n"
  },
  {
    "path": "docker/readme.md",
    "content": "Luxon provides a Docker container and some wrapping scripts to make it easier to run the tests.\n\n  1. The Dockerfile is really just here as an FYI. You shouldn't need to interact with it\n  1. `npm` is a bash script that runs `npm run [arg]` inside the Docker container.\n"
  },
  {
    "path": "docs/calendars.md",
    "content": "# Calendars\n\nThis covers Luxon's support for various calendar systems. If you don't need to use non-standard calendars, you don't need to read any of this.\n\n## Fully supported calendars\n\nLuxon has full support for Gregorian and ISO Week calendars. What I mean by that is that Luxon can parse dates specified in those calendars, format dates into strings using those calendars, and transform dates using the units of those calendars. For example, here is Luxon working directly with an ISO calendar:\n\n```js\nDateTime.fromISO('2017-W23-3').plus({ weeks: 1, days: 2 }).toISOWeekDate(); //=>  '2017-W24-5'\n```\n\nThe main reason I bring all this is up is to contrast it with the capabilities for other calendars described below.\n\n## Output calendars\n\nLuxon has limited support for other calendaring systems. Which calendars are supported at all is a platform-dependent, but can generally be expected to be these: Buddhist, Chinese, Coptic, Ethioaa, Ethiopic, Hebrew, Indian, Islamic, Islamicc, Japanese, Persian, and ROC. **Support is limited to formatting strings with them**, hence the qualified name \"output calendar\".\n\nIn practice this is pretty useful; you can show users the date in their preferred calendaring system while the software works with dates using Gregorian units or Epoch milliseconds. But the limitations are real enough; Luxon doesn't know how to do things like \"add one Islamic month\".\n\nThe output calendar is a property of the DateTime itself. For example:\n\n```js\nvar dtHebrew = DateTime.now().reconfigure({ outputCalendar: \"hebrew\" });\ndtHebrew.outputCalendar; //=> 'hebrew'\ndtHebrew.toLocaleString() //=> '4 Tishri 5778'\n```\n\nYou can modulate the structure of that string with arguments to `toLocaleString` (see [the docs on that](formatting.md?id=tolocalestring-strings-for-humans)), but the point here is just that you got the alternative calendar.\n\n### Generally supported calendars\n\nHere's a table of the different calendars with examples generated formatting the same date generated like this:\n\n```js\nDateTime.fromObject({ outputCalendar: c }).toLocaleString(DateTime.DATE_FULL);\n```\n\nSince Luxon uses the browser's **Intl API**, you can use all the supported calendars.\n(See [Intl.Locale.prototype.getCalendars()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/getCalendars) for a full list)\n\n| Calendar           | Example                  |\n| ------------------ | ------------------------ |\n| buddhist           | September 24, 2560 BE    |\n| chinese            | Eighth Month 5, 2017     |\n| coptic             | Tout 14, 1734 ERA1       |\n| ethioaa            | Meskerem 14, 7510 ERA0   |\n| ethiopic           | Meskerem 14, 2010 ERA1   |\n| hebrew             | 4 Tishri 5778            |\n| indian             | Asvina 2, 1939 Saka      |\n| islamic            | Muharram 4, 1439 AH      |\n| islamic-civil      | Muharram 3, 1439 AH      |\n| islamic-umalqura   | Muharram 3, 1439 AH      |\n| iso8601            | September 24, 2017       |\n| japanese           | September 24, 29 Heisei  |\n| persian            | Mehr 2, 1396 AP          |\n| roc                | September 24, 106 Minguo |\n\n\n\n### Default output calendar\n\nYou can set the default output calendar for new DateTime instances like this:\n\n```js\nSettings.defaultOutputCalendar = 'persian';\n```\n"
  },
  {
    "path": "docs/formatting.md",
    "content": "# Formatting\n\nThis section covers creating strings to represent a DateTime. There are three types of formatting capabilities:\n\n1.  Technical formats like ISO 8601 and RFC 2822\n2.  Internationalizable human-readable formats\n3.  Token-based formatting\n\n## Technical formats (strings for computers)\n\n### ISO 8601\n\n[ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) is the most widely used set of string formats for dates and times. Luxon can _parse_ a wide range of them, but provides direct support for formatting only a few of them:\n\n```js\ndt.toISO(); //=> '2017-04-20T11:32:00.000-04:00'\ndt.toISODate(); //=> '2017-04-20'\ndt.toISOWeekDate(); //=> '2017-W17-7'\ndt.toISOTime(); //=> '11:32:00.000-04:00'\n```\n\nGenerally, you'll want the first one. Use it by default when building or interacting with APIs, communicating times over a wire, etc.\n\n### HTTP and RFC 2822\n\nThere are a number of legacy standard date and time formats out there, and Luxon supports some of them. You shouldn't use them unless you have a specific reason to.\n\n```js\ndt.toRFC2822(); //=> 'Thu, 20 Apr 2017 11:32:00 -0400'\ndt.toHTTP(); //=> 'Thu, 20 Apr 2017 03:32:00 GMT'\n```\n\n### Unix timestamps\n\nDateTime objects can also be converted to numerical [Unix timestamps](https://en.wikipedia.org/wiki/Unix_time):\n\n```js\ndt.toMillis(); //=> 1492702320000\ndt.toSeconds(); //=> 1492702320.000\ndt.toUnixInteger(); // => 1492702320\ndt.valueOf(); //=> 1492702320000, same as .toMillis()\n```\n\n## toLocaleString (strings for humans)\n\n### The basics\n\nModern browsers (and other JS environments) provide support for human-readable, internationalized strings. Luxon provides convenient support for them, and you should use them anytime you want to display a time to a user. Use `toLocaleString` to do it:\n\n```js\ndt.toLocaleString(); //=> '4/20/2017'\ndt.toLocaleString(DateTime.DATETIME_FULL); //=> 'April 20, 2017 at 11:32 AM EDT'\ndt.setLocale('fr').toLocaleString(DateTime.DATETIME_FULL); //=> '20 avril 2017 à 11:32 UTC−4'\n```\n\n### Intl.DateTimeFormat\n\nIn the example above, `DateTime.DATETIME_FULL` is one of several convenience formats provided by Luxon. But the arguments are really any object of options that can be provided to [Intl.DateTimeFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat). For example:\n\n```js\ndt.toLocaleString({ month: 'long', day: 'numeric' }); //=> 'April 20'\n```\n\nAnd that's all the preset is:\n\n```\nDateTime.DATETIME_FULL;  //=> {\n                         //     year: 'numeric',\n                         //     month: 'long',\n                         //     day: 'numeric',\n                         //     hour: 'numeric',\n                         //     minute: '2-digit',\n                         //     timeZoneName: 'short'\n                         //   }\n```\n\nThis also means you can modify the presets as you choose:\n\n```js\ndt.toLocaleString(DateTime.DATE_SHORT); //=>  '4/20/2017'\nvar newFormat = {...DateTime.DATE_SHORT, weekday: 'long' };\ndt.toLocaleString(newFormat); //=>  'Thursday, 4/20/2017'\n```\n\n### Presets\n\nHere's the full set of provided presets using the October 14, 1983 at `13:30:23` as an example.\n\n| Name                         | Description                                                        | Example in en_US                                               | Example in fr                                              |\n| ---------------------------- | ------------------------------------------------------------------ | -------------------------------------------------------------- | ---------------------------------------------------------- |\n| `DATE_SHORT`                 | short date                                                         | `10/14/1983`                                                   | `14/10/1983`                                               |\n| `DATE_MED`                   | abbreviated date                                                   | `Oct 14, 1983`                                                 | `14 oct. 1983`                                             |\n| `DATE_MED_WITH_WEEKDAY`      | abbreviated date with abbreviated weekday                          | `Fri, Oct 14, 1983`                                            | `ven. 14 oct. 1983`                                        |\n| `DATE_FULL`                  | full date                                                          | `October 14, 1983`                                             | `14 octobre 1983`                                          |\n| `DATE_HUGE`                  | full date with weekday                                             | `Friday, October 14, 1983`                                     | `vendredi 14 octobre 1983`                                 |\n| `TIME_SIMPLE`                | time                                                               | `1:30 PM`                                                      | `13:30`                                                    |\n| `TIME_WITH_SECONDS`          | time with seconds                                                  | `1:30:23 PM`                                                   | `13:30:23`                                                 |\n| `TIME_WITH_SHORT_OFFSET`     | time with seconds and abbreviated named offset                     | `1:30:23 PM EDT`                                               | `13:30:23 UTC−4`                                           |\n| `TIME_WITH_LONG_OFFSET`      | time with seconds and full named offset                            | `1:30:23 PM Eastern Daylight Time`                             | `13:30:23 heure d’été de l’Est`                            |\n| `TIME_24_SIMPLE`             | 24-hour time                                                       | `13:30`                                                        | `13:30`                                                    |\n| `TIME_24_WITH_SECONDS`       | 24-hour time with seconds                                          | `13:30:23`                                                     | `13:30:23`                                                 |\n| `TIME_24_WITH_SHORT_OFFSET`  | 24-hour time with seconds and abbreviated named offset             | `13:30:23 EDT`                                                 | `13:30:23 UTC−4`                                           |\n| `TIME_24_WITH_LONG_OFFSET`   | 24-hour time with seconds and full named offset                    | `13:30:23 Eastern Daylight Time`                               | `13:30:23 heure d’été de l’Est`                            |\n| `DATETIME_SHORT`             | short date & time                                                  | `10/14/1983, 1:30 PM`                                          | `14/10/1983 à 13:30`                                       |\n| `DATETIME_MED`               | abbreviated date & time                                            | `Oct 14, 1983, 1:30 PM`                                        | `14 oct. 1983 à 13:30`                                     |\n| `DATETIME_MED_WITH_WEEKDAY`  | abbreviated date & time with abbreviated weekday                   | `Fri, Oct 14, 1983, 1:30 PM`                                   | `ven. 14 oct. 1983 à 13:30`                                |\n| `DATETIME_FULL`              | full date and time with abbreviated named offset                   | `October 14, 1983 at 1:30 PM EDT`                              | `14 octobre 1983 à 13:30 UTC−4`                            |\n| `DATETIME_HUGE`              | full date and time with weekday and full named offset              | `Friday, October 14, 1983 at 1:30 PM Eastern Daylight Time`    | `vendredi 14 octobre 1983 à 13:30 heure d’été de l’Est`    |\n| `DATETIME_SHORT_WITH_SECONDS`| short date & time with seconds                                     | `10/14/1983, 1:30:23 PM`                                       | `14/10/1983 à 13:30:23`                                    |\n| `DATETIME_MED_WITH_SECONDS`  | abbreviated date & time with seconds                               | `Oct 14, 1983, 1:30:23 PM`                                     | `14 oct. 1983 à 13:30:23`                                  |\n| `DATETIME_FULL_WITH_SECONDS` | full date and time with abbreviated named offset with seconds      | `October 14, 1983 at 1:30:23 PM EDT`                           | `14 octobre 1983 à 13:30:23 UTC−4`                         |\n| `DATETIME_HUGE_WITH_SECONDS` | full date and time with weekday and full named offset with seconds | `Friday, October 14, 1983 at 1:30:23 PM Eastern Daylight Time` | `vendredi 14 octobre 1983 à 13:30:23 heure d’été de l’Est` |\n\n### Intl\n\n`toLocaleString`'s behavior is affected by the DateTime's `locale`, `numberingSystem`, and `outputCalendar` properties. See the [Intl](intl.md) section for more.\n\n## Formatting with tokens (strings for Cthulhu)\n\nThis section covers generating strings from DateTimes with programmer-specified formats.\n\n### Consider alternatives\n\nYou shouldn't create ad-hoc string formats if you can avoid it. If you intend for a computer to read the string, prefer ISO 8601. If a human will read it, prefer `toLocaleString`. Both are covered above. However, if you have some esoteric need where you need some specific format (e.g. because some other software expects it), then `toFormat` is how you do it.\n\n### toFormat\n\nSee `DateTime#toFormat` for the API signature. As a brief motivating example:\n\n```js\nDateTime.fromISO('2014-08-06T13:07:04.054').toFormat('yyyy LLL dd'); //=> '2014 Aug 06'\n```\n\nThe supported tokens are described in the table below.\n\n### Intl\n\nAll of the strings (e.g. month names and weekday names) are internationalized by introspecting strings generated by the Intl API. Thus the exact strings you get are implementation-specific.\n\n```js\nDateTime.fromISO('2014-08-06T13:07:04.054')\n  .setLocale('fr')\n  .toFormat('yyyy LLL dd'); //=> '2014 août 06'\n```\n\nNote `toFormat` defaults to `en-US`. If you need the string to be internationalized, you need to set the locale explicitly like in the example above (or more preferably, use `toLocaleString`).\n\n### Escaping\n\nYou may escape strings using single quotes:\n\n```js\nDateTime.now().toFormat(\"HH 'hours and' mm 'minutes'\"); //=> '20 hours and 55 minutes'\n```\n\nSingle quotes themselves can be escaped by doubling them:\n```js\nDateTime.now().toFormat(\"MMM d ''yy\"); //=> Apr 7 '25\n```\n\n### Standalone vs format tokens\n\nSome tokens have a \"standalone\" and \"format\" version. Some languages require different forms of a word based on whether it is part of a longer phrase or just by itself (e.g. \"Monday the 22nd\" vs \"Monday\"). Use them accordingly.\n\n```js\nvar d = DateTime.fromISO('2014-08-06T13:07:04.054').setLocale('ru');\nd.toFormat('LLLL'); //=> 'август' (standalone)\nd.toFormat('MMMM'); //=> 'августа' (format)\n```\n\n### Macro tokens\n\nSome of the formats are \"macros\", meaning they correspond to multiple components. These use the native Intl API and will order their constituent parts in a locale-friendly way.\n\n```js\nDateTime.fromISO('2014-08-06T13:07:04.054').toFormat('ff'); //=> 'Aug 6, 2014, 1:07 PM'\n```\n\nThe macro options available correspond one-to-one with the preset formats defined for `toLocaleString`.\n\n### Table of tokens\n\n(Examples below given for `2014-08-06T13:07:04.054` considered as a local time in America/New_York).\n\n| Standalone token | Format token | Description                                                    | Example                                                       |\n|------------------| ------------ |----------------------------------------------------------------| ------------------------------------------------------------- |\n| S                |              | millisecond, no padding                                        | `54`                                                          |\n| SSS              |              | millisecond, padded to 3                                       | `054`                                                         |\n| u                |              | fractional seconds, functionally identical to SSS              | `054`                                                         |\n| uu               |              | fractional seconds, between 0 and 99, padded to 2              | `05`                                                          |\n| uuu              |              | fractional seconds, between 0 and 9                            | `0`                                                           |\n| s                |              | second, no padding                                             | `4`                                                           |\n| ss               |              | second, padded to 2 padding                                    | `04`                                                          |\n| m                |              | minute, no padding                                             | `7`                                                           |\n| mm               |              | minute, padded to 2                                            | `07`                                                          |\n| h                |              | hour in 12-hour time, no padding                               | `1`                                                           |\n| hh               |              | hour in 12-hour time, padded to 2                              | `01`                                                          |\n| H                |              | hour in 24-hour time, no padding                               | `9`                                                           |\n| HH               |              | hour in 24-hour time, padded to 2                              | `13`                                                          |\n| Z                |              | narrow offset                                                  | `+5`                                                          |\n| ZZ               |              | short offset                                                   | `+05:00`                                                      |\n| ZZZ              |              | techie offset                                                  | `+0500`                                                       |\n| ZZZZ             |              | abbreviated named offset                                       | `EST`                                                         |\n| ZZZZZ            |              | unabbreviated named offset                                     | `Eastern Standard Time`                                       |\n| z                |              | IANA zone                                                      | `America/New_York`                                            |\n| a                |              | meridiem                                                       | `AM`                                                          |\n| d                |              | day of the month, no padding                                   | `6`                                                           |\n| dd               |              | day of the month, padded to 2                                  | `06`                                                          |\n| c                | E            | day of the week, as number from 1-7 (Monday is 1, Sunday is 7) | `3`                                                           |\n| ccc              | EEE          | day of the week, as an abbreviate localized string             | `Wed`                                                         |\n| cccc             | EEEE         | day of the week, as an unabbreviated localized string          | `Wednesday`                                                   |\n| ccccc            | EEEEE        | day of the week, as a single localized letter                  | `W`                                                           |\n| L                | M            | month as an unpadded number                                    | `8`                                                           |\n| LL               | MM           | month as a padded number                                       | `08`                                                          |\n| LLL              | MMM          | month as an abbreviated localized string                       | `Aug`                                                         |\n| LLLL             | MMMM         | month as an unabbreviated localized string                     | `August`                                                      |\n| LLLLL            | MMMMM        | month as a single localized letter                             | `A`                                                           |\n| y                |              | year, unpadded                                                 | `2014`                                                        |\n| yy               |              | two-digit year                                                 | `14`                                                          |\n| yyyy             |              | four- to six- digit year, pads to 4                            | `2014`                                                        |\n| G                |              | abbreviated localized era                                      | `AD`                                                          |\n| GG               |              | unabbreviated localized era                                    | `Anno Domini`                                                 |\n| GGGGG            |              | one-letter localized era                                       | `A`                                                           |\n| kk               |              | ISO week year, unpadded                                        | `14`                                                          |\n| kkkk             |              | ISO week year, padded to 4                                     | `2014`                                                        |\n| W                |              | ISO week number, unpadded                                      | `32`                                                          |\n| WW               |              | ISO week number, padded to 2                                   | `32`                                                          |\n| ii               |              | Local week year, unpadded                                      | `14`                                                          |\n| iiii             |              | Local week year, padded to 4                                   | `2014`                                                        |\n| n                |              | Local week number, unpadded                                    | `32`                                                          |\n| nn               |              | Local week number, padded to 2                                 | `32`                                                          |\n| o                |              | ordinal (day of year), unpadded                                | `218`                                                         |\n| ooo              |              | ordinal (day of year), padded to 3                             | `218`                                                         |\n| q                |              | quarter, no padding                                            | `3`                                                           |\n| qq               |              | quarter, padded to 2                                           | `03`                                                          |\n| D                |              | localized numeric date                                         | `9/4/2017`                                                    |\n| DD               |              | localized date with abbreviated month                          | `Aug 6, 2014`                                                 |\n| DDD              |              | localized date with full month                                 | `August 6, 2014`                                              |\n| DDDD             |              | localized date with full month and weekday                     | `Wednesday, August 6, 2014`                                   |\n| t                |              | localized time                                                 | `9:07 AM`                                                     |\n| tt               |              | localized time with seconds                                    | `1:07:04 PM`                                                  |\n| ttt              |              | localized time with seconds and abbreviated offset             | `1:07:04 PM EDT`                                              |\n| tttt             |              | localized time with seconds and full offset                    | `1:07:04 PM Eastern Daylight Time`                            |\n| T                |              | localized 24-hour time                                         | `13:07`                                                       |\n| TT               |              | localized 24-hour time with seconds                            | `13:07:04`                                                    |\n| TTT              |              | localized 24-hour time with seconds and abbreviated offset     | `13:07:04 EDT`                                                |\n| TTTT             |              | localized 24-hour time with seconds and full offset            | `13:07:04 Eastern Daylight Time`                              |\n| f                |              | short localized date and time                                  | `8/6/2014, 1:07 PM`                                           |\n| ff               |              | less short localized date and time                             | `Aug 6, 2014, 1:07 PM`                                        |\n| fff              |              | verbose localized date and time                                | `August 6, 2014, 1:07 PM EDT`                                 |\n| ffff             |              | extra verbose localized date and time                          | `Wednesday, August 6, 2014, 1:07 PM Eastern Daylight Time`    |\n| F                |              | short localized date and time with seconds                     | `8/6/2014, 1:07:04 PM`                                        |\n| FF               |              | less short localized date and time with seconds                | `Aug 6, 2014, 1:07:04 PM`                                     |\n| FFF              |              | verbose localized date and time with seconds                   | `August 6, 2014, 1:07:04 PM EDT`                              |\n| FFFF             |              | extra verbose localized date and time with seconds             | `Wednesday, August 6, 2014, 1:07:04 PM Eastern Daylight Time` |\n| X                |              | unix timestamp in seconds                                      | `1407287224`                                                  |\n| x                |              | unix timestamp in milliseconds                                 | `1407287224054`                                               |\n"
  },
  {
    "path": "docs/home.md",
    "content": "## Luxon\n\nLuxon is a library for dealing with dates and times in JavaScript.\n\n```js\nDateTime.now().setZone('America/New_York').minus({weeks:1}).endOf('day').toISO();\n```\n\n### Features\n\n * A nice API for working with datetimes\n * Interval support (from time x to time y)\n * Duration support (14 days, 5 minutes, 33 seconds)\n * [Parsing](parsing.md) and [Formatting](formatting.md) datetimes, intervals, and durations\n * [Internationalization](intl.md) of strings using the Intl API\n * Detailed and unambiguous [math](math.md) operations\n * Built-in handling of [time zones](zones.md)\n * Partial support for multiple [calendar systems](calendars.md)\n \n For more, see the docs on the left, including the  [api docs](api-docs/index.html ':ignore')\n \n### Getting started\n \n  * [Demo](https://moment.github.io/luxon/demo/global.html ':ignore')\n  * Read the [quick tour](tour.md)\n  * Browse the topic docs on the left\n  * Read the [api docs](api-docs/index.html ':ignore')\n\nLogo by [John Dalziel](https://github.com/crashposition)\n"
  },
  {
    "path": "docs/install.md",
    "content": "# Install guide\n\nLuxon provides different builds for different JS environments. See below for a link to the right one and instructions on how to use it. Luxon supports all modern platforms, but see [the support matrix](matrix.md) for additional details.\n\n## Basic browser setup\n\n- [Download full](https://moment.github.io/luxon/global/luxon.js)\n- [Download minified](https://moment.github.io/luxon/global/luxon.min.js)\n\nYou can also load the files from a [CDN](https://www.jsdelivr.com/package/npm/luxon).\n\nJust include Luxon in a script tag. You can access its various classes through the `luxon` global.\n\n```html\n<script src=\"luxon.js\"></script>\n```\n\nYou may wish to alias the classes you use:\n\n```js\nvar DateTime = luxon.DateTime;\n```\n\n## Node.js\n\nSupports Node.js 6+. Install via NPM:\n\n```\nnpm install --save luxon\n```\n\n```js\nconst { DateTime } = require(\"luxon\");\n```\n\nIf you want to work with locales, you need ICU support:\n\n 1. **For Node.js 13+, it comes built-in, no action necessary**\n 2. For older versions of Node.js (only 12 is supported), you need to install it yourself:\n    1. Install a build of Node.js with full ICU baked in, such as via nvm: nvm install <version> -s --with-intl=full-icu --download=all or brew: brew install node --with-full-icu\n    2. Install the ICU data externally and point Node.js to it. The instructions on how to do that are below.\n\nThe instructions for using full-icu as a package are a little confusing. Node.js can't automatically discover that you've installed it, so you need to tell it where to find the data, like this:\n\n```\nnpm install full-icu\nnode --icu-data-dir=./node_modules/full-icu\n```\n\nYou can also point to the data with an environment var, like this:\n\n```\nNODE_ICU_DATA=\"$(pwd)/node_modules/full-icu\" node\n```\n\n## AMD (System.js, RequireJS, etc)\n\n- [Download full](https://moment.github.io/luxon/amd/luxon.js)\n- [Download minified](https://moment.github.io/luxon/amd/luxon.min.js)\n\n```js\nrequirejs([\"luxon\"], function(luxon) {\n  //...\n});\n```\n\n## ES6\n\n- [Download full](https://moment.github.io/luxon/es6/luxon.js)\n- [Download minified](https://moment.github.io/luxon/es6/luxon.min.js)\n\n```js\nimport { DateTime } from \"luxon\";\n```\n\n## Webpack\n\n```\nnpm install --save luxon\n```\n\n```js\nimport { DateTime } from \"luxon\";\n```\n\n## Types\n\nThere are third-party typing files for Flow (via [flow-typed](https://github.com/flowtype/flow-typed)) and TypeScript (via [DefinitelyTyped](https://github.com/DefinitelyTyped/DefinitelyTyped)).\n\nFor Flow, use:\n\n```\nflow-typed install luxon\n```\n\nFor TypeScript, use:\n\n```\nnpm install --save-dev @types/luxon\n```\n\n## React Native\n\nReact Native >=0.70 works just fine out of the box. Older versions of React Native for Android (or if you disable Hermes) doesn't include Intl support by default, which you need for [a lot of Luxon's functionality](matrix.md).\n\nFor React Native >=0.60, you should configure the build flavor of jsc in `android/app/build.gradle`:\n\n```diff\n-def jscFlavor = 'org.webkit:android-jsc:+'\n+def jscFlavor = 'org.webkit:android-jsc-intl:+'\n```\n\nFor even older versions of React Native you can use [jsc-android-buildscripts](https://github.com/SoftwareMansion/jsc-android-buildscripts) to fix it.\n"
  },
  {
    "path": "docs/intl.md",
    "content": "# Intl\n\nLuxon uses the native Intl API to provide easy-to-use internationalization. A quick example:\n\n```js\nDateTime.now()\n  .setLocale(\"el\")\n  .toLocaleString(DateTime.DATE_FULL); //=>  '24 Σεπτεμβρίου 2017'\n```\n\n## Making sure you have access to other locales\n\nPlease see the [install guide](install.md) for instructions on making sure your platform has access to the Intl APIs and the ICU data to power it. This especially important for Node, which doesn't ship with ICU data by default.\n\n## How locales work\n\nLuxon DateTimes can be configured using [BCP 47](https://tools.ietf.org/html/rfc5646) locale strings specifying the language to use generating or interpreting strings. The native Intl API provides the actual internationalized strings; Luxon just wraps it with a nice layer of convenience and integrates the localization functionality into the rest of Luxon. The Mozilla MDN Intl docs have a [good description](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl#Locale_identification_and_negotiation) of how the `locale` argument works. In Luxon, the methods are different but the semantics are the same, except in that Luxon allows you to specify a numbering system and output calendar independently of the locale string.\n\nThe rest of this document will concentrate on what Luxon does when provided with locale information.\n\n## Setting the locale\n\n`locale` is a property of Luxon object. Thus, locale is a sort of setting on the DateTime object, as opposed to an argument you provide the different methods that need internationalized.\n\nYou can generally set it at construction time:\n\n```js\nvar dt = DateTime.fromISO(\"2017-09-24\", { locale: \"fr\" });\ndt.locale; //=> 'fr'\n```\n\nIn this case, the specified locale didn't change the how the parsing worked (there's nothing localized about it), but it did set the locale property in the resulting instance. For other factory methods, such as `fromFormat`, the locale argument _does_ affect how the string is parsed. See further down for more.\n\nYou can change the locale of a DateTime instance (meaning, create a clone DateTime with a different locale) using `setLocale`:\n\n```js\nDateTime.now().setLocale(\"fr\").locale; //=> 'fr'\n```\n\n`setLocale` is just a convenience for `reconfigure`:\n\n```js\nDateTime.now().reconfigure({ locale: \"fr\" }).locale; //=> 'fr'\n```\n\n## Default locale\n\n### Out-of-the-box behavior\n\nBy default, the `locale` property of a new DateTime or Duration is the system locale. On a browser, that means whatever the user has their browser or OS language set to. On Node, that usually means en-US.\n\nAs a result, `DateTime#toLocaleString`, `DateTime#toLocaleParts`, and other human-readable-string methods like `Info.months` will by default generate strings in the user's locale.\n\nHowever, note that `DateTime.fromFormat` and `DateTime#toFormat` fall back on en-US. That's because these methods are often used to parse or format strings for consumption by APIs that don't care about the user's locale. So we need to pick a locale and stick with it, or the code will break depending on whose browser it runs in. There's an exception, though: `DateTime#toFormat` can take \"macro\" formats like \"D\" that produces localized strings as part of a larger string. These *do* default to the system locale because their entire purpose is to be provide localized strings.\n\n### Setting the default\n\nYou can set a default locale so that new instances will always be created with the specified locale:\n\n```js\nSettings.defaultLocale = \"fr\";\nDateTime.now().locale; //=> 'fr'\n```\n\nNote that this also alters the behavior of `DateTime#toFormat` and `DateTime#fromFormat`.\n\n### Using the system locale in string parsing\n\nYou generally don't want `DateTime#fromFormat` and `DateTime#toFormat` to use the system's locale, since your format won't be sensitive to the locale's string ordering. That's why Luxon doesn't behave that way by default. But if you really want that behavior, you can always do this:\n\n```js\nSettings.defaultLocale = DateTime.now().resolvedLocaleOptions().locale;\n```\n\n## Checking what you got\n\nThe local environment may not support the exact locale you asked for. The native Intl API will try to find the best match. If you want to know what that match was, use `resolvedLocaleOpts`:\n\n```js\nDateTime.local({ locale: \"fr-co\" }).resolvedLocaleOptions(); //=> { locale: 'fr',\n//     numberingSystem: 'latn',\n//     outputCalendar: 'gregory' }\n```\n\n## Methods affected by the locale\n\n### Formatting\n\nThe most important method affected by the locale setting is `toLocaleString`, which allows you to produce internationalized, human-readable strings.\n\n```js\ndt.setLocale(\"fr\").toLocaleString(DateTime.DATE_FULL); //=> '25 septembre 2017'\n```\n\nThat's the normal way to do it: set the locale as property of the DateTime itself and let the `toLocaleString` inherit it. But you can specify the locale directly to `toLocaleString` too:\n\n```js\ndt.toLocaleString({ locale: \"es\" , ...DateTime.DATE_FULL }); //=> '25 de septiembre de 2017'\n```\n\nAd-hoc formatting also respects the locale:\n\n```js\ndt.setLocale(\"fr\").toFormat(\"MMMM dd, yyyy GG\"); //=> 'septembre 25, 2017 après Jésus-Christ'\n```\n\n### Parsing\n\nYou can [parse](parsing.md) localized strings:\n\n```js\nDateTime.fromFormat(\"septembre 25, 2017 après Jésus-Christ\", \"MMMM dd, yyyy GG\", { locale: \"fr\" });\n```\n\n### Listing\n\nSome of the methods in the `Info` class let you list strings like months, weekdays, and eras, and they can be localized:\n\n```js\nInfo.months(\"long\", { locale: \"fr\" }); //=> [ 'janvier', 'février', ...\nInfo.weekdays(\"long\", { locale: \"fr\" }); //=> [ 'lundi', 'mardi', ...\nInfo.eras(\"long\", { locale: \"fr\" }); //=> [ 'avant Jésus-Christ', 'après Jésus-Christ' ]\n```\n\n## numberingSystem\n\nDateTimes also have a `numberingSystem` setting that lets you control what system of numerals is used in formatting. In general, you shouldn't override the numbering system provided by the locale. For example, no extra work is needed to get Arabic numbers to show up in Arabic-speaking locales:\n\n```js\nvar dt = DateTime.now().setLocale(\"ar\");\n\ndt.resolvedLocaleOptions(); //=> { locale: 'ar',\n//     numberingSystem: 'arab',\n//     outputCalendar: 'gregory' }\n\ndt.toLocaleString(); //=> '٢٤‏/٩‏/٢٠١٧'\n```\n\nFor this reason, Luxon defaults its own `numberingSystem` property to null, by which it means \"let the Intl API decide\". However, you can override it if you want. This example is admittedly ridiculous:\n\n```js\nconst dt = DateTime.local().reconfigure({ locale: \"it\", numberingSystem: \"beng\" });\ndt.toLocaleString(DateTime.DATE_FULL); //=> '২৪ settembre ২০১৭'\n```\n\nSimilar to `locale`, you can set the default numbering system for new instances:\n\n```js\nSettings.defaultNumberingSystem = \"beng\";\n```\n\n## Locale-based weeks\n\nMost of Luxon uses the [ISO week date](https://en.wikipedia.org/wiki/ISO_week_date) system when working with week-related data.\nThis means that the week starts on Monday and the first week of the year is that week, which has 4 or more of its days in January.\n\nThis definition works for most use-cases, however locales can define different rules. For example, in many English-speaking countries\nthe week is said to start on Sunday and the 1 January always defines the first week of the year. This information is\navailable through the Luxon as well.\n\nNote that your runtime needs to support [`Intl.Locale#getWeekInfo`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/getWeekInfo) for this to have an effect. If unsupported, Luxon will fall back\nto using the ISO week dates.\n\n### Accessing locale-based week info\n\nThe `Info` class exposes methods `getStartOfWeek`, `getMinimumDaysInFirstWeek` and `getWeekendWeekdays` for informational\npurposes.\n\n### Accessing locale-based week data\n\n```js\nconst dt = DateTime.local(2022, 1, 1, { locale: \"en-US\" });\ndt.localWeekday // 7, because Saturday is the 7th day of the week in en-US\ndt.localWeekNumber // 1, because 1 January is always in the first week in en-US\ndt.localWeekYear // 2022, because 1 January is always in the first week in en-US\ndt.weeksInLocalWeekYear // 53, because 2022 has 53 weeks in en-US\n```\n\n### Using locale-based week data when creating DateTimes\n\nWhen creating a DateTime instance using `fromObject`, you can use the `localWeekday`, `localWeekNumber` and `localWeekYear`\nproperties. They will use the same locale that the newly created DateTime will use, either explicitly provided or falling back\nto the system default.\n\n```js\nconst dt = DateTime.fromObject({localWeekYear: 2022, localWeekNumber: 53, localWeekday: 7});\ndt.toISODate(); // 2022-12-31\n```\n\n### Setting locale-based week data\n\nWhen modifying an existing DateTime instance using `set`, you can use the `localWeekday`, `localWeekNumber` and `localWeekYear`\nproperties. They will use the locale of the existing DateTime as reference.\n\n```js\nconst dt = DateTime.local(2022, 1, 2, { locale: \"en-US\" });\nconst modified = dt.set({localWeekNumber: 2});\nmodified.toISODate(); // 2022-01-08\n```\n\n### Locale-based weeks with math\n\nThe methods `startOf`, `endOf`, `hasSame` of `DateTime` as well as `count` of `Interval` accept an option `useLocaleWeeks`. \nIf enabled, the methods will treat the `week` unit according to the locale, respecting the correct start of the week.\n\n```js\nconst dt = DateTime.local(2022, 1, 6, { locale: \"en-US\" });\nconst startOfWeek = dt.startOf('week', {useLocaleWeeks: true});\nstartOfWeek.toISODate(); // 2022-01-02, a Sunday\n```\n\n### Overriding defaults\n\nYou can override the runtime-provided defaults for the week settings using `Settings.defaultWeekSettings`:\n\n```js\nSettings.defaultWeekSettings = { firstDay: 7, minimalDays: 1, weekend: [6, 7] }\n```"
  },
  {
    "path": "docs/math.md",
    "content": "# Math\n\nThis page covers some oddball topics related to date and time math, which has some quirky corner cases.\n\n## Calendar math vs time math\n\n### The basics\n\nMath with dates and times can be unintuitive to programmers. If it's Feb 13, 2017 and I say \"in exactly one month\", you know I mean March 13. Exactly one month after that is April 13. But because February is a shorter month than March, that means we added a different amount of time in each case. On the other hand, if I said \"30 days from February 13\", you'd try to figure out what day that landed on in March. Here it is in Luxon:\n\n```js\nDateTime.local(2017, 2, 13).plus({ months: 1 }).toISODate() //=> '2017-03-13'\n\nDateTime.local(2017, 2, 13).plus({ days: 30 }).toISODate() //=> '2017-03-15'\n```\n\nMore generally we can differentiate two modes of math:\n\n * Calendar math works with higher-order, variable-length units like years and months\n * Time math works with lower-order, constant-length units such as hours, minutes, and seconds.\n\n### Which units use which math?\n\nThese units use calendar math:\n\n * **Years** vary because of leap years.\n * **Months** vary because they're just different lengths.\n * **Days** vary because DST transitions mean some days are 23 or 25 hours long.\n * **Quarters** are always three months, but months vary in length so quarters do too.\n * **Weeks** are always the same number of days, but days vary so weeks do too.\n \nThese units use time math:\n\n * **Hours** are always 60 minutes\n * **Minutes** are always 60 seconds\n * **Seconds** are always 1000 milliseconds\n\n### Leap seconds\n\nLuxon ([as JavaScript in general](https://262.ecma-international.org/5.1/#sec-15.9.1.1)) doesn't account for [leap seconds](https://en.wikipedia.org/wiki/Leap_second); as in most programming environments, leap seconds happen as invisible changes to the underlying system's time. This can, in rare cases, cause the same second to \"occur\" twice from Luxon's perspective.\n\nThe practical effect of leaps seconds are quite limited:\n\n1. You can't represent the leap second itself (i.e., `DateTime.utc(2016, 12, 31, 23, 59, 60).isValid` returns `false`).\n2. A Luxon `diff()` calculation that crosses a leap second will not exactly match the number of seconds that passed in the outside world. This can come up in rare situations where it matters to your application exactly what happened in the last *n* seconds. Even this is increasingly mitigated by [leap smear](https://developers.google.com/time/smear).\n\n### How to think about calendar math\n\nIt's best not to think of calendar math as requiring arcane checks on the lengths of intervening periods. Instead, think of them as **adjusting that unit directly and keeping lower order date components constant**. Let's go back to the Feb 13 + 1 month example. If you didn't have Luxon, you would do something like this to accomplish that:\n\n```js\nvar d = new Date('2017-02-13')\nd.setMonth(d.getMonth() + 1)\nd.toLocaleString() //=> '3/13/2017, 12:00:00 AM'\n```\n\nAnd under the covers, that's more or less what Luxon does too. It doesn't boil the operation down to a milliseconds delta because that's not what's being asked. Instead, it fiddles with what it thinks the date should be and then uses the built-in Gregorian calendar to compute the new timestamp.\n\n### DSTs\n\nThere's a whole section about this in the [time zones documentation](zones.md?id=math-across-dsts). But here's a quick example (Spring Forward is early on March 12 in my time zone):\n\n```js\nvar start = DateTime.local(2017, 3, 11, 10);\nstart.hour                          //=> 10, just for comparison\nstart.plus({days: 1}).hour          //=> 10, stayed the same\nstart.plus({hours: 24}).hour        //=> 11, DST pushed forward an hour\n```\n\nSo in adding a day, we kept the hour at 10, even though that's only 23 hours later.\n\n### Time math\n\nTime math is different. In time math, we're just adjusting the clock, adding or subtracting from the epoch timestamp. Adding 63 hours is really the same as adding 63 hours' worth of milliseconds. Under the covers, Luxon does this exactly the opposite of how it does calendar math; it boils the operation down to milliseconds, computes the new timestamp, and then computes the date out of that.\n\n## Math with multiple units\n\nIt's possible to do math with multiple units:\n\n```js\nDateTime.fromISO('2017-05-15').plus({months: 2, days: 6}).toISODate(); //=> '2017-07-21'\n```\n\nThis isn't as simple as it looks. For example, what should you expect this to do?\n\n```js\nDateTime.fromISO('2017-04-30').plus({months: 1, days: 1}).toISODate();\n```\n\nIf the day is added first, we'll get an intermediate value of May 1. Adding a month to that gives us June 1. But if the month is added first, we'll an intermediate value of May 30 and day after that is May 31. (See \"Calendar math vs time math\" above if this is confusing.) So the order matters.\n\nLuxon has a simple rule for this: **math is done from highest order to lowest order**. So the result of the example above is May 31. This rule isn't logically necessary, but it does seem to reflect what people mean. Of course, Luxon can't enforce this rule if you do the math in separate operations:\n\n```js\nDateTime.fromISO('2017-04-30').plus({days: 1}).plus({months: 1}).toISODate() //=> '2017-06-01'\n```\n\nIt's not a coincidence that Luxon's interface makes it awkward to do this wrong.\n\n## Comparing DateTimes\n\nDateTime implements `#valueOf` to return the epoch timestamp, so you can compare DateTimes with `<`, `>`, `<=`, and `>=`. That lets you find out if one DateTime is after or before another DateTime.\n\n```js\nd1 < d2 // is d1 before d2?\n```\n\nHowever, be aware that `===` compares object identity, which is not a useful concept in a library with immutable types. Use `#equals` to compare both the time and additional metadata, such as the locale and time zone. If you're only interested in checking the equality of the timestamps, you can use:\n\n```js\nd1.toMillis() === d2.toMillis() // are d1 and d2 the same instant in time?\n+d1 === +d2 // same test, using object coercion\n```\n\nYou may also use `#hasSame` to make more subtle comparisons:\n\n```js\nd1.hasSame(d2, 'year');   // both DateTimes have the same calendar year\nd1.hasSame(d2, 'day');    // both DateTimes have the same calendar day (which implies they also have the same calendar year and month)\n```\n\nNote that these are checking against the calendar. For example, if `d1` is in 2017, calling `hasSame` with \"year\" asks if d2 is also in 2017, not whether the DateTimes within a year of each other. For that, you'd need `diff` (see below).\n\nIf you'd like to compare using a specific unit, you can achieve this by combining `#startOf` and the `#valueOf` comparisons above.\n\n```js\nvar d1 = DateTime.fromISO('2017-04-30');\nvar d2 = DateTime.fromISO('2017-04-01');\n\nd2 < d1                                   //=> true\nd2.startOf('year') < d1.startOf('year')   //=> false\nd2.startOf('month') < d1.startOf('month') //=> false\nd2.startOf('day') < d1.startOf('day')     //=> true\n```\n\n## Duration math\n\n### Basics\n\n`Durations` are quantities of time, like \"3 days and 6 hours\". Luxon has no idea *which* 3 days and 6 hours they represent; it's just how Luxon represents those quantities in abstract, unmoored from the timeline. This is both tremendously useful and occasionally confusing. I'm not going to give a detailed tour of their capabilities here (see the API docs for that), but I do want to clear up some of those confusions.\n\nHere's some very basic stuff to get us going:\n\n```js\nvar dur = Duration.fromObject({ days: 3, hours: 6})\n\n// examine it\ndur.toObject()          //=> { days: 3, hours: 6 }\n\n// express in minutes\ndur.as('minutes')       //=> 4680\n\n// convert to minutes\ndur.shiftTo('minutes').toObject() //=> { minutes: 4680 }\n\n// add to a DateTime\nDateTime.fromISO(\"2017-05-15\").plus(dur).toISO() //=> '2017-05-18T06:00:00.000-04:00'\n```\n\n### Diffs\n\nYou can subtract one time from another to find out how much time there is between them. Luxon's `DateTime.diff` method does this and it returns a Duration. For example:\n\n```js\nvar end = DateTime.fromISO('2017-03-13');\nvar start = DateTime.fromISO('2017-02-13');\n\nvar diffInMonths = end.diff(start, 'months');\ndiffInMonths.toObject(); //=> { months: 1 }\n```\n\nNotice we had to pick the unit to keep track of the diff in. The default is milliseconds:\n\n```js\nvar diff = end.diff(start);\ndiff.toObject() //=> { milliseconds: 2415600000 }\n```\n\nFinally, you can diff using multiple units:\n\n```js\nvar end = DateTime.fromISO('2017-03-13');\nvar start = DateTime.fromISO('2017-02-11');\nend.diff(start, ['months', 'days']).toObject() //=> { months: 1, days: 2 }\n```\n\n### Casual vs longterm conversion accuracy\n\nDurations represent bundles of time with specific units, but Luxon allows you to convert between them:\n\n * `shiftTo` returns a new Duration denominated in the specified units.\n * `as` converts the duration to just that unit and returns its value\n\n```js\nvar dur = Duration.fromObject({ months: 4, weeks: 2, days: 6 })\n\ndur.as('days')                            //=> 140\ndur.shiftTo('days').toObject()            //=> { days: 140 }\ndur.shiftTo('weeks', 'hours').toObject()  //=> { weeks: 18, hours: 144 }\n```\n\nBut how do those conversions actually work? First, uncontroversially:\n\n * 1 week = 7 days\n * 1 day = 24 hours\n * 1 hour = 60 minutes\n * 1 minute = 60 seconds\n * 1 second = 1000 milliseconds\n \nThese are always true and you can roll them up and down with consistency (e.g. `1 hour = 60 * 60 * 1000 milliseconds`). However, this isn't really true for the higher order units, which vary in length, even putting DSTs aside. A year is sometimes 365 days long and sometimes 366. Months are 28, 29, 30, or 31 days. By default Luxon converts between these units using what you might call \"casual\" conversions:\n\n|         | Month | Week | Day |\n| ---     | ---   |  --- | --- |\n| Year    | 12    |   52 | 365 |\n| Quarter | 3     |   13 |  91 |\n| Month   |       |    4 |  30 |\n\nThese should match your intuition and for most purposes they work well. But they're not just wrong; they're not even self-consistent:\n\n```js\nDuration.fromObject({ years:1 }).shiftTo('months').shiftTo('days').as('years') //=> 0.9863013698630136\n```\n\nThis is because 12 * 30 != 365. These errors can be annoying, but they can also cause significant issues if the errors accumulate:\n\n```js\nvar dur = Duration.fromObject({ years: 50000 });\nDateTime.now().plus(dur.shiftTo('milliseconds')).year //=> 51984\nDateTime.now().plus(dur).year                         //=> 52017\n```\n\nThose are 33 years apart! So Luxon offers an alternative conversion scheme called \"longterm\", based on the 400-year calendar cycle:\n\n|         | Month |     Week |       Day |\n|----     | ---   |      --- |       --- |\n| Year    | 12    |  52.1775 |  365.2425 |\n| Quarter |  3    | 13.04435 | 91.310625 |\n| Month   |       | 4.348125 | 30.436875 |\n\nYou can see why these are irritating to work with, which is why they're not the default.\n\nLuxon methods that create Durations de novo accept an option called `conversionAccuracy`. You can set it to \"casual\" or \"longterm\". It's a property of the Duration itself, so any conversions you do use the rule you've picked, and any new Durations you derive from it will retain that property.\n\n```js\nDuration.fromObject({ years: 23 }, { conversionAccuracy: 'longterm' });\nDuration.fromISO('PY23', { conversionAccuracy: 'longterm' });\n\nend.diff(start, 'days', { conversionAccuracy: 'longterm' })\n```\n\nYou can also create an accurate Duration out of an existing one:\n\n```js\nvar pedanticDuration = casualDuration.reconfigure({ conversionAccuracy: 'longterm' });\n```\n\nThese Durations will do their conversions differently.\n\n\n### Losing information\n\nBe careful of converting between units. It's easy to lose information. Let's say we converted a diff into days:\n\n\n```js\nvar end = DateTime.fromISO('2017-03-13');\nvar start = DateTime.fromISO('2017-02-13');\n\nvar diffInMonths = end.diff(start, 'months');\ndiffInMonths.as('days'); //=> 30\n```\n\nThat's our conversion between months and days (you could also do a longterm-accurate conversion; it wouldn't fix the issue ahead). But this isn't the number of days between February 13 and March 13!\n\n```js\nvar diffInDays = end.diff(start, 'days');\ndiffInDays.toObject(); //=> { days: 28 }\n```\n\nIt's important to remember that diffs are Duration objects, and a Duration is just a dumb pile of time units our computation spat out. Unlike an Interval, a Duration doesn't \"remember\" what the inputs to the diff were. So we lost some information converting between units. This mistake is really common when rolling up:\n\n\n```js\nvar diff = end.diff(start); // default unit is milliseconds\n\n// wtf, that's not a month!\ndiff.as('months'); //=> 0.9319444 \n\n// it's not even the right number of days! (hint: my time zone has a DST)\ndiff.shiftTo('hours').as('days'); //=> 27.958333333333332\n```\n\nNormally you won't run into this problem if you think clearly about what you want to do with a diff. Specifically, make sure you diff in the units you actually want to use. Then Luxon knows to answer the question you really want to ask.\n\n```js\nvar monthsDiff = end.diff(start, \"months\");\nvar daysDiff = end.diff(start, \"days\");\n```\n\nBut sometimes you really do want an object that represents the subtraction itself, not the result. `Intervals` can help. Intervals are mostly used to keep track of ranges of time, but they make for \"anchored\" diffs too. For example:\n\n```js\nvar end = DateTime.fromISO('2017-03-13');\nvar start = DateTime.fromISO('2017-02-13');\nvar i = Interval.fromDateTimes(start, end);\n\ni.length('days');       //=> 28\ni.length('months')      //=> 1\n```\n\nBecause the Interval stores its endpoints and computes `length` on the fly, it retakes the diff each time you query it. Of course, precisely because an Interval *isn't* an abstract bundle of time, it can't be used in places where Durations can. For example, you can't add them to DateTime via `plus()` because Luxon wouldn't know what units to do the math in (see \"Calendar vs time math\" above). But you can convert the interval into a Duration by picking the units:\n\n```js\ni.toDuration('months').toObject(); //=> { months: 1 }\ni.toDuration('days').toObject(); //=> { days: 28 }\n```\n\nYou can even pick multiple units:\n\n```js\nend = DateTime.fromISO('2018-05-25');\ni = start.until(end);\ni.toDuration(['years', 'months', 'days']).toObject(); //=> { years: 1, months: 3, days: 12 }\n```\n\nOf course, once you've converted to a Duration, you're back in the same spot you were with the diff case; *further* conversions will be lossy. So the point is to think carefully about what information you have when.\n"
  },
  {
    "path": "docs/matrix.md",
    "content": "# Support matrix\n\nThis page covers what platforms are supported by Luxon and what caveats apply to them.\n\n## Official support\n\nLuxon officially supports the last two versions of the major browsers, with some caveats. The table below shows which of the not-universally-supported features are available in what environments.\n\n| Browser                          | Versions | Intl relative time formatting |\n| -------------------------------- | -------- | ----------------------------- |\n| Chrome                           | >= 73    | ✓                             |\n| Firefox                          | >= 65    | ✓                             |\n| Edge                             | >= 79    | ✓                             |\n|                                  | 18       | ✗                             |\n| Safari                           | >= 14    | ✓                             |\n|                                  | 13       | ✗                             |\n| iOS Safari (iOS version numbers) | >= 14    | ✓                             |\n| Node                             | >= 12    | ✓                             |\n\n- Those capabilities are explained in the next sections, along with possible polyfill options\n- \"w/ICU\" refers to providing Node with ICU data. See the [install](install.md?id=node) for instructions\n\n## Effects of missing features\n\n**If the platforms you're targeting has all its boxes above check off, ignore this section**.\n\nIn the support table above, you can see that some environments are missing capabilities.  In the current version of\nLuxon, there's only one partially-supported feature, so this is currently pretty simple. (Older versions of Luxon supported\nolder browsers, so there were nuanced feature caveats. Newer versions will add more caveats as new browser capabilities\nbecome available and Luxon takes advantage of them if they're present.)\n\n1.  **Relative time formatting**. Luxon's support for relative time formatting (e.g. `DateTime#toRelative` and `DateTime#toRelativeCalendar`) depends on Intl.RelativeTimeFormat. Luxon will fall back to using English if that capability is missing.\n\nIf the browser lacks these capabilities, Luxon tries its best:\n\n| Feature                                | Full support |  No relative time format |\n| -------------------------------------- | ------------ |  ----------------------- |\n| Most things                            | OK           |  OK                      |\n| `DateTime#toRelative` in en-US         | OK           |  OK                      |\n| `DateTime#toRelative` in other locales | Uses English |  Uses English            |\n\n\n## Older platforms\n\n- **Older versions of both Chrome and Firefox** will most likely work. It's just that I only officially support the last two versions. As you get to older versions of these browsers, the missing capabilities listed above begin to apply to them. (e.g. FF started supporting `formatToParts` in 51 and time zones in 52). I haven't broken that out because it's complicated, Luxon doesn't officially support them, and no one runs them anyway.\n- **Older versions of IE** probably won't work at all.\n- **Older versions of Node** probably won't work without recompiling Luxon with a different Node target. In which case they'll work with some features missing.\n\n## Other platforms\n\nIf the platform you're targeting isn't on the list and you're unsure what caveats apply, you can check which pieces are supported:\n\n```js\nInfo.features(); //=> { relative: false }\n```\n\nSpecific notes on other platforms:\n\n- **React Native <0.70 on (specifically) Android** doesn't include Intl support by default, so all the possible-to-be-missing capabilities above are unavailable. To fix this on React Native >=0.60, you should configure the build flavor of jsc in `android/app/build.gradle`:\n\n```diff\n-def jscFlavor = 'org.webkit:android-jsc:+'\n+def jscFlavor = 'org.webkit:android-jsc-intl:+'\n```\n\nFor even older versions of React Native you can use [jsc-android-buildscripts](https://github.com/SoftwareMansion/jsc-android-buildscripts) to fix it.\n"
  },
  {
    "path": "docs/moment.md",
    "content": "# For Moment users\n\nLuxon borrows lots of ideas from [Moment.js](https://momentjs.com), but there are a lot of differences too. This document clarifies what they are.\n\n## Immutability\n\nLuxon's objects are immutable, whereas Moment's are mutable. For example, in Moment:\n\n```js\nvar m1 = moment();\nvar m2 = m1.add(1, 'hours');\nm1.valueOf() === m2.valueOf(); //=> true\n```\n\nThis happens because `m1` and `m2` are really the same object; `add()` *mutated* the object to be an hour later. Compare that to Luxon:\n\n```js\nvar d1 = DateTime.now();\nvar d2 = d1.plus({ hours: 1 });\nd1.valueOf() === d2.valueOf(); //=> false\n```\n\nThis happens because the `plus` method returns a new instance, leaving `d1` unmodified. It also means that Luxon doesn't require copy constructors or clone methods.\n\n## Major functional differences\n\n1. Months in Luxon are 1-indexed instead of 0-indexed like in Moment and the native Date type.\n1. Localizations and time zones are implemented by the native Intl API (or a polyfill of it), instead of by the library itself.\n1. Luxon has both a Duration type and an Interval type. The Interval type is like Twix.\n\n## Other API style differences\n\n1. Luxon methods often take option objects as their last parameter\n1. Luxon has different static methods for object creation (e.g. `fromISO`), as opposed to Moment's one function that dispatches based on the input\n1. Luxon parsers are very strict, whereas Moment's are more lenient.\n1. Luxon uses getters instead of accessor methods, so `dateTime.year` instead of `dateTime.year()`\n1. Luxon centralizes its \"setters\", like `dateTime.set({year: 2016, month: 4})` instead of `dateTime.year(2016).month(4)` like in Moment.\n1. Luxon's Durations are a separate top-level class.\n1. Arguments to Luxon's methods are not automatically coerced into Luxon instances. E.g. `m.diff('2017-04-01')` would be `dt.diff(DateTime.fromISO('2017-04-01'))`.\n\n## DateTime method equivalence\n\nHere's a rough mapping of DateTime methods in Moment to ones in Luxon. I haven't comprehensively documented stuff that's in Luxon but not in Moment, just a few odds and ends that seemed obvious for inclusion; there are more. I've probably missed a few things too.\n\n### Creation\n\n| Operation               | Moment                   | Luxon                                 | Notes                                                                                                                                 |\n| ----------------------- | ------------------------ | ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |\n| Now                     | `moment()`               | `DateTime.now()`                      |                                                                                                                                       |\n| From ISO                | `moment(String)`         | `DateTime.fromISO(String)`            |                                                                                                                                       |\n| From RFC 2822           | `moment(String)`         | `DateTime.fromRFC2822(String)`        |                                                                                                                                       |\n| From custom format      | `moment(String, String)` | `DateTime.fromFormat(String, String)` | The format tokens differ between Moment and Luxon, such that the same format string cannot be used between the two.                   |\n| From object             | `moment(Object)`         | `DateTime.fromObject(Object)`         |                                                                                                                                       |\n| From timestamp          | `moment(Number)`         | `DateTime.fromMillis(Number)`         |                                                                                                                                       |\n| From JS Date            | `moment(Date)`           | `DateTime.fromJSDate(Date)`           |                                                                                                                                       |\n| From civil time         | `moment(Array)`          | `DateTime.local(Number...)`           | Like `DateTime.local(2016, 12, 25, 10, 30)`                                                                                           |\n| From UTC civil time     | `moment.utc(Array)`      | `DateTime.utc(Number...)`             | Moment also uses `moment.utc()` to take other arguments. In Luxon, use the appropriate method and pass in the `{ zone: 'utc'}` option |\n| Clone                   | `moment(Moment)`         | N/A                                   | Immutability makes this pointless; just reuse the object                                                                              |\n| Use the string's offset | `parseZone`              | See note                              | Methods taking strings that can specify offset or zone take a `setZone` argument                                                      |\n\n### Getters and setters\n\n#### Basic information getters\n\n| Property | Moment      | Luxon     | Notes                                            |\n| -------- | ----------- | --------- | ------------------------------------------------ |\n| Validity | `isValid()` | `isValid` | See also `invalidReason`                         |\n| Locale   | `locale()`  | `locale`  |                                                  |\n| Zone     | `tz()`      | `zone`    | Moment requires a plugin for this, but not Luxon |\n\n#### Unit getters\n\n| Property               | Moment                               | Luxon         | Notes                                  |\n| ---------------------- | ------------------------------------ | ------------- | -------------------------------------- |\n| Year                   | `year()`                             | `year`        |                                        |\n| Month                  | `month()`                            | `month`       |                                        |\n| Day of month           | `date()`                             | `day`         |                                        |\n| Day of week            | `day()`, `weekday()`, `isoWeekday()` | `weekday`     | 1-7, Monday is 1, Sunday is 7, per ISO |\n| Day of year            | `dayOfYear()`                        | `ordinal`     |                                        |\n| Hour of day            | `hour()`                             | `hour`        |                                        |\n| Minute of hour         | `minute()`                           | `minute`      |                                        |\n| Second of minute       | `second()`                           | `second`      |                                        |\n| Millisecond of seconds | `millisecond()`                      | `millisecond` |                                        |\n| Week of ISO week year  | `weekYear`, `isoWeekYear`            | `weekYear`    |                                        |\n| Quarter                | `quarter`                            | None          | Just divide the months by 3            |\n\n#### Programmatic get and set\n\nFor programmatic getting and setting, Luxon and Moment are very similar here:\n\n| Operation  | Moment                | Luxon         | Notes                                   |\n| ---------- | --------------------- | ------------- | --------------------------------------- |\n| get value  | `get(String)`         | `get(String)` |                                         |\n| set value  | `set(String, Number)` | None          |                                         |\n| set values | `set(Object)`         | `set(Object)` | Like `dt.set({ year: 2016, month: 3 })` |\n\n### Transformation\n\n| Operation          | Moment                     | Luxon               | Notes                                   |\n| ------------------ | -------------------------- | ------------------- | --------------------------------------- |\n| Addition           | `add(Number, String)`      | `plus(Object)`      | Like `dt.plus({ months: 3, days: 2 })`  |\n| Subtraction        | `subtract(Number, String)` | `minus(Object)`     | Like `dt.minus({ months: 3, days: 2 })` |\n| Start of unit      | `startOf(String)`          | `startOf(String)`   |                                         |\n| End of unit        | `endOf(String)`            | `endOf(String)`     |                                         |\n| Change unit values | `set(Object)`              | `set(Object)`       | Like `dt.set({ year: 2016, month: 3 })` |\n| Change time zone   | `tz(String)`               | `setZone(string)`   | Luxon doesn't require a plugin          |\n| Change zone to utc | `utc()`                    | `toUTC()`           |                                         |\n| Change local zone  | `local()`                  | `toLocal()`         |                                         |\n| Change offset      | `utcOffset(Number)`        | None                | Set the zone instead                    |\n| Change locale      | `locale(String)`           | `setLocale(String)` |                                         |\n\n### Query\n\n| Question                                   | Moment                  | Luxon                                            | Notes                                                                                           |\n| ------------------------------------------ | ----------------------- | ------------------------------------------------ | ----------------------------------------------------------------------------------------------- |\n| Is this time before that time?             | `m1.isBefore(m2)`       | `dt1 < dt2`                                      | The Moment versions of these take a unit. To do that in Luxon, use `startOf` on both instances. |\n| Is this time after that time?              | `m1.isAfter(m2)`        | `dt1 > dt2`                                      |                                                                                                 |\n| Is this time the same or before that time? | `m1.isSameOrBefore(m2)` | `dt1 <= dt2`                                     |                                                                                                 |\n| Is this time the same or after that time?  | `m1.isSameOrAfter(m2)`  | `dt1 >= dt2`                                     |                                                                                                 |\n| Do these two times have the same [unit]?   | `m1.isSame(m2, unit)`   | `dt1.hasSame(dt2, unit)`                         |                                                                                                 |\n| Is this time's [unit] before that time's?  | `m1.isBefore(m2, unit)` | `dt1.startOf(unit) < dt2.startOf(unit)`          |                                                                                                 |\n| Is this time's [unit] after that time's?   | `m1.isAfter(m2, unit)`  | `dt1.startOf(unit) > dt2.startOf(unit)`          |                                                                                                 |\n| Is this time between these two times?      | `m1.isBetween(m2, m3)`  | `Interval.fromDateTimes(dt2, dt3).contains(dt1)` |                                                                                                 |\n| Is this time inside a DST                  | `isDST()`               | `isInDST`                                        |                                                                                                 |\n| Is this time's year a leap year?           | `isInLeapYear()`        | `isInLeapYear`                                   |                                                                                                 |\n| How many days are in this time's month?    | `daysInMonth()`         | `daysInMonth`                                    |                                                                                                 |\n| How many days are in this time's year?     | None                    | `daysInYear`                                     |                                                                                                 |\n\n### Output\n\n#### Basics\n\nSee the [formatting guide](formatting.md) for more about the string-outputting methods.\n\n| Output           | Moment         | Luxon                       | Notes                                                                                                                                             |\n| ---------------- | -------------- | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |\n| simple string    | `toString()`   | `toString()`                | Luxon just uses ISO 8601 for this. See Luxon's `toLocaleString()`                                                                                 |\n| full ISO 8601    | `iso()`        | `toISO()`                   |                                                                                                                                                   |\n| ISO date only    | None           | `toISODate()`               |                                                                                                                                                   |\n| ISO time only    | None           | `toISOTime()`               |                                                                                                                                                   |\n| custom format    | `format(...)`  | `toFormat(...)`             | The format tokens differ between Moment and Luxon, such that the same format string cannot be used between the two. Note `toFormat` is meant to be used for machine-readable formats. For anything human-readable, you really want to use `toLocaleString()` instead. |\n| RFC 2822         |                | `toRFC2822()`               |                                                                                                                                                   |\n| HTTP date string |                | `toHTTP()`                  |                                                                                                                                                   |\n| JS Date          | `toDate()`     | `toJSDate()`                |                                                                                                                                                   |\n| Epoch time       | `valueOf()`    | `toMillis()` or `valueOf()` |                                                                                                                                                   |\n| Object           | `toObject()`   | `toObject()`                |                                                                                                                                                   |\n| Duration         | `diff(Moment)` | `diff(DateTime)`            | Moment's diff returns a count of milliseconds, but Luxon's returns a Duration. To replicate the Moment behavior, use `dt1.diff(d2).milliseconds`. |\n\n#### Humanization\n\nLuxon has `toRelative` and `toRelativeCalendar`. For internationalization, they use Intl.RelativeTimeFormat (or fall back to English when it is not supported by the browser).\n\n| Operation            | Moment         | Luxon                                         |\n| -------------------- | -------------- | --------------------------------------------- |\n| Time from now        | `fromNow()`    | `toRelative()`                                |\n| Time from other time | `from(Moment)` | `toRelative({ base: DateTime })`              |\n| Time to now          | `toNow()`      | `DateTime.local().toRelative({ base: this })` |\n| Time to other time   | `to(Moment)`   | `otherTime.toRelative({ base: this })`        |\n| \"Calendar time\"      | `calendar()`   | `toRelativeCalendar()`                        |\n\n## Durations\n\nMoment Durations and Luxon Durations are broadly similar in purpose and capabilities. The main differences are:\n\n1.  Luxon durations have more sophisticated conversion capabilities. They can convert from one set of units to another using `shiftTo`. They can also be configured to use different unit conversions. See [Duration Math](math.md?id=duration-math) for more.\n1.  Luxon does not (yet) have an equivalent of Moment's Duration `humanize` method. Luxon will add that when [Intl.UnitFormat](https://github.com/tc39/proposal-intl-unit-format) is supported by browsers.\n1.  Like DateTimes, Luxon Durations have separate methods for creating objects from different sources.\n\nSee the `Duration` API docs for more.\n\n## Intervals\n\nMoment doesn't have direct support intervals, which must be provided by plugins like Twix or moment-range. Luxon's Intervals have similar capabilities to theirs, with the exception of the humanization features. See the `Interval` API docs for more.\n"
  },
  {
    "path": "docs/parsing.md",
    "content": "# Parsing\n\nLuxon is not an NLP tool and isn't suitable for all date parsing jobs. But it can do some parsing:\n\n1.  Direct support for several well-known formats, including most valid ISO 8601 formats\n2.  An ad-hoc parser for parsing specific formats\n\n## Parsing technical formats\n\n### ISO 8601\n\nLuxon supports a wide range of valid ISO 8601 formats through the `fromISO` method.\n\n```js\nDateTime.fromISO(\"2016-05-25\");\n```\n\nAll of these are parsable by `fromISO`:\n\n```\n2016\n2016-05\n201605\n2016-05-25\n20160525\n2016-05-25T09\n2016-05-25T09:24\n2016-05-25T09:24:15\n2016-05-25T09:24:15.123\n2016-05-25T0924\n2016-05-25T092415\n2016-05-25T092415.123\n2016-05-25T09:24:15,123\n2016-W21-3\n2016W213\n2016-W21-3T09:24:15.123\n2016W213T09:24:15.123\n2016-200\n2016200\n2016-200T09:24:15.123\n09:24\n09:24:15\n09:24:15.123\n09:24:15,123\n```\n\n- In addition, all the times support offset arguments like \"Z\" and \"+06:00\".\n- Missing lower-order values are always set to the minimum possible value; i.e. it always parses to a full DateTime. For example, \"2016-05-25\" parses to midnight of that day. \"2016-05\" parses to the first of the month, etc.\n- The time is parsed as a local time if no offset is specified, but see the method docs to see your options, and also check out [time zone docs](zones.md) for more details.\n\n### HTTP and RFC2822\n\nLuxon also provides parsing for strings formatted according to RFC 2822 and the HTTP header specs (RFC 850 and 1123):\n\n```js\nDateTime.fromRFC2822(\"Tue, 01 Nov 2016 13:23:12 +0630\");\nDateTime.fromHTTP(\"Sunday, 06-Nov-94 08:49:37 GMT\");\nDateTime.fromHTTP(\"Sun, 06 Nov 1994 08:49:37 GMT\");\n```\n\n### SQL\n\nLuxon accepts SQL dates, times, and datetimes, via `fromSQL`:\n\n```js\nDateTime.fromSQL(\"2017-05-15\");\nDateTime.fromSQL(\"2017-05-15 09:24:15\");\nDateTime.fromSQL(\"09:24:15\");\n```\n\nIt works similarly to `fromISO`, so see above for additional notes.\n\n### Unix timestamps\n\nLuxon can parse numerical [Unix timestamps](https://en.wikipedia.org/wiki/Unix_time):\n\n```js\nDateTime.fromMillis(1542674993410);\nDateTime.fromSeconds(1542674993);\n```\n\nBoth methods accept the same options, which allow you to specify a timezone, calendar, and/or numbering system.\n\n### JS Date Object\n\nA native JS `Date` object can be converted into a `DateTime` using `DateTime.fromJSDate`.\n\nAn optional `zone` parameter can be provided to set the zone on the resulting object.\n\n\n## Ad-hoc parsing\n\n### Consider alternatives\n\nYou generally shouldn't use Luxon to parse arbitrarily formatted date strings:\n\n1.  If the string was generated by a computer for programmatic access, use a standard format like ISO 8601. Then you can parse it using `DateTime.fromISO`.\n2.  If the string is typed out by a human, it may not conform to the format you specify when asking Luxon to parse it. Luxon is quite strict about the format matching the string exactly.\n\nSometimes, though, you get a string from some legacy system in some terrible ad-hoc format and you need to parse it.\n\n### fromFormat\n\nSee `DateTime.fromFormat` for the method signature. A brief example:\n\n```js\nDateTime.fromFormat(\"May 25 1982\", \"LLLL dd yyyy\");\n```\n\n### Intl\n\nLuxon supports parsing internationalized strings:\n\n```js\nDateTime.fromFormat(\"mai 25 1982\", \"LLLL dd yyyy\", { locale: \"fr\" });\n```\n\nNote, however, that Luxon derives the list of strings that can match, say, \"LLLL\" (and their meaning) by introspecting the environment's Intl implementation. Thus the exact strings may in some cases be environment-specific. You also need the Intl API available on the target platform (see the [support matrix](matrix.md)).\n\n### Limitations\n\nNot every token supported by `DateTime#toFormat` is supported in the parser. For example, there's no `ZZZZ` or `ZZZZZ` tokens. This is for a few reasons:\n\n- Luxon relies on natively-available functionality that only provides the mapping in one direction. We can ask what the named offset is and get \"Eastern Standard Time\" but not ask what \"Eastern Standard Time\" is most likely to mean.\n- Some things are ambiguous. There are several Eastern Standard Times in different countries and Luxon has no way to know which one you mean without additional information (such as that the zone is America/New_York) that would make EST superfluous anyway. Similarly, the single-letter month and weekday formats (EEEEE) that are useful in displaying calendars graphically can't be parsed because of their ambiguity.\n- Because of the limitations above, Luxon also doesn't support the \"macro\" tokens that include offset names, such as \"ttt\" and \"FFFF\".\n\n### Parsing Two Digit Years\n\nIn order to parse a two digit string as a year (or ISO week year), the `yy` (and `kk`) token must use a cutoff year after which the year being parsed is interpreted as referring to the current century. By default, the cutoff year is set to 60, meaning that the string \"60\" is parsed as the year 2060, and the string \"61\" is parsed as the year 1961. The cutoff year can be configured using `Settings.twoDigitCutoffYear`.\n\n### Debugging\n\nThere are two kinds of things that can go wrong when parsing a string: a) you make a mistake with the tokens or b) the information parsed from the string does not correspond to a valid date. To help you sort that out, Luxon provides a method called `fromFormatExplain`. It takes the same arguments as `fromFormat` but returns a map of information about the parse that can be useful in debugging.\n\nFor example, here the code is using \"MMMM\" where \"MMM\" was needed. You can see the regex Luxon uses and see that it didn't match anything:\n\n```js\n> DateTime.fromFormatExplain(\"Aug 6 1982\", \"MMMM d yyyy\")\n\n{ input: 'Aug 6 1982',\n  tokens:\n   [ { literal: false, val: 'MMMM' },\n     { literal: false, val: ' ' },\n     { literal: false, val: 'd' },\n     { literal: false, val: ' ' },\n     { literal: false, val: 'yyyy' } ],\n  regex: '(January|February|March|April|May|June|July|August|September|October|November|December)( )(\\\\d\\\\d?)( )(\\\\d{4})',\n  matches: {},\n  result: {},\n  zone: null }\n```\n\nIf you parse something and get an invalid date, the debugging steps are slightly different. Here, we're attempting to parse August 32nd, which doesn't exist:\n\n```js\nvar d = DateTime.fromFormat(\"August 32 1982\", \"MMMM d yyyy\");\nd.isValid; //=> false\nd.invalidReason; //=> 'day out of range'\n```\n\nFor more on validity and how to debug it, see [validity](validity.md). You may find more comprehensive tips there. But as it applies specifically to `fromFormat`, again try `fromFormatExplain`:\n\n```js\n> DateTime.fromFormatExplain(\"August 32 1982\", \"MMMM d yyyy\")\n\n{ input: 'August 32 1982',\n  tokens:\n   [ { literal: false, val: 'MMMM' },\n     { literal: false, val: ' ' },\n     { literal: false, val: 'd' },\n     { literal: false, val: ' ' },\n     { literal: false, val: 'yyyy' } ],\n  regex: '(January|February|March|April|May|June|July|August|September|October|November|December)( )(\\\\d\\\\d?)( )(\\\\d{4})',\n  matches: { M: 8, d: 32, y: 1982 },\n  result: { month: 8, day: 32, year: 1982 },\n  zone: null }\n```\n\nBecause Luxon was able to parse the string without difficulty, the output is a lot richer. And you can see that the \"day\" field is set to 32. Combined with the \"out of range\" explanation above, that should clear up the situation.\n\n### Table of tokens\n\n(Examples below given for `2014-08-06T13:07:04.054` considered as a local time in America/New_York). Note that many tokens supported by the [formatter](formatting.md) are **not** supported by the parser.\n\n| Standalone token | Format token | Description                                                    | Example                     |\n| ---------------- | ------------ | ----------------------------------------------------------------- | --------------------------- |\n| S                |              | millisecond, no padding                                           | `54`                        |\n| SSS              |              | millisecond, padded to 3                                          | `054`                       |\n| u                |              | fractional seconds, (5 is a half second, 54 is slightly more)     | `54`                        |\n| uu               |              | fractional seconds, (one or two digits)                           | `05`                        |\n| uuu              |              | fractional seconds, (only one digit)                              | `5`                         |\n| s                |              | second, no padding                                                | `4`                         |\n| ss               |              | second, padded to 2 padding                                       | `04`                        |\n| m                |              | minute, no padding                                                | `7`                         |\n| mm               |              | minute, padded to 2                                               | `07`                        |\n| h                |              | hour in 12-hour time, no padding                                  | `1`                         |\n| hh               |              | hour in 12-hour time, padded to 2                                 | `01`                        |\n| H                |              | hour in 24-hour time, no padding                                  | `13`                        |\n| HH               |              | hour in 24-hour time, padded to 2                                 | `13`                        |\n| Z                |              | narrow offset                                                     | `+5`                        |\n| ZZ               |              | short offset                                                      | `+05:00`                    |\n| ZZZ              |              | techie offset                                                     | `+0500`                     |\n| z                |              | IANA zone                                                         | `America/New_York`          |\n| a                |              | meridiem                                                          | `AM`                        |\n| d                |              | day of the month, no padding                                      | `6`                         |\n| dd               |              | day of the month, padded to 2                                     | `06`                        |\n| E                | c            | day of the week, as number from 1-7 (Monday is 1, Sunday is 7)    | `3`                         |\n| EEE              | ccc          | day of the week, as an abbreviate localized string                | `Wed`                       |\n| EEEE             | cccc         | day of the week, as an unabbreviated localized string             | `Wednesday`                 |\n| M                | L            | month as an unpadded number                                       | `8`                         |\n| MM               | LL           | month as an padded number                                         | `08`                        |\n| MMM              | LLL          | month as an abbreviated localized string                          | `Aug`                       |\n| MMMM             | LLLL         | month as an unabbreviated localized string                        | `August`                    |\n| y                |              | year, 1-6 digits, very literally                                  | `2014`                      |\n| yy               |              | two-digit year, interpreted as > 1960 by default (also accepts 4) | `14`                        |\n| yyyy             |              | four-digit year                                                   | `2014`                      |\n| yyyyy            |              | four- to six-digit years                                          | `10340`                     |\n| yyyyyy           |              | six-digit years                                                   | `010340`                    |\n| G                |              | abbreviated localized era                                         | `AD`                        |\n| GG               |              | unabbreviated localized era                                       | `Anno Domini`               |\n| GGGGG            |              | one-letter localized era                                          | `A`                         |\n| kk               |              | ISO week year, unpadded                                           | `17`                        |\n| kkkk             |              | ISO week year, padded to 4                                        | `2014`                      |\n| W                |              | ISO week number, unpadded                                         | `32`                        |\n| WW               |              | ISO week number, padded to 2                                      | `32`                        |\n| o                |              | ordinal (day of year), unpadded                                   | `218`                       |\n| ooo              |              | ordinal (day of year), padded to 3                                | `218`                       |\n| q                |              | quarter, no padding                                               | `3`                         |\n| D                |              | localized numeric date                                            | `9/6/2014`                  |\n| DD               |              | localized date with abbreviated month                             | `Aug 6, 2014`               |\n| DDD              |              | localized date with full month                                    | `August 6, 2014`            |\n| DDDD             |              | localized date with full month and weekday                        | `Wednesday, August 6, 2014` |\n| t                |              | localized time                                                    | `1:07 AM`                   |\n| tt               |              | localized time with seconds                                       | `1:07:04 PM`                |\n| T                |              | localized 24-hour time                                            | `13:07`                     |\n| TT               |              | localized 24-hour time with seconds                               | `13:07:04`                  |\n| f                |              | short localized date and time                                     | `8/6/2014, 1:07 PM`         |\n| ff               |              | less short localized date and time                                | `Aug 6, 2014, 1:07 PM`      |\n| F                |              | short localized date and time with seconds                        | `8/6/2014, 1:07:04 PM`      |\n| FF               |              | less short localized date and time with seconds                   | `Aug 6, 2014, 1:07:04 PM`   |\n| '                |              | literal start/end, characters between are not tokenized           | `'T'`                       |\n"
  },
  {
    "path": "docs/tour.md",
    "content": "# A quick tour\n\nLuxon is a library that makes it easier to work with dates and times in JavaScript. If you want, add and subtract them, format and parse them, ask them hard questions, and so on, Luxon provides a much easier and comprehensive interface than the native types it wraps. We're going to talk about the most immediately useful subset of that interface.\n\nThis is going to be a bit brisk, but keep in mind that the API docs are comprehensive, so if you want to know more, feel free to [dive into them](https://moment.github.io/luxon/api-docs/index.html).\n\n## Your first DateTime\n\nThe most important class in Luxon is `DateTime`. A DateTime represents a specific millisecond in time, along with a time zone and a locale. Here's one that represents May 15, 2017 at 8:30 in the morning:\n\n```js\nconst dt = DateTime.local(2017, 5, 15, 8, 30);\n```\n\n[DateTime.local](../class/src/datetime.js~DateTime.html#static-method-local) takes any number of arguments, all the way out to milliseconds (months are 1-indexed). Underneath, this is similar to a JavaScript Date object. But we've decorated it with lots of useful methods.\n\n## Creating a DateTime\n\nThere are lots of ways to create a DateTime by parsing strings or constructing them out of parts. You've already seen one, `DateTime.local()`, but let's talk about three more.\n\n### Get the current date and time\n\nTo get the current time, just do this:\n\n```js\nconst now = DateTime.now();\n```\n\nThis is really the equivalent to calling `DateTime.local()` with no arguments, but it's a little clearer.\n\n### Create from an object\n\nThe most powerful way to create a DateTime instance is to provide an object containing all the information:\n\n```js\ndt = DateTime.fromObject({day: 22, hour: 12 }, { zone: 'America/Los_Angeles', numberingSystem: 'beng'})\n```\n\nDon't worry too much about the properties you don't understand yet; the point is that you can set every attribute of a DateTime when you create it. One thing to notice from the example is that we just set the day and hour; the year and month get defaulted to the current one and the minutes, seconds, and milliseconds get defaulted to 0. So `DateTime.fromObject` is sort of the power user interface.\n\n### Parse from ISO 8601\n\nLuxon has lots of parsing capabilities, but the most important one is parsing [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) strings, because they're more-or-less the standard wire format for dates and times. Use `DateTime.fromISO`.\n\n\n```js\nDateTime.fromISO(\"2017-05-15\")          //=> May 15, 2017 at midnight\nDateTime.fromISO(\"2017-05-15T08:30:00\") //=> May 15, 2017 at 8:30\n```\n\nYou can parse a bunch of other formats, including [your own custom ones](parsing.md).\n\n## Getting to know your DateTime instance\n\nNow that we've made some DateTimes, let's see what we can ask of it.\n\n### toString\n\nThe first thing we want to see is the DateTime as a string. Luxon returns ISO 8601 strings:\n\n```js\nDateTime.now().toString(); //=> '2017-09-14T03:20:34.091-04:00'\n```\n\n### Getting at components\n\nWe can get at the components of the time individually through getters. For example:\n\n```js\ndt = DateTime.now();\ndt.year     //=> 2017\ndt.month    //=> 9\ndt.day      //=> 14\ndt.second   //=> 47\ndt.weekday  //=> 4\n```\n\n### Other fun accessors\n\n```js\ndt.zoneName     //=> 'America/New_York'\ndt.offset       //=> -240\ndt.daysInMonth  //=> 30\n```\n\nThere are lots more!\n\n## Formatting your DateTime\n\nYou may want to output your DateTime to a string for a machine or a human to read. Luxon has lots of tools for this, but two of them are most important. If you want to format a human-readable string, use `toLocaleString`:\n\n```js\ndt.toLocaleString()      //=> '9/14/2017'\ndt.toLocaleString(DateTime.DATETIME_MED) //=> 'September 14, 3:21 AM'\n```\n\nThis works well across different locales (languages) by letting the browser figure out what order the different parts go in and how to punctuate them.\n\nIf you want the string read by another program, you almost certainly want to use `toISO`:\n\n```js\ndt.toISO() //=> '2017-09-14T03:21:47.070-04:00'\n```\n\nCustom formats are also supported. See [formatting](formatting).\n\n## Transforming your DateTime\n\n### Immutability\n\nLuxon objects are immutable. That means that you can't alter them in place, just create altered copies. Throughout the documentation, we use terms like \"alter\", \"change\", and \"set\" loosely, but rest assured we mean \"create a new instance with different properties\".\n\n### Math\n\nThis is easier to show than to tell. All of these calls return new DateTime instances:\n\n```js\nvar dt = DateTime.now();\ndt.plus({ hours: 3, minutes: 2 });\ndt.minus({ days: 7 });\ndt.startOf('day');\ndt.endOf('hour');\n```\n\n### Set\n\nYou can create new instances by overriding specific properties:\n\n```js\nvar dt = DateTime.now();\ndt.set({hour: 3}).hour   //=> 3\n```\n\n## Intl\n\nLuxon provides several different Intl capabilities, but the most important one is in formatting:\n\n```js\nvar dt = DateTime.now();\nvar f = {month: 'long', day: 'numeric'};\ndt.setLocale('fr').toLocaleString(f)      //=> '14 septembre'\ndt.setLocale('en-GB').toLocaleString(f)   //=> '14 September'\ndt.setLocale('en-US').toLocaleString(f)  //=> 'September 14'\n```\n\nLuxon's Info class can also list months or weekdays for different locales:\n\n```js\nInfo.months('long', {locale: 'fr'}) //=> [ 'janvier', 'février', 'mars', 'avril', ... ]\n```\n\n## Time zones\n\nLuxon supports time zones. There's a whole [big section](zones) about it. But briefly, you can create DateTimes in specific zones and change their zones:\n\n```js\nDateTime.fromObject({}, {zone: 'America/Los_Angeles'}); // now, but expressed in LA's local time\nDateTime.now().setZone(\"America/Los_Angeles\"); // same\n```\n\nLuxon also supports UTC directly:\n\n```js\nDateTime.utc(2017, 5, 15);\nDateTime.utc(); // now, in UTC time zone\nDateTime.now().toUTC();\nDateTime.utc().toLocal();\n```\n\n## Durations\n\nThe Duration class represents a quantity of time such as \"2 hours and 7 minutes\". You create them like this:\n\n```js\nvar dur = Duration.fromObject({ hours: 2, minutes: 7 });\n```\n\nThey can be added or subtracted from DateTimes like this:\n\n```js\ndt.plus(dur);\n```\n\nThey have getters just like DateTime:\n\n```js\ndur.hours   //=> 2\ndur.minutes //=> 7\ndur.seconds //=> 0\n```\n\nAnd some other useful stuff:\n\n```js\ndur.as('seconds') //=> 7620\ndur.toObject()    //=> { hours: 2, minutes: 7 }\ndur.toISO()       //=> 'PT2H7M'\n```\n\nYou can also format, negate, and normalize them. See it all in the `Duration` API docs.\n\n## Intervals\n\nIntervals are a specific period of time, such as \"between now and midnight\". They're really a wrapper for two DateTimes that form its endpoints. Here's what you can do with them:\n\n```js\nnow = DateTime.now();\nlater = DateTime.local(2020, 10, 12);\ni = Interval.fromDateTimes(now, later);\n\ni.length()                             //=> 97098768468\ni.length('years')                //=> 3.0762420239726027\ni.contains(DateTime.local(2019))       //=> true\n\ni.toISO()       //=> '2017-09-14T04:07:11.532-04:00/2020-10-12T00:00:00.000-04:00'\ni.toString()    //=> '[2017-09-14T04:07:11.532-04:00 – 2020-10-12T00:00:00.000-04:00)\n```\n\nNote that Luxon's Intervals are always half-open, meaning the starting point is included in the interval while the end point is not.\nThe following code will not work as expected:\n```js\nconst start = DateTime.now().startOf('day');\nconst end = start.endOf('day');\nconst i = Interval.fromDateTimes(start, end);\n\ni.length('hours')    //=> 23.99999972222222\ni.toString()         //=> [2025-07-09T00:00:00.000+02:00 – 2025-07-09T23:59:59.999+02:00)\n```\n\nThis is because `endOf('day')` returns the last millisecond of the day and as a result that last millisecond is _not_\nincluded in the interval.\n\n`Interval.after` or `Interval.before` are better suited for creating Intervals of a specific length:\n```js\nconst start = DateTime.now().startOf('day');\nconst i1 = Interval.after(start, { days: 1 });\nconst i2 = Interval.before(start, { days: 1 });\n\ni1.length('hours')    //=> 24\ni1.toString()         //=> [2025-07-09T00:00:00.000+02:00 – 2025-07-10T00:00:00.000+02:00)\n\ni2.length('hours')    //=> 24\ni2.toString()         //=> [2025-07-08T00:00:00.000+02:00 – 2025-07-09T00:00:00.000+02:00)\n```\n\nIntervals can be split up into smaller intervals, perform set-like operations with other intervals, and few other handy features. See the `Interval` API docs.\n"
  },
  {
    "path": "docs/upgrading.md",
    "content": "# Upgrading Luxon\n\n## 2.x to 3.0\n\nVersion 3.0 has one breaking change: specifying \"system\" as the zone always results in the system zone, regardless of what you have the default set to. To get the default zone (whatever it is set to), use \"default\":\n\n```js\nSettings.defaultZone = \"America/Chicago\";\n\nDateTime.now().setZone(\"default\") // results in Chicago time\nDateTime.now().setZone(\"system\") // uses the user's system time\n```\n\nIf this seems obvious, just be aware that it didn't work like that before!\n\n## 1.x to 2.0\n\nVersion 2.0 of Luxon has a number of breaking changes.\n\n### Environment support\n\nLuxon 2.0 does not support Node < 12, or any version of IE. It also only supports newer versions of major browsers. This change\nallows Luxon to make more assumptions about what's supported in the environment and will allow Luxon's code to simplify. See\nthe [Support Matrix](matrix.md) for more.\n\nFor this same reason, a polyfilled build is no longer provided; everything Luxon needs comes standard on browsers.\n\n### Breaking signature changes\n\nThere are many more specific breaking changes. Most are aimed and making Luxon's handling of option parameters more consistent.\n\n#### fromObject\n`DateTime.fromObject()` and `Duration.fromObject()` now accept two parameters: one for the object and one for the options.\n\nFor example:\n\n```js\n// Luxon 1.x\nDateTime.fromObject({ hour: 3, minute: 2, zone: \"America/New_York\", locale: \"ru\" });\nDuration.fromObject({ hours: 3, minutes: 2, conversionAccuracy: \"casual\", locale: \"ru\" });\n\n// vs Luxon 2.x\nDateTime.fromObject({ hour: 3, minute: 2 }, { zone: \"America/New_York\", locale: \"ru\" });\nDuration.fromObject({ hours: 3, minutes: 2 }, { conversionAccuracy: \"casual\", locale: \"ru\" });\n```\n\n#### toLocaleString\n\nIn Luxon 1.x, you can mix Intl options with overrides of the DateTime configuration into the same options parameter. These are now\ntwo separate parameters:\n\n```js\n\n// Luxon 1.x\nDateTime.now().toLocaleString({ hour: \"2-digit\", locale: \"ru\" })\n\n// vs Luxon 2.x\n\nDateTime.now().toLocaleString({ hour: \"2-digit\" }, { locale: \"ru\" })\n```\n\n#### System zone\n\nThe zone of the executing environment (e.g. the time set on the computer running the browser running Luxon), is now called\n\"system\" instead of \"local\" to reduce confusion.\n\n```js\nDateTime.fromObject({}, { zone: \"local\" }) // still works\nDateTime.fromObject({}, { zone: \"system\" }) // preferred\n\nDateTime.fromObject({}, { zone: \"system\" }).zone // => type is SystemZone\nDateTime.fromObject({}, { zone: \"system\" }).zone.type // => \"system\"\n```\n\n#### Default zone\n\nLuxon 2.x cleans up the handling of `Settings.defaultZone`:\n\n```js\n\n// setting\nSettings.defaultZone = \"America/New_York\"; // can take a string\nSettings.defaultZone = IANAZone.create(\"America/New_York\"); // or a Zone instance\n\n// getting\nSettings.defaultZone //=> a Zone instance\n```\n\nThe most significant breaking change here is that `Settings.defaultZoneName` no longer exists.\n\n#### Other breaking changes\n\n * `DateTime#toObject` no longer accepts an `includeConfig` option\n * `resolvedLocaleOpts` is now `resolvedLocaleOptions`\n * `Zone#universal` is now `Zone#isUniversal`\n\n### Non-breaking changes\n\n * `DateTime.local()` and `DateTime.utc()` now take an options parameter for setting zone and locale, same as `fromObject()`.\n\n### A note\n\nWe originally had more ambitious plans for Luxon 2.0: a port to Typescript, an overhaul of error handling, and lots of other changes.\nThe problem is that we're very busy, and in the meantime browsers have evolved quickly, the mistakes in our API bothered a lot\nof developers, and our need to support old environments made Luxon more difficult to change. So we made a basic set of changes\nto give us some operating room. And hopefully someday we'll get back to those more ambitious plans.\n"
  },
  {
    "path": "docs/validity.md",
    "content": "# Validity\n\n## Invalid DateTimes\n\nOne of the most irritating aspects of programming with time is that it's possible to end up with invalid dates. This is a bit subtle: barring integer overflows, there's no count of milliseconds that don't correspond to a valid DateTime, but when working with calendar units, it's pretty easy to say something like \"June 400th\". Luxon considers that invalid and will mark it accordingly.\n\nUnless you've asked Luxon to throw an exception when it creates an invalid DateTime (see more on that below), it will fail silently, creating an instance that doesn't know how to do anything. You can check validity with `isValid`:\n\n```js\n> var dt = DateTime.fromObject({ month: 6, day: 400 });\ndt.isValid //=> false\n```\n\nAll of the methods or getters that return primitives return degenerate ones:\n\n```js\ndt.year; //=>  NaN\ndt.toString(); //=> 'Invalid DateTime'\ndt.toObject(); //=> {}\n```\n\nMethods that return other Luxon objects will return invalid ones:\n\n```js\ndt.plus({ days: 4 }).isValid; //=> false\n```\n\n## Reasons a DateTimes can be invalid\n\nThe most common way to do that is to over- or underflow some unit:\n\n- February 40th\n- 28:00\n- -4 pm\n- etc\n\nBut there are other ways to do it:\n\n```js\n// specify a time zone that doesn't exist\nDateTime.now().setZone(\"America/Blorp\").isValid; //=> false\n\n// provide contradictory information (here, this date is not a Wednesday)\nDateTime.fromObject({ year: 2017, month: 5, day: 25, weekday: 3 }).isValid; //=> false\n```\n\nNote that some other kinds of mistakes throw, based on our judgment that they are more likely programmer errors than data issues:\n\n```js\nDateTime.now().set({ blorp: 7 }); //=> kerplosion\n```\n\n## Debugging invalid DateTimes\n\nBecause DateTimes fail silently, they can be a pain to debug. Luxon has some features that can help.\n\n### invalidReason and invalidExplanation\n\nInvalid DateTime objects are happy to tell you why they're invalid. `invalidReason` will give you a consistent error code you can use, whereas `invalidExplanation` will spell it out\n\n```js\nvar dt = DateTime.now().setZone(\"America/Blorp\");\ndt.invalidReason; //=>  'unsupported zone'\ndt.invalidExplanation; //=> 'the zone \"America/Blorp\" is not supported'\n```\n\n### throwOnInvalid\n\nYou can make Luxon throw whenever it creates an invalid DateTime. The message will combine `invalidReason` and `invalidExplanation`:\n\n```js\nSettings.throwOnInvalid = true;\nDateTime.now().setZone(\"America/Blorp\"); //=> Error: Invalid DateTime: unsupported zone: the zone \"America/Blorp\" is not supported\n```\n\nYou can of course leave this on in production too, but be sure to try/catch it appropriately.\n\n## Invalid Durations\n\nDurations can be invalid too. The easiest way to get one is to diff an invalid DateTime.\n\n```js\nDateTime.local(2017, 28).diffNow().isValid; //=> false\n```\n\n## Invalid Intervals\n\nIntervals can be invalid. This can happen a few different ways:\n\n- The end time is before the start time\n- It was created from invalid DateTime or Duration\n"
  },
  {
    "path": "docs/why.md",
    "content": "# Why does Luxon exist?\n\nWhat's the deal with this whole Luxon thing anyway? Why did I write it? How is it related to the Moment project? What's different about it? This page tries to hash all that out.\n\n## A disclaimer\n\nI should clarify here that I'm just one of Moment's maintainers; I'm not in charge and I'm not Moment's creator. The opinions here are solely mine. Finally, none of this is meant to bash Moment, a project I've spent a lot of time on and whose other developers I respect.\n\n## Origin\n\nLuxon started because I had a bunch of ideas on how to improve Moment but kept finding Moment wasn't a good codebase to explore them with. Namely:\n\n- I wanted to try out some ideas that I thought would provide a better, more explicit API but didn't want to break everything in Moment.\n- I had an idea on how to provide out-of-the-box, no-data-files-required support for time zones, but Moment's design made that difficult.\n- I wanted to completely rethink how internationalization worked by using the Intl API that comes packaged in browsers.\n- I wanted to use a modern JS toolchain, which would require a major retrofit to Moment.\n\nSo I decided to write something from scratch, a sort of modernized Moment. It's a combination of all the things I learned maintaining Moment and Twix, plus a bunch of fresh ideas. I worked on it in little slivers of spare time for about two years. But now it's ready to actually use, and the Moment team likes it enough that we pulled it under the organization's umbrella.\n\n## Ideas in Luxon\n\nLuxon is built around a few core ideas:\n\n1.  Keep the basic chainable date wrapper idea from Moment.\n1.  Make all the types immutable.\n1.  Make the API explicit; different methods do different things and have well-defined options.\n1.  Use the Intl API to provide internationalization, including token parsing. Fall back to English if the browser doesn't support those APIs.\n1.  Abuse the Intl API horribly to provide time zone support. Only possible for modern browsers.\n1.  Provide more comprehensive duration support.\n1.  Directly provide interval support.\n1.  Write inline docs for everything.\n\nThese ideas have some big advantages:\n\n1.  It's much easier to understand and debug code that uses Luxon.\n1.  Using native browser capabilities for internationalization leads to a much better behavior and is dramatically easier to maintain.\n1.  Luxon has the best time zone support of any JS date library.\n1.  Luxon's durations are both flexible and easy to use.\n1.  The documentation is very good.\n\nThey also have some disadvantages:\n\n1.  Using modern browser capabilities means that the fallback behavior introduces complexity for the programmer.\n1.  Never keeping internationalized strings in the code base means that some capabilities have to wait until the browsers provide it.\n1.  Some aspects of the Intl API are browser-dependent, which means Luxon's behavior is too.\n\n## Place in the Moment project\n\nLuxon lives in the Moment project because, basically, we all really like it, and it represents a huge improvement.\n\nBut Luxon doesn't quite fulfill Moment's mandate. Since it sometimes relies on browsers' implementations of the `Intl` specifications, it doesn't provide some of Moment's most commonly-used features on all browsers. Relative date formatting is for instance not supported in IE11 and [other older browsers](https://caniuse.com/?search=Intl%20RelativeTimeFormat). Luxon's Intl features do not work as expected on sufficiently outdated browsers, whereas Moment's all work everywhere. That represents a good tradeoff, IMO, but it's clearly a different one than Moment makes.\n\nLuxon makes a major break in API conventions. Part of Moment's charm is that you just call `moment()` on basically anything and you get date, whereas Luxon forces you to decide that you want to call `fromISO` or whatever. The upshot of all that is that Luxon feels like a different library; that's why it's not Moment 3.0.\n\nSo what is it then? We're not really sure. We're calling it a Moment labs project. Will its ideas get backported into Moment 3? Will it gradually siphon users away from Moment and become the focus of the Moment project? Will the march of modern browsers retire the arguments above and cause us to revisit branding Luxon as Moment? We don't know.\n\nThere, now you know as much as I do.\n\n## Future plans\n\nLuxon is fully usable and I plan to support it indefinitely. It's also largely complete. Luxon will eventually strip out its fallbacks for missing platform features. But overall I expect the core functionality to stay basically as it is, adding mostly minor tweaks and bugfixes.\n"
  },
  {
    "path": "docs/zones.md",
    "content": "# Time zones and offsets\n\nLuxon has support for time zones. This page explains how to use them.\n\n## Don't worry!\n\nYou _usually_ don't need to worry about time zones. Your code runs on a computer with a particular time zone and everything will work consistently in that zone without you doing anything. It's when you want to do complicated stuff _across_ zones that you have to think about it. Even then, here are some pointers to help you avoid situations where you have to think carefully about time zones:\n\n1.  Don't make servers think about _local_ times. Configure them to use UTC and write your server's code to work in UTC. Times can often be thought of as a simple count of epoch milliseconds; what you would call that time (e.g. 9:30) in what zone doesn't (again, often) matter.\n2.  Communicate times between systems in ISO 8601, like `2017-05-15T13:30:34Z` where possible (it doesn't matter if you use Z or some local offset; the point is that it precisely identifies the millisecond on the global timeline).\n3.  Where possible, only think of time zones as a formatting concern; your application ideally never knows that the time it's working with is called \"9:00\" until it's being rendered to the user.\n4.  Barring 3, do as much manipulation of the time (say, adding an hour to the current time) in the client code that's already running in the time zone where the results will matter.\n\nAll those things will make it less likely you ever need to work explicitly with time zones and may also save you plenty of other headaches. But those aren't possible for some applications; you might need to work with times in zones other than the one the program is running in, for any number of reasons. And that's where Luxon's time zone support comes in.\n\n## Terminology\n\nBear with me here. Time zones are a pain in the ass. Luxon has lots of tools to deal with them, but there's no getting around the fact that they're complicated. The terminology for time zones and offsets isn't well-established. But let's try to impose some order:\n\n1.  An **offset** is a difference between the local time and the UTC time, such as +5 (hours) or -12:30. They may be expressed directly in minutes, or in hours, or in a combination of minutes and hours. Here we'll use hours.\n1.  A **time zone** is a set of rules, associated with a geographical location, that determines the local offset from UTC at any given time. The best way to identify a zone is by its IANA string, such as \"America/New_York\". That zone says something to the effect of \"The offset is -5, except between March and November, when it's -4\".\n1.  A **fixed-offset time zone** is any time zone that never changes offsets, such as UTC. Luxon supports fixed-offset zones directly; they're specified like UTC+7, which you can interpret as \"always with an offset of +7\".\n1.  A **named offset** is a time zone-specific name for an offset, such as Eastern Daylight Time. It expresses both the zone (America's EST roughly implies America/New_York) and the current offset (EST means -5). They are also confusing in that they overspecify the offset (e.g. for any given time it is unnecessary to specify EST vs EDT; it's always whichever one is right). They are also ambiguous (BST is both British Summer Time and Bangladesh Standard Time), unstandardized, and internationalized (what would a Frenchman call the US's EST?). For all these reasons, you should avoid them when specifying times programmatically. Luxon only supports their use in formatting.\n\nSome subtleties:\n\n1.  Multiple zones can have the same offset (think about the US's zones and their Canadian equivalents), though they might not have the same offset all the time, depending on when their DSTs are. Thus zones and offsets have a many-to-many relationship.\n1.  Just because a time zone doesn't have a DST now doesn't mean it's fixed. Perhaps it had one in the past. Regardless, Luxon does not have first-class access to the list of rules, so it assumes any IANA-specified zone is not fixed and checks for its current offset programmatically.\n\nIf all this seems too terse, check out these articles. The terminology in them is subtly different but the concepts are the same:\n\n- [Time Zones Aren’t Offsets – Offsets Aren’t Time Zones](https://spin.atomicobject.com/2016/07/06/time-zones-offsets/)\n- [Stack Overflow's timezone wiki page](https://stackoverflow.com/tags/timezone/info)\n\n## Luxon works with time zones\n\nLuxon's DateTime class supports zones directly. By default, a date created in Luxon is \"in\" the local time zone of the machine it's running on. By \"in\" we mean that the DateTime has, as one of its properties, an associated zone.\n\nIt's important to remember that a DateTime represents a specific instant in time and that instant has an unambiguous meaning independent of what time zone you're in; the zone is really a piece of social metadata that affects how humans interact with the time, rather than a fact about the passing of time itself. Of course, Luxon is a library for humans, so that social metadata affects Luxon's behavior too. It just doesn't change _what time it is_.\n\nSpecifically, a DateTime's zone affects its behavior in these ways:\n\n1.  Times will be formatted as they would be in that zone.\n1.  Transformations to the DateTime (such as `plus` or `startOf`) will obey any DSTs in that zone that affect the calculation (see \"Math across DSTs\" below)\n\nGenerally speaking, Luxon does not support changing a DateTime's offset, just its zone. That allows it to enforce the behaviors in the list above. The offset for that DateTime is just whatever the zone says it is. If you are unconcerned with the effects above, then you can always give your DateTime a fixed-offset zone.\n\n## Specifying a zone\n\nLuxon's API methods that take a zone as an argument all let you specify the zone in a few ways.\n\n| Type         | Example            | Description                                                       |\n| ------------ | ------------------ | ----------------------------------------------------------------- |\n| IANA         | 'America/New_York' | that zone                                                         |\n| system       | 'system'           | the system's local zone                                           |\n| default      | 'default'          | the default zone set by Settings.defaultZone                      |\n| UTC          | 'utc'              | Universal Coordinated Time                                        |\n| fixed offset | 'UTC+7'            | a fixed offset zone                                               |\n| Zone         | new YourZone()     | A custom implementation of Luxon's Zone interface (advanced only) |\n\n### IANA support\n\nIANA-specified zones are string identifiers like \"America/New_York\" or \"Asia/Tokyo\". Luxon gains direct support for them by abusing built-in Intl APIs.\n\nIf you specify a zone and your environment doesn't support that zone, you'll get an [invalid](validity.md) DateTime. That could be because the environment doesn't support zones at all (generally browsers older than Luxon supports), because for whatever reason it doesn't support that _particular_ zone, or because the zone is just bogus. Like this:\n\n```js\nbogus = DateTime.local().setZone(\"America/Bogus\");\n\nbogus.isValid; //=> false\nbogus.invalidReason; //=> 'unsupported zone'\n```\n\n## Creating DateTimes\n\n### System zone by default\n\nBy default, DateTime instances are created in the system's local zone and parsed strings are interpreted as specifying times in the system's zone. For example, my computer is configured to use `America/New_York`, which has an offset of -4 in May:\n\n```js\nvar local = DateTime.local(2017, 05, 15, 9, 10, 23);\n\nlocal.zoneName; //=> 'America/New_York'\nlocal.toString(); //=> '2017-05-15T09:10:23.000-04:00'\n\nvar iso = DateTime.fromISO(\"2017-05-15T09:10:23\");\n\niso.zoneName; //=> 'America/New_York'\niso.toString(); //=> '2017-05-15T09:10:23.000-04:00'\n```\n\n### Creating DateTimes in a zone\n\nMany of Luxon's factory methods allow you to tell it specifically what zone to create the DateTime in:\n\n```js\nvar overrideZone = DateTime.fromISO(\"2017-05-15T09:10:23\", { zone: \"Europe/Paris\" });\n\noverrideZone.zoneName; //=> 'Europe/Paris'\noverrideZone.toString(); //=> '2017-05-15T09:10:23.000+02:00'\n```\n\nNote two things:\n\n1.  The date and time specified in the string was interpreted as a Parisian local time (i.e. it's the time that corresponds to what would be called 9:10 _there_).\n2.  The resulting DateTime object is in Europe/Paris.\n\nThose are conceptually independent (i.e. Luxon could have converted the time to the system zone), but in practice it's more convenient for the same option to govern both.\n\nIn addition, one static method, `utc()`, specifically interprets the input as being specified in UTC. It also returns a DateTime in UTC:\n\n```js\nvar utc = DateTime.utc(2017, 05, 15, 9, 10, 23);\n\nutc.zoneName; //=> 'UTC'\nutc.toString(); //=> '2017-05-15T09:10:23.000Z'\n```\n\n### Strings that specify an offset\n\nSome input strings may specify an offset as part of the string itself. In these cases, Luxon interprets the time as being specified with that offset, but converts the resulting DateTime into the system's local zone:\n\n```js\nvar specifyOffset = DateTime.fromISO(\"2017-05-15T09:10:23-09:00\");\n\nspecifyOffset.zoneName; //=> 'America/New_York'\nspecifyOffset.toString(); //=> '2017-05-15T14:10:23.000-04:00'\n\nvar specifyZone = DateTime.fromFormat(\n  \"2017-05-15T09:10:23 Europe/Paris\",\n  \"yyyy-MM-dd'T'HH:mm:ss z\"\n);\n\nspecifyZone.zoneName; //=> 'America/New_York'\nspecifyZone.toString(); //=> '2017-05-15T03:10:23.000-04:00'\n```\n\n...unless a zone is specified as an option (see previous section), in which case the DateTime gets converted to _that_ zone:\n\n```js\nvar specifyOffsetAndOverrideZone = DateTime.fromISO(\"2017-05-15T09:10:23-09:00\", {\n  zone: \"Europe/Paris\"\n});\n\nspecifyOffsetAndOverrideZone.zoneName; //=> 'Europe/Paris'\nspecifyOffsetAndOverrideZone.toString(); //=> '2017-05-15T20:10:23.000+02:00'\n```\n\n### setZone\n\nFinally, some parsing functions allow you to \"keep\" the zone in the string as the DateTime's zone. Note that if only an offset is provided by the string, the zone will be a fixed-offset one, since Luxon doesn't know which zone is meant, even if you do.\n\n```js\nvar keepOffset = DateTime.fromISO(\"2017-05-15T09:10:23-09:00\", { setZone: true });\n\nkeepOffset.zoneName; //=> 'UTC-9'\nkeepOffset.toString(); //=> '2017-05-15T09:10:23.000-09:00'\n\nvar keepZone = DateTime.fromFormat(\"2017-05-15T09:10:23 Europe/Paris\", \"yyyy-MM-dd'T'HH:mm:ss z\", {\n  setZone: true\n});\n\nkeepZone.zoneName; //=> 'Europe/Paris'\nkeepZone.toString(); //=> '2017-05-15T09:10:23.000+02:00'\n```\n\n## Changing zones\n\n### setZone\n\nLuxon objects are immutable, so when we say \"changing zones\" we really mean \"creating a new instance with a different zone\". Changing zone generally means \"change the zone in which this DateTime is expressed (and according to which rules it is manipulated), but don't change the underlying timestamp.\" For example:\n\n```js\nvar local = DateTime.local();\nvar rezoned = local.setZone(\"America/Los_Angeles\");\n\n// different local times with different offsets\nlocal.toString(); //=> '2017-09-13T18:30:51.141-04:00'\nrezoned.toString(); //=> '2017-09-13T15:30:51.141-07:00'\n\n// but actually the same time\nlocal.valueOf() === rezoned.valueOf(); //=> true\n```\n\n### keepLocalTime\n\nGenerally, it's best to think of the zone as a sort of metadata that you slide around independent of the underlying count of milliseconds. However, sometimes that's not what you want. Sometimes you want to change zones while keeping the local time fixed and instead altering the timestamp. Luxon supports this:\n\n```js\nvar local = DateTime.local();\nvar rezoned = local.setZone(\"America/Los_Angeles\", { keepLocalTime: true });\n\nlocal.toString(); //=> '2017-09-13T18:36:23.187-04:00'\nrezoned.toString(); //=> '2017-09-13T18:36:23.187-07:00'\n\nlocal.valueOf() === rezoned.valueOf(); //=> false\n```\n\nIf you find that confusing, I recommend just not using it. On the other hand, if you find yourself using this all the time, you are probably doing something wrong.\n\n## Accessors\n\nLuxon DateTimes have a few different accessors that let you find out about the zone and offset:\n\n```js\nvar dt = DateTime.local();\n\ndt.zoneName; //=> 'America/New_York'\ndt.offset; //=> -240\ndt.offsetNameShort; //=> 'EDT'\ndt.offsetNameLong; //=> 'Eastern Daylight Time'\ndt.isOffsetFixed; //=> false\ndt.isInDST; //=> true\n```\n\nThose are all documented in the [DateTime API docs](https://moment.github.io/luxon/api-docs/index.html#datetime).\n\n## DST weirdness\n\nBecause our ancestors were morons, they opted for a system wherein many governments shift around the local time twice a year for no good reason. And it's not like they do it in a neat, coordinated fashion. No, they do it whimsically, varying the shifts' timing from country to country (or region to region!) and from year to year. And of course, they do it the opposite way south of the Equator. This is all a tremendous waste of everyone's energy and, er, time, but it is how the world works and a date and time library has to deal with it.\n\nMost of the time, DST shifts will happen without you having to do anything about it and everything will just work. Luxon goes to some pains to make DSTs as unweird as possible. But there are exceptions. This section covers them.\n\n### Invalid times\n\nSome local times simply don't exist. The Spring Forward DST shift involves shifting the local time forward by (usually) one hour. In my zone, `America/New_York`, on March 12, 2017 the millisecond after `1:59:59.999` is `3:00:00.000`. Thus the times between `2:00:00.000` and `2:59:59.999`, inclusive, don't exist in that zone. But of course, nothing stops a user from constructing a DateTime out of that local time.\n\nIf you create such a DateTime from scratch, the missing time will be advanced by an hour:\n\n```js\nDateTime.local(2017, 3, 12, 2, 30).toString(); //=> '2017-03-12T03:30:00.000-04:00'\n```\n\nYou can also do date math that lands you in the middle of the shift. These also push forward:\n\n```js\nDateTime.local(2017, 3, 11, 2, 30)\n  .plus({ days: 1 })\n  .toString(); //=> '2017-03-12T03:30:00.000-04:00'\nDateTime.local(2017, 3, 13, 2, 30)\n  .minus({ days: 1 })\n  .toString(); //=> '2017-03-12T03:30:00.000-04:00'\n```\n\n### Ambiguous times\n\nHarder to handle are ambiguous times. During Fall Back, some local times happen twice. In my zone, `America/New_York`, on November 5, 2017 the millisecond after `1:59:59.999` became `1:00:00.000`. But of course there was already a 1:00 that day, one hour before this one. So if you create a DateTime with a local time of 1:30, which time do you mean? It's an important question, because they correspond to different moments in time.\n\nHowever, Luxon's behavior here is undefined. It makes no promises about which of the two possible timestamps the instance will represent. Currently, its specific behavior is like this:\n\n```js\nDateTime.local(2017, 11, 5, 1, 30).offset / 60; //=> -4\nDateTime.local(2017, 11, 4, 1, 30).plus({ days: 1 }).offset / 60; //=> -4\nDateTime.local(2017, 11, 6, 1, 30).minus({ days: 1 }).offset / 60; //=> -5\n```\n\nIn other words, sometimes it picks one and sometimes the other. Luxon doesn't guarantee the specific behavior above. That's just what it happens to do.\n\nIf you're curious, this lack of definition is because Luxon doesn't actually know that any particular DateTime is an ambiguous time. It doesn't know the time zones rules at all. It just knows the local time does not contradict the offset and leaves it at that. To find out the time is ambiguous and define exact rules for how to resolve it, Luxon would have to test nearby times to see if it can find duplicate local time, and it would have to do that on every creation of a DateTime, regardless of whether it was anywhere near a real DST shift. Because that's onerous, Luxon doesn't bother.\n\n### Math across DSTs\n\nThere's a whole [section](math.md) about date and time math, but it's worth highlighting one thing here: when Luxon does math across DSTs, it adjusts for them when working with higher-order, variable-length units like days, weeks, months, and years. When working with lower-order, exact units like hours, minutes, and seconds, it does not. For example, DSTs mean that days are not always the same length: one day a year is (usually) 23 hours long and another is 25 hours long. Luxon makes sure that adding days takes that into account. On the other hand, an hour is always 3,600,000 milliseconds.\n\nAn easy way to think of it is that if you add a day to a DateTime, you should always get the same time the next day, regardless of any intervening DSTs. On the other hand, adding 24 hours will result in DateTime that is 24 hours later, which may or may not be the same time the next day. In this example, my zone is `America/New_York`, which had a Spring Forward DST in the early hours of March 12.\n\n```js\nvar start = DateTime.local(2017, 3, 11, 10);\nstart.hour; //=> 10, just for comparison\nstart.plus({ days: 1 }).hour; //=> 10, stayed the same\nstart.plus({ hours: 24 }).hour; //=> 11, DST pushed forward an hour\n```\n\n## Changing the default zone\n\nBy default, Luxon creates DateTimes in the system's zone. However, you can override this behavior globally:\n\n```js\nSettings.defaultZone = \"Asia/Tokyo\";\nDateTime.local().zoneName; //=> 'Asia/Tokyo'\n\nSettings.defaultZone = \"utc\";\nDateTime.local().zoneName; //=> 'UTC'\n\n// you can reset by setting to 'system'\n\nSettings.defaultZone = \"system\";\nDateTime.local().zoneName; //=> 'America/New_York'\n```\n"
  },
  {
    "path": "jest.config.js",
    "content": "module.exports = {\n  testEnvironment: \"node\",\n  roots: [\"test\"],\n  coverageDirectory: \"build/coverage\",\n  collectCoverageFrom: [\"src/**/*.js\", \"!src/zone.js\"],\n  transform: {\n    \"^.+\\\\.js$\": \"babel-jest\",\n  },\n};\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"luxon\",\n  \"version\": \"3.7.2\",\n  \"description\": \"Immutable date wrapper\",\n  \"author\": \"Isaac Cambron\",\n  \"keywords\": [\n    \"date\",\n    \"immutable\"\n  ],\n  \"repository\": \"https://github.com/moment/luxon\",\n  \"exports\": {\n    \".\": {\n      \"import\": \"./build/es6/luxon.mjs\",\n      \"require\": \"./build/node/luxon.js\"\n    },\n    \"./package.json\": \"./package.json\"\n  },\n  \"scripts\": {\n    \"build\": \"babel-node tasks/buildAll.js\",\n    \"build-node\": \"babel-node tasks/buildNode.js\",\n    \"build-global\": \"babel-node tasks/buildGlobal.js\",\n    \"jest\": \"jest\",\n    \"test\": \"jest --coverage\",\n    \"api-docs\": \"mkdir -p build && documentation build src/luxon.js -f html -o build/api-docs && sed -i.bak 's/<\\\\/body>/<script src=\\\"\\\\..\\\\/global\\\\/luxon.js\\\"><\\\\/script><script>console.log(\\\"You can try Luxon right here using the `luxon` global, like `luxon.DateTime.now()`\\\");<\\\\/script><\\\\/body>/g' build/api-docs/index.html && rm build/api-docs/index.html.bak\",\n    \"copy-site\": \"mkdir -p build && rsync -a docs/ build/docs && rsync -a site/ build\",\n    \"site\": \"npm run api-docs && npm run copy-site\",\n    \"format\": \"prettier --write 'src/**/*.js' 'test/**/*.js' 'benchmarks/*.js'\",\n    \"format-check\": \"prettier --check 'src/**/*.js' 'test/**/*.js' 'benchmarks/*.js'\",\n    \"benchmark\": \"node benchmarks/index.js\",\n    \"codecov\": \"codecov\",\n    \"prepack\": \"babel-node tasks/buildAll.js\",\n    \"prepare\": \"husky install\",\n    \"show-site\": \"http-server build\"\n  },\n  \"lint-staged\": {\n    \"*.{js,json}\": [\n      \"prettier --write\"\n    ]\n  },\n  \"devDependencies\": {\n    \"@babel/core\": \"^7.18.6\",\n    \"@babel/node\": \"^7.18.6\",\n    \"@babel/plugin-external-helpers\": \"^7.18.6\",\n    \"@babel/preset-env\": \"^7.18.6\",\n    \"@rollup/plugin-babel\": \"^5.3.0\",\n    \"@rollup/plugin-commonjs\": \"^19.0.0\",\n    \"@rollup/plugin-node-resolve\": \"^13.0.0\",\n    \"babel-jest\": \"^28.1.2\",\n    \"benchmark\": \"latest\",\n    \"codecov\": \"latest\",\n    \"documentation\": \"latest\",\n    \"fs-extra\": \"^6.0.1\",\n    \"http-server\": \"^14.1.1\",\n    \"husky\": \"^7.0.0\",\n    \"jest\": \"^29.4.3\",\n    \"lint-staged\": \"^13.2.1\",\n    \"prettier\": \"latest\",\n    \"rollup\": \"^2.52.7\",\n    \"rollup-plugin-terser\": \"^7.0.2\",\n    \"uglify-js\": \"^3.13.10\"\n  },\n  \"main\": \"build/node/luxon.js\",\n  \"module\": \"src/luxon.js\",\n  \"browser\": \"build/cjs-browser/luxon.js\",\n  \"jsdelivr\": \"build/global/luxon.min.js\",\n  \"unpkg\": \"build/global/luxon.min.js\",\n  \"engines\": {\n    \"node\": \">=12\"\n  },\n  \"files\": [\n    \"build/node/luxon.js\",\n    \"build/node/luxon.js.map\",\n    \"build/cjs-browser/luxon.js\",\n    \"build/cjs-browser/luxon.js.map\",\n    \"build/amd/luxon.js\",\n    \"build/amd/luxon.js.map\",\n    \"build/global/luxon.js\",\n    \"build/global/luxon.js.map\",\n    \"build/global/luxon.min.js\",\n    \"build/global/luxon.min.js.map\",\n    \"build/es6/luxon.mjs\",\n    \"build/es6/luxon.mjs.map\",\n    \"src\"\n  ],\n  \"license\": \"MIT\",\n  \"sideEffects\": false\n}\n"
  },
  {
    "path": "scripts/bootstrap.js",
    "content": "const luxon = require(\"../build/node/luxon\");\nglobal.DateTime = luxon.DateTime;\nglobal.Duration = luxon.Duration;\nglobal.Interval = luxon.Interval;\nglobal.Settings = luxon.Settings;\nglobal.Info = luxon.Info;\nglobal.IANAZone = luxon.IANAZone;\n"
  },
  {
    "path": "scripts/deploy-site",
    "content": "#!/usr/bin/env bash\nset -o errexit #abort if any command fails\nme=$(basename \"$0\")\n\nhelp_message=\"\\\nUsage: $me [-c FILE] [<options>]\nDeploy generated files to a git branch.\nOptions:\n  -h, --help               Show this help information.\n  -v, --verbose            Increase verbosity. Useful for debugging.\n  -e, --allow-empty        Allow deployment of an empty directory.\n  -m, --message MESSAGE    Specify the message used when committing on the\n                           deploy branch.\n  -n, --no-hash            Don't append the source commit's hash to the deploy\n                           commit's message.\n  -c, --config-file PATH   Override default & environment variables' values\n                           with those in set in the file at 'PATH'. Must be the\n                           first option specified.\nVariables:\n  GIT_DEPLOY_DIR      Folder path containing the files to deploy.\n  GIT_DEPLOY_BRANCH   Commit deployable files to this branch.\n  GIT_DEPLOY_REPO     Push the deploy branch to this repository.\nThese variables have default values defined in the script. The defaults can be\noverridden by environment variables. Any environment variables are overridden\nby values set in a '.env' file (if it exists), and in turn by those set in a\nfile specified by the '--config-file' option.\"\n\nparse_args() {\n\t# Set args from a local environment file.\n\tif [ -e \".env\" ]; then\n\t\tsource .env\n\tfi\n\n\t# Set args from file specified on the command-line.\n\tif [[ $1 = \"-c\" || $1 = \"--config-file\" ]]; then\n\t\tsource \"$2\"\n\t\tshift 2\n\tfi\n\n\t# Parse arg flags\n\t# If something is exposed as an environment variable, set/overwrite it\n\t# here. Otherwise, set/overwrite the internal variable instead.\n\twhile : ; do\n\t\tif [[ $1 = \"-h\" || $1 = \"--help\" ]]; then\n\t\t\techo \"$help_message\"\n\t\t\treturn 0\n\t\telif [[ $1 = \"-v\" || $1 = \"--verbose\" ]]; then\n\t\t\tverbose=true\n\t\t\tshift\n\t\telif [[ $1 = \"-e\" || $1 = \"--allow-empty\" ]]; then\n\t\t\tallow_empty=true\n\t\t\tshift\n\t\telif [[ ( $1 = \"-m\" || $1 = \"--message\" ) && -n $2 ]]; then\n\t\t\tcommit_message=$2\n\t\t\tshift 2\n\t\telif [[ $1 = \"-n\" || $1 = \"--no-hash\" ]]; then\n\t\t\tGIT_DEPLOY_APPEND_HASH=false\n\t\t\tshift\n\t\telse\n\t\t\tbreak\n\t\tfi\n\tdone\n\n\t# Set internal option vars from the environment and arg flags. All internal\n\t# vars should be declared here, with sane defaults if applicable.\n\n\t# Source directory & target branch.\n\tdeploy_directory=${GIT_DEPLOY_DIR:-build}\n\tdeploy_branch=${GIT_DEPLOY_BRANCH:-gh-pages}\n\n\t#if no user identity is already set in the current git environment, use this:\n\tdefault_username=${GIT_DEPLOY_USERNAME:-deploy.sh}\n\tdefault_email=${GIT_DEPLOY_EMAIL:-}\n\n\t#repository to deploy to. must be readable and writable.\n\trepo=${GIT_DEPLOY_REPO:-origin}\n\n\t#append commit hash to the end of message by default\n\tappend_hash=${GIT_DEPLOY_APPEND_HASH:-true}\n}\n\nmain() {\n\tparse_args \"$@\"\n\n\tenable_expanded_output\n\n\tif ! git diff --exit-code --quiet --cached; then\n\t\techo Aborting due to uncommitted changes in the index >&2\n\t\treturn 1\n\tfi\n\n\tcommit_title=`git log -n 1 --format=\"%s\" HEAD`\n\tcommit_hash=` git log -n 1 --format=\"%H\" HEAD`\n\n\t#default commit message uses last title if a custom one is not supplied\n\tif [[ -z $commit_message ]]; then\n\t\tcommit_message=\"publish: $commit_title\"\n\tfi\n\n\t#append hash to commit message unless no hash flag was found\n\tif [ $append_hash = true ]; then\n\t\tcommit_message=\"$commit_message\"$'\\n\\n'\"generated from commit $commit_hash\"\n\tfi\n\n\tprevious_branch=`git rev-parse --abbrev-ref HEAD`\n\n\tif [ ! -d \"$deploy_directory\" ]; then\n\t\techo \"Deploy directory '$deploy_directory' does not exist. Aborting.\" >&2\n\t\treturn 1\n\tfi\n\n\t# must use short form of flag in ls for compatibility with OS X and BSD\n\tif [[ -z `ls -A \"$deploy_directory\" 2> /dev/null` && -z $allow_empty ]]; then\n\t\techo \"Deploy directory '$deploy_directory' is empty. Aborting. If you're sure you want to deploy an empty tree, use the --allow-empty / -e flag.\" >&2\n\t\treturn 1\n\tfi\n\n\tif git ls-remote --exit-code $repo \"refs/heads/$deploy_branch\" ; then\n\t\t# deploy_branch exists in $repo; make sure we have the latest version\n\n\t\tdisable_expanded_output\n\t\tgit fetch --force $repo $deploy_branch:$deploy_branch\n\t\tenable_expanded_output\n\tfi\n\n\t# check if deploy_branch exists locally\n\tif git show-ref --verify --quiet \"refs/heads/$deploy_branch\"\n\tthen incremental_deploy\n\telse initial_deploy\n\tfi\n\n\trestore_head\n}\n\ninitial_deploy() {\n\tgit --work-tree \"$deploy_directory\" checkout --orphan $deploy_branch\n\tgit --work-tree \"$deploy_directory\" add --all\n\tcommit+push\n}\n\nincremental_deploy() {\n\t#make deploy_branch the current branch\n\tgit symbolic-ref HEAD refs/heads/$deploy_branch\n\t#put the previously committed contents of deploy_branch into the index\n\tgit --work-tree \"$deploy_directory\" reset --mixed --quiet\n\tgit --work-tree \"$deploy_directory\" add --all\n\n\tset +o errexit\n\tdiff=$(git --work-tree \"$deploy_directory\" diff --exit-code --quiet HEAD --)$?\n\tset -o errexit\n\tcase $diff in\n\t\t0) echo No changes to files in $deploy_directory. Skipping commit.;;\n\t\t1) commit+push;;\n\t\t*)\n\t\t\techo git diff exited with code $diff. Aborting. Staying on branch $deploy_branch so you can debug. To switch back to master, use: git symbolic-ref HEAD refs/heads/master && git reset --mixed >&2\n\t\t\treturn $diff\n\t\t\t;;\n\tesac\n}\n\ncommit+push() {\n\tset_user_id\n\tgit --work-tree \"$deploy_directory\" commit -m \"$commit_message\"\n\n\tdisable_expanded_output\n\t#--quiet is important here to avoid outputting the repo URL, which may contain a secret token\n\tgit push --quiet $repo $deploy_branch\n\tenable_expanded_output\n}\n\n#echo expanded commands as they are executed (for debugging)\nenable_expanded_output() {\n\tif [ $verbose ]; then\n\t\tset -o xtrace\n\t\tset +o verbose\n\tfi\n}\n\n#this is used to avoid outputting the repo URL, which may contain a secret token\ndisable_expanded_output() {\n\tif [ $verbose ]; then\n\t\tset +o xtrace\n\t\tset -o verbose\n\tfi\n}\n\nset_user_id() {\n\tif [[ -z `git config user.name` ]]; then\n\t\tgit config user.name \"$default_username\"\n\tfi\n\tif [[ -z `git config user.email` ]]; then\n\t\tgit config user.email \"$default_email\"\n\tfi\n}\n\nrestore_head() {\n\tif [[ $previous_branch = \"HEAD\" ]]; then\n\t\t#we weren't on any branch before, so just set HEAD back to the commit it was on\n\t\tgit update-ref --no-deref HEAD $commit_hash $deploy_branch\n\telse\n\t\tgit symbolic-ref HEAD refs/heads/$previous_branch\n\tfi\n\n\tgit reset --mixed\n}\n\nfilter() {\n\tsed -e \"s|$repo|\\$repo|g\"\n}\n\nsanitize() {\n\t\"$@\" 2> >(filter 1>&2) | filter\n}\n\n[[ $1 = --source-only ]] || main \"$@\""
  },
  {
    "path": "scripts/jest",
    "content": "TZ=\"America/New_York\" NODE_ICU_DATA=\"$(pwd)/node_modules/full-icu\" LANG=en_US.utf8 jest $@"
  },
  {
    "path": "scripts/readme.md",
    "content": "These are scripts useful for development:\n\n 1. `test` is a script for more conveniently running the tests if you've installed `full-icu` as an npm module\n 1. `release`, `tag`, and `deploy-site` are administrative tasks you won't need\n"
  },
  {
    "path": "scripts/release",
    "content": "#!/usr/bin/env bash\n./scripts/version || exit $?\n\nnpm run build\nnpm run site\nnpm publish\n./scripts/tag\n./scripts/deploy-site"
  },
  {
    "path": "scripts/repl",
    "content": "#!/bin/bash\nLANG=en-US.utf8 node -i -r \"./scripts/bootstrap.js\"\n"
  },
  {
    "path": "scripts/tag",
    "content": "#!/usr/bin/env bash\n./scripts/version || exit $?\n\nPACKAGE_VERSION=$(node -p \"require('./package.json').version\")\n\ngit tag $PACKAGE_VERSION\ngit push origin master $PACKAGE_VERSION\n"
  },
  {
    "path": "scripts/test",
    "content": "#!/usr/bin/env bash\nTZ=\"America/New_York\" LANG=en_US.utf8 npm run test"
  },
  {
    "path": "scripts/version",
    "content": "#!/usr/bin/env bash\n\nabort() {\n  echo \"ERROR: $1\"\n  exit 1\n}\n\nif [[ -x $(which ggrep) ]]\nthen\n  grepper='ggrep'\nelse\n  grepper='grep'\nfi\n\nPACKAGE_VERSION=$(node -p \"require('./package.json').version\")\nPACKAGE_LOCK_VERSION=$(node -p \"require('./package-lock.json').version\")\nMODULE_VERSION=$(node -p \"require('./src/package.json').version\")\nEXPORTED_VERSION=$($grepper -oP \"(?<=const VERSION = \\\").*(?=\\\";)\" ./src/luxon.js)\n\nif [ $PACKAGE_VERSION != $PACKAGE_LOCK_VERSION ]; then\n  abort \"package-lock.json's version differs from package.json's\"\nelif [ $PACKAGE_VERSION != $EXPORTED_VERSION ]; then\n  abort \"exported version differs from package.json's\"\nelif [ $PACKAGE_VERSION != $MODULE_VERSION ]; then\n  abort \"src/package.json differs from package.json's\"\nelif [ $(git tag -l \"$PACKAGE_VERSION\") ]; then\n  abort \"tag already exists\"\nfi\n"
  },
  {
    "path": "site/.nojekyll",
    "content": ""
  },
  {
    "path": "site/demo/demo.css",
    "content": ".divider {\n  color: blue;\n}\n\n.code {\n  display: inline-block;\n  font-family: monospace;\n}\n\n.result {\n  color: darkgreen;\n  font-style: italic;\n}\n"
  },
  {
    "path": "site/demo/demo.js",
    "content": "function demo(luxon) {\n  const DateTime = luxon.DateTime;\n  const Duration = luxon.Duration;\n  const Info = luxon.Info;\n\n  const run = function (code) {\n    let result;\n    try {\n      result = eval(code);\n    } catch (e) {\n      console.error(e);\n      return \"[error]\";\n    }\n\n    switch (true) {\n      case result.isValid === false:\n        return \"Invalid\";\n      case result instanceof DateTime:\n        return \"[ DateTime \" + result.toISO() + \" ]\";\n      case result instanceof Duration:\n        return \"[ Duration \" + JSON.stringify(result.toObject()) + \" ]\";\n      case result instanceof Date:\n        return \"[ Date \" + result.toString() + \" ]\";\n      default:\n        return JSON.stringify(result);\n    }\n  };\n\n  const examples = [];\n  const example = function (code) {\n    examples.push(\n      \"<tr class='example'><td class='code'>\" +\n        code +\n        \"</td><td class='divider'>//=> </td><td class='result'>\" +\n        run(code) +\n        \"</td></tr>\"\n    );\n  };\n\n  example(\"Info.features()\");\n  example(\"DateTime.now()\");\n  example(\"DateTime.now().toUnixInteger()\");\n  example(\"DateTime.now().toJSDate()\");\n  example(\"DateTime.utc().toISO()\");\n  example(\"DateTime.utc(2017, 5, 15, 17, 36)\");\n  example(\"DateTime.utc(2017, 5, 15, 17, 36).toLocal()\");\n  example(\"DateTime.local(2017, 5, 15, 17, 36)\");\n  example(\"DateTime.local(2017, 5, 15, 17, 36).toUTC()\");\n  example(\"DateTime.now().toObject()\");\n  example(\"DateTime.fromObject({ year: 2017, month: 5, day: 15, hour: 17, minute: 36 })\");\n  example(\n    \"DateTime.fromObject({ year: 2017, month: 5, day: 15, hour: 17, minute: 36 }, { zone: 'America/New_York' })\"\n  );\n  example(\n    \"DateTime.fromObject({ year: 2017, month: 5, day: 15, hour: 17, minute: 36 }, { zone: 'Asia/Singapore' })\"\n  );\n  example(\"DateTime.now().setZone('America/New_York')\");\n  example(\"DateTime.now().setZone('America/New_York').startOf('day')\");\n  example(\"DateTime.now().plus({minutes: 15, seconds: 8})\");\n  example(\"DateTime.now().plus({days: 6})\");\n  example(\"DateTime.now().minus({days: 6})\");\n  example(\"DateTime.now().diff(DateTime.local(1982, 5, 25)).milliseconds\");\n  example(\"DateTime.now().diff(DateTime.local(1982, 5, 25), 'days').days\");\n  example(\"DateTime.now().diff(DateTime.local(1982, 5, 25), ['days', 'hours'])\");\n  example(\"DateTime.now().toLocaleString()\");\n  example(\"DateTime.now().setLocale('zh').toLocaleString()\");\n  example(\"DateTime.now().toLocaleString(DateTime.DATE_MED)\");\n  example(\"DateTime.now().setLocale('zh').toLocaleString(DateTime.DATE_MED)\");\n  example(\"DateTime.now().setLocale('fr').toLocaleString(DateTime.DATE_FULL)\");\n  example(\"DateTime.fromISO('2017-05-15')\");\n  example(\"DateTime.fromISO('2017-05-15T17:36')\");\n  example(\"DateTime.fromISO('2017-W33-4')\");\n  example(\"DateTime.fromISO('2017-W33-4T04:45:32.343')\");\n  example(\"DateTime.fromFormat('12-16-2017', 'MM-dd-yyyy')\");\n  example(\"DateTime.now().toFormat('MM-dd-yyyy')\");\n  example(\"DateTime.now().toFormat('MMMM dd, yyyy')\");\n  example(\"DateTime.now().setLocale('fr').toFormat('MMMM dd, yyyy')\");\n  example(\"DateTime.fromFormat('May 25, 1982', 'MMMM dd, yyyy')\");\n  example(\"DateTime.fromFormat('mai 25, 1982', 'MMMM dd, yyyy', { locale: 'fr' })\");\n  example(\"DateTime.now().plus({ days: 1 }).toRelativeCalendar()\");\n  example(\"DateTime.now().plus({ days: -1 }).toRelativeCalendar()\");\n  example(\"DateTime.now().plus({ months: 1 }).toRelativeCalendar()\");\n  example(\"DateTime.now().setLocale('fr').plus({ days: 1 }).toRelativeCalendar()\");\n  example(\"DateTime.now().setLocale('fr').plus({ days: -1 }).toRelativeCalendar()\");\n  example(\"DateTime.now().setLocale('fr').plus({ months: 1 }).toRelativeCalendar()\");\n\n  let all = \"<h1>Some Luxon examples</h1>\";\n  all +=\n    \"<p>This is not meant to be a comprehensive showcase of Luxon's capabilities, just a quick flavoring.</p>\";\n  all += \"<table>\";\n  all += examples.join(\"\");\n  all += \"</table>\";\n\n  document.body.innerHTML = all;\n}\n\nif (typeof define !== \"undefined\") {\n  define([\"luxon\"], function (luxon) {\n    return function () {\n      demo(luxon);\n    };\n  });\n} else {\n  window.demo = demo;\n}\n"
  },
  {
    "path": "site/demo/global.html",
    "content": "<!DOCTYPE html>\n<html>\n    <head>\n        <link rel='stylesheet' href='demo.css'/>\n        <script src='../global/luxon.js'></script>\n        <script src='demo.js'></script>\n        <script>document.addEventListener(\"DOMContentLoaded\", function(){demo(window.luxon)});</script>\n    </head>\n</html>\n"
  },
  {
    "path": "site/demo/requirejs.html",
    "content": "<!DOCTYPE html>\n<html>\n    <head>\n        <link rel='stylesheet' href='demo.css'/>\n        <script src='https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.3/require.min.js'></script>\n        <script>\n            requirejs.config({\n                paths: {\n                    luxon: '../amd/luxon.min',\n                }\n            });\n            requirejs(['demo'], function(demo) {\n                demo();\n            });\n         </script>\n    </head>\n    <body>\n    </body>\n</html>\n"
  },
  {
    "path": "site/docs/_coverpage.md",
    "content": "![logo](_media/Luxon_icon_64x64.png)\n\n# Luxon <small>3.x</small>\n\n> A powerful, modern, and friendly wrapper for JavaScript dates and times.\n\n * DateTimes, Durations, and Intervals\n * Immutable, chainable, unambiguous API.\n * Native time zone and Intl support (no locale or tz files)\n\n[GitHub](https://github.com/moment/luxon/)\n[Get started](#Luxon)\n\n![color](#e8dffc)\n"
  },
  {
    "path": "site/docs/_sidebar.md",
    "content": "* [Home](/)\n* [Install guide](install.md)\n* [A quick tour](tour.md)\n* [Upgrade guide](upgrading.md)\n* [Intl](intl.md)\n* [Time zones and offsets](zones.md)\n* [Calendars](calendars.md)\n* [Formatting](formatting.md)\n* [Parsing](parsing.md)\n* [Math](math.md)\n* [Validity](validity.md)\n* [API docs](api-docs/index.html ':ignore')\n* [Support matrix](matrix.md)\n* [For Moment users](moment.md)\n* [Why does Luxon exist?](why.md)\n\n"
  },
  {
    "path": "site/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"UTF-8\">\n  <title>luxon - Immutable date wrapper</title>\n  <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge,chrome=1\" />\n  <meta name=\"description\" content=\"Immutable date wrapper\">\n  <meta name=\"viewport\" content=\"width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0\">\n  <link rel=\"stylesheet\" href=\"https://unpkg.com/docsify/lib/themes/vue.css\">\n  <link rel=\"stylesheet\" href=\"https://unpkg.com/docsify/lib/themes/dark.css\">\n  <link rel=\"stylesheet\" href=\"plugins/dark-theme-toggle.css\">\n  <style>\n    .dark .sidebar li {\n      margin-right: 0;\n    }\n    .dark .cover.show {\n      background-color: rgb(79 58 120) !important;\n    }\n\n    .dark .markdown-section p.tip, .dark .markdown-section tr:nth-child(2n) {\n      background-color: #4c4c4c;\n    }\n  </style>\n</head>\n<body>\n  <div id=\"app\"></div>\n  <script>\n    window.$docsify = {\n      search: 'auto',\n      basePath: \"docs/\",\n      name: \"luxon\",\n      repo: \"https://github.com/moment/luxon\",\n      homepage: \"home.md\",\n      loadSidebar: true,\n      subMaxLevel: 3,\n      coverpage: true,\n    }\n  </script>\n  <script src=\"https://unpkg.com/docsify/lib/docsify.min.js\"></script>\n  <script src=\"https://unpkg.com/docsify/lib/plugins/search.min.js\"></script>\n  <script src=\"plugins/dark-theme-toggle.js\"></script>\n  <script src=\"global/luxon.js\"></script>\n  <script>\n    var DateTime = luxon.DateTime;\n    console.log(\n      \"You can try Luxon right here using the `luxon` global, like `luxon.DateTime.now()`.\"\n    );\n  </script>\n</body>\n</html>\n"
  },
  {
    "path": "site/plugins/dark-theme-toggle.css",
    "content": "#docsify-dark-theme-toggle {\n  position: absolute;\n  display: inline-block;\n  width: 52px;\n  height: 28px;\n  margin-left: 2rem;\n  margin-top: 1.5rem;\n  left: 0;\n  top: 0;\n  z-index: 0;\n  cursor: pointer;\n}\n.sidebar > .app-name {\n  position: relative;\n}\n.app-name > #docsify-dark-theme-toggle {\n  margin-right: 1rem;\n  margin-top: 0;\n  right: 0;\n  left: unset;\n}\n#docsify-dark-theme-toggle::before, #docsify-dark-theme-toggle::after {\n  position: absolute;\n  top: 0.1em;\n  font-size: 16px;\n  transition: opacity 0.3s;\n}\n#docsify-dark-theme-toggle::before {\n  content: \"🌙\";\n  left: 0.1em;\n  opacity: 0;\n  z-index: 1;\n}\n#docsify-dark-theme-toggle::after {\n  content: \"🌞\";\n  right: 0.1em;\n  opacity: 1;\n}\n#docsify-dark-theme-toggle > span {\n  position: absolute;\n  top: 0;\n  left: 0;\n  right: 0;\n  bottom: 0;\n  background-color: var(--theme-color, #42b983);\n  border-radius: 28px;\n  transition: background-color 0.3s;\n}\n#docsify-dark-theme-toggle > span::before {\n  content: \"\";\n  position: absolute;\n  height: 22px;\n  width: 22px;\n  left: 3px;\n  top: 3px;\n  z-index: 1;\n  background-color: white;\n  border-radius: 50%;\n  transition: transform 0.3s;\n}\n.dark #docsify-dark-theme-toggle::after {\n  opacity: 0;\n}\n.dark #docsify-dark-theme-toggle::before {\n  opacity: 1;\n}\n.dark #docsify-dark-theme-toggle > span {\n  background-color: var(--theme-color, #ea6f5a);\n}\n.dark #docsify-dark-theme-toggle > span::before {\n  transform: translateX(24px);\n}"
  },
  {
    "path": "site/plugins/dark-theme-toggle.js",
    "content": "(() => {\n  const darkThemeTogglePlugin = (hook, vm) => {\n    const TOGGLE_ID = \"docsify-dark-theme-toggle\",\n      dom = Docsify.dom,\n      darkThemeStyleSheet = dom.find('link[href$=\"dark.css\"]'),\n      toggleEl = dom.create(\"div\", \"<span />\"),\n      applyTheme = (swap = false) => {\n        const isDark = Boolean(swap ^ (localStorage[TOGGLE_ID] == \"true\"));\n        localStorage[TOGGLE_ID] = isDark;\n        darkThemeStyleSheet.disabled = !isDark;\n        dom.toggleClass(dom.body, isDark ? \"add\" : \"remove\", \"dark\");\n      };\n    localStorage[TOGGLE_ID] ??= matchMedia(\"(prefers-color-scheme: dark)\").matches;\n    toggleEl.id = TOGGLE_ID;\n    dom.on(toggleEl, \"click\", () => applyTheme(true));\n    hook.init(applyTheme);\n    hook.doneEach(() => dom.before(dom.find(\".cover.show, .sidebar > .app-name\"), toggleEl));\n  };\n  $docsify ??= {};\n  $docsify.plugins = [...($docsify.plugins ?? []), darkThemeTogglePlugin];\n})();\n"
  },
  {
    "path": "src/datetime.js",
    "content": "import Duration from \"./duration.js\";\nimport Interval from \"./interval.js\";\nimport Settings from \"./settings.js\";\nimport Info from \"./info.js\";\nimport Formatter from \"./impl/formatter.js\";\nimport FixedOffsetZone from \"./zones/fixedOffsetZone.js\";\nimport Locale from \"./impl/locale.js\";\nimport {\n  isUndefined,\n  maybeArray,\n  isDate,\n  isNumber,\n  bestBy,\n  daysInMonth,\n  daysInYear,\n  isLeapYear,\n  weeksInWeekYear,\n  normalizeObject,\n  roundTo,\n  objToLocalTS,\n  padStart,\n} from \"./impl/util.js\";\nimport { normalizeZone } from \"./impl/zoneUtil.js\";\nimport diff from \"./impl/diff.js\";\nimport { parseRFC2822Date, parseISODate, parseHTTPDate, parseSQL } from \"./impl/regexParser.js\";\nimport {\n  parseFromTokens,\n  explainFromTokens,\n  formatOptsToTokens,\n  expandMacroTokens,\n  TokenParser,\n} from \"./impl/tokenParser.js\";\nimport {\n  gregorianToWeek,\n  weekToGregorian,\n  gregorianToOrdinal,\n  ordinalToGregorian,\n  hasInvalidGregorianData,\n  hasInvalidWeekData,\n  hasInvalidOrdinalData,\n  hasInvalidTimeData,\n  usesLocalWeekValues,\n  isoWeekdayToLocal,\n} from \"./impl/conversions.js\";\nimport * as Formats from \"./impl/formats.js\";\nimport {\n  InvalidArgumentError,\n  ConflictingSpecificationError,\n  InvalidUnitError,\n  InvalidDateTimeError,\n} from \"./errors.js\";\nimport Invalid from \"./impl/invalid.js\";\n\nconst INVALID = \"Invalid DateTime\";\nconst MAX_DATE = 8.64e15;\n\nfunction unsupportedZone(zone) {\n  return new Invalid(\"unsupported zone\", `the zone \"${zone.name}\" is not supported`);\n}\n\n// we cache week data on the DT object and this intermediates the cache\n/**\n * @param {DateTime} dt\n */\nfunction possiblyCachedWeekData(dt) {\n  if (dt.weekData === null) {\n    dt.weekData = gregorianToWeek(dt.c);\n  }\n  return dt.weekData;\n}\n\n/**\n * @param {DateTime} dt\n */\nfunction possiblyCachedLocalWeekData(dt) {\n  if (dt.localWeekData === null) {\n    dt.localWeekData = gregorianToWeek(\n      dt.c,\n      dt.loc.getMinDaysInFirstWeek(),\n      dt.loc.getStartOfWeek()\n    );\n  }\n  return dt.localWeekData;\n}\n\n// clone really means, \"make a new object with these modifications\". all \"setters\" really use this\n// to create a new object while only changing some of the properties\nfunction clone(inst, alts) {\n  const current = {\n    ts: inst.ts,\n    zone: inst.zone,\n    c: inst.c,\n    o: inst.o,\n    loc: inst.loc,\n    invalid: inst.invalid,\n  };\n  return new DateTime({ ...current, ...alts, old: current });\n}\n\n// find the right offset a given local time. The o input is our guess, which determines which\n// offset we'll pick in ambiguous cases (e.g. there are two 3 AMs b/c Fallback DST)\nfunction fixOffset(localTS, o, tz) {\n  // Our UTC time is just a guess because our offset is just a guess\n  let utcGuess = localTS - o * 60 * 1000;\n\n  // Test whether the zone matches the offset for this ts\n  const o2 = tz.offset(utcGuess);\n\n  // If so, offset didn't change and we're done\n  if (o === o2) {\n    return [utcGuess, o];\n  }\n\n  // If not, change the ts by the difference in the offset\n  utcGuess -= (o2 - o) * 60 * 1000;\n\n  // If that gives us the local time we want, we're done\n  const o3 = tz.offset(utcGuess);\n  if (o2 === o3) {\n    return [utcGuess, o2];\n  }\n\n  // If it's different, we're in a hole time. The offset has changed, but the we don't adjust the time\n  return [localTS - Math.min(o2, o3) * 60 * 1000, Math.max(o2, o3)];\n}\n\n// convert an epoch timestamp into a calendar object with the given offset\nfunction tsToObj(ts, offset) {\n  ts += offset * 60 * 1000;\n\n  const d = new Date(ts);\n\n  return {\n    year: d.getUTCFullYear(),\n    month: d.getUTCMonth() + 1,\n    day: d.getUTCDate(),\n    hour: d.getUTCHours(),\n    minute: d.getUTCMinutes(),\n    second: d.getUTCSeconds(),\n    millisecond: d.getUTCMilliseconds(),\n  };\n}\n\n// convert a calendar object to a epoch timestamp\nfunction objToTS(obj, offset, zone) {\n  return fixOffset(objToLocalTS(obj), offset, zone);\n}\n\n// create a new DT instance by adding a duration, adjusting for DSTs\nfunction adjustTime(inst, dur) {\n  const oPre = inst.o,\n    year = inst.c.year + Math.trunc(dur.years),\n    month = inst.c.month + Math.trunc(dur.months) + Math.trunc(dur.quarters) * 3,\n    c = {\n      ...inst.c,\n      year,\n      month,\n      day:\n        Math.min(inst.c.day, daysInMonth(year, month)) +\n        Math.trunc(dur.days) +\n        Math.trunc(dur.weeks) * 7,\n    },\n    millisToAdd = Duration.fromObject({\n      years: dur.years - Math.trunc(dur.years),\n      quarters: dur.quarters - Math.trunc(dur.quarters),\n      months: dur.months - Math.trunc(dur.months),\n      weeks: dur.weeks - Math.trunc(dur.weeks),\n      days: dur.days - Math.trunc(dur.days),\n      hours: dur.hours,\n      minutes: dur.minutes,\n      seconds: dur.seconds,\n      milliseconds: dur.milliseconds,\n    }).as(\"milliseconds\"),\n    localTS = objToLocalTS(c);\n\n  let [ts, o] = fixOffset(localTS, oPre, inst.zone);\n\n  if (millisToAdd !== 0) {\n    ts += millisToAdd;\n    // that could have changed the offset by going over a DST, but we want to keep the ts the same\n    o = inst.zone.offset(ts);\n  }\n\n  return { ts, o };\n}\n\n// helper useful in turning the results of parsing into real dates\n// by handling the zone options\nexport function parseDataToDateTime(parsed, parsedZone, opts, format, text, specificOffset) {\n  const { setZone, zone } = opts;\n  if ((parsed && Object.keys(parsed).length !== 0) || parsedZone) {\n    const interpretationZone = parsedZone || zone,\n      inst = DateTime.fromObject(parsed, {\n        ...opts,\n        zone: interpretationZone,\n        specificOffset,\n      });\n    return setZone ? inst : inst.setZone(zone);\n  } else {\n    return DateTime.invalid(\n      new Invalid(\"unparsable\", `the input \"${text}\" can't be parsed as ${format}`)\n    );\n  }\n}\n\n// if you want to output a technical format (e.g. RFC 2822), this helper\n// helps handle the details\nfunction toTechFormat(dt, format, allowZ = true) {\n  return dt.isValid\n    ? Formatter.create(Locale.create(\"en-US\"), {\n        allowZ,\n        forceSimple: true,\n      }).formatDateTimeFromString(dt, format)\n    : null;\n}\n\nfunction toISODate(o, extended, precision) {\n  const longFormat = o.c.year > 9999 || o.c.year < 0;\n  let c = \"\";\n  if (longFormat && o.c.year >= 0) c += \"+\";\n  c += padStart(o.c.year, longFormat ? 6 : 4);\n  if (precision === \"year\") return c;\n  if (extended) {\n    c += \"-\";\n    c += padStart(o.c.month);\n    if (precision === \"month\") return c;\n    c += \"-\";\n  } else {\n    c += padStart(o.c.month);\n    if (precision === \"month\") return c;\n  }\n  c += padStart(o.c.day);\n  return c;\n}\n\nfunction toISOTime(\n  o,\n  extended,\n  suppressSeconds,\n  suppressMilliseconds,\n  includeOffset,\n  extendedZone,\n  precision\n) {\n  let showSeconds = !suppressSeconds || o.c.millisecond !== 0 || o.c.second !== 0,\n    c = \"\";\n  switch (precision) {\n    case \"day\":\n    case \"month\":\n    case \"year\":\n      break;\n    default:\n      c += padStart(o.c.hour);\n      if (precision === \"hour\") break;\n      if (extended) {\n        c += \":\";\n        c += padStart(o.c.minute);\n        if (precision === \"minute\") break;\n        if (showSeconds) {\n          c += \":\";\n          c += padStart(o.c.second);\n        }\n      } else {\n        c += padStart(o.c.minute);\n        if (precision === \"minute\") break;\n        if (showSeconds) {\n          c += padStart(o.c.second);\n        }\n      }\n      if (precision === \"second\") break;\n      if (showSeconds && (!suppressMilliseconds || o.c.millisecond !== 0)) {\n        c += \".\";\n        c += padStart(o.c.millisecond, 3);\n      }\n  }\n\n  if (includeOffset) {\n    if (o.isOffsetFixed && o.offset === 0 && !extendedZone) {\n      c += \"Z\";\n    } else if (o.o < 0) {\n      c += \"-\";\n      c += padStart(Math.trunc(-o.o / 60));\n      if (extended) {\n        c += \":\";\n      }\n      c += padStart(Math.trunc(-o.o % 60));\n    } else {\n      c += \"+\";\n      c += padStart(Math.trunc(o.o / 60));\n      if (extended) {\n        c += \":\";\n      }\n      c += padStart(Math.trunc(o.o % 60));\n    }\n  }\n\n  if (extendedZone) {\n    c += \"[\" + o.zone.ianaName + \"]\";\n  }\n  return c;\n}\n\n// defaults for unspecified units in the supported calendars\nconst defaultUnitValues = {\n    month: 1,\n    day: 1,\n    hour: 0,\n    minute: 0,\n    second: 0,\n    millisecond: 0,\n  },\n  defaultWeekUnitValues = {\n    weekNumber: 1,\n    weekday: 1,\n    hour: 0,\n    minute: 0,\n    second: 0,\n    millisecond: 0,\n  },\n  defaultOrdinalUnitValues = {\n    ordinal: 1,\n    hour: 0,\n    minute: 0,\n    second: 0,\n    millisecond: 0,\n  };\n\n// Units in the supported calendars, sorted by bigness\nconst orderedUnits = [\"year\", \"month\", \"day\", \"hour\", \"minute\", \"second\", \"millisecond\"],\n  orderedWeekUnits = [\n    \"weekYear\",\n    \"weekNumber\",\n    \"weekday\",\n    \"hour\",\n    \"minute\",\n    \"second\",\n    \"millisecond\",\n  ],\n  orderedOrdinalUnits = [\"year\", \"ordinal\", \"hour\", \"minute\", \"second\", \"millisecond\"];\n\n// standardize case and plurality in units\nfunction normalizeUnit(unit) {\n  const normalized = {\n    year: \"year\",\n    years: \"year\",\n    month: \"month\",\n    months: \"month\",\n    day: \"day\",\n    days: \"day\",\n    hour: \"hour\",\n    hours: \"hour\",\n    minute: \"minute\",\n    minutes: \"minute\",\n    quarter: \"quarter\",\n    quarters: \"quarter\",\n    second: \"second\",\n    seconds: \"second\",\n    millisecond: \"millisecond\",\n    milliseconds: \"millisecond\",\n    weekday: \"weekday\",\n    weekdays: \"weekday\",\n    weeknumber: \"weekNumber\",\n    weeksnumber: \"weekNumber\",\n    weeknumbers: \"weekNumber\",\n    weekyear: \"weekYear\",\n    weekyears: \"weekYear\",\n    ordinal: \"ordinal\",\n  }[unit.toLowerCase()];\n\n  if (!normalized) throw new InvalidUnitError(unit);\n\n  return normalized;\n}\n\nfunction normalizeUnitWithLocalWeeks(unit) {\n  switch (unit.toLowerCase()) {\n    case \"localweekday\":\n    case \"localweekdays\":\n      return \"localWeekday\";\n    case \"localweeknumber\":\n    case \"localweeknumbers\":\n      return \"localWeekNumber\";\n    case \"localweekyear\":\n    case \"localweekyears\":\n      return \"localWeekYear\";\n    default:\n      return normalizeUnit(unit);\n  }\n}\n\n// cache offsets for zones based on the current timestamp when this function is\n// first called. When we are handling a datetime from components like (year,\n// month, day, hour) in a time zone, we need a guess about what the timezone\n// offset is so that we can convert into a UTC timestamp. One way is to find the\n// offset of now in the zone. The actual date may have a different offset (for\n// example, if we handle a date in June while we're in December in a zone that\n// observes DST), but we can check and adjust that.\n//\n// When handling many dates, calculating the offset for now every time is\n// expensive. It's just a guess, so we can cache the offset to use even if we\n// are right on a time change boundary (we'll just correct in the other\n// direction). Using a timestamp from first read is a slight optimization for\n// handling dates close to the current date, since those dates will usually be\n// in the same offset (we could set the timestamp statically, instead). We use a\n// single timestamp for all zones to make things a bit more predictable.\n//\n// This is safe for quickDT (used by local() and utc()) because we don't fill in\n// higher-order units from tsNow (as we do in fromObject, this requires that\n// offset is calculated from tsNow).\n/**\n * @param {Zone} zone\n * @return {number}\n */\nfunction guessOffsetForZone(zone) {\n  if (zoneOffsetTs === undefined) {\n    zoneOffsetTs = Settings.now();\n  }\n\n  // Do not cache anything but IANA zones, because it is not safe to do so.\n  // Guessing an offset which is not present in the zone can cause wrong results from fixOffset\n  if (zone.type !== \"iana\") {\n    return zone.offset(zoneOffsetTs);\n  }\n  const zoneName = zone.name;\n  let offsetGuess = zoneOffsetGuessCache.get(zoneName);\n  if (offsetGuess === undefined) {\n    offsetGuess = zone.offset(zoneOffsetTs);\n    zoneOffsetGuessCache.set(zoneName, offsetGuess);\n  }\n  return offsetGuess;\n}\n\n// this is a dumbed down version of fromObject() that runs about 60% faster\n// but doesn't do any validation, makes a bunch of assumptions about what units\n// are present, and so on.\nfunction quickDT(obj, opts) {\n  const zone = normalizeZone(opts.zone, Settings.defaultZone);\n  if (!zone.isValid) {\n    return DateTime.invalid(unsupportedZone(zone));\n  }\n\n  const loc = Locale.fromObject(opts);\n\n  let ts, o;\n\n  // assume we have the higher-order units\n  if (!isUndefined(obj.year)) {\n    for (const u of orderedUnits) {\n      if (isUndefined(obj[u])) {\n        obj[u] = defaultUnitValues[u];\n      }\n    }\n\n    const invalid = hasInvalidGregorianData(obj) || hasInvalidTimeData(obj);\n    if (invalid) {\n      return DateTime.invalid(invalid);\n    }\n\n    const offsetProvis = guessOffsetForZone(zone);\n    [ts, o] = objToTS(obj, offsetProvis, zone);\n  } else {\n    ts = Settings.now();\n  }\n\n  return new DateTime({ ts, zone, loc, o });\n}\n\nfunction diffRelative(start, end, opts) {\n  const round = isUndefined(opts.round) ? true : opts.round,\n    rounding = isUndefined(opts.rounding) ? \"trunc\" : opts.rounding,\n    format = (c, unit) => {\n      c = roundTo(c, round || opts.calendary ? 0 : 2, opts.calendary ? \"round\" : rounding);\n      const formatter = end.loc.clone(opts).relFormatter(opts);\n      return formatter.format(c, unit);\n    },\n    differ = (unit) => {\n      if (opts.calendary) {\n        if (!end.hasSame(start, unit)) {\n          return end.startOf(unit).diff(start.startOf(unit), unit).get(unit);\n        } else return 0;\n      } else {\n        return end.diff(start, unit).get(unit);\n      }\n    };\n\n  if (opts.unit) {\n    return format(differ(opts.unit), opts.unit);\n  }\n\n  for (const unit of opts.units) {\n    const count = differ(unit);\n    if (Math.abs(count) >= 1) {\n      return format(count, unit);\n    }\n  }\n  return format(start > end ? -0 : 0, opts.units[opts.units.length - 1]);\n}\n\nfunction lastOpts(argList) {\n  let opts = {},\n    args;\n  if (argList.length > 0 && typeof argList[argList.length - 1] === \"object\") {\n    opts = argList[argList.length - 1];\n    args = Array.from(argList).slice(0, argList.length - 1);\n  } else {\n    args = Array.from(argList);\n  }\n  return [opts, args];\n}\n\n/**\n * Timestamp to use for cached zone offset guesses (exposed for test)\n */\nlet zoneOffsetTs;\n/**\n * Cache for zone offset guesses (exposed for test).\n *\n * This optimizes quickDT via guessOffsetForZone to avoid repeated calls of\n * zone.offset().\n */\nconst zoneOffsetGuessCache = new Map();\n\n/**\n * A DateTime is an immutable data structure representing a specific date and time and accompanying methods. It contains class and instance methods for creating, parsing, interrogating, transforming, and formatting them.\n *\n * A DateTime comprises of:\n * * A timestamp. Each DateTime instance refers to a specific millisecond of the Unix epoch.\n * * A time zone. Each instance is considered in the context of a specific zone (by default the local system's zone).\n * * Configuration properties that effect how output strings are formatted, such as `locale`, `numberingSystem`, and `outputCalendar`.\n *\n * Here is a brief overview of the most commonly used functionality it provides:\n *\n * * **Creation**: To create a DateTime from its components, use one of its factory class methods: {@link DateTime.local}, {@link DateTime.utc}, and (most flexibly) {@link DateTime.fromObject}. To create one from a standard string format, use {@link DateTime.fromISO}, {@link DateTime.fromHTTP}, and {@link DateTime.fromRFC2822}. To create one from a custom string format, use {@link DateTime.fromFormat}. To create one from a native JS date, use {@link DateTime.fromJSDate}.\n * * **Gregorian calendar and time**: To examine the Gregorian properties of a DateTime individually (i.e as opposed to collectively through {@link DateTime#toObject}), use the {@link DateTime#year}, {@link DateTime#month},\n * {@link DateTime#day}, {@link DateTime#hour}, {@link DateTime#minute}, {@link DateTime#second}, {@link DateTime#millisecond} accessors.\n * * **Week calendar**: For ISO week calendar attributes, see the {@link DateTime#weekYear}, {@link DateTime#weekNumber}, and {@link DateTime#weekday} accessors.\n * * **Configuration** See the {@link DateTime#locale} and {@link DateTime#numberingSystem} accessors.\n * * **Transformation**: To transform the DateTime into other DateTimes, use {@link DateTime#set}, {@link DateTime#reconfigure}, {@link DateTime#setZone}, {@link DateTime#setLocale}, {@link DateTime.plus}, {@link DateTime#minus}, {@link DateTime#endOf}, {@link DateTime#startOf}, {@link DateTime#toUTC}, and {@link DateTime#toLocal}.\n * * **Output**: To convert the DateTime to other representations, use the {@link DateTime#toRelative}, {@link DateTime#toRelativeCalendar}, {@link DateTime#toJSON}, {@link DateTime#toISO}, {@link DateTime#toHTTP}, {@link DateTime#toObject}, {@link DateTime#toRFC2822}, {@link DateTime#toString}, {@link DateTime#toLocaleString}, {@link DateTime#toFormat}, {@link DateTime#toMillis} and {@link DateTime#toJSDate}.\n *\n * There's plenty others documented below. In addition, for more information on subtler topics like internationalization, time zones, alternative calendars, validity, and so on, see the external documentation.\n */\nexport default class DateTime {\n  /**\n   * @access private\n   */\n  constructor(config) {\n    const zone = config.zone || Settings.defaultZone;\n\n    let invalid =\n      config.invalid ||\n      (Number.isNaN(config.ts) ? new Invalid(\"invalid input\") : null) ||\n      (!zone.isValid ? unsupportedZone(zone) : null);\n    /**\n     * @access private\n     */\n    this.ts = isUndefined(config.ts) ? Settings.now() : config.ts;\n\n    let c = null,\n      o = null;\n    if (!invalid) {\n      const unchanged = config.old && config.old.ts === this.ts && config.old.zone.equals(zone);\n\n      if (unchanged) {\n        [c, o] = [config.old.c, config.old.o];\n      } else {\n        // If an offset has been passed and we have not been called from\n        // clone(), we can trust it and avoid the offset calculation.\n        const ot = isNumber(config.o) && !config.old ? config.o : zone.offset(this.ts);\n        c = tsToObj(this.ts, ot);\n        invalid = Number.isNaN(c.year) ? new Invalid(\"invalid input\") : null;\n        c = invalid ? null : c;\n        o = invalid ? null : ot;\n      }\n    }\n\n    /**\n     * @access private\n     */\n    this._zone = zone;\n    /**\n     * @access private\n     */\n    this.loc = config.loc || Locale.create();\n    /**\n     * @access private\n     */\n    this.invalid = invalid;\n    /**\n     * @access private\n     */\n    this.weekData = null;\n    /**\n     * @access private\n     */\n    this.localWeekData = null;\n    /**\n     * @access private\n     */\n    this.c = c;\n    /**\n     * @access private\n     */\n    this.o = o;\n    /**\n     * @access private\n     */\n    this.isLuxonDateTime = true;\n  }\n\n  // CONSTRUCT\n\n  /**\n   * Create a DateTime for the current instant, in the system's time zone.\n   *\n   * Use Settings to override these default values if needed.\n   * @example DateTime.now().toISO() //~> now in the ISO format\n   * @return {DateTime}\n   */\n  static now() {\n    return new DateTime({});\n  }\n\n  /**\n   * Create a local DateTime\n   * @param {number} [year] - The calendar year. If omitted (as in, call `local()` with no arguments), the current time will be used\n   * @param {number} [month=1] - The month, 1-indexed\n   * @param {number} [day=1] - The day of the month, 1-indexed\n   * @param {number} [hour=0] - The hour of the day, in 24-hour time\n   * @param {number} [minute=0] - The minute of the hour, meaning a number between 0 and 59\n   * @param {number} [second=0] - The second of the minute, meaning a number between 0 and 59\n   * @param {number} [millisecond=0] - The millisecond of the second, meaning a number between 0 and 999\n   * @example DateTime.local()                                  //~> now\n   * @example DateTime.local({ zone: \"America/New_York\" })      //~> now, in US east coast time\n   * @example DateTime.local(2017)                              //~> 2017-01-01T00:00:00\n   * @example DateTime.local(2017, 3)                           //~> 2017-03-01T00:00:00\n   * @example DateTime.local(2017, 3, 12, { locale: \"fr\" })     //~> 2017-03-12T00:00:00, with a French locale\n   * @example DateTime.local(2017, 3, 12, 5)                    //~> 2017-03-12T05:00:00\n   * @example DateTime.local(2017, 3, 12, 5, { zone: \"utc\" })   //~> 2017-03-12T05:00:00, in UTC\n   * @example DateTime.local(2017, 3, 12, 5, 45)                //~> 2017-03-12T05:45:00\n   * @example DateTime.local(2017, 3, 12, 5, 45, 10)            //~> 2017-03-12T05:45:10\n   * @example DateTime.local(2017, 3, 12, 5, 45, 10, 765)       //~> 2017-03-12T05:45:10.765\n   * @return {DateTime}\n   */\n  static local() {\n    const [opts, args] = lastOpts(arguments),\n      [year, month, day, hour, minute, second, millisecond] = args;\n    return quickDT({ year, month, day, hour, minute, second, millisecond }, opts);\n  }\n\n  /**\n   * Create a DateTime in UTC\n   * @param {number} [year] - The calendar year. If omitted (as in, call `utc()` with no arguments), the current time will be used\n   * @param {number} [month=1] - The month, 1-indexed\n   * @param {number} [day=1] - The day of the month\n   * @param {number} [hour=0] - The hour of the day, in 24-hour time\n   * @param {number} [minute=0] - The minute of the hour, meaning a number between 0 and 59\n   * @param {number} [second=0] - The second of the minute, meaning a number between 0 and 59\n   * @param {number} [millisecond=0] - The millisecond of the second, meaning a number between 0 and 999\n   * @param {Object} options - configuration options for the DateTime\n   * @param {string} [options.locale] - a locale to set on the resulting DateTime instance\n   * @param {string} [options.outputCalendar] - the output calendar to set on the resulting DateTime instance\n   * @param {string} [options.numberingSystem] - the numbering system to set on the resulting DateTime instance\n   * @param {string} [options.weekSettings] - the week settings to set on the resulting DateTime instance\n   * @example DateTime.utc()                                              //~> now\n   * @example DateTime.utc(2017)                                          //~> 2017-01-01T00:00:00Z\n   * @example DateTime.utc(2017, 3)                                       //~> 2017-03-01T00:00:00Z\n   * @example DateTime.utc(2017, 3, 12)                                   //~> 2017-03-12T00:00:00Z\n   * @example DateTime.utc(2017, 3, 12, 5)                                //~> 2017-03-12T05:00:00Z\n   * @example DateTime.utc(2017, 3, 12, 5, 45)                            //~> 2017-03-12T05:45:00Z\n   * @example DateTime.utc(2017, 3, 12, 5, 45, { locale: \"fr\" })          //~> 2017-03-12T05:45:00Z with a French locale\n   * @example DateTime.utc(2017, 3, 12, 5, 45, 10)                        //~> 2017-03-12T05:45:10Z\n   * @example DateTime.utc(2017, 3, 12, 5, 45, 10, 765, { locale: \"fr\" }) //~> 2017-03-12T05:45:10.765Z with a French locale\n   * @return {DateTime}\n   */\n  static utc() {\n    const [opts, args] = lastOpts(arguments),\n      [year, month, day, hour, minute, second, millisecond] = args;\n\n    opts.zone = FixedOffsetZone.utcInstance;\n    return quickDT({ year, month, day, hour, minute, second, millisecond }, opts);\n  }\n\n  /**\n   * Create a DateTime from a JavaScript Date object. Uses the default zone.\n   * @param {Date} date - a JavaScript Date object\n   * @param {Object} options - configuration options for the DateTime\n   * @param {string|Zone} [options.zone='local'] - the zone to place the DateTime into\n   * @return {DateTime}\n   */\n  static fromJSDate(date, options = {}) {\n    const ts = isDate(date) ? date.valueOf() : NaN;\n    if (Number.isNaN(ts)) {\n      return DateTime.invalid(\"invalid input\");\n    }\n\n    const zoneToUse = normalizeZone(options.zone, Settings.defaultZone);\n    if (!zoneToUse.isValid) {\n      return DateTime.invalid(unsupportedZone(zoneToUse));\n    }\n\n    return new DateTime({\n      ts: ts,\n      zone: zoneToUse,\n      loc: Locale.fromObject(options),\n    });\n  }\n\n  /**\n   * Create a DateTime from a number of milliseconds since the epoch (meaning since 1 January 1970 00:00:00 UTC). Uses the default zone.\n   * @param {number} milliseconds - a number of milliseconds since 1970 UTC\n   * @param {Object} options - configuration options for the DateTime\n   * @param {string|Zone} [options.zone='local'] - the zone to place the DateTime into\n   * @param {string} [options.locale] - a locale to set on the resulting DateTime instance\n   * @param {string} options.outputCalendar - the output calendar to set on the resulting DateTime instance\n   * @param {string} options.numberingSystem - the numbering system to set on the resulting DateTime instance\n   * @param {string} options.weekSettings - the week settings to set on the resulting DateTime instance\n   * @return {DateTime}\n   */\n  static fromMillis(milliseconds, options = {}) {\n    if (!isNumber(milliseconds)) {\n      throw new InvalidArgumentError(\n        `fromMillis requires a numerical input, but received a ${typeof milliseconds} with value ${milliseconds}`\n      );\n    } else if (milliseconds < -MAX_DATE || milliseconds > MAX_DATE) {\n      // this isn't perfect because we can still end up out of range because of additional shifting, but it's a start\n      return DateTime.invalid(\"Timestamp out of range\");\n    } else {\n      return new DateTime({\n        ts: milliseconds,\n        zone: normalizeZone(options.zone, Settings.defaultZone),\n        loc: Locale.fromObject(options),\n      });\n    }\n  }\n\n  /**\n   * Create a DateTime from a number of seconds since the epoch (meaning since 1 January 1970 00:00:00 UTC). Uses the default zone.\n   * @param {number} seconds - a number of seconds since 1970 UTC\n   * @param {Object} options - configuration options for the DateTime\n   * @param {string|Zone} [options.zone='local'] - the zone to place the DateTime into\n   * @param {string} [options.locale] - a locale to set on the resulting DateTime instance\n   * @param {string} options.outputCalendar - the output calendar to set on the resulting DateTime instance\n   * @param {string} options.numberingSystem - the numbering system to set on the resulting DateTime instance\n   * @param {string} options.weekSettings - the week settings to set on the resulting DateTime instance\n   * @return {DateTime}\n   */\n  static fromSeconds(seconds, options = {}) {\n    if (!isNumber(seconds)) {\n      throw new InvalidArgumentError(\"fromSeconds requires a numerical input\");\n    } else {\n      return new DateTime({\n        ts: seconds * 1000,\n        zone: normalizeZone(options.zone, Settings.defaultZone),\n        loc: Locale.fromObject(options),\n      });\n    }\n  }\n\n  /**\n   * Create a DateTime from a JavaScript object with keys like 'year' and 'hour' with reasonable defaults.\n   * @param {Object} obj - the object to create the DateTime from\n   * @param {number} obj.year - a year, such as 1987\n   * @param {number} obj.month - a month, 1-12\n   * @param {number} obj.day - a day of the month, 1-31, depending on the month\n   * @param {number} obj.ordinal - day of the year, 1-365 or 366\n   * @param {number} obj.weekYear - an ISO week year\n   * @param {number} obj.weekNumber - an ISO week number, between 1 and 52 or 53, depending on the year\n   * @param {number} obj.weekday - an ISO weekday, 1-7, where 1 is Monday and 7 is Sunday\n   * @param {number} obj.localWeekYear - a week year, according to the locale\n   * @param {number} obj.localWeekNumber - a week number, between 1 and 52 or 53, depending on the year, according to the locale\n   * @param {number} obj.localWeekday - a weekday, 1-7, where 1 is the first and 7 is the last day of the week, according to the locale\n   * @param {number} obj.hour - hour of the day, 0-23\n   * @param {number} obj.minute - minute of the hour, 0-59\n   * @param {number} obj.second - second of the minute, 0-59\n   * @param {number} obj.millisecond - millisecond of the second, 0-999\n   * @param {Object} opts - options for creating this DateTime\n   * @param {string|Zone} [opts.zone='local'] - interpret the numbers in the context of a particular zone. Can take any value taken as the first argument to setZone()\n   * @param {string} [opts.locale='system\\'s locale'] - a locale to set on the resulting DateTime instance\n   * @param {string} opts.outputCalendar - the output calendar to set on the resulting DateTime instance\n   * @param {string} opts.numberingSystem - the numbering system to set on the resulting DateTime instance\n   * @param {string} opts.weekSettings - the week settings to set on the resulting DateTime instance\n   * @example DateTime.fromObject({ year: 1982, month: 5, day: 25}).toISODate() //=> '1982-05-25'\n   * @example DateTime.fromObject({ year: 1982 }).toISODate() //=> '1982-01-01'\n   * @example DateTime.fromObject({ hour: 10, minute: 26, second: 6 }) //~> today at 10:26:06\n   * @example DateTime.fromObject({ hour: 10, minute: 26, second: 6 }, { zone: 'utc' }),\n   * @example DateTime.fromObject({ hour: 10, minute: 26, second: 6 }, { zone: 'local' })\n   * @example DateTime.fromObject({ hour: 10, minute: 26, second: 6 }, { zone: 'America/New_York' })\n   * @example DateTime.fromObject({ weekYear: 2016, weekNumber: 2, weekday: 3 }).toISODate() //=> '2016-01-13'\n   * @example DateTime.fromObject({ localWeekYear: 2022, localWeekNumber: 1, localWeekday: 1 }, { locale: \"en-US\" }).toISODate() //=> '2021-12-26'\n   * @return {DateTime}\n   */\n  static fromObject(obj, opts = {}) {\n    obj = obj || {};\n    const zoneToUse = normalizeZone(opts.zone, Settings.defaultZone);\n    if (!zoneToUse.isValid) {\n      return DateTime.invalid(unsupportedZone(zoneToUse));\n    }\n\n    const loc = Locale.fromObject(opts);\n    const normalized = normalizeObject(obj, normalizeUnitWithLocalWeeks);\n    const { minDaysInFirstWeek, startOfWeek } = usesLocalWeekValues(normalized, loc);\n\n    const tsNow = opts.overrideNow ?? Settings.now(),\n      offsetProvis = !isUndefined(opts.specificOffset)\n        ? opts.specificOffset\n        : zoneToUse.offset(tsNow),\n      containsOrdinal = !isUndefined(normalized.ordinal),\n      containsGregorYear = !isUndefined(normalized.year),\n      containsGregorMD = !isUndefined(normalized.month) || !isUndefined(normalized.day),\n      containsGregor = containsGregorYear || containsGregorMD,\n      definiteWeekDef = normalized.weekYear || normalized.weekNumber;\n\n    // cases:\n    // just a weekday -> this week's instance of that weekday, no worries\n    // (gregorian data or ordinal) + (weekYear or weekNumber) -> error\n    // (gregorian month or day) + ordinal -> error\n    // otherwise just use weeks or ordinals or gregorian, depending on what's specified\n\n    if ((containsGregor || containsOrdinal) && definiteWeekDef) {\n      throw new ConflictingSpecificationError(\n        \"Can't mix weekYear/weekNumber units with year/month/day or ordinals\"\n      );\n    }\n\n    if (containsGregorMD && containsOrdinal) {\n      throw new ConflictingSpecificationError(\"Can't mix ordinal dates with month/day\");\n    }\n\n    const useWeekData = definiteWeekDef || (normalized.weekday && !containsGregor);\n\n    // configure ourselves to deal with gregorian dates or week stuff\n    let units,\n      defaultValues,\n      objNow = tsToObj(tsNow, offsetProvis);\n    if (useWeekData) {\n      units = orderedWeekUnits;\n      defaultValues = defaultWeekUnitValues;\n      objNow = gregorianToWeek(objNow, minDaysInFirstWeek, startOfWeek);\n    } else if (containsOrdinal) {\n      units = orderedOrdinalUnits;\n      defaultValues = defaultOrdinalUnitValues;\n      objNow = gregorianToOrdinal(objNow);\n    } else {\n      units = orderedUnits;\n      defaultValues = defaultUnitValues;\n    }\n\n    // set default values for missing stuff\n    let foundFirst = false;\n    for (const u of units) {\n      const v = normalized[u];\n      if (!isUndefined(v)) {\n        foundFirst = true;\n      } else if (foundFirst) {\n        normalized[u] = defaultValues[u];\n      } else {\n        normalized[u] = objNow[u];\n      }\n    }\n\n    // make sure the values we have are in range\n    const higherOrderInvalid = useWeekData\n        ? hasInvalidWeekData(normalized, minDaysInFirstWeek, startOfWeek)\n        : containsOrdinal\n        ? hasInvalidOrdinalData(normalized)\n        : hasInvalidGregorianData(normalized),\n      invalid = higherOrderInvalid || hasInvalidTimeData(normalized);\n\n    if (invalid) {\n      return DateTime.invalid(invalid);\n    }\n\n    // compute the actual time\n    const gregorian = useWeekData\n        ? weekToGregorian(normalized, minDaysInFirstWeek, startOfWeek)\n        : containsOrdinal\n        ? ordinalToGregorian(normalized)\n        : normalized,\n      [tsFinal, offsetFinal] = objToTS(gregorian, offsetProvis, zoneToUse),\n      inst = new DateTime({\n        ts: tsFinal,\n        zone: zoneToUse,\n        o: offsetFinal,\n        loc,\n      });\n\n    // gregorian data + weekday serves only to validate\n    if (normalized.weekday && containsGregor && obj.weekday !== inst.weekday) {\n      return DateTime.invalid(\n        \"mismatched weekday\",\n        `you can't specify both a weekday of ${normalized.weekday} and a date of ${inst.toISO()}`\n      );\n    }\n\n    if (!inst.isValid) {\n      return DateTime.invalid(inst.invalid);\n    }\n\n    return inst;\n  }\n\n  /**\n   * Create a DateTime from an ISO 8601 string\n   * @param {string} text - the ISO string\n   * @param {Object} opts - options to affect the creation\n   * @param {string|Zone} [opts.zone='local'] - use this zone if no offset is specified in the input string itself. Will also convert the time to this zone\n   * @param {boolean} [opts.setZone=false] - override the zone with a fixed-offset zone specified in the string itself, if it specifies one\n   * @param {string} [opts.locale='system's locale'] - a locale to set on the resulting DateTime instance\n   * @param {string} [opts.outputCalendar] - the output calendar to set on the resulting DateTime instance\n   * @param {string} [opts.numberingSystem] - the numbering system to set on the resulting DateTime instance\n   * @param {string} [opts.weekSettings] - the week settings to set on the resulting DateTime instance\n   * @example DateTime.fromISO('2016-05-25T09:08:34.123')\n   * @example DateTime.fromISO('2016-05-25T09:08:34.123+06:00')\n   * @example DateTime.fromISO('2016-05-25T09:08:34.123+06:00', {setZone: true})\n   * @example DateTime.fromISO('2016-05-25T09:08:34.123', {zone: 'utc'})\n   * @example DateTime.fromISO('2016-W05-4')\n   * @return {DateTime}\n   */\n  static fromISO(text, opts = {}) {\n    const [vals, parsedZone] = parseISODate(text);\n    return parseDataToDateTime(vals, parsedZone, opts, \"ISO 8601\", text);\n  }\n\n  /**\n   * Create a DateTime from an RFC 2822 string\n   * @param {string} text - the RFC 2822 string\n   * @param {Object} opts - options to affect the creation\n   * @param {string|Zone} [opts.zone='local'] - convert the time to this zone. Since the offset is always specified in the string itself, this has no effect on the interpretation of string, merely the zone the resulting DateTime is expressed in.\n   * @param {boolean} [opts.setZone=false] - override the zone with a fixed-offset zone specified in the string itself, if it specifies one\n   * @param {string} [opts.locale='system's locale'] - a locale to set on the resulting DateTime instance\n   * @param {string} opts.outputCalendar - the output calendar to set on the resulting DateTime instance\n   * @param {string} opts.numberingSystem - the numbering system to set on the resulting DateTime instance\n   * @param {string} opts.weekSettings - the week settings to set on the resulting DateTime instance\n   * @example DateTime.fromRFC2822('25 Nov 2016 13:23:12 GMT')\n   * @example DateTime.fromRFC2822('Fri, 25 Nov 2016 13:23:12 +0600')\n   * @example DateTime.fromRFC2822('25 Nov 2016 13:23 Z')\n   * @return {DateTime}\n   */\n  static fromRFC2822(text, opts = {}) {\n    const [vals, parsedZone] = parseRFC2822Date(text);\n    return parseDataToDateTime(vals, parsedZone, opts, \"RFC 2822\", text);\n  }\n\n  /**\n   * Create a DateTime from an HTTP header date\n   * @see https://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1\n   * @param {string} text - the HTTP header date\n   * @param {Object} opts - options to affect the creation\n   * @param {string|Zone} [opts.zone='local'] - convert the time to this zone. Since HTTP dates are always in UTC, this has no effect on the interpretation of string, merely the zone the resulting DateTime is expressed in.\n   * @param {boolean} [opts.setZone=false] - override the zone with the fixed-offset zone specified in the string. For HTTP dates, this is always UTC, so this option is equivalent to setting the `zone` option to 'utc', but this option is included for consistency with similar methods.\n   * @param {string} [opts.locale='system's locale'] - a locale to set on the resulting DateTime instance\n   * @param {string} opts.outputCalendar - the output calendar to set on the resulting DateTime instance\n   * @param {string} opts.numberingSystem - the numbering system to set on the resulting DateTime instance\n   * @param {string} opts.weekSettings - the week settings to set on the resulting DateTime instance\n   * @example DateTime.fromHTTP('Sun, 06 Nov 1994 08:49:37 GMT')\n   * @example DateTime.fromHTTP('Sunday, 06-Nov-94 08:49:37 GMT')\n   * @example DateTime.fromHTTP('Sun Nov  6 08:49:37 1994')\n   * @return {DateTime}\n   */\n  static fromHTTP(text, opts = {}) {\n    const [vals, parsedZone] = parseHTTPDate(text);\n    return parseDataToDateTime(vals, parsedZone, opts, \"HTTP\", opts);\n  }\n\n  /**\n   * Create a DateTime from an input string and format string.\n   * Defaults to en-US if no locale has been specified, regardless of the system's locale. For a table of tokens and their interpretations, see [here](https://moment.github.io/luxon/#/parsing?id=table-of-tokens).\n   * @param {string} text - the string to parse\n   * @param {string} fmt - the format the string is expected to be in (see the link below for the formats)\n   * @param {Object} opts - options to affect the creation\n   * @param {string|Zone} [opts.zone='local'] - use this zone if no offset is specified in the input string itself. Will also convert the DateTime to this zone\n   * @param {boolean} [opts.setZone=false] - override the zone with a zone specified in the string itself, if it specifies one\n   * @param {string} [opts.locale='en-US'] - a locale string to use when parsing. Will also set the DateTime to this locale\n   * @param {string} opts.numberingSystem - the numbering system to use when parsing. Will also set the resulting DateTime to this numbering system\n   * @param {string} opts.weekSettings - the week settings to set on the resulting DateTime instance\n   * @param {string} opts.outputCalendar - the output calendar to set on the resulting DateTime instance\n   * @return {DateTime}\n   */\n  static fromFormat(text, fmt, opts = {}) {\n    if (isUndefined(text) || isUndefined(fmt)) {\n      throw new InvalidArgumentError(\"fromFormat requires an input string and a format\");\n    }\n\n    const { locale = null, numberingSystem = null } = opts,\n      localeToUse = Locale.fromOpts({\n        locale,\n        numberingSystem,\n        defaultToEN: true,\n      }),\n      [vals, parsedZone, specificOffset, invalid] = parseFromTokens(localeToUse, text, fmt);\n    if (invalid) {\n      return DateTime.invalid(invalid);\n    } else {\n      return parseDataToDateTime(vals, parsedZone, opts, `format ${fmt}`, text, specificOffset);\n    }\n  }\n\n  /**\n   * @deprecated use fromFormat instead\n   */\n  static fromString(text, fmt, opts = {}) {\n    return DateTime.fromFormat(text, fmt, opts);\n  }\n\n  /**\n   * Create a DateTime from a SQL date, time, or datetime\n   * Defaults to en-US if no locale has been specified, regardless of the system's locale\n   * @param {string} text - the string to parse\n   * @param {Object} opts - options to affect the creation\n   * @param {string|Zone} [opts.zone='local'] - use this zone if no offset is specified in the input string itself. Will also convert the DateTime to this zone\n   * @param {boolean} [opts.setZone=false] - override the zone with a zone specified in the string itself, if it specifies one\n   * @param {string} [opts.locale='en-US'] - a locale string to use when parsing. Will also set the DateTime to this locale\n   * @param {string} opts.numberingSystem - the numbering system to use when parsing. Will also set the resulting DateTime to this numbering system\n   * @param {string} opts.weekSettings - the week settings to set on the resulting DateTime instance\n   * @param {string} opts.outputCalendar - the output calendar to set on the resulting DateTime instance\n   * @example DateTime.fromSQL('2017-05-15')\n   * @example DateTime.fromSQL('2017-05-15 09:12:34')\n   * @example DateTime.fromSQL('2017-05-15 09:12:34.342')\n   * @example DateTime.fromSQL('2017-05-15 09:12:34.342+06:00')\n   * @example DateTime.fromSQL('2017-05-15 09:12:34.342 America/Los_Angeles')\n   * @example DateTime.fromSQL('2017-05-15 09:12:34.342 America/Los_Angeles', { setZone: true })\n   * @example DateTime.fromSQL('2017-05-15 09:12:34.342', { zone: 'America/Los_Angeles' })\n   * @example DateTime.fromSQL('09:12:34.342')\n   * @return {DateTime}\n   */\n  static fromSQL(text, opts = {}) {\n    const [vals, parsedZone] = parseSQL(text);\n    return parseDataToDateTime(vals, parsedZone, opts, \"SQL\", text);\n  }\n\n  /**\n   * Create an invalid DateTime.\n   * @param {string} reason - simple string of why this DateTime is invalid. Should not contain parameters or anything else data-dependent.\n   * @param {string} [explanation=null] - longer explanation, may include parameters and other useful debugging information\n   * @return {DateTime}\n   */\n  static invalid(reason, explanation = null) {\n    if (!reason) {\n      throw new InvalidArgumentError(\"need to specify a reason the DateTime is invalid\");\n    }\n\n    const invalid = reason instanceof Invalid ? reason : new Invalid(reason, explanation);\n\n    if (Settings.throwOnInvalid) {\n      throw new InvalidDateTimeError(invalid);\n    } else {\n      return new DateTime({ invalid });\n    }\n  }\n\n  /**\n   * Check if an object is an instance of DateTime. Works across context boundaries\n   * @param {object} o\n   * @return {boolean}\n   */\n  static isDateTime(o) {\n    return (o && o.isLuxonDateTime) || false;\n  }\n\n  /**\n   * Produce the format string for a set of options\n   * @param formatOpts\n   * @param localeOpts\n   * @returns {string}\n   */\n  static parseFormatForOpts(formatOpts, localeOpts = {}) {\n    const tokenList = formatOptsToTokens(formatOpts, Locale.fromObject(localeOpts));\n    return !tokenList ? null : tokenList.map((t) => (t ? t.val : null)).join(\"\");\n  }\n\n  /**\n   * Produce the the fully expanded format token for the locale\n   * Does NOT quote characters, so quoted tokens will not round trip correctly\n   * @param fmt\n   * @param localeOpts\n   * @returns {string}\n   */\n  static expandFormat(fmt, localeOpts = {}) {\n    const expanded = expandMacroTokens(Formatter.parseFormat(fmt), Locale.fromObject(localeOpts));\n    return expanded.map((t) => t.val).join(\"\");\n  }\n\n  static resetCache() {\n    zoneOffsetTs = undefined;\n    zoneOffsetGuessCache.clear();\n  }\n\n  // INFO\n\n  /**\n   * Get the value of unit.\n   * @param {string} unit - a unit such as 'minute' or 'day'\n   * @example DateTime.local(2017, 7, 4).get('month'); //=> 7\n   * @example DateTime.local(2017, 7, 4).get('day'); //=> 4\n   * @return {number}\n   */\n  get(unit) {\n    return this[unit];\n  }\n\n  /**\n   * Returns whether the DateTime is valid. Invalid DateTimes occur when:\n   * * The DateTime was created from invalid calendar information, such as the 13th month or February 30\n   * * The DateTime was created by an operation on another invalid date\n   * @type {boolean}\n   */\n  get isValid() {\n    return this.invalid === null;\n  }\n\n  /**\n   * Returns an error code if this DateTime is invalid, or null if the DateTime is valid\n   * @type {string}\n   */\n  get invalidReason() {\n    return this.invalid ? this.invalid.reason : null;\n  }\n\n  /**\n   * Returns an explanation of why this DateTime became invalid, or null if the DateTime is valid\n   * @type {string}\n   */\n  get invalidExplanation() {\n    return this.invalid ? this.invalid.explanation : null;\n  }\n\n  /**\n   * Get the locale of a DateTime, such 'en-GB'. The locale is used when formatting the DateTime\n   *\n   * @type {string}\n   */\n  get locale() {\n    return this.isValid ? this.loc.locale : null;\n  }\n\n  /**\n   * Get the numbering system of a DateTime, such 'beng'. The numbering system is used when formatting the DateTime\n   *\n   * @type {string}\n   */\n  get numberingSystem() {\n    return this.isValid ? this.loc.numberingSystem : null;\n  }\n\n  /**\n   * Get the output calendar of a DateTime, such 'islamic'. The output calendar is used when formatting the DateTime\n   *\n   * @type {string}\n   */\n  get outputCalendar() {\n    return this.isValid ? this.loc.outputCalendar : null;\n  }\n\n  /**\n   * Get the time zone associated with this DateTime.\n   * @type {Zone}\n   */\n  get zone() {\n    return this._zone;\n  }\n\n  /**\n   * Get the name of the time zone.\n   * @type {string}\n   */\n  get zoneName() {\n    return this.isValid ? this.zone.name : null;\n  }\n\n  /**\n   * Get the year\n   * @example DateTime.local(2017, 5, 25).year //=> 2017\n   * @type {number}\n   */\n  get year() {\n    return this.isValid ? this.c.year : NaN;\n  }\n\n  /**\n   * Get the quarter\n   * @example DateTime.local(2017, 5, 25).quarter //=> 2\n   * @type {number}\n   */\n  get quarter() {\n    return this.isValid ? Math.ceil(this.c.month / 3) : NaN;\n  }\n\n  /**\n   * Get the month (1-12).\n   * @example DateTime.local(2017, 5, 25).month //=> 5\n   * @type {number}\n   */\n  get month() {\n    return this.isValid ? this.c.month : NaN;\n  }\n\n  /**\n   * Get the day of the month (1-30ish).\n   * @example DateTime.local(2017, 5, 25).day //=> 25\n   * @type {number}\n   */\n  get day() {\n    return this.isValid ? this.c.day : NaN;\n  }\n\n  /**\n   * Get the hour of the day (0-23).\n   * @example DateTime.local(2017, 5, 25, 9).hour //=> 9\n   * @type {number}\n   */\n  get hour() {\n    return this.isValid ? this.c.hour : NaN;\n  }\n\n  /**\n   * Get the minute of the hour (0-59).\n   * @example DateTime.local(2017, 5, 25, 9, 30).minute //=> 30\n   * @type {number}\n   */\n  get minute() {\n    return this.isValid ? this.c.minute : NaN;\n  }\n\n  /**\n   * Get the second of the minute (0-59).\n   * @example DateTime.local(2017, 5, 25, 9, 30, 52).second //=> 52\n   * @type {number}\n   */\n  get second() {\n    return this.isValid ? this.c.second : NaN;\n  }\n\n  /**\n   * Get the millisecond of the second (0-999).\n   * @example DateTime.local(2017, 5, 25, 9, 30, 52, 654).millisecond //=> 654\n   * @type {number}\n   */\n  get millisecond() {\n    return this.isValid ? this.c.millisecond : NaN;\n  }\n\n  /**\n   * Get the week year\n   * @see https://en.wikipedia.org/wiki/ISO_week_date\n   * @example DateTime.local(2014, 12, 31).weekYear //=> 2015\n   * @type {number}\n   */\n  get weekYear() {\n    return this.isValid ? possiblyCachedWeekData(this).weekYear : NaN;\n  }\n\n  /**\n   * Get the week number of the week year (1-52ish).\n   * @see https://en.wikipedia.org/wiki/ISO_week_date\n   * @example DateTime.local(2017, 5, 25).weekNumber //=> 21\n   * @type {number}\n   */\n  get weekNumber() {\n    return this.isValid ? possiblyCachedWeekData(this).weekNumber : NaN;\n  }\n\n  /**\n   * Get the day of the week.\n   * 1 is Monday and 7 is Sunday\n   * @see https://en.wikipedia.org/wiki/ISO_week_date\n   * @example DateTime.local(2014, 11, 31).weekday //=> 4\n   * @type {number}\n   */\n  get weekday() {\n    return this.isValid ? possiblyCachedWeekData(this).weekday : NaN;\n  }\n\n  /**\n   * Returns true if this date is on a weekend according to the locale, false otherwise\n   * @returns {boolean}\n   */\n  get isWeekend() {\n    return this.isValid && this.loc.getWeekendDays().includes(this.weekday);\n  }\n\n  /**\n   * Get the day of the week according to the locale.\n   * 1 is the first day of the week and 7 is the last day of the week.\n   * If the locale assigns Sunday as the first day of the week, then a date which is a Sunday will return 1,\n   * @returns {number}\n   */\n  get localWeekday() {\n    return this.isValid ? possiblyCachedLocalWeekData(this).weekday : NaN;\n  }\n\n  /**\n   * Get the week number of the week year according to the locale. Different locales assign week numbers differently,\n   * because the week can start on different days of the week (see localWeekday) and because a different number of days\n   * is required for a week to count as the first week of a year.\n   * @returns {number}\n   */\n  get localWeekNumber() {\n    return this.isValid ? possiblyCachedLocalWeekData(this).weekNumber : NaN;\n  }\n\n  /**\n   * Get the week year according to the locale. Different locales assign week numbers (and therefor week years)\n   * differently, see localWeekNumber.\n   * @returns {number}\n   */\n  get localWeekYear() {\n    return this.isValid ? possiblyCachedLocalWeekData(this).weekYear : NaN;\n  }\n\n  /**\n   * Get the ordinal (meaning the day of the year)\n   * @example DateTime.local(2017, 5, 25).ordinal //=> 145\n   * @type {number|DateTime}\n   */\n  get ordinal() {\n    return this.isValid ? gregorianToOrdinal(this.c).ordinal : NaN;\n  }\n\n  /**\n   * Get the human readable short month name, such as 'Oct'.\n   * Defaults to the system's locale if no locale has been specified\n   * @example DateTime.local(2017, 10, 30).monthShort //=> Oct\n   * @type {string}\n   */\n  get monthShort() {\n    return this.isValid ? Info.months(\"short\", { locObj: this.loc })[this.month - 1] : null;\n  }\n\n  /**\n   * Get the human readable long month name, such as 'October'.\n   * Defaults to the system's locale if no locale has been specified\n   * @example DateTime.local(2017, 10, 30).monthLong //=> October\n   * @type {string}\n   */\n  get monthLong() {\n    return this.isValid ? Info.months(\"long\", { locObj: this.loc })[this.month - 1] : null;\n  }\n\n  /**\n   * Get the human readable short weekday, such as 'Mon'.\n   * Defaults to the system's locale if no locale has been specified\n   * @example DateTime.local(2017, 10, 30).weekdayShort //=> Mon\n   * @type {string}\n   */\n  get weekdayShort() {\n    return this.isValid ? Info.weekdays(\"short\", { locObj: this.loc })[this.weekday - 1] : null;\n  }\n\n  /**\n   * Get the human readable long weekday, such as 'Monday'.\n   * Defaults to the system's locale if no locale has been specified\n   * @example DateTime.local(2017, 10, 30).weekdayLong //=> Monday\n   * @type {string}\n   */\n  get weekdayLong() {\n    return this.isValid ? Info.weekdays(\"long\", { locObj: this.loc })[this.weekday - 1] : null;\n  }\n\n  /**\n   * Get the UTC offset of this DateTime in minutes\n   * @example DateTime.now().offset //=> -240\n   * @example DateTime.utc().offset //=> 0\n   * @type {number}\n   */\n  get offset() {\n    return this.isValid ? +this.o : NaN;\n  }\n\n  /**\n   * Get the short human name for the zone's current offset, for example \"EST\" or \"EDT\".\n   * Defaults to the system's locale if no locale has been specified\n   * @type {string}\n   */\n  get offsetNameShort() {\n    if (this.isValid) {\n      return this.zone.offsetName(this.ts, {\n        format: \"short\",\n        locale: this.locale,\n      });\n    } else {\n      return null;\n    }\n  }\n\n  /**\n   * Get the long human name for the zone's current offset, for example \"Eastern Standard Time\" or \"Eastern Daylight Time\".\n   * Defaults to the system's locale if no locale has been specified\n   * @type {string}\n   */\n  get offsetNameLong() {\n    if (this.isValid) {\n      return this.zone.offsetName(this.ts, {\n        format: \"long\",\n        locale: this.locale,\n      });\n    } else {\n      return null;\n    }\n  }\n\n  /**\n   * Get whether this zone's offset ever changes, as in a DST.\n   * @type {boolean}\n   */\n  get isOffsetFixed() {\n    return this.isValid ? this.zone.isUniversal : null;\n  }\n\n  /**\n   * Get whether the DateTime is in a DST.\n   * @type {boolean}\n   */\n  get isInDST() {\n    if (this.isOffsetFixed) {\n      return false;\n    } else {\n      return (\n        this.offset > this.set({ month: 1, day: 1 }).offset ||\n        this.offset > this.set({ month: 5 }).offset\n      );\n    }\n  }\n\n  /**\n   * Get those DateTimes which have the same local time as this DateTime, but a different offset from UTC\n   * in this DateTime's zone. During DST changes local time can be ambiguous, for example\n   * `2023-10-29T02:30:00` in `Europe/Berlin` can have offset `+01:00` or `+02:00`.\n   * This method will return both possible DateTimes if this DateTime's local time is ambiguous.\n   * @returns {DateTime[]}\n   */\n  getPossibleOffsets() {\n    if (!this.isValid || this.isOffsetFixed) {\n      return [this];\n    }\n    const dayMs = 86400000;\n    const minuteMs = 60000;\n    const localTS = objToLocalTS(this.c);\n    const oEarlier = this.zone.offset(localTS - dayMs);\n    const oLater = this.zone.offset(localTS + dayMs);\n\n    const o1 = this.zone.offset(localTS - oEarlier * minuteMs);\n    const o2 = this.zone.offset(localTS - oLater * minuteMs);\n    if (o1 === o2) {\n      return [this];\n    }\n    const ts1 = localTS - o1 * minuteMs;\n    const ts2 = localTS - o2 * minuteMs;\n    const c1 = tsToObj(ts1, o1);\n    const c2 = tsToObj(ts2, o2);\n    if (\n      c1.hour === c2.hour &&\n      c1.minute === c2.minute &&\n      c1.second === c2.second &&\n      c1.millisecond === c2.millisecond\n    ) {\n      return [clone(this, { ts: ts1 }), clone(this, { ts: ts2 })];\n    }\n    return [this];\n  }\n\n  /**\n   * Returns true if this DateTime is in a leap year, false otherwise\n   * @example DateTime.local(2016).isInLeapYear //=> true\n   * @example DateTime.local(2013).isInLeapYear //=> false\n   * @type {boolean}\n   */\n  get isInLeapYear() {\n    return isLeapYear(this.year);\n  }\n\n  /**\n   * Returns the number of days in this DateTime's month\n   * @example DateTime.local(2016, 2).daysInMonth //=> 29\n   * @example DateTime.local(2016, 3).daysInMonth //=> 31\n   * @type {number}\n   */\n  get daysInMonth() {\n    return daysInMonth(this.year, this.month);\n  }\n\n  /**\n   * Returns the number of days in this DateTime's year\n   * @example DateTime.local(2016).daysInYear //=> 366\n   * @example DateTime.local(2013).daysInYear //=> 365\n   * @type {number}\n   */\n  get daysInYear() {\n    return this.isValid ? daysInYear(this.year) : NaN;\n  }\n\n  /**\n   * Returns the number of weeks in this DateTime's year\n   * @see https://en.wikipedia.org/wiki/ISO_week_date\n   * @example DateTime.local(2004).weeksInWeekYear //=> 53\n   * @example DateTime.local(2013).weeksInWeekYear //=> 52\n   * @type {number}\n   */\n  get weeksInWeekYear() {\n    return this.isValid ? weeksInWeekYear(this.weekYear) : NaN;\n  }\n\n  /**\n   * Returns the number of weeks in this DateTime's local week year\n   * @example DateTime.local(2020, 6, {locale: 'en-US'}).weeksInLocalWeekYear //=> 52\n   * @example DateTime.local(2020, 6, {locale: 'de-DE'}).weeksInLocalWeekYear //=> 53\n   * @type {number}\n   */\n  get weeksInLocalWeekYear() {\n    return this.isValid\n      ? weeksInWeekYear(\n          this.localWeekYear,\n          this.loc.getMinDaysInFirstWeek(),\n          this.loc.getStartOfWeek()\n        )\n      : NaN;\n  }\n\n  /**\n   * Returns the resolved Intl options for this DateTime.\n   * This is useful in understanding the behavior of formatting methods\n   * @param {Object} opts - the same options as toLocaleString\n   * @return {Object}\n   */\n  resolvedLocaleOptions(opts = {}) {\n    const { locale, numberingSystem, calendar } = Formatter.create(\n      this.loc.clone(opts),\n      opts\n    ).resolvedOptions(this);\n    return { locale, numberingSystem, outputCalendar: calendar };\n  }\n\n  // TRANSFORM\n\n  /**\n   * \"Set\" the DateTime's zone to UTC. Returns a newly-constructed DateTime.\n   *\n   * Equivalent to {@link DateTime#setZone}('utc')\n   * @param {number} [offset=0] - optionally, an offset from UTC in minutes\n   * @param {Object} [opts={}] - options to pass to `setZone()`\n   * @return {DateTime}\n   */\n  toUTC(offset = 0, opts = {}) {\n    return this.setZone(FixedOffsetZone.instance(offset), opts);\n  }\n\n  /**\n   * \"Set\" the DateTime's zone to the host's local zone. Returns a newly-constructed DateTime.\n   *\n   * Equivalent to `setZone('local')`\n   * @return {DateTime}\n   */\n  toLocal() {\n    return this.setZone(Settings.defaultZone);\n  }\n\n  /**\n   * \"Set\" the DateTime's zone to specified zone. Returns a newly-constructed DateTime.\n   *\n   * By default, the setter keeps the underlying time the same (as in, the same timestamp), but the new instance will report different local times and consider DSTs when making computations, as with {@link DateTime#plus}. You may wish to use {@link DateTime#toLocal} and {@link DateTime#toUTC} which provide simple convenience wrappers for commonly used zones.\n   * @param {string|Zone} [zone='local'] - a zone identifier. As a string, that can be any IANA zone supported by the host environment, or a fixed-offset name of the form 'UTC+3', or the strings 'local' or 'utc'. You may also supply an instance of a {@link DateTime#Zone} class.\n   * @param {Object} opts - options\n   * @param {boolean} [opts.keepLocalTime=false] - If true, adjust the underlying time so that the local time stays the same, but in the target zone. You should rarely need this.\n   * @return {DateTime}\n   */\n  setZone(zone, { keepLocalTime = false, keepCalendarTime = false } = {}) {\n    zone = normalizeZone(zone, Settings.defaultZone);\n    if (zone.equals(this.zone)) {\n      return this;\n    } else if (!zone.isValid) {\n      return DateTime.invalid(unsupportedZone(zone));\n    } else {\n      let newTS = this.ts;\n      if (keepLocalTime || keepCalendarTime) {\n        const offsetGuess = zone.offset(this.ts);\n        const asObj = this.toObject();\n        [newTS] = objToTS(asObj, offsetGuess, zone);\n      }\n      return clone(this, { ts: newTS, zone });\n    }\n  }\n\n  /**\n   * \"Set\" the locale, numberingSystem, or outputCalendar. Returns a newly-constructed DateTime.\n   * @param {Object} properties - the properties to set\n   * @example DateTime.local(2017, 5, 25).reconfigure({ locale: 'en-GB' })\n   * @return {DateTime}\n   */\n  reconfigure({ locale, numberingSystem, outputCalendar } = {}) {\n    const loc = this.loc.clone({ locale, numberingSystem, outputCalendar });\n    return clone(this, { loc });\n  }\n\n  /**\n   * \"Set\" the locale. Returns a newly-constructed DateTime.\n   * Just a convenient alias for reconfigure({ locale })\n   * @example DateTime.local(2017, 5, 25).setLocale('en-GB')\n   * @return {DateTime}\n   */\n  setLocale(locale) {\n    return this.reconfigure({ locale });\n  }\n\n  /**\n   * \"Set\" the values of specified units. Returns a newly-constructed DateTime.\n   * You can only set units with this method; for \"setting\" metadata, see {@link DateTime#reconfigure} and {@link DateTime#setZone}.\n   *\n   * This method also supports setting locale-based week units, i.e. `localWeekday`, `localWeekNumber` and `localWeekYear`.\n   * They cannot be mixed with ISO-week units like `weekday`.\n   * @param {Object} values - a mapping of units to numbers\n   * @example dt.set({ year: 2017 })\n   * @example dt.set({ hour: 8, minute: 30 })\n   * @example dt.set({ weekday: 5 })\n   * @example dt.set({ year: 2005, ordinal: 234 })\n   * @return {DateTime}\n   */\n  set(values) {\n    if (!this.isValid) return this;\n\n    const normalized = normalizeObject(values, normalizeUnitWithLocalWeeks);\n    const { minDaysInFirstWeek, startOfWeek } = usesLocalWeekValues(normalized, this.loc);\n\n    const settingWeekStuff =\n        !isUndefined(normalized.weekYear) ||\n        !isUndefined(normalized.weekNumber) ||\n        !isUndefined(normalized.weekday),\n      containsOrdinal = !isUndefined(normalized.ordinal),\n      containsGregorYear = !isUndefined(normalized.year),\n      containsGregorMD = !isUndefined(normalized.month) || !isUndefined(normalized.day),\n      containsGregor = containsGregorYear || containsGregorMD,\n      definiteWeekDef = normalized.weekYear || normalized.weekNumber;\n\n    if ((containsGregor || containsOrdinal) && definiteWeekDef) {\n      throw new ConflictingSpecificationError(\n        \"Can't mix weekYear/weekNumber units with year/month/day or ordinals\"\n      );\n    }\n\n    if (containsGregorMD && containsOrdinal) {\n      throw new ConflictingSpecificationError(\"Can't mix ordinal dates with month/day\");\n    }\n\n    let mixed;\n    if (settingWeekStuff) {\n      mixed = weekToGregorian(\n        { ...gregorianToWeek(this.c, minDaysInFirstWeek, startOfWeek), ...normalized },\n        minDaysInFirstWeek,\n        startOfWeek\n      );\n    } else if (!isUndefined(normalized.ordinal)) {\n      mixed = ordinalToGregorian({ ...gregorianToOrdinal(this.c), ...normalized });\n    } else {\n      mixed = { ...this.toObject(), ...normalized };\n\n      // if we didn't set the day but we ended up on an overflow date,\n      // use the last day of the right month\n      if (isUndefined(normalized.day)) {\n        mixed.day = Math.min(daysInMonth(mixed.year, mixed.month), mixed.day);\n      }\n    }\n\n    const [ts, o] = objToTS(mixed, this.o, this.zone);\n    return clone(this, { ts, o });\n  }\n\n  /**\n   * Add a period of time to this DateTime and return the resulting DateTime\n   *\n   * Adding hours, minutes, seconds, or milliseconds increases the timestamp by the right number of milliseconds. Adding days, months, or years shifts the calendar, accounting for DSTs and leap years along the way. Thus, `dt.plus({ hours: 24 })` may result in a different time than `dt.plus({ days: 1 })` if there's a DST shift in between.\n   * @param {Duration|Object|number} duration - The amount to add. Either a Luxon Duration, a number of milliseconds, the object argument to Duration.fromObject()\n   * @example DateTime.now().plus(123) //~> in 123 milliseconds\n   * @example DateTime.now().plus({ minutes: 15 }) //~> in 15 minutes\n   * @example DateTime.now().plus({ days: 1 }) //~> this time tomorrow\n   * @example DateTime.now().plus({ days: -1 }) //~> this time yesterday\n   * @example DateTime.now().plus({ hours: 3, minutes: 13 }) //~> in 3 hr, 13 min\n   * @example DateTime.now().plus(Duration.fromObject({ hours: 3, minutes: 13 })) //~> in 3 hr, 13 min\n   * @return {DateTime}\n   */\n  plus(duration) {\n    if (!this.isValid) return this;\n    const dur = Duration.fromDurationLike(duration);\n    return clone(this, adjustTime(this, dur));\n  }\n\n  /**\n   * Subtract a period of time to this DateTime and return the resulting DateTime\n   * See {@link DateTime#plus}\n   * @param {Duration|Object|number} duration - The amount to subtract. Either a Luxon Duration, a number of milliseconds, the object argument to Duration.fromObject()\n   @return {DateTime}\n   */\n  minus(duration) {\n    if (!this.isValid) return this;\n    const dur = Duration.fromDurationLike(duration).negate();\n    return clone(this, adjustTime(this, dur));\n  }\n\n  /**\n   * \"Set\" this DateTime to the beginning of a unit of time.\n   * @param {string} unit - The unit to go to the beginning of. Can be 'year', 'quarter', 'month', 'week', 'day', 'hour', 'minute', 'second', or 'millisecond'.\n   * @param {Object} opts - options\n   * @param {boolean} [opts.useLocaleWeeks=false] - If true, use weeks based on the locale, i.e. use the locale-dependent start of the week\n   * @example DateTime.local(2014, 3, 3).startOf('month').toISODate(); //=> '2014-03-01'\n   * @example DateTime.local(2014, 3, 3).startOf('year').toISODate(); //=> '2014-01-01'\n   * @example DateTime.local(2014, 3, 3).startOf('week').toISODate(); //=> '2014-03-03', weeks always start on Mondays\n   * @example DateTime.local(2014, 3, 3, 5, 30).startOf('day').toISOTime(); //=> '00:00.000-05:00'\n   * @example DateTime.local(2014, 3, 3, 5, 30).startOf('hour').toISOTime(); //=> '05:00:00.000-05:00'\n   * @return {DateTime}\n   */\n  startOf(unit, { useLocaleWeeks = false } = {}) {\n    if (!this.isValid) return this;\n\n    const o = {},\n      normalizedUnit = Duration.normalizeUnit(unit);\n    switch (normalizedUnit) {\n      case \"years\":\n        o.month = 1;\n      // falls through\n      case \"quarters\":\n      case \"months\":\n        o.day = 1;\n      // falls through\n      case \"weeks\":\n      case \"days\":\n        o.hour = 0;\n      // falls through\n      case \"hours\":\n        o.minute = 0;\n      // falls through\n      case \"minutes\":\n        o.second = 0;\n      // falls through\n      case \"seconds\":\n        o.millisecond = 0;\n        break;\n      case \"milliseconds\":\n        break;\n      // no default, invalid units throw in normalizeUnit()\n    }\n\n    if (normalizedUnit === \"weeks\") {\n      if (useLocaleWeeks) {\n        const startOfWeek = this.loc.getStartOfWeek();\n        const { weekday } = this;\n        if (weekday < startOfWeek) {\n          o.weekNumber = this.weekNumber - 1;\n        }\n        o.weekday = startOfWeek;\n      } else {\n        o.weekday = 1;\n      }\n    }\n\n    if (normalizedUnit === \"quarters\") {\n      const q = Math.ceil(this.month / 3);\n      o.month = (q - 1) * 3 + 1;\n    }\n\n    return this.set(o);\n  }\n\n  /**\n   * \"Set\" this DateTime to the end (meaning the last millisecond) of a unit of time\n   * @param {string} unit - The unit to go to the end of. Can be 'year', 'quarter', 'month', 'week', 'day', 'hour', 'minute', 'second', or 'millisecond'.\n   * @param {Object} opts - options\n   * @param {boolean} [opts.useLocaleWeeks=false] - If true, use weeks based on the locale, i.e. use the locale-dependent start of the week\n   * @example DateTime.local(2014, 3, 3).endOf('month').toISO(); //=> '2014-03-31T23:59:59.999-05:00'\n   * @example DateTime.local(2014, 3, 3).endOf('year').toISO(); //=> '2014-12-31T23:59:59.999-05:00'\n   * @example DateTime.local(2014, 3, 3).endOf('week').toISO(); // => '2014-03-09T23:59:59.999-05:00', weeks start on Mondays\n   * @example DateTime.local(2014, 3, 3, 5, 30).endOf('day').toISO(); //=> '2014-03-03T23:59:59.999-05:00'\n   * @example DateTime.local(2014, 3, 3, 5, 30).endOf('hour').toISO(); //=> '2014-03-03T05:59:59.999-05:00'\n   * @return {DateTime}\n   */\n  endOf(unit, opts) {\n    return this.isValid\n      ? this.plus({ [unit]: 1 })\n          .startOf(unit, opts)\n          .minus(1)\n      : this;\n  }\n\n  // OUTPUT\n\n  /**\n   * Returns a string representation of this DateTime formatted according to the specified format string.\n   * **You may not want this.** See {@link DateTime#toLocaleString} for a more flexible formatting tool. For a table of tokens and their interpretations, see [here](https://moment.github.io/luxon/#/formatting?id=table-of-tokens).\n   * Defaults to en-US if no locale has been specified, regardless of the system's locale.\n   * @param {string} fmt - the format string\n   * @param {Object} opts - opts to override the configuration options on this DateTime\n   * @example DateTime.now().toFormat('yyyy LLL dd') //=> '2017 Apr 22'\n   * @example DateTime.now().setLocale('fr').toFormat('yyyy LLL dd') //=> '2017 avr. 22'\n   * @example DateTime.now().toFormat('yyyy LLL dd', { locale: \"fr\" }) //=> '2017 avr. 22'\n   * @example DateTime.now().toFormat(\"HH 'hours and' mm 'minutes'\") //=> '20 hours and 55 minutes'\n   * @return {string}\n   */\n  toFormat(fmt, opts = {}) {\n    return this.isValid\n      ? Formatter.create(this.loc.redefaultToEN(opts)).formatDateTimeFromString(this, fmt)\n      : INVALID;\n  }\n\n  /**\n   * Returns a localized string representing this date. Accepts the same options as the Intl.DateTimeFormat constructor and any presets defined by Luxon, such as `DateTime.DATE_FULL` or `DateTime.TIME_SIMPLE`.\n   * The exact behavior of this method is browser-specific, but in general it will return an appropriate representation\n   * of the DateTime in the assigned locale.\n   * Defaults to the system's locale if no locale has been specified\n   * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat\n   * @param formatOpts {Object} - Intl.DateTimeFormat constructor options and configuration options\n   * @param {Object} opts - opts to override the configuration options on this DateTime\n   * @example DateTime.now().toLocaleString(); //=> 4/20/2017\n   * @example DateTime.now().setLocale('en-gb').toLocaleString(); //=> '20/04/2017'\n   * @example DateTime.now().toLocaleString(DateTime.DATE_FULL); //=> 'April 20, 2017'\n   * @example DateTime.now().toLocaleString(DateTime.DATE_FULL, { locale: 'fr' }); //=> '28 août 2022'\n   * @example DateTime.now().toLocaleString(DateTime.TIME_SIMPLE); //=> '11:32 AM'\n   * @example DateTime.now().toLocaleString(DateTime.DATETIME_SHORT); //=> '4/20/2017, 11:32 AM'\n   * @example DateTime.now().toLocaleString({ weekday: 'long', month: 'long', day: '2-digit' }); //=> 'Thursday, April 20'\n   * @example DateTime.now().toLocaleString({ weekday: 'short', month: 'short', day: '2-digit', hour: '2-digit', minute: '2-digit' }); //=> 'Thu, Apr 20, 11:27 AM'\n   * @example DateTime.now().toLocaleString({ hour: '2-digit', minute: '2-digit', hourCycle: 'h23' }); //=> '11:32'\n   * @return {string}\n   */\n  toLocaleString(formatOpts = Formats.DATE_SHORT, opts = {}) {\n    return this.isValid\n      ? Formatter.create(this.loc.clone(opts), formatOpts).formatDateTime(this)\n      : INVALID;\n  }\n\n  /**\n   * Returns an array of format \"parts\", meaning individual tokens along with metadata. This is allows callers to post-process individual sections of the formatted output.\n   * Defaults to the system's locale if no locale has been specified\n   * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat/formatToParts\n   * @param opts {Object} - Intl.DateTimeFormat constructor options, same as `toLocaleString`.\n   * @example DateTime.now().toLocaleParts(); //=> [\n   *                                   //=>   { type: 'day', value: '25' },\n   *                                   //=>   { type: 'literal', value: '/' },\n   *                                   //=>   { type: 'month', value: '05' },\n   *                                   //=>   { type: 'literal', value: '/' },\n   *                                   //=>   { type: 'year', value: '1982' }\n   *                                   //=> ]\n   */\n  toLocaleParts(opts = {}) {\n    return this.isValid\n      ? Formatter.create(this.loc.clone(opts), opts).formatDateTimeParts(this)\n      : [];\n  }\n\n  /**\n   * Returns an ISO 8601-compliant string representation of this DateTime\n   * @param {Object} opts - options\n   * @param {boolean} [opts.suppressMilliseconds=false] - exclude milliseconds from the format if they're 0\n   * @param {boolean} [opts.suppressSeconds=false] - exclude seconds from the format if they're 0\n   * @param {boolean} [opts.includeOffset=true] - include the offset, such as 'Z' or '-04:00'\n   * @param {boolean} [opts.extendedZone=false] - add the time zone format extension\n   * @param {string} [opts.format='extended'] - choose between the basic and extended format\n   * @param {string} [opts.precision='milliseconds'] - truncate output to desired presicion: 'years', 'months', 'days', 'hours', 'minutes', 'seconds' or 'milliseconds'. When precision and suppressSeconds or suppressMilliseconds are used together, precision sets the maximum unit shown in the output, however seconds or milliseconds will still be suppressed if they are 0.\n   * @example DateTime.utc(1983, 5, 25).toISO() //=> '1982-05-25T00:00:00.000Z'\n   * @example DateTime.now().toISO() //=> '2017-04-22T20:47:05.335-04:00'\n   * @example DateTime.now().toISO({ includeOffset: false }) //=> '2017-04-22T20:47:05.335'\n   * @example DateTime.now().toISO({ format: 'basic' }) //=> '20170422T204705.335-0400'\n   * @example DateTime.now().toISO({ precision: 'day' }) //=> '2017-04-22Z'\n   * @example DateTime.now().toISO({ precision: 'minute' }) //=> '2017-04-22T20:47Z'\n   * @return {string|null}\n   */\n  toISO({\n    format = \"extended\",\n    suppressSeconds = false,\n    suppressMilliseconds = false,\n    includeOffset = true,\n    extendedZone = false,\n    precision = \"milliseconds\",\n  } = {}) {\n    if (!this.isValid) {\n      return null;\n    }\n\n    precision = normalizeUnit(precision);\n    const ext = format === \"extended\";\n\n    let c = toISODate(this, ext, precision);\n    if (orderedUnits.indexOf(precision) >= 3) c += \"T\";\n    c += toISOTime(\n      this,\n      ext,\n      suppressSeconds,\n      suppressMilliseconds,\n      includeOffset,\n      extendedZone,\n      precision\n    );\n    return c;\n  }\n\n  /**\n   * Returns an ISO 8601-compliant string representation of this DateTime's date component\n   * @param {Object} opts - options\n   * @param {string} [opts.format='extended'] - choose between the basic and extended format\n   * @param {string} [opts.precision='day'] - truncate output to desired precision: 'years', 'months', or 'days'.\n   * @example DateTime.utc(1982, 5, 25).toISODate() //=> '1982-05-25'\n   * @example DateTime.utc(1982, 5, 25).toISODate({ format: 'basic' }) //=> '19820525'\n   * @example DateTime.utc(1982, 5, 25).toISODate({ precision: 'month' }) //=> '1982-05'\n   * @return {string|null}\n   */\n  toISODate({ format = \"extended\", precision = \"day\" } = {}) {\n    if (!this.isValid) {\n      return null;\n    }\n    return toISODate(this, format === \"extended\", normalizeUnit(precision));\n  }\n\n  /**\n   * Returns an ISO 8601-compliant string representation of this DateTime's week date\n   * @example DateTime.utc(1982, 5, 25).toISOWeekDate() //=> '1982-W21-2'\n   * @return {string}\n   */\n  toISOWeekDate() {\n    return toTechFormat(this, \"kkkk-'W'WW-c\");\n  }\n\n  /**\n   * Returns an ISO 8601-compliant string representation of this DateTime's time component\n   * @param {Object} opts - options\n   * @param {boolean} [opts.suppressMilliseconds=false] - exclude milliseconds from the format if they're 0\n   * @param {boolean} [opts.suppressSeconds=false] - exclude seconds from the format if they're 0\n   * @param {boolean} [opts.includeOffset=true] - include the offset, such as 'Z' or '-04:00'\n   * @param {boolean} [opts.extendedZone=true] - add the time zone format extension\n   * @param {boolean} [opts.includePrefix=false] - include the `T` prefix\n   * @param {string} [opts.format='extended'] - choose between the basic and extended format\n   * @param {string} [opts.precision='milliseconds'] - truncate output to desired presicion: 'hours', 'minutes', 'seconds' or 'milliseconds'. When precision and suppressSeconds or suppressMilliseconds are used together, precision sets the maximum unit shown in the output, however seconds or milliseconds will still be suppressed if they are 0.\n   * @example DateTime.utc().set({ hour: 7, minute: 34 }).toISOTime() //=> '07:34:19.361Z'\n   * @example DateTime.utc().set({ hour: 7, minute: 34, seconds: 0, milliseconds: 0 }).toISOTime({ suppressSeconds: true }) //=> '07:34Z'\n   * @example DateTime.utc().set({ hour: 7, minute: 34 }).toISOTime({ format: 'basic' }) //=> '073419.361Z'\n   * @example DateTime.utc().set({ hour: 7, minute: 34 }).toISOTime({ includePrefix: true }) //=> 'T07:34:19.361Z'\n   * @example DateTime.utc().set({ hour: 7, minute: 34, second: 56 }).toISOTime({ precision: 'minute' }) //=> '07:34Z'\n   * @return {string}\n   */\n  toISOTime({\n    suppressMilliseconds = false,\n    suppressSeconds = false,\n    includeOffset = true,\n    includePrefix = false,\n    extendedZone = false,\n    format = \"extended\",\n    precision = \"milliseconds\",\n  } = {}) {\n    if (!this.isValid) {\n      return null;\n    }\n\n    precision = normalizeUnit(precision);\n    let c = includePrefix && orderedUnits.indexOf(precision) >= 3 ? \"T\" : \"\";\n    return (\n      c +\n      toISOTime(\n        this,\n        format === \"extended\",\n        suppressSeconds,\n        suppressMilliseconds,\n        includeOffset,\n        extendedZone,\n        precision\n      )\n    );\n  }\n\n  /**\n   * Returns an RFC 2822-compatible string representation of this DateTime\n   * @example DateTime.utc(2014, 7, 13).toRFC2822() //=> 'Sun, 13 Jul 2014 00:00:00 +0000'\n   * @example DateTime.local(2014, 7, 13).toRFC2822() //=> 'Sun, 13 Jul 2014 00:00:00 -0400'\n   * @return {string}\n   */\n  toRFC2822() {\n    return toTechFormat(this, \"EEE, dd LLL yyyy HH:mm:ss ZZZ\", false);\n  }\n\n  /**\n   * Returns a string representation of this DateTime appropriate for use in HTTP headers. The output is always expressed in GMT.\n   * Specifically, the string conforms to RFC 1123.\n   * @see https://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1\n   * @example DateTime.utc(2014, 7, 13).toHTTP() //=> 'Sun, 13 Jul 2014 00:00:00 GMT'\n   * @example DateTime.utc(2014, 7, 13, 19).toHTTP() //=> 'Sun, 13 Jul 2014 19:00:00 GMT'\n   * @return {string}\n   */\n  toHTTP() {\n    return toTechFormat(this.toUTC(), \"EEE, dd LLL yyyy HH:mm:ss 'GMT'\");\n  }\n\n  /**\n   * Returns a string representation of this DateTime appropriate for use in SQL Date\n   * @example DateTime.utc(2014, 7, 13).toSQLDate() //=> '2014-07-13'\n   * @return {string|null}\n   */\n  toSQLDate() {\n    if (!this.isValid) {\n      return null;\n    }\n    return toISODate(this, true);\n  }\n\n  /**\n   * Returns a string representation of this DateTime appropriate for use in SQL Time\n   * @param {Object} opts - options\n   * @param {boolean} [opts.includeZone=false] - include the zone, such as 'America/New_York'. Overrides includeOffset.\n   * @param {boolean} [opts.includeOffset=true] - include the offset, such as 'Z' or '-04:00'\n   * @param {boolean} [opts.includeOffsetSpace=true] - include the space between the time and the offset, such as '05:15:16.345 -04:00'\n   * @example DateTime.utc().toSQL() //=> '05:15:16.345'\n   * @example DateTime.now().toSQL() //=> '05:15:16.345 -04:00'\n   * @example DateTime.now().toSQL({ includeOffset: false }) //=> '05:15:16.345'\n   * @example DateTime.now().toSQL({ includeZone: false }) //=> '05:15:16.345 America/New_York'\n   * @return {string}\n   */\n  toSQLTime({ includeOffset = true, includeZone = false, includeOffsetSpace = true } = {}) {\n    let fmt = \"HH:mm:ss.SSS\";\n\n    if (includeZone || includeOffset) {\n      if (includeOffsetSpace) {\n        fmt += \" \";\n      }\n      if (includeZone) {\n        fmt += \"z\";\n      } else if (includeOffset) {\n        fmt += \"ZZ\";\n      }\n    }\n\n    return toTechFormat(this, fmt, true);\n  }\n\n  /**\n   * Returns a string representation of this DateTime appropriate for use in SQL DateTime\n   * @param {Object} opts - options\n   * @param {boolean} [opts.includeZone=false] - include the zone, such as 'America/New_York'. Overrides includeOffset.\n   * @param {boolean} [opts.includeOffset=true] - include the offset, such as 'Z' or '-04:00'\n   * @param {boolean} [opts.includeOffsetSpace=true] - include the space between the time and the offset, such as '05:15:16.345 -04:00'\n   * @example DateTime.utc(2014, 7, 13).toSQL() //=> '2014-07-13 00:00:00.000 Z'\n   * @example DateTime.local(2014, 7, 13).toSQL() //=> '2014-07-13 00:00:00.000 -04:00'\n   * @example DateTime.local(2014, 7, 13).toSQL({ includeOffset: false }) //=> '2014-07-13 00:00:00.000'\n   * @example DateTime.local(2014, 7, 13).toSQL({ includeZone: true }) //=> '2014-07-13 00:00:00.000 America/New_York'\n   * @return {string}\n   */\n  toSQL(opts = {}) {\n    if (!this.isValid) {\n      return null;\n    }\n\n    return `${this.toSQLDate()} ${this.toSQLTime(opts)}`;\n  }\n\n  /**\n   * Returns a string representation of this DateTime appropriate for debugging\n   * @return {string}\n   */\n  toString() {\n    return this.isValid ? this.toISO() : INVALID;\n  }\n\n  /**\n   * Returns a string representation of this DateTime appropriate for the REPL.\n   * @return {string}\n   */\n  [Symbol.for(\"nodejs.util.inspect.custom\")]() {\n    if (this.isValid) {\n      return `DateTime { ts: ${this.toISO()}, zone: ${this.zone.name}, locale: ${this.locale} }`;\n    } else {\n      return `DateTime { Invalid, reason: ${this.invalidReason} }`;\n    }\n  }\n\n  /**\n   * Returns the epoch milliseconds of this DateTime. Alias of {@link DateTime#toMillis}\n   * @return {number}\n   */\n  valueOf() {\n    return this.toMillis();\n  }\n\n  /**\n   * Returns the epoch milliseconds of this DateTime.\n   * @return {number}\n   */\n  toMillis() {\n    return this.isValid ? this.ts : NaN;\n  }\n\n  /**\n   * Returns the epoch seconds (including milliseconds in the fractional part) of this DateTime.\n   * @return {number}\n   */\n  toSeconds() {\n    return this.isValid ? this.ts / 1000 : NaN;\n  }\n\n  /**\n   * Returns the epoch seconds (as a whole number) of this DateTime.\n   * @return {number}\n   */\n  toUnixInteger() {\n    return this.isValid ? Math.floor(this.ts / 1000) : NaN;\n  }\n\n  /**\n   * Returns an ISO 8601 representation of this DateTime appropriate for use in JSON.\n   * @return {string}\n   */\n  toJSON() {\n    return this.toISO();\n  }\n\n  /**\n   * Returns a BSON serializable equivalent to this DateTime.\n   * @return {Date}\n   */\n  toBSON() {\n    return this.toJSDate();\n  }\n\n  /**\n   * Returns a JavaScript object with this DateTime's year, month, day, and so on.\n   * @param opts - options for generating the object\n   * @param {boolean} [opts.includeConfig=false] - include configuration attributes in the output\n   * @example DateTime.now().toObject() //=> { year: 2017, month: 4, day: 22, hour: 20, minute: 49, second: 42, millisecond: 268 }\n   * @return {Object}\n   */\n  toObject(opts = {}) {\n    if (!this.isValid) return {};\n\n    const base = { ...this.c };\n\n    if (opts.includeConfig) {\n      base.outputCalendar = this.outputCalendar;\n      base.numberingSystem = this.loc.numberingSystem;\n      base.locale = this.loc.locale;\n    }\n    return base;\n  }\n\n  /**\n   * Returns a JavaScript Date equivalent to this DateTime.\n   * @return {Date}\n   */\n  toJSDate() {\n    return new Date(this.isValid ? this.ts : NaN);\n  }\n\n  // COMPARE\n\n  /**\n   * Return the difference between two DateTimes as a Duration.\n   * @param {DateTime} otherDateTime - the DateTime to compare this one to\n   * @param {string|string[]} [unit=['milliseconds']] - the unit or array of units (such as 'hours' or 'days') to include in the duration.\n   * @param {Object} opts - options that affect the creation of the Duration\n   * @param {string} [opts.conversionAccuracy='casual'] - the conversion system to use\n   * @example\n   * var i1 = DateTime.fromISO('1982-05-25T09:45'),\n   *     i2 = DateTime.fromISO('1983-10-14T10:30');\n   * i2.diff(i1).toObject() //=> { milliseconds: 43807500000 }\n   * i2.diff(i1, 'hours').toObject() //=> { hours: 12168.75 }\n   * i2.diff(i1, ['months', 'days']).toObject() //=> { months: 16, days: 19.03125 }\n   * i2.diff(i1, ['months', 'days', 'hours']).toObject() //=> { months: 16, days: 19, hours: 0.75 }\n   * @return {Duration}\n   */\n  diff(otherDateTime, unit = \"milliseconds\", opts = {}) {\n    if (!this.isValid || !otherDateTime.isValid) {\n      return Duration.invalid(\"created by diffing an invalid DateTime\");\n    }\n\n    const durOpts = { locale: this.locale, numberingSystem: this.numberingSystem, ...opts };\n\n    const units = maybeArray(unit).map(Duration.normalizeUnit),\n      otherIsLater = otherDateTime.valueOf() > this.valueOf(),\n      earlier = otherIsLater ? this : otherDateTime,\n      later = otherIsLater ? otherDateTime : this,\n      diffed = diff(earlier, later, units, durOpts);\n\n    return otherIsLater ? diffed.negate() : diffed;\n  }\n\n  /**\n   * Return the difference between this DateTime and right now.\n   * See {@link DateTime#diff}\n   * @param {string|string[]} [unit=['milliseconds']] - the unit or units units (such as 'hours' or 'days') to include in the duration\n   * @param {Object} opts - options that affect the creation of the Duration\n   * @param {string} [opts.conversionAccuracy='casual'] - the conversion system to use\n   * @return {Duration}\n   */\n  diffNow(unit = \"milliseconds\", opts = {}) {\n    return this.diff(DateTime.now(), unit, opts);\n  }\n\n  /**\n   * Return an Interval spanning between this DateTime and another DateTime\n   * @param {DateTime} otherDateTime - the other end point of the Interval\n   * @return {Interval|DateTime}\n   */\n  until(otherDateTime) {\n    return this.isValid ? Interval.fromDateTimes(this, otherDateTime) : this;\n  }\n\n  /**\n   * Return whether this DateTime is in the same unit of time as another DateTime.\n   * Higher-order units must also be identical for this function to return `true`.\n   * Note that time zones are **ignored** in this comparison, which compares the **local** calendar time. Use {@link DateTime#setZone} to convert one of the dates if needed.\n   * @param {DateTime} otherDateTime - the other DateTime\n   * @param {string} unit - the unit of time to check sameness on\n   * @param {Object} opts - options\n   * @param {boolean} [opts.useLocaleWeeks=false] - If true, use weeks based on the locale, i.e. use the locale-dependent start of the week; only the locale of this DateTime is used\n   * @example DateTime.now().hasSame(otherDT, 'day'); //~> true if otherDT is in the same current calendar day\n   * @return {boolean}\n   */\n  hasSame(otherDateTime, unit, opts) {\n    if (!this.isValid) return false;\n\n    const inputMs = otherDateTime.valueOf();\n    const adjustedToZone = this.setZone(otherDateTime.zone, { keepLocalTime: true });\n    return (\n      adjustedToZone.startOf(unit, opts) <= inputMs && inputMs <= adjustedToZone.endOf(unit, opts)\n    );\n  }\n\n  /**\n   * Equality check\n   * Two DateTimes are equal if and only if they represent the same millisecond, have the same zone and location, and are both valid.\n   * To compare just the millisecond values, use `+dt1 === +dt2`.\n   * @param {DateTime} other - the other DateTime\n   * @return {boolean}\n   */\n  equals(other) {\n    return (\n      this.isValid &&\n      other.isValid &&\n      this.valueOf() === other.valueOf() &&\n      this.zone.equals(other.zone) &&\n      this.loc.equals(other.loc)\n    );\n  }\n\n  /**\n   * Returns a string representation of a this time relative to now, such as \"in two days\". Can only internationalize if your\n   * platform supports Intl.RelativeTimeFormat. Rounds towards zero by default.\n   * @param {Object} options - options that affect the output\n   * @param {DateTime} [options.base=DateTime.now()] - the DateTime to use as the basis to which this time is compared. Defaults to now.\n   * @param {string} [options.style=\"long\"] - the style of units, must be \"long\", \"short\", or \"narrow\"\n   * @param {string|string[]} options.unit - use a specific unit or array of units; if omitted, or an array, the method will pick the best unit. Use an array or one of \"years\", \"quarters\", \"months\", \"weeks\", \"days\", \"hours\", \"minutes\", or \"seconds\"\n   * @param {boolean} [options.round=true] - whether to round the numbers in the output.\n   * @param {string} [options.rounding=\"trunc\"] - rounding method to use when rounding the numbers in the output. Can be \"trunc\" (toward zero), \"expand\" (away from zero), \"round\", \"floor\", or \"ceil\".\n   * @param {number} [options.padding=0] - padding in milliseconds. This allows you to round up the result if it fits inside the threshold. Don't use in combination with {round: false} because the decimal output will include the padding.\n   * @param {string} options.locale - override the locale of this DateTime\n   * @param {string} options.numberingSystem - override the numberingSystem of this DateTime. The Intl system may choose not to honor this\n   * @example DateTime.now().plus({ days: 1 }).toRelative() //=> \"in 1 day\"\n   * @example DateTime.now().setLocale(\"es\").toRelative({ days: 1 }) //=> \"dentro de 1 día\"\n   * @example DateTime.now().plus({ days: 1 }).toRelative({ locale: \"fr\" }) //=> \"dans 23 heures\"\n   * @example DateTime.now().minus({ days: 2 }).toRelative() //=> \"2 days ago\"\n   * @example DateTime.now().minus({ days: 2 }).toRelative({ unit: \"hours\" }) //=> \"48 hours ago\"\n   * @example DateTime.now().minus({ hours: 36 }).toRelative({ round: false }) //=> \"1.5 days ago\"\n   */\n  toRelative(options = {}) {\n    if (!this.isValid) return null;\n    const base = options.base || DateTime.fromObject({}, { zone: this.zone }),\n      padding = options.padding ? (this < base ? -options.padding : options.padding) : 0;\n    let units = [\"years\", \"months\", \"days\", \"hours\", \"minutes\", \"seconds\"];\n    let unit = options.unit;\n    if (Array.isArray(options.unit)) {\n      units = options.unit;\n      unit = undefined;\n    }\n    return diffRelative(base, this.plus(padding), {\n      ...options,\n      numeric: \"always\",\n      units,\n      unit,\n    });\n  }\n\n  /**\n   * Returns a string representation of this date relative to today, such as \"yesterday\" or \"next month\".\n   * Only internationalizes on platforms that supports Intl.RelativeTimeFormat.\n   * @param {Object} options - options that affect the output\n   * @param {DateTime} [options.base=DateTime.now()] - the DateTime to use as the basis to which this time is compared. Defaults to now.\n   * @param {string} options.locale - override the locale of this DateTime\n   * @param {string} options.unit - use a specific unit; if omitted, the method will pick the unit. Use one of \"years\", \"quarters\", \"months\", \"weeks\", or \"days\"\n   * @param {string} options.numberingSystem - override the numberingSystem of this DateTime. The Intl system may choose not to honor this\n   * @example DateTime.now().plus({ days: 1 }).toRelativeCalendar() //=> \"tomorrow\"\n   * @example DateTime.now().setLocale(\"es\").plus({ days: 1 }).toRelative() //=> \"\"mañana\"\n   * @example DateTime.now().plus({ days: 1 }).toRelativeCalendar({ locale: \"fr\" }) //=> \"demain\"\n   * @example DateTime.now().minus({ days: 2 }).toRelativeCalendar() //=> \"2 days ago\"\n   */\n  toRelativeCalendar(options = {}) {\n    if (!this.isValid) return null;\n\n    return diffRelative(options.base || DateTime.fromObject({}, { zone: this.zone }), this, {\n      ...options,\n      numeric: \"auto\",\n      units: [\"years\", \"months\", \"days\"],\n      calendary: true,\n    });\n  }\n\n  /**\n   * Return the min of several date times\n   * @param {...DateTime} dateTimes - the DateTimes from which to choose the minimum\n   * @return {DateTime} the min DateTime, or undefined if called with no argument\n   */\n  static min(...dateTimes) {\n    if (!dateTimes.every(DateTime.isDateTime)) {\n      throw new InvalidArgumentError(\"min requires all arguments be DateTimes\");\n    }\n    return bestBy(dateTimes, (i) => i.valueOf(), Math.min);\n  }\n\n  /**\n   * Return the max of several date times\n   * @param {...DateTime} dateTimes - the DateTimes from which to choose the maximum\n   * @return {DateTime} the max DateTime, or undefined if called with no argument\n   */\n  static max(...dateTimes) {\n    if (!dateTimes.every(DateTime.isDateTime)) {\n      throw new InvalidArgumentError(\"max requires all arguments be DateTimes\");\n    }\n    return bestBy(dateTimes, (i) => i.valueOf(), Math.max);\n  }\n\n  // MISC\n\n  /**\n   * Explain how a string would be parsed by fromFormat()\n   * @param {string} text - the string to parse\n   * @param {string} fmt - the format the string is expected to be in (see description)\n   * @param {Object} options - options taken by fromFormat()\n   * @return {Object}\n   */\n  static fromFormatExplain(text, fmt, options = {}) {\n    const { locale = null, numberingSystem = null } = options,\n      localeToUse = Locale.fromOpts({\n        locale,\n        numberingSystem,\n        defaultToEN: true,\n      });\n    return explainFromTokens(localeToUse, text, fmt);\n  }\n\n  /**\n   * @deprecated use fromFormatExplain instead\n   */\n  static fromStringExplain(text, fmt, options = {}) {\n    return DateTime.fromFormatExplain(text, fmt, options);\n  }\n\n  /**\n   * Build a parser for `fmt` using the given locale. This parser can be passed\n   * to {@link DateTime.fromFormatParser} to a parse a date in this format. This\n   * can be used to optimize cases where many dates need to be parsed in a\n   * specific format.\n   *\n   * @param {String} fmt - the format the string is expected to be in (see\n   * description)\n   * @param {Object} options - options used to set locale and numberingSystem\n   * for parser\n   * @returns {TokenParser} - opaque object to be used\n   */\n  static buildFormatParser(fmt, options = {}) {\n    const { locale = null, numberingSystem = null } = options,\n      localeToUse = Locale.fromOpts({\n        locale,\n        numberingSystem,\n        defaultToEN: true,\n      });\n    return new TokenParser(localeToUse, fmt);\n  }\n\n  /**\n   * Create a DateTime from an input string and format parser.\n   *\n   * The format parser must have been created with the same locale as this call.\n   *\n   * @param {String} text - the string to parse\n   * @param {TokenParser} formatParser - parser from {@link DateTime.buildFormatParser}\n   * @param {Object} opts - options taken by fromFormat()\n   * @returns {DateTime}\n   */\n  static fromFormatParser(text, formatParser, opts = {}) {\n    if (isUndefined(text) || isUndefined(formatParser)) {\n      throw new InvalidArgumentError(\n        \"fromFormatParser requires an input string and a format parser\"\n      );\n    }\n    const { locale = null, numberingSystem = null } = opts,\n      localeToUse = Locale.fromOpts({\n        locale,\n        numberingSystem,\n        defaultToEN: true,\n      });\n\n    if (!localeToUse.equals(formatParser.locale)) {\n      throw new InvalidArgumentError(\n        `fromFormatParser called with a locale of ${localeToUse}, ` +\n          `but the format parser was created for ${formatParser.locale}`\n      );\n    }\n\n    const { result, zone, specificOffset, invalidReason } = formatParser.explainFromTokens(text);\n\n    if (invalidReason) {\n      return DateTime.invalid(invalidReason);\n    } else {\n      return parseDataToDateTime(\n        result,\n        zone,\n        opts,\n        `format ${formatParser.format}`,\n        text,\n        specificOffset\n      );\n    }\n  }\n\n  // FORMAT PRESETS\n\n  /**\n   * {@link DateTime#toLocaleString} format like 10/14/1983\n   * @type {Object}\n   */\n  static get DATE_SHORT() {\n    return Formats.DATE_SHORT;\n  }\n\n  /**\n   * {@link DateTime#toLocaleString} format like 'Oct 14, 1983'\n   * @type {Object}\n   */\n  static get DATE_MED() {\n    return Formats.DATE_MED;\n  }\n\n  /**\n   * {@link DateTime#toLocaleString} format like 'Fri, Oct 14, 1983'\n   * @type {Object}\n   */\n  static get DATE_MED_WITH_WEEKDAY() {\n    return Formats.DATE_MED_WITH_WEEKDAY;\n  }\n\n  /**\n   * {@link DateTime#toLocaleString} format like 'October 14, 1983'\n   * @type {Object}\n   */\n  static get DATE_FULL() {\n    return Formats.DATE_FULL;\n  }\n\n  /**\n   * {@link DateTime#toLocaleString} format like 'Tuesday, October 14, 1983'\n   * @type {Object}\n   */\n  static get DATE_HUGE() {\n    return Formats.DATE_HUGE;\n  }\n\n  /**\n   * {@link DateTime#toLocaleString} format like '09:30 AM'. Only 12-hour if the locale is.\n   * @type {Object}\n   */\n  static get TIME_SIMPLE() {\n    return Formats.TIME_SIMPLE;\n  }\n\n  /**\n   * {@link DateTime#toLocaleString} format like '09:30:23 AM'. Only 12-hour if the locale is.\n   * @type {Object}\n   */\n  static get TIME_WITH_SECONDS() {\n    return Formats.TIME_WITH_SECONDS;\n  }\n\n  /**\n   * {@link DateTime#toLocaleString} format like '09:30:23 AM EDT'. Only 12-hour if the locale is.\n   * @type {Object}\n   */\n  static get TIME_WITH_SHORT_OFFSET() {\n    return Formats.TIME_WITH_SHORT_OFFSET;\n  }\n\n  /**\n   * {@link DateTime#toLocaleString} format like '09:30:23 AM Eastern Daylight Time'. Only 12-hour if the locale is.\n   * @type {Object}\n   */\n  static get TIME_WITH_LONG_OFFSET() {\n    return Formats.TIME_WITH_LONG_OFFSET;\n  }\n\n  /**\n   * {@link DateTime#toLocaleString} format like '09:30', always 24-hour.\n   * @type {Object}\n   */\n  static get TIME_24_SIMPLE() {\n    return Formats.TIME_24_SIMPLE;\n  }\n\n  /**\n   * {@link DateTime#toLocaleString} format like '09:30:23', always 24-hour.\n   * @type {Object}\n   */\n  static get TIME_24_WITH_SECONDS() {\n    return Formats.TIME_24_WITH_SECONDS;\n  }\n\n  /**\n   * {@link DateTime#toLocaleString} format like '09:30:23 EDT', always 24-hour.\n   * @type {Object}\n   */\n  static get TIME_24_WITH_SHORT_OFFSET() {\n    return Formats.TIME_24_WITH_SHORT_OFFSET;\n  }\n\n  /**\n   * {@link DateTime#toLocaleString} format like '09:30:23 Eastern Daylight Time', always 24-hour.\n   * @type {Object}\n   */\n  static get TIME_24_WITH_LONG_OFFSET() {\n    return Formats.TIME_24_WITH_LONG_OFFSET;\n  }\n\n  /**\n   * {@link DateTime#toLocaleString} format like '10/14/1983, 9:30 AM'. Only 12-hour if the locale is.\n   * @type {Object}\n   */\n  static get DATETIME_SHORT() {\n    return Formats.DATETIME_SHORT;\n  }\n\n  /**\n   * {@link DateTime#toLocaleString} format like '10/14/1983, 9:30:33 AM'. Only 12-hour if the locale is.\n   * @type {Object}\n   */\n  static get DATETIME_SHORT_WITH_SECONDS() {\n    return Formats.DATETIME_SHORT_WITH_SECONDS;\n  }\n\n  /**\n   * {@link DateTime#toLocaleString} format like 'Oct 14, 1983, 9:30 AM'. Only 12-hour if the locale is.\n   * @type {Object}\n   */\n  static get DATETIME_MED() {\n    return Formats.DATETIME_MED;\n  }\n\n  /**\n   * {@link DateTime#toLocaleString} format like 'Oct 14, 1983, 9:30:33 AM'. Only 12-hour if the locale is.\n   * @type {Object}\n   */\n  static get DATETIME_MED_WITH_SECONDS() {\n    return Formats.DATETIME_MED_WITH_SECONDS;\n  }\n\n  /**\n   * {@link DateTime#toLocaleString} format like 'Fri, 14 Oct 1983, 9:30 AM'. Only 12-hour if the locale is.\n   * @type {Object}\n   */\n  static get DATETIME_MED_WITH_WEEKDAY() {\n    return Formats.DATETIME_MED_WITH_WEEKDAY;\n  }\n\n  /**\n   * {@link DateTime#toLocaleString} format like 'October 14, 1983, 9:30 AM EDT'. Only 12-hour if the locale is.\n   * @type {Object}\n   */\n  static get DATETIME_FULL() {\n    return Formats.DATETIME_FULL;\n  }\n\n  /**\n   * {@link DateTime#toLocaleString} format like 'October 14, 1983, 9:30:33 AM EDT'. Only 12-hour if the locale is.\n   * @type {Object}\n   */\n  static get DATETIME_FULL_WITH_SECONDS() {\n    return Formats.DATETIME_FULL_WITH_SECONDS;\n  }\n\n  /**\n   * {@link DateTime#toLocaleString} format like 'Friday, October 14, 1983, 9:30 AM Eastern Daylight Time'. Only 12-hour if the locale is.\n   * @type {Object}\n   */\n  static get DATETIME_HUGE() {\n    return Formats.DATETIME_HUGE;\n  }\n\n  /**\n   * {@link DateTime#toLocaleString} format like 'Friday, October 14, 1983, 9:30:33 AM Eastern Daylight Time'. Only 12-hour if the locale is.\n   * @type {Object}\n   */\n  static get DATETIME_HUGE_WITH_SECONDS() {\n    return Formats.DATETIME_HUGE_WITH_SECONDS;\n  }\n}\n\n/**\n * @private\n */\nexport function friendlyDateTime(dateTimeish) {\n  if (DateTime.isDateTime(dateTimeish)) {\n    return dateTimeish;\n  } else if (dateTimeish && dateTimeish.valueOf && isNumber(dateTimeish.valueOf())) {\n    return DateTime.fromJSDate(dateTimeish);\n  } else if (dateTimeish && typeof dateTimeish === \"object\") {\n    return DateTime.fromObject(dateTimeish);\n  } else {\n    throw new InvalidArgumentError(\n      `Unknown datetime argument: ${dateTimeish}, of type ${typeof dateTimeish}`\n    );\n  }\n}\n"
  },
  {
    "path": "src/duration.js",
    "content": "import { InvalidArgumentError, InvalidDurationError, InvalidUnitError } from \"./errors.js\";\nimport Formatter from \"./impl/formatter.js\";\nimport Invalid from \"./impl/invalid.js\";\nimport Locale from \"./impl/locale.js\";\nimport { parseISODuration, parseISOTimeOnly } from \"./impl/regexParser.js\";\nimport {\n  asNumber,\n  hasOwnProperty,\n  isNumber,\n  isUndefined,\n  normalizeObject,\n  roundTo,\n} from \"./impl/util.js\";\nimport Settings from \"./settings.js\";\nimport DateTime from \"./datetime.js\";\n\nconst INVALID = \"Invalid Duration\";\n\n// unit conversion constants\nexport const lowOrderMatrix = {\n    weeks: {\n      days: 7,\n      hours: 7 * 24,\n      minutes: 7 * 24 * 60,\n      seconds: 7 * 24 * 60 * 60,\n      milliseconds: 7 * 24 * 60 * 60 * 1000,\n    },\n    days: {\n      hours: 24,\n      minutes: 24 * 60,\n      seconds: 24 * 60 * 60,\n      milliseconds: 24 * 60 * 60 * 1000,\n    },\n    hours: { minutes: 60, seconds: 60 * 60, milliseconds: 60 * 60 * 1000 },\n    minutes: { seconds: 60, milliseconds: 60 * 1000 },\n    seconds: { milliseconds: 1000 },\n  },\n  casualMatrix = {\n    years: {\n      quarters: 4,\n      months: 12,\n      weeks: 52,\n      days: 365,\n      hours: 365 * 24,\n      minutes: 365 * 24 * 60,\n      seconds: 365 * 24 * 60 * 60,\n      milliseconds: 365 * 24 * 60 * 60 * 1000,\n    },\n    quarters: {\n      months: 3,\n      weeks: 13,\n      days: 91,\n      hours: 91 * 24,\n      minutes: 91 * 24 * 60,\n      seconds: 91 * 24 * 60 * 60,\n      milliseconds: 91 * 24 * 60 * 60 * 1000,\n    },\n    months: {\n      weeks: 4,\n      days: 30,\n      hours: 30 * 24,\n      minutes: 30 * 24 * 60,\n      seconds: 30 * 24 * 60 * 60,\n      milliseconds: 30 * 24 * 60 * 60 * 1000,\n    },\n\n    ...lowOrderMatrix,\n  },\n  daysInYearAccurate = 146097.0 / 400,\n  daysInMonthAccurate = 146097.0 / 4800,\n  accurateMatrix = {\n    years: {\n      quarters: 4,\n      months: 12,\n      weeks: daysInYearAccurate / 7,\n      days: daysInYearAccurate,\n      hours: daysInYearAccurate * 24,\n      minutes: daysInYearAccurate * 24 * 60,\n      seconds: daysInYearAccurate * 24 * 60 * 60,\n      milliseconds: daysInYearAccurate * 24 * 60 * 60 * 1000,\n    },\n    quarters: {\n      months: 3,\n      weeks: daysInYearAccurate / 28,\n      days: daysInYearAccurate / 4,\n      hours: (daysInYearAccurate * 24) / 4,\n      minutes: (daysInYearAccurate * 24 * 60) / 4,\n      seconds: (daysInYearAccurate * 24 * 60 * 60) / 4,\n      milliseconds: (daysInYearAccurate * 24 * 60 * 60 * 1000) / 4,\n    },\n    months: {\n      weeks: daysInMonthAccurate / 7,\n      days: daysInMonthAccurate,\n      hours: daysInMonthAccurate * 24,\n      minutes: daysInMonthAccurate * 24 * 60,\n      seconds: daysInMonthAccurate * 24 * 60 * 60,\n      milliseconds: daysInMonthAccurate * 24 * 60 * 60 * 1000,\n    },\n    ...lowOrderMatrix,\n  };\n\n// units ordered by size\nconst orderedUnits = [\n  \"years\",\n  \"quarters\",\n  \"months\",\n  \"weeks\",\n  \"days\",\n  \"hours\",\n  \"minutes\",\n  \"seconds\",\n  \"milliseconds\",\n];\n\nconst reverseUnits = orderedUnits.slice(0).reverse();\n\n// This is a map of which units to convert\n// for toHuman due to missing support from Intl.\n// if value is set, then key is what to convert to and value is what to convert\n// if value is null, then key is a unit to be ignored\nconst humanizeUnitConversion = {\n  months: \"quarters\",\n  quarters: null,\n};\n\n// clone really means \"create another instance just like this one, but with these changes\"\nfunction clone(dur, alts, clear = false) {\n  // deep merge for vals\n  const conf = {\n    values: clear ? alts.values : { ...dur.values, ...(alts.values || {}) },\n    loc: dur.loc.clone(alts.loc),\n    conversionAccuracy: alts.conversionAccuracy || dur.conversionAccuracy,\n    matrix: alts.matrix || dur.matrix,\n  };\n  return new Duration(conf);\n}\n\nfunction durationToMillis(matrix, vals) {\n  let sum = vals.milliseconds ?? 0;\n  for (const unit of reverseUnits.slice(1)) {\n    if (vals[unit]) {\n      sum += vals[unit] * matrix[unit][\"milliseconds\"];\n    }\n  }\n  return sum;\n}\n\n// NB: mutates parameters\nfunction normalizeValues(matrix, vals) {\n  // the logic below assumes the overall value of the duration is positive\n  // if this is not the case, factor is used to make it so\n  const factor = durationToMillis(matrix, vals) < 0 ? -1 : 1;\n\n  orderedUnits.reduceRight((previous, current) => {\n    if (!isUndefined(vals[current])) {\n      if (previous) {\n        const previousVal = vals[previous] * factor;\n        const conv = matrix[current][previous];\n\n        // if (previousVal < 0):\n        // lower order unit is negative (e.g. { years: 2, days: -2 })\n        // normalize this by reducing the higher order unit by the appropriate amount\n        // and increasing the lower order unit\n        // this can never make the higher order unit negative, because this function only operates\n        // on positive durations, so the amount of time represented by the lower order unit cannot\n        // be larger than the higher order unit\n        // else:\n        // lower order unit is positive (e.g. { years: 2, days: 450 } or { years: -2, days: 450 })\n        // in this case we attempt to convert as much as possible from the lower order unit into\n        // the higher order one\n        //\n        // Math.floor takes care of both of these cases, rounding away from 0\n        // if previousVal < 0 it makes the absolute value larger\n        // if previousVal >= it makes the absolute value smaller\n        const rollUp = Math.floor(previousVal / conv);\n        vals[current] += rollUp * factor;\n        vals[previous] -= rollUp * conv * factor;\n      }\n      return current;\n    } else {\n      return previous;\n    }\n  }, null);\n\n  // try to convert any decimals into smaller units if possible\n  // for example for { years: 2.5, days: 0, seconds: 0 } we want to get { years: 2, days: 182, hours: 12 }\n  orderedUnits.reduce((previous, current) => {\n    if (!isUndefined(vals[current])) {\n      if (previous) {\n        const fraction = vals[previous] % 1;\n        vals[previous] -= fraction;\n        vals[current] += fraction * matrix[previous][current];\n      }\n      return current;\n    } else {\n      return previous;\n    }\n  }, null);\n}\n\n// Remove all properties with a value of 0 from an object\nfunction removeZeroes(vals) {\n  const newVals = {};\n  for (const [key, value] of Object.entries(vals)) {\n    if (value !== 0) {\n      newVals[key] = value;\n    }\n  }\n  return newVals;\n}\n\n/**\n * A Duration object represents a period of time, like \"2 months\" or \"1 day, 1 hour\". Conceptually, it's just a map of units to their quantities, accompanied by some additional configuration and methods for creating, parsing, interrogating, transforming, and formatting them. They can be used on their own or in conjunction with other Luxon types; for example, you can use {@link DateTime#plus} to add a Duration object to a DateTime, producing another DateTime.\n *\n * Here is a brief overview of commonly used methods and getters in Duration:\n *\n * * **Creation** To create a Duration, use {@link Duration.fromMillis}, {@link Duration.fromObject}, or {@link Duration.fromISO}.\n * * **Unit values** See the {@link Duration#years}, {@link Duration#months}, {@link Duration#weeks}, {@link Duration#days}, {@link Duration#hours}, {@link Duration#minutes}, {@link Duration#seconds}, {@link Duration#milliseconds} accessors.\n * * **Configuration** See  {@link Duration#locale} and {@link Duration#numberingSystem} accessors.\n * * **Transformation** To create new Durations out of old ones use {@link Duration#plus}, {@link Duration#minus}, {@link Duration#normalize}, {@link Duration#set}, {@link Duration#reconfigure}, {@link Duration#shiftTo}, and {@link Duration#negate}.\n * * **Output** To convert the Duration into other representations, see {@link Duration#as}, {@link Duration#toISO}, {@link Duration#toFormat}, and {@link Duration#toJSON}\n *\n * There's are more methods documented below. In addition, for more information on subtler topics like internationalization and validity, see the external documentation.\n */\nexport default class Duration {\n  /**\n   * @private\n   */\n  constructor(config) {\n    const accurate = config.conversionAccuracy === \"longterm\" || false;\n    let matrix = accurate ? accurateMatrix : casualMatrix;\n\n    if (config.matrix) {\n      matrix = config.matrix;\n    }\n\n    /**\n     * @access private\n     */\n    this.values = config.values;\n    /**\n     * @access private\n     */\n    this.loc = config.loc || Locale.create();\n    /**\n     * @access private\n     */\n    this.conversionAccuracy = accurate ? \"longterm\" : \"casual\";\n    /**\n     * @access private\n     */\n    this.invalid = config.invalid || null;\n    /**\n     * @access private\n     */\n    this.matrix = matrix;\n    /**\n     * @access private\n     */\n    this.isLuxonDuration = true;\n  }\n\n  /**\n   * Create Duration from a number of milliseconds.\n   * @param {number} count of milliseconds\n   * @param {Object} opts - options for parsing\n   * @param {string} [opts.locale='en-US'] - the locale to use\n   * @param {string} opts.numberingSystem - the numbering system to use\n   * @param {string} [opts.conversionAccuracy='casual'] - the conversion system to use\n   * @return {Duration}\n   */\n  static fromMillis(count, opts) {\n    return Duration.fromObject({ milliseconds: count }, opts);\n  }\n\n  /**\n   * Create a Duration from a JavaScript object with keys like 'years' and 'hours'.\n   * If this object is empty then a zero milliseconds duration is returned.\n   * @param {Object} obj - the object to create the DateTime from\n   * @param {number} obj.years\n   * @param {number} obj.quarters\n   * @param {number} obj.months\n   * @param {number} obj.weeks\n   * @param {number} obj.days\n   * @param {number} obj.hours\n   * @param {number} obj.minutes\n   * @param {number} obj.seconds\n   * @param {number} obj.milliseconds\n   * @param {Object} [opts=[]] - options for creating this Duration\n   * @param {string} [opts.locale='en-US'] - the locale to use\n   * @param {string} opts.numberingSystem - the numbering system to use\n   * @param {string} [opts.conversionAccuracy='casual'] - the preset conversion system to use\n   * @param {string} [opts.matrix=Object] - the custom conversion system to use\n   * @return {Duration}\n   */\n  static fromObject(obj, opts = {}) {\n    if (obj == null || typeof obj !== \"object\") {\n      throw new InvalidArgumentError(\n        `Duration.fromObject: argument expected to be an object, got ${\n          obj === null ? \"null\" : typeof obj\n        }`\n      );\n    }\n\n    return new Duration({\n      values: normalizeObject(obj, Duration.normalizeUnit),\n      loc: Locale.fromObject(opts),\n      conversionAccuracy: opts.conversionAccuracy,\n      matrix: opts.matrix,\n    });\n  }\n\n  /**\n   * Create a Duration from DurationLike.\n   *\n   * @param {Object | number | Duration} durationLike\n   * One of:\n   * - object with keys like 'years' and 'hours'.\n   * - number representing milliseconds\n   * - Duration instance\n   * @return {Duration}\n   */\n  static fromDurationLike(durationLike) {\n    if (isNumber(durationLike)) {\n      return Duration.fromMillis(durationLike);\n    } else if (Duration.isDuration(durationLike)) {\n      return durationLike;\n    } else if (typeof durationLike === \"object\") {\n      return Duration.fromObject(durationLike);\n    } else {\n      throw new InvalidArgumentError(\n        `Unknown duration argument ${durationLike} of type ${typeof durationLike}`\n      );\n    }\n  }\n\n  /**\n   * Create a Duration from an ISO 8601 duration string.\n   * @param {string} text - text to parse\n   * @param {Object} opts - options for parsing\n   * @param {string} [opts.locale='en-US'] - the locale to use\n   * @param {string} opts.numberingSystem - the numbering system to use\n   * @param {string} [opts.conversionAccuracy='casual'] - the preset conversion system to use\n   * @param {string} [opts.matrix=Object] - the preset conversion system to use\n   * @see https://en.wikipedia.org/wiki/ISO_8601#Durations\n   * @example Duration.fromISO('P3Y6M1W4DT12H30M5S').toObject() //=> { years: 3, months: 6, weeks: 1, days: 4, hours: 12, minutes: 30, seconds: 5 }\n   * @example Duration.fromISO('PT23H').toObject() //=> { hours: 23 }\n   * @example Duration.fromISO('P5Y3M').toObject() //=> { years: 5, months: 3 }\n   * @return {Duration}\n   */\n  static fromISO(text, opts) {\n    const [parsed] = parseISODuration(text);\n    if (parsed) {\n      return Duration.fromObject(parsed, opts);\n    } else {\n      return Duration.invalid(\"unparsable\", `the input \"${text}\" can't be parsed as ISO 8601`);\n    }\n  }\n\n  /**\n   * Create a Duration from an ISO 8601 time string.\n   * @param {string} text - text to parse\n   * @param {Object} opts - options for parsing\n   * @param {string} [opts.locale='en-US'] - the locale to use\n   * @param {string} opts.numberingSystem - the numbering system to use\n   * @param {string} [opts.conversionAccuracy='casual'] - the preset conversion system to use\n   * @param {string} [opts.matrix=Object] - the conversion system to use\n   * @see https://en.wikipedia.org/wiki/ISO_8601#Times\n   * @example Duration.fromISOTime('11:22:33.444').toObject() //=> { hours: 11, minutes: 22, seconds: 33, milliseconds: 444 }\n   * @example Duration.fromISOTime('11:00').toObject() //=> { hours: 11, minutes: 0, seconds: 0 }\n   * @example Duration.fromISOTime('T11:00').toObject() //=> { hours: 11, minutes: 0, seconds: 0 }\n   * @example Duration.fromISOTime('1100').toObject() //=> { hours: 11, minutes: 0, seconds: 0 }\n   * @example Duration.fromISOTime('T1100').toObject() //=> { hours: 11, minutes: 0, seconds: 0 }\n   * @return {Duration}\n   */\n  static fromISOTime(text, opts) {\n    const [parsed] = parseISOTimeOnly(text);\n    if (parsed) {\n      return Duration.fromObject(parsed, opts);\n    } else {\n      return Duration.invalid(\"unparsable\", `the input \"${text}\" can't be parsed as ISO 8601`);\n    }\n  }\n\n  /**\n   * Create an invalid Duration.\n   * @param {string} reason - simple string of why this datetime is invalid. Should not contain parameters or anything else data-dependent\n   * @param {string} [explanation=null] - longer explanation, may include parameters and other useful debugging information\n   * @return {Duration}\n   */\n  static invalid(reason, explanation = null) {\n    if (!reason) {\n      throw new InvalidArgumentError(\"need to specify a reason the Duration is invalid\");\n    }\n\n    const invalid = reason instanceof Invalid ? reason : new Invalid(reason, explanation);\n\n    if (Settings.throwOnInvalid) {\n      throw new InvalidDurationError(invalid);\n    } else {\n      return new Duration({ invalid });\n    }\n  }\n\n  /**\n   * @private\n   */\n  static normalizeUnit(unit) {\n    const normalized = {\n      year: \"years\",\n      years: \"years\",\n      quarter: \"quarters\",\n      quarters: \"quarters\",\n      month: \"months\",\n      months: \"months\",\n      week: \"weeks\",\n      weeks: \"weeks\",\n      day: \"days\",\n      days: \"days\",\n      hour: \"hours\",\n      hours: \"hours\",\n      minute: \"minutes\",\n      minutes: \"minutes\",\n      second: \"seconds\",\n      seconds: \"seconds\",\n      millisecond: \"milliseconds\",\n      milliseconds: \"milliseconds\",\n    }[unit ? unit.toLowerCase() : unit];\n\n    if (!normalized) throw new InvalidUnitError(unit);\n\n    return normalized;\n  }\n\n  /**\n   * Check if an object is a Duration. Works across context boundaries\n   * @param {object} o\n   * @return {boolean}\n   */\n  static isDuration(o) {\n    return (o && o.isLuxonDuration) || false;\n  }\n\n  /**\n   * Get  the locale of a Duration, such 'en-GB'\n   * @type {string}\n   */\n  get locale() {\n    return this.isValid ? this.loc.locale : null;\n  }\n\n  /**\n   * Get the numbering system of a Duration, such 'beng'. The numbering system is used when formatting the Duration\n   *\n   * @type {string}\n   */\n  get numberingSystem() {\n    return this.isValid ? this.loc.numberingSystem : null;\n  }\n\n  /**\n   * Returns a string representation of this Duration formatted according to the specified format string. You may use these tokens:\n   * * `S` for milliseconds\n   * * `s` for seconds\n   * * `m` for minutes\n   * * `h` for hours\n   * * `d` for days\n   * * `w` for weeks\n   * * `M` for months\n   * * `y` for years\n   * Notes:\n   * * Add padding by repeating the token, e.g. \"yy\" pads the years to two digits, \"hhhh\" pads the hours out to four digits\n   * * Tokens can be escaped by wrapping with single quotes.\n   * * The duration will be converted to the set of units in the format string using {@link Duration#shiftTo} and the Durations's conversion accuracy setting.\n   * @param {string} fmt - the format string\n   * @param {Object} opts - options\n   * @param {boolean} [opts.floor=true] - floor numerical values\n   * @param {'negative'|'all'|'negativeLargestOnly'} [opts.signMode=negative] - How to handle signs\n   * @example Duration.fromObject({ years: 1, days: 6, seconds: 2 }).toFormat(\"y d s\") //=> \"1 6 2\"\n   * @example Duration.fromObject({ years: 1, days: 6, seconds: 2 }).toFormat(\"yy dd sss\") //=> \"01 06 002\"\n   * @example Duration.fromObject({ years: 1, days: 6, seconds: 2 }).toFormat(\"M S\") //=> \"12 518402000\"\n   * @example Duration.fromObject({ days: 6, seconds: 2 }).toFormat(\"d s\", { signMode: \"all\" }) //=> \"+6 +2\"\n   * @example Duration.fromObject({ days: -6, seconds: -2 }).toFormat(\"d s\", { signMode: \"all\" }) //=> \"-6 -2\"\n   * @example Duration.fromObject({ days: -6, seconds: -2 }).toFormat(\"d s\", { signMode: \"negativeLargestOnly\" }) //=> \"-6 2\"\n   * @return {string}\n   */\n  toFormat(fmt, opts = {}) {\n    // reverse-compat since 1.2; we always round down now, never up, and we do it by default\n    const fmtOpts = {\n      ...opts,\n      floor: opts.round !== false && opts.floor !== false,\n    };\n    return this.isValid\n      ? Formatter.create(this.loc, fmtOpts).formatDurationFromString(this, fmt)\n      : INVALID;\n  }\n\n  /**\n   * Returns a string representation of a Duration with all units included.\n   * To modify its behavior, use `listStyle` and any Intl.NumberFormat option, though `unitDisplay` is especially relevant.\n   * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat#options\n   * @param {Object} opts - Formatting options. Accepts the same keys as the options parameter of the native `Intl.NumberFormat` constructor, as well as `listStyle`.\n   * @param {string} [opts.listStyle='narrow'] - How to format the merged list. Corresponds to the `style` property of the options parameter of the native `Intl.ListFormat` constructor.\n   * @param {boolean} [opts.showZeros=true] - Show all units previously used by the duration even if they are zero\n   * @example\n   * ```js\n   * var dur = Duration.fromObject({ months: 1, weeks: 0, hours: 5, minutes: 6 })\n   * dur.toHuman() //=> '1 month, 0 weeks, 5 hours, 6 minutes'\n   * dur.toHuman({ listStyle: \"long\" }) //=> '1 month, 0 weeks, 5 hours, and 6 minutes'\n   * dur.toHuman({ unitDisplay: \"short\" }) //=> '1 mth, 0 wks, 5 hr, 6 min'\n   * dur.toHuman({ showZeros: false }) //=> '1 month, 5 hours, 6 minutes'\n   * ```\n   */\n  toHuman(opts = {}) {\n    if (!this.isValid) return INVALID;\n\n    const showZeros = opts.showZeros !== false;\n\n    const l = orderedUnits\n      .map((unit) => {\n        const convertUnit = humanizeUnitConversion[unit];\n        if (convertUnit === null) return null;\n        let val = this.values[unit];\n        if (convertUnit) {\n          const val2 = this.values[convertUnit];\n          if (val2) {\n            val = (val ?? 0) + val2 * this.matrix[convertUnit][unit];\n          }\n        }\n        if (isUndefined(val) || (val === 0 && !showZeros)) {\n          return null;\n        }\n        return this.loc\n          .numberFormatter({ style: \"unit\", unitDisplay: \"long\", ...opts, unit: unit.slice(0, -1) })\n          .format(val);\n      })\n      .filter((n) => n);\n\n    return this.loc\n      .listFormatter({ type: \"conjunction\", style: opts.listStyle || \"narrow\", ...opts })\n      .format(l);\n  }\n\n  /**\n   * Returns a JavaScript object with this Duration's values.\n   * @example Duration.fromObject({ years: 1, days: 6, seconds: 2 }).toObject() //=> { years: 1, days: 6, seconds: 2 }\n   * @return {Object}\n   */\n  toObject() {\n    if (!this.isValid) return {};\n    return { ...this.values };\n  }\n\n  /**\n   * Returns an ISO 8601-compliant string representation of this Duration.\n   * @see https://en.wikipedia.org/wiki/ISO_8601#Durations\n   * @example Duration.fromObject({ years: 3, seconds: 45 }).toISO() //=> 'P3YT45S'\n   * @example Duration.fromObject({ months: 4, seconds: 45 }).toISO() //=> 'P4MT45S'\n   * @example Duration.fromObject({ months: 5 }).toISO() //=> 'P5M'\n   * @example Duration.fromObject({ minutes: 5 }).toISO() //=> 'PT5M'\n   * @example Duration.fromObject({ milliseconds: 6 }).toISO() //=> 'PT0.006S'\n   * @return {string}\n   */\n  toISO() {\n    // we could use the formatter, but this is an easier way to get the minimum string\n    if (!this.isValid) return null;\n\n    let s = \"P\";\n    if (this.years !== 0) s += this.years + \"Y\";\n    if (this.months !== 0 || this.quarters !== 0) s += this.months + this.quarters * 3 + \"M\";\n    if (this.weeks !== 0) s += this.weeks + \"W\";\n    if (this.days !== 0) s += this.days + \"D\";\n    if (this.hours !== 0 || this.minutes !== 0 || this.seconds !== 0 || this.milliseconds !== 0)\n      s += \"T\";\n    if (this.hours !== 0) s += this.hours + \"H\";\n    if (this.minutes !== 0) s += this.minutes + \"M\";\n    if (this.seconds !== 0 || this.milliseconds !== 0)\n      // this will handle \"floating point madness\" by removing extra decimal places\n      // https://stackoverflow.com/questions/588004/is-floating-point-math-broken\n      s += roundTo(this.seconds + this.milliseconds / 1000, 3) + \"S\";\n    if (s === \"P\") s += \"T0S\";\n    return s;\n  }\n\n  /**\n   * Returns an ISO 8601-compliant string representation of this Duration, formatted as a time of day.\n   * Note that this will return null if the duration is invalid, negative, or equal to or greater than 24 hours.\n   * @see https://en.wikipedia.org/wiki/ISO_8601#Times\n   * @param {Object} opts - options\n   * @param {boolean} [opts.suppressMilliseconds=false] - exclude milliseconds from the format if they're 0\n   * @param {boolean} [opts.suppressSeconds=false] - exclude seconds from the format if they're 0\n   * @param {boolean} [opts.includePrefix=false] - include the `T` prefix\n   * @param {string} [opts.format='extended'] - choose between the basic and extended format\n   * @example Duration.fromObject({ hours: 11 }).toISOTime() //=> '11:00:00.000'\n   * @example Duration.fromObject({ hours: 11 }).toISOTime({ suppressMilliseconds: true }) //=> '11:00:00'\n   * @example Duration.fromObject({ hours: 11 }).toISOTime({ suppressSeconds: true }) //=> '11:00'\n   * @example Duration.fromObject({ hours: 11 }).toISOTime({ includePrefix: true }) //=> 'T11:00:00.000'\n   * @example Duration.fromObject({ hours: 11 }).toISOTime({ format: 'basic' }) //=> '110000.000'\n   * @return {string}\n   */\n  toISOTime(opts = {}) {\n    if (!this.isValid) return null;\n\n    const millis = this.toMillis();\n    if (millis < 0 || millis >= 86400000) return null;\n\n    opts = {\n      suppressMilliseconds: false,\n      suppressSeconds: false,\n      includePrefix: false,\n      format: \"extended\",\n      ...opts,\n      includeOffset: false,\n    };\n\n    const dateTime = DateTime.fromMillis(millis, { zone: \"UTC\" });\n    return dateTime.toISOTime(opts);\n  }\n\n  /**\n   * Returns an ISO 8601 representation of this Duration appropriate for use in JSON.\n   * @return {string}\n   */\n  toJSON() {\n    return this.toISO();\n  }\n\n  /**\n   * Returns an ISO 8601 representation of this Duration appropriate for use in debugging.\n   * @return {string}\n   */\n  toString() {\n    return this.toISO();\n  }\n\n  /**\n   * Returns a string representation of this Duration appropriate for the REPL.\n   * @return {string}\n   */\n  [Symbol.for(\"nodejs.util.inspect.custom\")]() {\n    if (this.isValid) {\n      return `Duration { values: ${JSON.stringify(this.values)} }`;\n    } else {\n      return `Duration { Invalid, reason: ${this.invalidReason} }`;\n    }\n  }\n\n  /**\n   * Returns an milliseconds value of this Duration.\n   * @return {number}\n   */\n  toMillis() {\n    if (!this.isValid) return NaN;\n\n    return durationToMillis(this.matrix, this.values);\n  }\n\n  /**\n   * Returns an milliseconds value of this Duration. Alias of {@link toMillis}\n   * @return {number}\n   */\n  valueOf() {\n    return this.toMillis();\n  }\n\n  /**\n   * Make this Duration longer by the specified amount. Return a newly-constructed Duration.\n   * @param {Duration|Object|number} duration - The amount to add. Either a Luxon Duration, a number of milliseconds, the object argument to Duration.fromObject()\n   * @return {Duration}\n   */\n  plus(duration) {\n    if (!this.isValid) return this;\n\n    const dur = Duration.fromDurationLike(duration),\n      result = {};\n\n    for (const k of orderedUnits) {\n      if (hasOwnProperty(dur.values, k) || hasOwnProperty(this.values, k)) {\n        result[k] = dur.get(k) + this.get(k);\n      }\n    }\n\n    return clone(this, { values: result }, true);\n  }\n\n  /**\n   * Make this Duration shorter by the specified amount. Return a newly-constructed Duration.\n   * @param {Duration|Object|number} duration - The amount to subtract. Either a Luxon Duration, a number of milliseconds, the object argument to Duration.fromObject()\n   * @return {Duration}\n   */\n  minus(duration) {\n    if (!this.isValid) return this;\n\n    const dur = Duration.fromDurationLike(duration);\n    return this.plus(dur.negate());\n  }\n\n  /**\n   * Scale this Duration by the specified amount. Return a newly-constructed Duration.\n   * @param {function} fn - The function to apply to each unit. Arity is 1 or 2: the value of the unit and, optionally, the unit name. Must return a number.\n   * @example Duration.fromObject({ hours: 1, minutes: 30 }).mapUnits(x => x * 2) //=> { hours: 2, minutes: 60 }\n   * @example Duration.fromObject({ hours: 1, minutes: 30 }).mapUnits((x, u) => u === \"hours\" ? x * 2 : x) //=> { hours: 2, minutes: 30 }\n   * @return {Duration}\n   */\n  mapUnits(fn) {\n    if (!this.isValid) return this;\n    const result = {};\n    for (const k of Object.keys(this.values)) {\n      result[k] = asNumber(fn(this.values[k], k));\n    }\n    return clone(this, { values: result }, true);\n  }\n\n  /**\n   * Get the value of unit.\n   * @param {string} unit - a unit such as 'minute' or 'day'\n   * @example Duration.fromObject({years: 2, days: 3}).get('years') //=> 2\n   * @example Duration.fromObject({years: 2, days: 3}).get('months') //=> 0\n   * @example Duration.fromObject({years: 2, days: 3}).get('days') //=> 3\n   * @return {number}\n   */\n  get(unit) {\n    return this[Duration.normalizeUnit(unit)];\n  }\n\n  /**\n   * \"Set\" the values of specified units. Return a newly-constructed Duration.\n   * @param {Object} values - a mapping of units to numbers\n   * @example dur.set({ years: 2017 })\n   * @example dur.set({ hours: 8, minutes: 30 })\n   * @return {Duration}\n   */\n  set(values) {\n    if (!this.isValid) return this;\n\n    const mixed = { ...this.values, ...normalizeObject(values, Duration.normalizeUnit) };\n    return clone(this, { values: mixed });\n  }\n\n  /**\n   * \"Set\" the locale and/or numberingSystem.  Returns a newly-constructed Duration.\n   * @example dur.reconfigure({ locale: 'en-GB' })\n   * @return {Duration}\n   */\n  reconfigure({ locale, numberingSystem, conversionAccuracy, matrix } = {}) {\n    const loc = this.loc.clone({ locale, numberingSystem });\n    const opts = { loc, matrix, conversionAccuracy };\n    return clone(this, opts);\n  }\n\n  /**\n   * Return the length of the duration in the specified unit.\n   * @param {string} unit - a unit such as 'minutes' or 'days'\n   * @example Duration.fromObject({years: 1}).as('days') //=> 365\n   * @example Duration.fromObject({years: 1}).as('months') //=> 12\n   * @example Duration.fromObject({hours: 60}).as('days') //=> 2.5\n   * @return {number}\n   */\n  as(unit) {\n    return this.isValid ? this.shiftTo(unit).get(unit) : NaN;\n  }\n\n  /**\n   * Reduce this Duration to its canonical representation in its current units.\n   * Assuming the overall value of the Duration is positive, this means:\n   * - excessive values for lower-order units are converted to higher-order units (if possible, see first and second example)\n   * - negative lower-order units are converted to higher order units (there must be such a higher order unit, otherwise\n   *   the overall value would be negative, see third example)\n   * - fractional values for higher-order units are converted to lower-order units (if possible, see fourth example)\n   *\n   * If the overall value is negative, the result of this method is equivalent to `this.negate().normalize().negate()`.\n   * @example Duration.fromObject({ years: 2, days: 5000 }).normalize().toObject() //=> { years: 15, days: 255 }\n   * @example Duration.fromObject({ days: 5000 }).normalize().toObject() //=> { days: 5000 }\n   * @example Duration.fromObject({ hours: 12, minutes: -45 }).normalize().toObject() //=> { hours: 11, minutes: 15 }\n   * @example Duration.fromObject({ years: 2.5, days: 0, hours: 0 }).normalize().toObject() //=> { years: 2, days: 182, hours: 12 }\n   * @return {Duration}\n   */\n  normalize() {\n    if (!this.isValid) return this;\n    const vals = this.toObject();\n    normalizeValues(this.matrix, vals);\n    return clone(this, { values: vals }, true);\n  }\n\n  /**\n   * Rescale units to its largest representation\n   * @example Duration.fromObject({ milliseconds: 90000 }).rescale().toObject() //=> { minutes: 1, seconds: 30 }\n   * @return {Duration}\n   */\n  rescale() {\n    if (!this.isValid) return this;\n    const vals = removeZeroes(this.normalize().shiftToAll().toObject());\n    return clone(this, { values: vals }, true);\n  }\n\n  /**\n   * Convert this Duration into its representation in a different set of units.\n   * @example Duration.fromObject({ hours: 1, seconds: 30 }).shiftTo('minutes', 'milliseconds').toObject() //=> { minutes: 60, milliseconds: 30000 }\n   * @return {Duration}\n   */\n  shiftTo(...units) {\n    if (!this.isValid) return this;\n\n    if (units.length === 0) {\n      return this;\n    }\n\n    units = units.map((u) => Duration.normalizeUnit(u));\n\n    const built = {},\n      accumulated = {},\n      vals = this.toObject();\n    let lastUnit;\n\n    for (const k of orderedUnits) {\n      if (units.indexOf(k) >= 0) {\n        lastUnit = k;\n\n        let own = 0;\n\n        // anything we haven't boiled down yet should get boiled to this unit\n        for (const ak in accumulated) {\n          own += this.matrix[ak][k] * accumulated[ak];\n          accumulated[ak] = 0;\n        }\n\n        // plus anything that's already in this unit\n        if (isNumber(vals[k])) {\n          own += vals[k];\n        }\n\n        // only keep the integer part for now in the hopes of putting any decimal part\n        // into a smaller unit later\n        const i = Math.trunc(own);\n        built[k] = i;\n        accumulated[k] = (own * 1000 - i * 1000) / 1000;\n\n        // otherwise, keep it in the wings to boil it later\n      } else if (isNumber(vals[k])) {\n        accumulated[k] = vals[k];\n      }\n    }\n\n    // anything leftover becomes the decimal for the last unit\n    // lastUnit must be defined since units is not empty\n    for (const key in accumulated) {\n      if (accumulated[key] !== 0) {\n        built[lastUnit] +=\n          key === lastUnit ? accumulated[key] : accumulated[key] / this.matrix[lastUnit][key];\n      }\n    }\n\n    normalizeValues(this.matrix, built);\n    return clone(this, { values: built }, true);\n  }\n\n  /**\n   * Shift this Duration to all available units.\n   * Same as shiftTo(\"years\", \"months\", \"weeks\", \"days\", \"hours\", \"minutes\", \"seconds\", \"milliseconds\")\n   * @return {Duration}\n   */\n  shiftToAll() {\n    if (!this.isValid) return this;\n    return this.shiftTo(\n      \"years\",\n      \"months\",\n      \"weeks\",\n      \"days\",\n      \"hours\",\n      \"minutes\",\n      \"seconds\",\n      \"milliseconds\"\n    );\n  }\n\n  /**\n   * Return the negative of this Duration.\n   * @example Duration.fromObject({ hours: 1, seconds: 30 }).negate().toObject() //=> { hours: -1, seconds: -30 }\n   * @return {Duration}\n   */\n  negate() {\n    if (!this.isValid) return this;\n    const negated = {};\n    for (const k of Object.keys(this.values)) {\n      negated[k] = this.values[k] === 0 ? 0 : -this.values[k];\n    }\n    return clone(this, { values: negated }, true);\n  }\n\n  /**\n   * Removes all units with values equal to 0 from this Duration.\n   * @example Duration.fromObject({ years: 2, days: 0, hours: 0, minutes: 0 }).removeZeros().toObject() //=> { years: 2 }\n   * @return {Duration}\n   */\n  removeZeros() {\n    if (!this.isValid) return this;\n    const vals = removeZeroes(this.values);\n    return clone(this, { values: vals }, true);\n  }\n\n  /**\n   * Get the years.\n   * @type {number}\n   */\n  get years() {\n    return this.isValid ? this.values.years || 0 : NaN;\n  }\n\n  /**\n   * Get the quarters.\n   * @type {number}\n   */\n  get quarters() {\n    return this.isValid ? this.values.quarters || 0 : NaN;\n  }\n\n  /**\n   * Get the months.\n   * @type {number}\n   */\n  get months() {\n    return this.isValid ? this.values.months || 0 : NaN;\n  }\n\n  /**\n   * Get the weeks\n   * @type {number}\n   */\n  get weeks() {\n    return this.isValid ? this.values.weeks || 0 : NaN;\n  }\n\n  /**\n   * Get the days.\n   * @type {number}\n   */\n  get days() {\n    return this.isValid ? this.values.days || 0 : NaN;\n  }\n\n  /**\n   * Get the hours.\n   * @type {number}\n   */\n  get hours() {\n    return this.isValid ? this.values.hours || 0 : NaN;\n  }\n\n  /**\n   * Get the minutes.\n   * @type {number}\n   */\n  get minutes() {\n    return this.isValid ? this.values.minutes || 0 : NaN;\n  }\n\n  /**\n   * Get the seconds.\n   * @return {number}\n   */\n  get seconds() {\n    return this.isValid ? this.values.seconds || 0 : NaN;\n  }\n\n  /**\n   * Get the milliseconds.\n   * @return {number}\n   */\n  get milliseconds() {\n    return this.isValid ? this.values.milliseconds || 0 : NaN;\n  }\n\n  /**\n   * Returns whether the Duration is invalid. Invalid durations are returned by diff operations\n   * on invalid DateTimes or Intervals.\n   * @return {boolean}\n   */\n  get isValid() {\n    return this.invalid === null;\n  }\n\n  /**\n   * Returns an error code if this Duration became invalid, or null if the Duration is valid\n   * @return {string}\n   */\n  get invalidReason() {\n    return this.invalid ? this.invalid.reason : null;\n  }\n\n  /**\n   * Returns an explanation of why this Duration became invalid, or null if the Duration is valid\n   * @type {string}\n   */\n  get invalidExplanation() {\n    return this.invalid ? this.invalid.explanation : null;\n  }\n\n  /**\n   * Equality check\n   * Two Durations are equal iff they have the same units and the same values for each unit.\n   * @param {Duration} other\n   * @return {boolean}\n   */\n  equals(other) {\n    if (!this.isValid || !other.isValid) {\n      return false;\n    }\n\n    if (!this.loc.equals(other.loc)) {\n      return false;\n    }\n\n    function eq(v1, v2) {\n      // Consider 0 and undefined as equal\n      if (v1 === undefined || v1 === 0) return v2 === undefined || v2 === 0;\n      return v1 === v2;\n    }\n\n    for (const u of orderedUnits) {\n      if (!eq(this.values[u], other.values[u])) {\n        return false;\n      }\n    }\n    return true;\n  }\n}\n"
  },
  {
    "path": "src/errors.js",
    "content": "// these aren't really private, but nor are they really useful to document\n\n/**\n * @private\n */\nclass LuxonError extends Error {}\n\n/**\n * @private\n */\nexport class InvalidDateTimeError extends LuxonError {\n  constructor(reason) {\n    super(`Invalid DateTime: ${reason.toMessage()}`);\n  }\n}\n\n/**\n * @private\n */\nexport class InvalidIntervalError extends LuxonError {\n  constructor(reason) {\n    super(`Invalid Interval: ${reason.toMessage()}`);\n  }\n}\n\n/**\n * @private\n */\nexport class InvalidDurationError extends LuxonError {\n  constructor(reason) {\n    super(`Invalid Duration: ${reason.toMessage()}`);\n  }\n}\n\n/**\n * @private\n */\nexport class ConflictingSpecificationError extends LuxonError {}\n\n/**\n * @private\n */\nexport class InvalidUnitError extends LuxonError {\n  constructor(unit) {\n    super(`Invalid unit ${unit}`);\n  }\n}\n\n/**\n * @private\n */\nexport class InvalidArgumentError extends LuxonError {}\n\n/**\n * @private\n */\nexport class ZoneIsAbstractError extends LuxonError {\n  constructor() {\n    super(\"Zone is an abstract class\");\n  }\n}\n"
  },
  {
    "path": "src/impl/conversions.js",
    "content": "import {\n  integerBetween,\n  isLeapYear,\n  timeObject,\n  daysInYear,\n  daysInMonth,\n  weeksInWeekYear,\n  isInteger,\n  isUndefined,\n} from \"./util.js\";\nimport Invalid from \"./invalid.js\";\nimport { ConflictingSpecificationError } from \"../errors.js\";\n\nconst nonLeapLadder = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334],\n  leapLadder = [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335];\n\nfunction unitOutOfRange(unit, value) {\n  return new Invalid(\n    \"unit out of range\",\n    `you specified ${value} (of type ${typeof value}) as a ${unit}, which is invalid`\n  );\n}\n\nexport function dayOfWeek(year, month, day) {\n  const d = new Date(Date.UTC(year, month - 1, day));\n\n  if (year < 100 && year >= 0) {\n    d.setUTCFullYear(d.getUTCFullYear() - 1900);\n  }\n\n  const js = d.getUTCDay();\n\n  return js === 0 ? 7 : js;\n}\n\nfunction computeOrdinal(year, month, day) {\n  return day + (isLeapYear(year) ? leapLadder : nonLeapLadder)[month - 1];\n}\n\nfunction uncomputeOrdinal(year, ordinal) {\n  const table = isLeapYear(year) ? leapLadder : nonLeapLadder,\n    month0 = table.findIndex((i) => i < ordinal),\n    day = ordinal - table[month0];\n  return { month: month0 + 1, day };\n}\n\nexport function isoWeekdayToLocal(isoWeekday, startOfWeek) {\n  return ((isoWeekday - startOfWeek + 7) % 7) + 1;\n}\n\n/**\n * @private\n */\n\nexport function gregorianToWeek(gregObj, minDaysInFirstWeek = 4, startOfWeek = 1) {\n  const { year, month, day } = gregObj,\n    ordinal = computeOrdinal(year, month, day),\n    weekday = isoWeekdayToLocal(dayOfWeek(year, month, day), startOfWeek);\n\n  let weekNumber = Math.floor((ordinal - weekday + 14 - minDaysInFirstWeek) / 7),\n    weekYear;\n\n  if (weekNumber < 1) {\n    weekYear = year - 1;\n    weekNumber = weeksInWeekYear(weekYear, minDaysInFirstWeek, startOfWeek);\n  } else if (weekNumber > weeksInWeekYear(year, minDaysInFirstWeek, startOfWeek)) {\n    weekYear = year + 1;\n    weekNumber = 1;\n  } else {\n    weekYear = year;\n  }\n\n  return { weekYear, weekNumber, weekday, ...timeObject(gregObj) };\n}\n\nexport function weekToGregorian(weekData, minDaysInFirstWeek = 4, startOfWeek = 1) {\n  const { weekYear, weekNumber, weekday } = weekData,\n    weekdayOfJan4 = isoWeekdayToLocal(dayOfWeek(weekYear, 1, minDaysInFirstWeek), startOfWeek),\n    yearInDays = daysInYear(weekYear);\n\n  let ordinal = weekNumber * 7 + weekday - weekdayOfJan4 - 7 + minDaysInFirstWeek,\n    year;\n\n  if (ordinal < 1) {\n    year = weekYear - 1;\n    ordinal += daysInYear(year);\n  } else if (ordinal > yearInDays) {\n    year = weekYear + 1;\n    ordinal -= daysInYear(weekYear);\n  } else {\n    year = weekYear;\n  }\n\n  const { month, day } = uncomputeOrdinal(year, ordinal);\n  return { year, month, day, ...timeObject(weekData) };\n}\n\nexport function gregorianToOrdinal(gregData) {\n  const { year, month, day } = gregData;\n  const ordinal = computeOrdinal(year, month, day);\n  return { year, ordinal, ...timeObject(gregData) };\n}\n\nexport function ordinalToGregorian(ordinalData) {\n  const { year, ordinal } = ordinalData;\n  const { month, day } = uncomputeOrdinal(year, ordinal);\n  return { year, month, day, ...timeObject(ordinalData) };\n}\n\n/**\n * Check if local week units like localWeekday are used in obj.\n * If so, validates that they are not mixed with ISO week units and then copies them to the normal week unit properties.\n * Modifies obj in-place!\n * @param obj the object values\n */\nexport function usesLocalWeekValues(obj, loc) {\n  const hasLocaleWeekData =\n    !isUndefined(obj.localWeekday) ||\n    !isUndefined(obj.localWeekNumber) ||\n    !isUndefined(obj.localWeekYear);\n  if (hasLocaleWeekData) {\n    const hasIsoWeekData =\n      !isUndefined(obj.weekday) || !isUndefined(obj.weekNumber) || !isUndefined(obj.weekYear);\n\n    if (hasIsoWeekData) {\n      throw new ConflictingSpecificationError(\n        \"Cannot mix locale-based week fields with ISO-based week fields\"\n      );\n    }\n    if (!isUndefined(obj.localWeekday)) obj.weekday = obj.localWeekday;\n    if (!isUndefined(obj.localWeekNumber)) obj.weekNumber = obj.localWeekNumber;\n    if (!isUndefined(obj.localWeekYear)) obj.weekYear = obj.localWeekYear;\n    delete obj.localWeekday;\n    delete obj.localWeekNumber;\n    delete obj.localWeekYear;\n    return {\n      minDaysInFirstWeek: loc.getMinDaysInFirstWeek(),\n      startOfWeek: loc.getStartOfWeek(),\n    };\n  } else {\n    return { minDaysInFirstWeek: 4, startOfWeek: 1 };\n  }\n}\n\nexport function hasInvalidWeekData(obj, minDaysInFirstWeek = 4, startOfWeek = 1) {\n  const validYear = isInteger(obj.weekYear),\n    validWeek = integerBetween(\n      obj.weekNumber,\n      1,\n      weeksInWeekYear(obj.weekYear, minDaysInFirstWeek, startOfWeek)\n    ),\n    validWeekday = integerBetween(obj.weekday, 1, 7);\n\n  if (!validYear) {\n    return unitOutOfRange(\"weekYear\", obj.weekYear);\n  } else if (!validWeek) {\n    return unitOutOfRange(\"week\", obj.weekNumber);\n  } else if (!validWeekday) {\n    return unitOutOfRange(\"weekday\", obj.weekday);\n  } else return false;\n}\n\nexport function hasInvalidOrdinalData(obj) {\n  const validYear = isInteger(obj.year),\n    validOrdinal = integerBetween(obj.ordinal, 1, daysInYear(obj.year));\n\n  if (!validYear) {\n    return unitOutOfRange(\"year\", obj.year);\n  } else if (!validOrdinal) {\n    return unitOutOfRange(\"ordinal\", obj.ordinal);\n  } else return false;\n}\n\nexport function hasInvalidGregorianData(obj) {\n  const validYear = isInteger(obj.year),\n    validMonth = integerBetween(obj.month, 1, 12),\n    validDay = integerBetween(obj.day, 1, daysInMonth(obj.year, obj.month));\n\n  if (!validYear) {\n    return unitOutOfRange(\"year\", obj.year);\n  } else if (!validMonth) {\n    return unitOutOfRange(\"month\", obj.month);\n  } else if (!validDay) {\n    return unitOutOfRange(\"day\", obj.day);\n  } else return false;\n}\n\nexport function hasInvalidTimeData(obj) {\n  const { hour, minute, second, millisecond } = obj;\n  const validHour =\n      integerBetween(hour, 0, 23) ||\n      (hour === 24 && minute === 0 && second === 0 && millisecond === 0),\n    validMinute = integerBetween(minute, 0, 59),\n    validSecond = integerBetween(second, 0, 59),\n    validMillisecond = integerBetween(millisecond, 0, 999);\n\n  if (!validHour) {\n    return unitOutOfRange(\"hour\", hour);\n  } else if (!validMinute) {\n    return unitOutOfRange(\"minute\", minute);\n  } else if (!validSecond) {\n    return unitOutOfRange(\"second\", second);\n  } else if (!validMillisecond) {\n    return unitOutOfRange(\"millisecond\", millisecond);\n  } else return false;\n}\n"
  },
  {
    "path": "src/impl/diff.js",
    "content": "import Duration from \"../duration.js\";\n\nfunction dayDiff(earlier, later) {\n  const utcDayStart = (dt) => dt.toUTC(0, { keepLocalTime: true }).startOf(\"day\").valueOf(),\n    ms = utcDayStart(later) - utcDayStart(earlier);\n  return Math.floor(Duration.fromMillis(ms).as(\"days\"));\n}\n\nfunction highOrderDiffs(cursor, later, units) {\n  const differs = [\n    [\"years\", (a, b) => b.year - a.year],\n    [\"quarters\", (a, b) => b.quarter - a.quarter + (b.year - a.year) * 4],\n    [\"months\", (a, b) => b.month - a.month + (b.year - a.year) * 12],\n    [\n      \"weeks\",\n      (a, b) => {\n        const days = dayDiff(a, b);\n        return (days - (days % 7)) / 7;\n      },\n    ],\n    [\"days\", dayDiff],\n  ];\n\n  const results = {};\n  const earlier = cursor;\n  let lowestOrder, highWater;\n\n  /* This loop tries to diff using larger units first.\n     If we overshoot, we backtrack and try the next smaller unit.\n     \"cursor\" starts out at the earlier timestamp and moves closer and closer to \"later\"\n     as we use smaller and smaller units.\n     highWater keeps track of where we would be if we added one more of the smallest unit,\n     this is used later to potentially convert any difference smaller than the smallest higher order unit\n     into a fraction of that smallest higher order unit\n  */\n  for (const [unit, differ] of differs) {\n    if (units.indexOf(unit) >= 0) {\n      lowestOrder = unit;\n\n      results[unit] = differ(cursor, later);\n      highWater = earlier.plus(results);\n\n      if (highWater > later) {\n        // we overshot the end point, backtrack cursor by 1\n        results[unit]--;\n        cursor = earlier.plus(results);\n\n        // if we are still overshooting now, we need to backtrack again\n        // this happens in certain situations when diffing times in different zones,\n        // because this calculation ignores time zones\n        if (cursor > later) {\n          // keep the \"overshot by 1\" around as highWater\n          highWater = cursor;\n          // backtrack cursor by 1\n          results[unit]--;\n          cursor = earlier.plus(results);\n        }\n      } else {\n        cursor = highWater;\n      }\n    }\n  }\n\n  return [cursor, results, highWater, lowestOrder];\n}\n\nexport default function (earlier, later, units, opts) {\n  let [cursor, results, highWater, lowestOrder] = highOrderDiffs(earlier, later, units);\n\n  const remainingMillis = later - cursor;\n\n  const lowerOrderUnits = units.filter(\n    (u) => [\"hours\", \"minutes\", \"seconds\", \"milliseconds\"].indexOf(u) >= 0\n  );\n\n  if (lowerOrderUnits.length === 0) {\n    if (highWater < later) {\n      highWater = cursor.plus({ [lowestOrder]: 1 });\n    }\n\n    if (highWater !== cursor) {\n      results[lowestOrder] = (results[lowestOrder] || 0) + remainingMillis / (highWater - cursor);\n    }\n  }\n\n  const duration = Duration.fromObject(results, opts);\n\n  if (lowerOrderUnits.length > 0) {\n    return Duration.fromMillis(remainingMillis, opts)\n      .shiftTo(...lowerOrderUnits)\n      .plus(duration);\n  } else {\n    return duration;\n  }\n}\n"
  },
  {
    "path": "src/impl/digits.js",
    "content": "const numberingSystems = {\n  arab: \"[\\u0660-\\u0669]\",\n  arabext: \"[\\u06F0-\\u06F9]\",\n  bali: \"[\\u1B50-\\u1B59]\",\n  beng: \"[\\u09E6-\\u09EF]\",\n  deva: \"[\\u0966-\\u096F]\",\n  fullwide: \"[\\uFF10-\\uFF19]\",\n  gujr: \"[\\u0AE6-\\u0AEF]\",\n  hanidec: \"[〇|一|二|三|四|五|六|七|八|九]\",\n  khmr: \"[\\u17E0-\\u17E9]\",\n  knda: \"[\\u0CE6-\\u0CEF]\",\n  laoo: \"[\\u0ED0-\\u0ED9]\",\n  limb: \"[\\u1946-\\u194F]\",\n  mlym: \"[\\u0D66-\\u0D6F]\",\n  mong: \"[\\u1810-\\u1819]\",\n  mymr: \"[\\u1040-\\u1049]\",\n  orya: \"[\\u0B66-\\u0B6F]\",\n  tamldec: \"[\\u0BE6-\\u0BEF]\",\n  telu: \"[\\u0C66-\\u0C6F]\",\n  thai: \"[\\u0E50-\\u0E59]\",\n  tibt: \"[\\u0F20-\\u0F29]\",\n  latn: \"\\\\d\",\n};\n\nconst numberingSystemsUTF16 = {\n  arab: [1632, 1641],\n  arabext: [1776, 1785],\n  bali: [6992, 7001],\n  beng: [2534, 2543],\n  deva: [2406, 2415],\n  fullwide: [65296, 65303],\n  gujr: [2790, 2799],\n  khmr: [6112, 6121],\n  knda: [3302, 3311],\n  laoo: [3792, 3801],\n  limb: [6470, 6479],\n  mlym: [3430, 3439],\n  mong: [6160, 6169],\n  mymr: [4160, 4169],\n  orya: [2918, 2927],\n  tamldec: [3046, 3055],\n  telu: [3174, 3183],\n  thai: [3664, 3673],\n  tibt: [3872, 3881],\n};\n\nconst hanidecChars = numberingSystems.hanidec.replace(/[\\[|\\]]/g, \"\").split(\"\");\n\nexport function parseDigits(str) {\n  let value = parseInt(str, 10);\n  if (isNaN(value)) {\n    value = \"\";\n    for (let i = 0; i < str.length; i++) {\n      const code = str.charCodeAt(i);\n\n      if (str[i].search(numberingSystems.hanidec) !== -1) {\n        value += hanidecChars.indexOf(str[i]);\n      } else {\n        for (const key in numberingSystemsUTF16) {\n          const [min, max] = numberingSystemsUTF16[key];\n          if (code >= min && code <= max) {\n            value += code - min;\n          }\n        }\n      }\n    }\n    return parseInt(value, 10);\n  } else {\n    return value;\n  }\n}\n\n// cache of {numberingSystem: {append: regex}}\nconst digitRegexCache = new Map();\nexport function resetDigitRegexCache() {\n  digitRegexCache.clear();\n}\n\nexport function digitRegex({ numberingSystem }, append = \"\") {\n  const ns = numberingSystem || \"latn\";\n\n  let appendCache = digitRegexCache.get(ns);\n  if (appendCache === undefined) {\n    appendCache = new Map();\n    digitRegexCache.set(ns, appendCache);\n  }\n  let regex = appendCache.get(append);\n  if (regex === undefined) {\n    regex = new RegExp(`${numberingSystems[ns]}${append}`);\n    appendCache.set(append, regex);\n  }\n\n  return regex;\n}\n"
  },
  {
    "path": "src/impl/english.js",
    "content": "import * as Formats from \"./formats.js\";\nimport { pick } from \"./util.js\";\n\nfunction stringify(obj) {\n  return JSON.stringify(obj, Object.keys(obj).sort());\n}\n\n/**\n * @private\n */\n\nexport const monthsLong = [\n  \"January\",\n  \"February\",\n  \"March\",\n  \"April\",\n  \"May\",\n  \"June\",\n  \"July\",\n  \"August\",\n  \"September\",\n  \"October\",\n  \"November\",\n  \"December\",\n];\n\nexport const monthsShort = [\n  \"Jan\",\n  \"Feb\",\n  \"Mar\",\n  \"Apr\",\n  \"May\",\n  \"Jun\",\n  \"Jul\",\n  \"Aug\",\n  \"Sep\",\n  \"Oct\",\n  \"Nov\",\n  \"Dec\",\n];\n\nexport const monthsNarrow = [\"J\", \"F\", \"M\", \"A\", \"M\", \"J\", \"J\", \"A\", \"S\", \"O\", \"N\", \"D\"];\n\nexport function months(length) {\n  switch (length) {\n    case \"narrow\":\n      return [...monthsNarrow];\n    case \"short\":\n      return [...monthsShort];\n    case \"long\":\n      return [...monthsLong];\n    case \"numeric\":\n      return [\"1\", \"2\", \"3\", \"4\", \"5\", \"6\", \"7\", \"8\", \"9\", \"10\", \"11\", \"12\"];\n    case \"2-digit\":\n      return [\"01\", \"02\", \"03\", \"04\", \"05\", \"06\", \"07\", \"08\", \"09\", \"10\", \"11\", \"12\"];\n    default:\n      return null;\n  }\n}\n\nexport const weekdaysLong = [\n  \"Monday\",\n  \"Tuesday\",\n  \"Wednesday\",\n  \"Thursday\",\n  \"Friday\",\n  \"Saturday\",\n  \"Sunday\",\n];\n\nexport const weekdaysShort = [\"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\", \"Sun\"];\n\nexport const weekdaysNarrow = [\"M\", \"T\", \"W\", \"T\", \"F\", \"S\", \"S\"];\n\nexport function weekdays(length) {\n  switch (length) {\n    case \"narrow\":\n      return [...weekdaysNarrow];\n    case \"short\":\n      return [...weekdaysShort];\n    case \"long\":\n      return [...weekdaysLong];\n    case \"numeric\":\n      return [\"1\", \"2\", \"3\", \"4\", \"5\", \"6\", \"7\"];\n    default:\n      return null;\n  }\n}\n\nexport const meridiems = [\"AM\", \"PM\"];\n\nexport const erasLong = [\"Before Christ\", \"Anno Domini\"];\n\nexport const erasShort = [\"BC\", \"AD\"];\n\nexport const erasNarrow = [\"B\", \"A\"];\n\nexport function eras(length) {\n  switch (length) {\n    case \"narrow\":\n      return [...erasNarrow];\n    case \"short\":\n      return [...erasShort];\n    case \"long\":\n      return [...erasLong];\n    default:\n      return null;\n  }\n}\n\nexport function meridiemForDateTime(dt) {\n  return meridiems[dt.hour < 12 ? 0 : 1];\n}\n\nexport function weekdayForDateTime(dt, length) {\n  return weekdays(length)[dt.weekday - 1];\n}\n\nexport function monthForDateTime(dt, length) {\n  return months(length)[dt.month - 1];\n}\n\nexport function eraForDateTime(dt, length) {\n  return eras(length)[dt.year < 0 ? 0 : 1];\n}\n\nexport function formatRelativeTime(unit, count, numeric = \"always\", narrow = false) {\n  const units = {\n    years: [\"year\", \"yr.\"],\n    quarters: [\"quarter\", \"qtr.\"],\n    months: [\"month\", \"mo.\"],\n    weeks: [\"week\", \"wk.\"],\n    days: [\"day\", \"day\", \"days\"],\n    hours: [\"hour\", \"hr.\"],\n    minutes: [\"minute\", \"min.\"],\n    seconds: [\"second\", \"sec.\"],\n  };\n\n  const lastable = [\"hours\", \"minutes\", \"seconds\"].indexOf(unit) === -1;\n\n  if (numeric === \"auto\" && lastable) {\n    const isDay = unit === \"days\";\n    switch (count) {\n      case 1:\n        return isDay ? \"tomorrow\" : `next ${units[unit][0]}`;\n      case -1:\n        return isDay ? \"yesterday\" : `last ${units[unit][0]}`;\n      case 0:\n        return isDay ? \"today\" : `this ${units[unit][0]}`;\n      default: // fall through\n    }\n  }\n\n  const isInPast = Object.is(count, -0) || count < 0,\n    fmtValue = Math.abs(count),\n    singular = fmtValue === 1,\n    lilUnits = units[unit],\n    fmtUnit = narrow\n      ? singular\n        ? lilUnits[1]\n        : lilUnits[2] || lilUnits[1]\n      : singular\n      ? units[unit][0]\n      : unit;\n  return isInPast ? `${fmtValue} ${fmtUnit} ago` : `in ${fmtValue} ${fmtUnit}`;\n}\n\nexport function formatString(knownFormat) {\n  // these all have the offsets removed because we don't have access to them\n  // without all the intl stuff this is backfilling\n  const filtered = pick(knownFormat, [\n      \"weekday\",\n      \"era\",\n      \"year\",\n      \"month\",\n      \"day\",\n      \"hour\",\n      \"minute\",\n      \"second\",\n      \"timeZoneName\",\n      \"hourCycle\",\n    ]),\n    key = stringify(filtered),\n    dateTimeHuge = \"EEEE, LLLL d, yyyy, h:mm a\";\n  switch (key) {\n    case stringify(Formats.DATE_SHORT):\n      return \"M/d/yyyy\";\n    case stringify(Formats.DATE_MED):\n      return \"LLL d, yyyy\";\n    case stringify(Formats.DATE_MED_WITH_WEEKDAY):\n      return \"EEE, LLL d, yyyy\";\n    case stringify(Formats.DATE_FULL):\n      return \"LLLL d, yyyy\";\n    case stringify(Formats.DATE_HUGE):\n      return \"EEEE, LLLL d, yyyy\";\n    case stringify(Formats.TIME_SIMPLE):\n      return \"h:mm a\";\n    case stringify(Formats.TIME_WITH_SECONDS):\n      return \"h:mm:ss a\";\n    case stringify(Formats.TIME_WITH_SHORT_OFFSET):\n      return \"h:mm a\";\n    case stringify(Formats.TIME_WITH_LONG_OFFSET):\n      return \"h:mm a\";\n    case stringify(Formats.TIME_24_SIMPLE):\n      return \"HH:mm\";\n    case stringify(Formats.TIME_24_WITH_SECONDS):\n      return \"HH:mm:ss\";\n    case stringify(Formats.TIME_24_WITH_SHORT_OFFSET):\n      return \"HH:mm\";\n    case stringify(Formats.TIME_24_WITH_LONG_OFFSET):\n      return \"HH:mm\";\n    case stringify(Formats.DATETIME_SHORT):\n      return \"M/d/yyyy, h:mm a\";\n    case stringify(Formats.DATETIME_MED):\n      return \"LLL d, yyyy, h:mm a\";\n    case stringify(Formats.DATETIME_FULL):\n      return \"LLLL d, yyyy, h:mm a\";\n    case stringify(Formats.DATETIME_HUGE):\n      return dateTimeHuge;\n    case stringify(Formats.DATETIME_SHORT_WITH_SECONDS):\n      return \"M/d/yyyy, h:mm:ss a\";\n    case stringify(Formats.DATETIME_MED_WITH_SECONDS):\n      return \"LLL d, yyyy, h:mm:ss a\";\n    case stringify(Formats.DATETIME_MED_WITH_WEEKDAY):\n      return \"EEE, d LLL yyyy, h:mm a\";\n    case stringify(Formats.DATETIME_FULL_WITH_SECONDS):\n      return \"LLLL d, yyyy, h:mm:ss a\";\n    case stringify(Formats.DATETIME_HUGE_WITH_SECONDS):\n      return \"EEEE, LLLL d, yyyy, h:mm:ss a\";\n    default:\n      return dateTimeHuge;\n  }\n}\n"
  },
  {
    "path": "src/impl/formats.js",
    "content": "/**\n * @private\n */\n\nconst n = \"numeric\",\n  s = \"short\",\n  l = \"long\";\n\nexport const DATE_SHORT = {\n  year: n,\n  month: n,\n  day: n,\n};\n\nexport const DATE_MED = {\n  year: n,\n  month: s,\n  day: n,\n};\n\nexport const DATE_MED_WITH_WEEKDAY = {\n  year: n,\n  month: s,\n  day: n,\n  weekday: s,\n};\n\nexport const DATE_FULL = {\n  year: n,\n  month: l,\n  day: n,\n};\n\nexport const DATE_HUGE = {\n  year: n,\n  month: l,\n  day: n,\n  weekday: l,\n};\n\nexport const TIME_SIMPLE = {\n  hour: n,\n  minute: n,\n};\n\nexport const TIME_WITH_SECONDS = {\n  hour: n,\n  minute: n,\n  second: n,\n};\n\nexport const TIME_WITH_SHORT_OFFSET = {\n  hour: n,\n  minute: n,\n  second: n,\n  timeZoneName: s,\n};\n\nexport const TIME_WITH_LONG_OFFSET = {\n  hour: n,\n  minute: n,\n  second: n,\n  timeZoneName: l,\n};\n\nexport const TIME_24_SIMPLE = {\n  hour: n,\n  minute: n,\n  hourCycle: \"h23\",\n};\n\nexport const TIME_24_WITH_SECONDS = {\n  hour: n,\n  minute: n,\n  second: n,\n  hourCycle: \"h23\",\n};\n\nexport const TIME_24_WITH_SHORT_OFFSET = {\n  hour: n,\n  minute: n,\n  second: n,\n  hourCycle: \"h23\",\n  timeZoneName: s,\n};\n\nexport const TIME_24_WITH_LONG_OFFSET = {\n  hour: n,\n  minute: n,\n  second: n,\n  hourCycle: \"h23\",\n  timeZoneName: l,\n};\n\nexport const DATETIME_SHORT = {\n  year: n,\n  month: n,\n  day: n,\n  hour: n,\n  minute: n,\n};\n\nexport const DATETIME_SHORT_WITH_SECONDS = {\n  year: n,\n  month: n,\n  day: n,\n  hour: n,\n  minute: n,\n  second: n,\n};\n\nexport const DATETIME_MED = {\n  year: n,\n  month: s,\n  day: n,\n  hour: n,\n  minute: n,\n};\n\nexport const DATETIME_MED_WITH_SECONDS = {\n  year: n,\n  month: s,\n  day: n,\n  hour: n,\n  minute: n,\n  second: n,\n};\n\nexport const DATETIME_MED_WITH_WEEKDAY = {\n  year: n,\n  month: s,\n  day: n,\n  weekday: s,\n  hour: n,\n  minute: n,\n};\n\nexport const DATETIME_FULL = {\n  year: n,\n  month: l,\n  day: n,\n  hour: n,\n  minute: n,\n  timeZoneName: s,\n};\n\nexport const DATETIME_FULL_WITH_SECONDS = {\n  year: n,\n  month: l,\n  day: n,\n  hour: n,\n  minute: n,\n  second: n,\n  timeZoneName: s,\n};\n\nexport const DATETIME_HUGE = {\n  year: n,\n  month: l,\n  day: n,\n  weekday: l,\n  hour: n,\n  minute: n,\n  timeZoneName: l,\n};\n\nexport const DATETIME_HUGE_WITH_SECONDS = {\n  year: n,\n  month: l,\n  day: n,\n  weekday: l,\n  hour: n,\n  minute: n,\n  second: n,\n  timeZoneName: l,\n};\n"
  },
  {
    "path": "src/impl/formatter.js",
    "content": "import * as English from \"./english.js\";\nimport * as Formats from \"./formats.js\";\nimport { padStart } from \"./util.js\";\n\nfunction stringifyTokens(splits, tokenToString) {\n  let s = \"\";\n  for (const token of splits) {\n    if (token.literal) {\n      s += token.val;\n    } else {\n      s += tokenToString(token.val);\n    }\n  }\n  return s;\n}\n\nconst macroTokenToFormatOpts = {\n  D: Formats.DATE_SHORT,\n  DD: Formats.DATE_MED,\n  DDD: Formats.DATE_FULL,\n  DDDD: Formats.DATE_HUGE,\n  t: Formats.TIME_SIMPLE,\n  tt: Formats.TIME_WITH_SECONDS,\n  ttt: Formats.TIME_WITH_SHORT_OFFSET,\n  tttt: Formats.TIME_WITH_LONG_OFFSET,\n  T: Formats.TIME_24_SIMPLE,\n  TT: Formats.TIME_24_WITH_SECONDS,\n  TTT: Formats.TIME_24_WITH_SHORT_OFFSET,\n  TTTT: Formats.TIME_24_WITH_LONG_OFFSET,\n  f: Formats.DATETIME_SHORT,\n  ff: Formats.DATETIME_MED,\n  fff: Formats.DATETIME_FULL,\n  ffff: Formats.DATETIME_HUGE,\n  F: Formats.DATETIME_SHORT_WITH_SECONDS,\n  FF: Formats.DATETIME_MED_WITH_SECONDS,\n  FFF: Formats.DATETIME_FULL_WITH_SECONDS,\n  FFFF: Formats.DATETIME_HUGE_WITH_SECONDS,\n};\n\n/**\n * @private\n */\n\nexport default class Formatter {\n  static create(locale, opts = {}) {\n    return new Formatter(locale, opts);\n  }\n\n  static parseFormat(fmt) {\n    // white-space is always considered a literal in user-provided formats\n    // the \" \" token has a special meaning (see unitForToken)\n\n    let current = null,\n      currentFull = \"\",\n      bracketed = false;\n    const splits = [];\n    for (let i = 0; i < fmt.length; i++) {\n      const c = fmt.charAt(i);\n      if (c === \"'\") {\n        // turn '' into a literal signal quote instead of just skipping the empty literal\n        if (currentFull.length > 0 || bracketed) {\n          splits.push({\n            literal: bracketed || /^\\s+$/.test(currentFull),\n            val: currentFull === \"\" ? \"'\" : currentFull,\n          });\n        }\n        current = null;\n        currentFull = \"\";\n        bracketed = !bracketed;\n      } else if (bracketed) {\n        currentFull += c;\n      } else if (c === current) {\n        currentFull += c;\n      } else {\n        if (currentFull.length > 0) {\n          splits.push({ literal: /^\\s+$/.test(currentFull), val: currentFull });\n        }\n        currentFull = c;\n        current = c;\n      }\n    }\n\n    if (currentFull.length > 0) {\n      splits.push({ literal: bracketed || /^\\s+$/.test(currentFull), val: currentFull });\n    }\n\n    return splits;\n  }\n\n  static macroTokenToFormatOpts(token) {\n    return macroTokenToFormatOpts[token];\n  }\n\n  constructor(locale, formatOpts) {\n    this.opts = formatOpts;\n    this.loc = locale;\n    this.systemLoc = null;\n  }\n\n  formatWithSystemDefault(dt, opts) {\n    if (this.systemLoc === null) {\n      this.systemLoc = this.loc.redefaultToSystem();\n    }\n    const df = this.systemLoc.dtFormatter(dt, { ...this.opts, ...opts });\n    return df.format();\n  }\n\n  dtFormatter(dt, opts = {}) {\n    return this.loc.dtFormatter(dt, { ...this.opts, ...opts });\n  }\n\n  formatDateTime(dt, opts) {\n    return this.dtFormatter(dt, opts).format();\n  }\n\n  formatDateTimeParts(dt, opts) {\n    return this.dtFormatter(dt, opts).formatToParts();\n  }\n\n  formatInterval(interval, opts) {\n    const df = this.dtFormatter(interval.start, opts);\n    return df.dtf.formatRange(interval.start.toJSDate(), interval.end.toJSDate());\n  }\n\n  resolvedOptions(dt, opts) {\n    return this.dtFormatter(dt, opts).resolvedOptions();\n  }\n\n  num(n, p = 0, signDisplay = undefined) {\n    // we get some perf out of doing this here, annoyingly\n    if (this.opts.forceSimple) {\n      return padStart(n, p);\n    }\n\n    const opts = { ...this.opts };\n\n    if (p > 0) {\n      opts.padTo = p;\n    }\n    if (signDisplay) {\n      opts.signDisplay = signDisplay;\n    }\n\n    return this.loc.numberFormatter(opts).format(n);\n  }\n\n  formatDateTimeFromString(dt, fmt) {\n    const knownEnglish = this.loc.listingMode() === \"en\",\n      useDateTimeFormatter = this.loc.outputCalendar && this.loc.outputCalendar !== \"gregory\",\n      string = (opts, extract) => this.loc.extract(dt, opts, extract),\n      formatOffset = (opts) => {\n        if (dt.isOffsetFixed && dt.offset === 0 && opts.allowZ) {\n          return \"Z\";\n        }\n\n        return dt.isValid ? dt.zone.formatOffset(dt.ts, opts.format) : \"\";\n      },\n      meridiem = () =>\n        knownEnglish\n          ? English.meridiemForDateTime(dt)\n          : string({ hour: \"numeric\", hourCycle: \"h12\" }, \"dayperiod\"),\n      month = (length, standalone) =>\n        knownEnglish\n          ? English.monthForDateTime(dt, length)\n          : string(standalone ? { month: length } : { month: length, day: \"numeric\" }, \"month\"),\n      weekday = (length, standalone) =>\n        knownEnglish\n          ? English.weekdayForDateTime(dt, length)\n          : string(\n              standalone ? { weekday: length } : { weekday: length, month: \"long\", day: \"numeric\" },\n              \"weekday\"\n            ),\n      maybeMacro = (token) => {\n        const formatOpts = Formatter.macroTokenToFormatOpts(token);\n        if (formatOpts) {\n          return this.formatWithSystemDefault(dt, formatOpts);\n        } else {\n          return token;\n        }\n      },\n      era = (length) =>\n        knownEnglish ? English.eraForDateTime(dt, length) : string({ era: length }, \"era\"),\n      tokenToString = (token) => {\n        // Where possible: https://cldr.unicode.org/translation/date-time/date-time-symbols\n        switch (token) {\n          // ms\n          case \"S\":\n            return this.num(dt.millisecond);\n          case \"u\":\n          // falls through\n          case \"SSS\":\n            return this.num(dt.millisecond, 3);\n          // seconds\n          case \"s\":\n            return this.num(dt.second);\n          case \"ss\":\n            return this.num(dt.second, 2);\n          // fractional seconds\n          case \"uu\":\n            return this.num(Math.floor(dt.millisecond / 10), 2);\n          case \"uuu\":\n            return this.num(Math.floor(dt.millisecond / 100));\n          // minutes\n          case \"m\":\n            return this.num(dt.minute);\n          case \"mm\":\n            return this.num(dt.minute, 2);\n          // hours\n          case \"h\":\n            return this.num(dt.hour % 12 === 0 ? 12 : dt.hour % 12);\n          case \"hh\":\n            return this.num(dt.hour % 12 === 0 ? 12 : dt.hour % 12, 2);\n          case \"H\":\n            return this.num(dt.hour);\n          case \"HH\":\n            return this.num(dt.hour, 2);\n          // offset\n          case \"Z\":\n            // like +6\n            return formatOffset({ format: \"narrow\", allowZ: this.opts.allowZ });\n          case \"ZZ\":\n            // like +06:00\n            return formatOffset({ format: \"short\", allowZ: this.opts.allowZ });\n          case \"ZZZ\":\n            // like +0600\n            return formatOffset({ format: \"techie\", allowZ: this.opts.allowZ });\n          case \"ZZZZ\":\n            // like EST\n            return dt.zone.offsetName(dt.ts, { format: \"short\", locale: this.loc.locale });\n          case \"ZZZZZ\":\n            // like Eastern Standard Time\n            return dt.zone.offsetName(dt.ts, { format: \"long\", locale: this.loc.locale });\n          // zone\n          case \"z\":\n            // like America/New_York\n            return dt.zoneName;\n          // meridiems\n          case \"a\":\n            return meridiem();\n          // dates\n          case \"d\":\n            return useDateTimeFormatter ? string({ day: \"numeric\" }, \"day\") : this.num(dt.day);\n          case \"dd\":\n            return useDateTimeFormatter ? string({ day: \"2-digit\" }, \"day\") : this.num(dt.day, 2);\n          // weekdays - standalone\n          case \"c\":\n            // like 1\n            return this.num(dt.weekday);\n          case \"ccc\":\n            // like 'Tues'\n            return weekday(\"short\", true);\n          case \"cccc\":\n            // like 'Tuesday'\n            return weekday(\"long\", true);\n          case \"ccccc\":\n            // like 'T'\n            return weekday(\"narrow\", true);\n          // weekdays - format\n          case \"E\":\n            // like 1\n            return this.num(dt.weekday);\n          case \"EEE\":\n            // like 'Tues'\n            return weekday(\"short\", false);\n          case \"EEEE\":\n            // like 'Tuesday'\n            return weekday(\"long\", false);\n          case \"EEEEE\":\n            // like 'T'\n            return weekday(\"narrow\", false);\n          // months - standalone\n          case \"L\":\n            // like 1\n            return useDateTimeFormatter\n              ? string({ month: \"numeric\", day: \"numeric\" }, \"month\")\n              : this.num(dt.month);\n          case \"LL\":\n            // like 01, doesn't seem to work\n            return useDateTimeFormatter\n              ? string({ month: \"2-digit\", day: \"numeric\" }, \"month\")\n              : this.num(dt.month, 2);\n          case \"LLL\":\n            // like Jan\n            return month(\"short\", true);\n          case \"LLLL\":\n            // like January\n            return month(\"long\", true);\n          case \"LLLLL\":\n            // like J\n            return month(\"narrow\", true);\n          // months - format\n          case \"M\":\n            // like 1\n            return useDateTimeFormatter\n              ? string({ month: \"numeric\" }, \"month\")\n              : this.num(dt.month);\n          case \"MM\":\n            // like 01\n            return useDateTimeFormatter\n              ? string({ month: \"2-digit\" }, \"month\")\n              : this.num(dt.month, 2);\n          case \"MMM\":\n            // like Jan\n            return month(\"short\", false);\n          case \"MMMM\":\n            // like January\n            return month(\"long\", false);\n          case \"MMMMM\":\n            // like J\n            return month(\"narrow\", false);\n          // years\n          case \"y\":\n            // like 2014\n            return useDateTimeFormatter ? string({ year: \"numeric\" }, \"year\") : this.num(dt.year);\n          case \"yy\":\n            // like 14\n            return useDateTimeFormatter\n              ? string({ year: \"2-digit\" }, \"year\")\n              : this.num(dt.year.toString().slice(-2), 2);\n          case \"yyyy\":\n            // like 0012\n            return useDateTimeFormatter\n              ? string({ year: \"numeric\" }, \"year\")\n              : this.num(dt.year, 4);\n          case \"yyyyyy\":\n            // like 000012\n            return useDateTimeFormatter\n              ? string({ year: \"numeric\" }, \"year\")\n              : this.num(dt.year, 6);\n          // eras\n          case \"G\":\n            // like AD\n            return era(\"short\");\n          case \"GG\":\n            // like Anno Domini\n            return era(\"long\");\n          case \"GGGGG\":\n            return era(\"narrow\");\n          case \"kk\":\n            return this.num(dt.weekYear.toString().slice(-2), 2);\n          case \"kkkk\":\n            return this.num(dt.weekYear, 4);\n          case \"W\":\n            return this.num(dt.weekNumber);\n          case \"WW\":\n            return this.num(dt.weekNumber, 2);\n          case \"n\":\n            return this.num(dt.localWeekNumber);\n          case \"nn\":\n            return this.num(dt.localWeekNumber, 2);\n          case \"ii\":\n            return this.num(dt.localWeekYear.toString().slice(-2), 2);\n          case \"iiii\":\n            return this.num(dt.localWeekYear, 4);\n          case \"o\":\n            return this.num(dt.ordinal);\n          case \"ooo\":\n            return this.num(dt.ordinal, 3);\n          case \"q\":\n            // like 1\n            return this.num(dt.quarter);\n          case \"qq\":\n            // like 01\n            return this.num(dt.quarter, 2);\n          case \"X\":\n            return this.num(Math.floor(dt.ts / 1000));\n          case \"x\":\n            return this.num(dt.ts);\n          default:\n            return maybeMacro(token);\n        }\n      };\n\n    return stringifyTokens(Formatter.parseFormat(fmt), tokenToString);\n  }\n\n  formatDurationFromString(dur, fmt) {\n    const invertLargest = this.opts.signMode === \"negativeLargestOnly\" ? -1 : 1;\n    const tokenToField = (token) => {\n        switch (token[0]) {\n          case \"S\":\n            return \"milliseconds\";\n          case \"s\":\n            return \"seconds\";\n          case \"m\":\n            return \"minutes\";\n          case \"h\":\n            return \"hours\";\n          case \"d\":\n            return \"days\";\n          case \"w\":\n            return \"weeks\";\n          case \"M\":\n            return \"months\";\n          case \"y\":\n            return \"years\";\n          default:\n            return null;\n        }\n      },\n      tokenToString = (lildur, info) => (token) => {\n        const mapped = tokenToField(token);\n        if (mapped) {\n          const inversionFactor =\n            info.isNegativeDuration && mapped !== info.largestUnit ? invertLargest : 1;\n          let signDisplay;\n          if (this.opts.signMode === \"negativeLargestOnly\" && mapped !== info.largestUnit) {\n            signDisplay = \"never\";\n          } else if (this.opts.signMode === \"all\") {\n            signDisplay = \"always\";\n          } else {\n            // \"auto\" and \"negative\" are the same, but \"auto\" has better support\n            signDisplay = \"auto\";\n          }\n          return this.num(lildur.get(mapped) * inversionFactor, token.length, signDisplay);\n        } else {\n          return token;\n        }\n      },\n      tokens = Formatter.parseFormat(fmt),\n      realTokens = tokens.reduce(\n        (found, { literal, val }) => (literal ? found : found.concat(val)),\n        []\n      ),\n      collapsed = dur.shiftTo(...realTokens.map(tokenToField).filter((t) => t)),\n      durationInfo = {\n        isNegativeDuration: collapsed < 0,\n        // this relies on \"collapsed\" being based on \"shiftTo\", which builds up the object\n        // in order\n        largestUnit: Object.keys(collapsed.values)[0],\n      };\n    return stringifyTokens(tokens, tokenToString(collapsed, durationInfo));\n  }\n}\n"
  },
  {
    "path": "src/impl/invalid.js",
    "content": "export default class Invalid {\n  constructor(reason, explanation) {\n    this.reason = reason;\n    this.explanation = explanation;\n  }\n\n  toMessage() {\n    if (this.explanation) {\n      return `${this.reason}: ${this.explanation}`;\n    } else {\n      return this.reason;\n    }\n  }\n}\n"
  },
  {
    "path": "src/impl/locale.js",
    "content": "import { hasLocaleWeekInfo, hasRelative, padStart, roundTo, validateWeekSettings } from \"./util.js\";\nimport * as English from \"./english.js\";\nimport Settings from \"../settings.js\";\nimport DateTime from \"../datetime.js\";\nimport IANAZone from \"../zones/IANAZone.js\";\n\n// todo - remap caching\n\nlet intlLFCache = {};\nfunction getCachedLF(locString, opts = {}) {\n  const key = JSON.stringify([locString, opts]);\n  let dtf = intlLFCache[key];\n  if (!dtf) {\n    dtf = new Intl.ListFormat(locString, opts);\n    intlLFCache[key] = dtf;\n  }\n  return dtf;\n}\n\nconst intlDTCache = new Map();\nfunction getCachedDTF(locString, opts = {}) {\n  const key = JSON.stringify([locString, opts]);\n  let dtf = intlDTCache.get(key);\n  if (dtf === undefined) {\n    dtf = new Intl.DateTimeFormat(locString, opts);\n    intlDTCache.set(key, dtf);\n  }\n  return dtf;\n}\n\nconst intlNumCache = new Map();\nfunction getCachedINF(locString, opts = {}) {\n  const key = JSON.stringify([locString, opts]);\n  let inf = intlNumCache.get(key);\n  if (inf === undefined) {\n    inf = new Intl.NumberFormat(locString, opts);\n    intlNumCache.set(key, inf);\n  }\n  return inf;\n}\n\nconst intlRelCache = new Map();\nfunction getCachedRTF(locString, opts = {}) {\n  const { base, ...cacheKeyOpts } = opts; // exclude `base` from the options\n  const key = JSON.stringify([locString, cacheKeyOpts]);\n  let inf = intlRelCache.get(key);\n  if (inf === undefined) {\n    inf = new Intl.RelativeTimeFormat(locString, opts);\n    intlRelCache.set(key, inf);\n  }\n  return inf;\n}\n\nlet sysLocaleCache = null;\nfunction systemLocale() {\n  if (sysLocaleCache) {\n    return sysLocaleCache;\n  } else {\n    sysLocaleCache = new Intl.DateTimeFormat().resolvedOptions().locale;\n    return sysLocaleCache;\n  }\n}\n\nconst intlResolvedOptionsCache = new Map();\nfunction getCachedIntResolvedOptions(locString) {\n  let opts = intlResolvedOptionsCache.get(locString);\n  if (opts === undefined) {\n    opts = new Intl.DateTimeFormat(locString).resolvedOptions();\n    intlResolvedOptionsCache.set(locString, opts);\n  }\n  return opts;\n}\n\nconst weekInfoCache = new Map();\nfunction getCachedWeekInfo(locString) {\n  let data = weekInfoCache.get(locString);\n  if (!data) {\n    const locale = new Intl.Locale(locString);\n    // browsers currently implement this as a property, but spec says it should be a getter function\n    data = \"getWeekInfo\" in locale ? locale.getWeekInfo() : locale.weekInfo;\n    // minimalDays was removed from WeekInfo: https://github.com/tc39/proposal-intl-locale-info/issues/86\n    if (!(\"minimalDays\" in data)) {\n      data = { ...fallbackWeekSettings, ...data };\n    }\n    weekInfoCache.set(locString, data);\n  }\n  return data;\n}\n\nfunction parseLocaleString(localeStr) {\n  // I really want to avoid writing a BCP 47 parser\n  // see, e.g. https://github.com/wooorm/bcp-47\n  // Instead, we'll do this:\n\n  // a) if the string has no -u extensions, just leave it alone\n  // b) if it does, use Intl to resolve everything\n  // c) if Intl fails, try again without the -u\n\n  // private subtags and unicode subtags have ordering requirements,\n  // and we're not properly parsing this, so just strip out the\n  // private ones if they exist.\n  const xIndex = localeStr.indexOf(\"-x-\");\n  if (xIndex !== -1) {\n    localeStr = localeStr.substring(0, xIndex);\n  }\n\n  const uIndex = localeStr.indexOf(\"-u-\");\n  if (uIndex === -1) {\n    return [localeStr];\n  } else {\n    let options;\n    let selectedStr;\n    try {\n      options = getCachedDTF(localeStr).resolvedOptions();\n      selectedStr = localeStr;\n    } catch (e) {\n      const smaller = localeStr.substring(0, uIndex);\n      options = getCachedDTF(smaller).resolvedOptions();\n      selectedStr = smaller;\n    }\n\n    const { numberingSystem, calendar } = options;\n    return [selectedStr, numberingSystem, calendar];\n  }\n}\n\nfunction intlConfigString(localeStr, numberingSystem, outputCalendar) {\n  if (outputCalendar || numberingSystem) {\n    if (!localeStr.includes(\"-u-\")) {\n      localeStr += \"-u\";\n    }\n\n    if (outputCalendar) {\n      localeStr += `-ca-${outputCalendar}`;\n    }\n\n    if (numberingSystem) {\n      localeStr += `-nu-${numberingSystem}`;\n    }\n    return localeStr;\n  } else {\n    return localeStr;\n  }\n}\n\nfunction mapMonths(f) {\n  const ms = [];\n  for (let i = 1; i <= 12; i++) {\n    const dt = DateTime.utc(2009, i, 1);\n    ms.push(f(dt));\n  }\n  return ms;\n}\n\nfunction mapWeekdays(f) {\n  const ms = [];\n  for (let i = 1; i <= 7; i++) {\n    const dt = DateTime.utc(2016, 11, 13 + i);\n    ms.push(f(dt));\n  }\n  return ms;\n}\n\nfunction listStuff(loc, length, englishFn, intlFn) {\n  const mode = loc.listingMode();\n\n  if (mode === \"error\") {\n    return null;\n  } else if (mode === \"en\") {\n    return englishFn(length);\n  } else {\n    return intlFn(length);\n  }\n}\n\nfunction supportsFastNumbers(loc) {\n  if (loc.numberingSystem && loc.numberingSystem !== \"latn\") {\n    return false;\n  } else {\n    return (\n      loc.numberingSystem === \"latn\" ||\n      !loc.locale ||\n      loc.locale.startsWith(\"en\") ||\n      getCachedIntResolvedOptions(loc.locale).numberingSystem === \"latn\"\n    );\n  }\n}\n\n/**\n * @private\n */\n\nclass PolyNumberFormatter {\n  constructor(intl, forceSimple, opts) {\n    this.padTo = opts.padTo || 0;\n    this.floor = opts.floor || false;\n\n    const { padTo, floor, ...otherOpts } = opts;\n\n    if (!forceSimple || Object.keys(otherOpts).length > 0) {\n      const intlOpts = { useGrouping: false, ...opts };\n      if (opts.padTo > 0) intlOpts.minimumIntegerDigits = opts.padTo;\n      this.inf = getCachedINF(intl, intlOpts);\n    }\n  }\n\n  format(i) {\n    if (this.inf) {\n      const fixed = this.floor ? Math.floor(i) : i;\n      return this.inf.format(fixed);\n    } else {\n      // to match the browser's numberformatter defaults\n      const fixed = this.floor ? Math.floor(i) : roundTo(i, 3);\n      return padStart(fixed, this.padTo);\n    }\n  }\n}\n\n/**\n * @private\n */\n\nclass PolyDateFormatter {\n  constructor(dt, intl, opts) {\n    this.opts = opts;\n    this.originalZone = undefined;\n\n    let z = undefined;\n    if (this.opts.timeZone) {\n      // Don't apply any workarounds if a timeZone is explicitly provided in opts\n      this.dt = dt;\n    } else if (dt.zone.type === \"fixed\") {\n      // UTC-8 or Etc/UTC-8 are not part of tzdata, only Etc/GMT+8 and the like.\n      // That is why fixed-offset TZ is set to that unless it is:\n      // 1. Representing offset 0 when UTC is used to maintain previous behavior and does not become GMT.\n      // 2. Unsupported by the browser:\n      //    - some do not support Etc/\n      //    - < Etc/GMT-14, > Etc/GMT+12, and 30-minute or 45-minute offsets are not part of tzdata\n      const gmtOffset = -1 * (dt.offset / 60);\n      const offsetZ = gmtOffset >= 0 ? `Etc/GMT+${gmtOffset}` : `Etc/GMT${gmtOffset}`;\n      if (dt.offset !== 0 && IANAZone.create(offsetZ).valid) {\n        z = offsetZ;\n        this.dt = dt;\n      } else {\n        // Not all fixed-offset zones like Etc/+4:30 are present in tzdata so\n        // we manually apply the offset and substitute the zone as needed.\n        z = \"UTC\";\n        this.dt = dt.offset === 0 ? dt : dt.setZone(\"UTC\").plus({ minutes: dt.offset });\n        this.originalZone = dt.zone;\n      }\n    } else if (dt.zone.type === \"system\") {\n      this.dt = dt;\n    } else if (dt.zone.type === \"iana\") {\n      this.dt = dt;\n      z = dt.zone.name;\n    } else {\n      // Custom zones can have any offset / offsetName so we just manually\n      // apply the offset and substitute the zone as needed.\n      z = \"UTC\";\n      this.dt = dt.setZone(\"UTC\").plus({ minutes: dt.offset });\n      this.originalZone = dt.zone;\n    }\n\n    const intlOpts = { ...this.opts };\n    intlOpts.timeZone = intlOpts.timeZone || z;\n    this.dtf = getCachedDTF(intl, intlOpts);\n  }\n\n  format() {\n    if (this.originalZone) {\n      // If we have to substitute in the actual zone name, we have to use\n      // formatToParts so that the timezone can be replaced.\n      return this.formatToParts()\n        .map(({ value }) => value)\n        .join(\"\");\n    }\n    return this.dtf.format(this.dt.toJSDate());\n  }\n\n  formatToParts() {\n    const parts = this.dtf.formatToParts(this.dt.toJSDate());\n    if (this.originalZone) {\n      return parts.map((part) => {\n        if (part.type === \"timeZoneName\") {\n          const offsetName = this.originalZone.offsetName(this.dt.ts, {\n            locale: this.dt.locale,\n            format: this.opts.timeZoneName,\n          });\n          return {\n            ...part,\n            value: offsetName,\n          };\n        } else {\n          return part;\n        }\n      });\n    }\n    return parts;\n  }\n\n  resolvedOptions() {\n    return this.dtf.resolvedOptions();\n  }\n}\n\n/**\n * @private\n */\nclass PolyRelFormatter {\n  constructor(intl, isEnglish, opts) {\n    this.opts = { style: \"long\", ...opts };\n    if (!isEnglish && hasRelative()) {\n      this.rtf = getCachedRTF(intl, opts);\n    }\n  }\n\n  format(count, unit) {\n    if (this.rtf) {\n      return this.rtf.format(count, unit);\n    } else {\n      return English.formatRelativeTime(unit, count, this.opts.numeric, this.opts.style !== \"long\");\n    }\n  }\n\n  formatToParts(count, unit) {\n    if (this.rtf) {\n      return this.rtf.formatToParts(count, unit);\n    } else {\n      return [];\n    }\n  }\n}\n\nconst fallbackWeekSettings = {\n  firstDay: 1,\n  minimalDays: 4,\n  weekend: [6, 7],\n};\n\n/**\n * @private\n */\nexport default class Locale {\n  static fromOpts(opts) {\n    return Locale.create(\n      opts.locale,\n      opts.numberingSystem,\n      opts.outputCalendar,\n      opts.weekSettings,\n      opts.defaultToEN\n    );\n  }\n\n  static create(locale, numberingSystem, outputCalendar, weekSettings, defaultToEN = false) {\n    const specifiedLocale = locale || Settings.defaultLocale;\n    // the system locale is useful for human-readable strings but annoying for parsing/formatting known formats\n    const localeR = specifiedLocale || (defaultToEN ? \"en-US\" : systemLocale());\n    const numberingSystemR = numberingSystem || Settings.defaultNumberingSystem;\n    const outputCalendarR = outputCalendar || Settings.defaultOutputCalendar;\n    const weekSettingsR = validateWeekSettings(weekSettings) || Settings.defaultWeekSettings;\n    return new Locale(localeR, numberingSystemR, outputCalendarR, weekSettingsR, specifiedLocale);\n  }\n\n  static resetCache() {\n    sysLocaleCache = null;\n    intlDTCache.clear();\n    intlNumCache.clear();\n    intlRelCache.clear();\n    intlResolvedOptionsCache.clear();\n    weekInfoCache.clear();\n  }\n\n  static fromObject({ locale, numberingSystem, outputCalendar, weekSettings } = {}) {\n    return Locale.create(locale, numberingSystem, outputCalendar, weekSettings);\n  }\n\n  constructor(locale, numbering, outputCalendar, weekSettings, specifiedLocale) {\n    const [parsedLocale, parsedNumberingSystem, parsedOutputCalendar] = parseLocaleString(locale);\n\n    this.locale = parsedLocale;\n    this.numberingSystem = numbering || parsedNumberingSystem || null;\n    this.outputCalendar = outputCalendar || parsedOutputCalendar || null;\n    this.weekSettings = weekSettings;\n    this.intl = intlConfigString(this.locale, this.numberingSystem, this.outputCalendar);\n\n    this.weekdaysCache = { format: {}, standalone: {} };\n    this.monthsCache = { format: {}, standalone: {} };\n    this.meridiemCache = null;\n    this.eraCache = {};\n\n    this.specifiedLocale = specifiedLocale;\n    this.fastNumbersCached = null;\n  }\n\n  get fastNumbers() {\n    if (this.fastNumbersCached == null) {\n      this.fastNumbersCached = supportsFastNumbers(this);\n    }\n\n    return this.fastNumbersCached;\n  }\n\n  listingMode() {\n    const isActuallyEn = this.isEnglish();\n    const hasNoWeirdness =\n      (this.numberingSystem === null || this.numberingSystem === \"latn\") &&\n      (this.outputCalendar === null || this.outputCalendar === \"gregory\");\n    return isActuallyEn && hasNoWeirdness ? \"en\" : \"intl\";\n  }\n\n  clone(alts) {\n    if (!alts || Object.getOwnPropertyNames(alts).length === 0) {\n      return this;\n    } else {\n      return Locale.create(\n        alts.locale || this.specifiedLocale,\n        alts.numberingSystem || this.numberingSystem,\n        alts.outputCalendar || this.outputCalendar,\n        validateWeekSettings(alts.weekSettings) || this.weekSettings,\n        alts.defaultToEN || false\n      );\n    }\n  }\n\n  redefaultToEN(alts = {}) {\n    return this.clone({ ...alts, defaultToEN: true });\n  }\n\n  redefaultToSystem(alts = {}) {\n    return this.clone({ ...alts, defaultToEN: false });\n  }\n\n  months(length, format = false) {\n    return listStuff(this, length, English.months, () => {\n      // Workaround for \"ja\" locale: formatToParts does not label all parts of the month\n      // as \"month\" and for this locale there is no difference between \"format\" and \"non-format\".\n      // As such, just use format() instead of formatToParts() and take the whole string\n      const monthSpecialCase = this.intl === \"ja\" || this.intl.startsWith(\"ja-\");\n      format &= !monthSpecialCase;\n      const intl = format ? { month: length, day: \"numeric\" } : { month: length },\n        formatStr = format ? \"format\" : \"standalone\";\n      if (!this.monthsCache[formatStr][length]) {\n        const mapper = !monthSpecialCase\n          ? (dt) => this.extract(dt, intl, \"month\")\n          : (dt) => this.dtFormatter(dt, intl).format();\n        this.monthsCache[formatStr][length] = mapMonths(mapper);\n      }\n      return this.monthsCache[formatStr][length];\n    });\n  }\n\n  weekdays(length, format = false) {\n    return listStuff(this, length, English.weekdays, () => {\n      const intl = format\n          ? { weekday: length, year: \"numeric\", month: \"long\", day: \"numeric\" }\n          : { weekday: length },\n        formatStr = format ? \"format\" : \"standalone\";\n      if (!this.weekdaysCache[formatStr][length]) {\n        this.weekdaysCache[formatStr][length] = mapWeekdays((dt) =>\n          this.extract(dt, intl, \"weekday\")\n        );\n      }\n      return this.weekdaysCache[formatStr][length];\n    });\n  }\n\n  meridiems() {\n    return listStuff(\n      this,\n      undefined,\n      () => English.meridiems,\n      () => {\n        // In theory there could be aribitrary day periods. We're gonna assume there are exactly two\n        // for AM and PM. This is probably wrong, but it's makes parsing way easier.\n        if (!this.meridiemCache) {\n          const intl = { hour: \"numeric\", hourCycle: \"h12\" };\n          this.meridiemCache = [DateTime.utc(2016, 11, 13, 9), DateTime.utc(2016, 11, 13, 19)].map(\n            (dt) => this.extract(dt, intl, \"dayperiod\")\n          );\n        }\n\n        return this.meridiemCache;\n      }\n    );\n  }\n\n  eras(length) {\n    return listStuff(this, length, English.eras, () => {\n      const intl = { era: length };\n\n      // This is problematic. Different calendars are going to define eras totally differently. What I need is the minimum set of dates\n      // to definitely enumerate them.\n      if (!this.eraCache[length]) {\n        this.eraCache[length] = [DateTime.utc(-40, 1, 1), DateTime.utc(2017, 1, 1)].map((dt) =>\n          this.extract(dt, intl, \"era\")\n        );\n      }\n\n      return this.eraCache[length];\n    });\n  }\n\n  extract(dt, intlOpts, field) {\n    const df = this.dtFormatter(dt, intlOpts),\n      results = df.formatToParts(),\n      matching = results.find((m) => m.type.toLowerCase() === field);\n    return matching ? matching.value : null;\n  }\n\n  numberFormatter(opts = {}) {\n    // this forcesimple option is never used (the only caller short-circuits on it, but it seems safer to leave)\n    // (in contrast, the rest of the condition is used heavily)\n    return new PolyNumberFormatter(this.intl, opts.forceSimple || this.fastNumbers, opts);\n  }\n\n  dtFormatter(dt, intlOpts = {}) {\n    return new PolyDateFormatter(dt, this.intl, intlOpts);\n  }\n\n  relFormatter(opts = {}) {\n    return new PolyRelFormatter(this.intl, this.isEnglish(), opts);\n  }\n\n  listFormatter(opts = {}) {\n    return getCachedLF(this.intl, opts);\n  }\n\n  isEnglish() {\n    return (\n      this.locale === \"en\" ||\n      this.locale.toLowerCase() === \"en-us\" ||\n      getCachedIntResolvedOptions(this.intl).locale.startsWith(\"en-us\")\n    );\n  }\n\n  getWeekSettings() {\n    if (this.weekSettings) {\n      return this.weekSettings;\n    } else if (!hasLocaleWeekInfo()) {\n      return fallbackWeekSettings;\n    } else {\n      return getCachedWeekInfo(this.locale);\n    }\n  }\n\n  getStartOfWeek() {\n    return this.getWeekSettings().firstDay;\n  }\n\n  getMinDaysInFirstWeek() {\n    return this.getWeekSettings().minimalDays;\n  }\n\n  getWeekendDays() {\n    return this.getWeekSettings().weekend;\n  }\n\n  equals(other) {\n    return (\n      this.locale === other.locale &&\n      this.numberingSystem === other.numberingSystem &&\n      this.outputCalendar === other.outputCalendar\n    );\n  }\n\n  toString() {\n    return `Locale(${this.locale}, ${this.numberingSystem}, ${this.outputCalendar})`;\n  }\n}\n"
  },
  {
    "path": "src/impl/regexParser.js",
    "content": "import {\n  untruncateYear,\n  signedOffset,\n  parseInteger,\n  parseMillis,\n  isUndefined,\n  parseFloating,\n} from \"./util.js\";\nimport * as English from \"./english.js\";\nimport FixedOffsetZone from \"../zones/fixedOffsetZone.js\";\nimport IANAZone from \"../zones/IANAZone.js\";\n\n/*\n * This file handles parsing for well-specified formats. Here's how it works:\n * Two things go into parsing: a regex to match with and an extractor to take apart the groups in the match.\n * An extractor is just a function that takes a regex match array and returns a { year: ..., month: ... } object\n * parse() does the work of executing the regex and applying the extractor. It takes multiple regex/extractor pairs to try in sequence.\n * Extractors can take a \"cursor\" representing the offset in the match to look at. This makes it easy to combine extractors.\n * combineExtractors() does the work of combining them, keeping track of the cursor through multiple extractions.\n * Some extractions are super dumb and simpleParse and fromStrings help DRY them.\n */\n\nconst ianaRegex = /[A-Za-z_+-]{1,256}(?::?\\/[A-Za-z0-9_+-]{1,256}(?:\\/[A-Za-z0-9_+-]{1,256})?)?/;\n\nfunction combineRegexes(...regexes) {\n  const full = regexes.reduce((f, r) => f + r.source, \"\");\n  return RegExp(`^${full}$`);\n}\n\nfunction combineExtractors(...extractors) {\n  return (m) =>\n    extractors\n      .reduce(\n        ([mergedVals, mergedZone, cursor], ex) => {\n          const [val, zone, next] = ex(m, cursor);\n          return [{ ...mergedVals, ...val }, zone || mergedZone, next];\n        },\n        [{}, null, 1]\n      )\n      .slice(0, 2);\n}\n\nfunction parse(s, ...patterns) {\n  if (s == null) {\n    return [null, null];\n  }\n\n  for (const [regex, extractor] of patterns) {\n    const m = regex.exec(s);\n    if (m) {\n      return extractor(m);\n    }\n  }\n  return [null, null];\n}\n\nfunction simpleParse(...keys) {\n  return (match, cursor) => {\n    const ret = {};\n    let i;\n\n    for (i = 0; i < keys.length; i++) {\n      ret[keys[i]] = parseInteger(match[cursor + i]);\n    }\n    return [ret, null, cursor + i];\n  };\n}\n\n// ISO and SQL parsing\nconst offsetRegex = /(?:([Zz])|([+-]\\d\\d)(?::?(\\d\\d))?)/;\nconst isoExtendedZone = `(?:${offsetRegex.source}?(?:\\\\[(${ianaRegex.source})\\\\])?)?`;\nconst isoTimeBaseRegex = /(\\d\\d)(?::?(\\d\\d)(?::?(\\d\\d)(?:[.,](\\d{1,30}))?)?)?/;\nconst isoTimeRegex = RegExp(`${isoTimeBaseRegex.source}${isoExtendedZone}`);\nconst isoTimeExtensionRegex = RegExp(`(?:[Tt]${isoTimeRegex.source})?`);\nconst isoYmdRegex = /([+-]\\d{6}|\\d{4})(?:-?(\\d\\d)(?:-?(\\d\\d))?)?/;\nconst isoWeekRegex = /(\\d{4})-?W(\\d\\d)(?:-?(\\d))?/;\nconst isoOrdinalRegex = /(\\d{4})-?(\\d{3})/;\nconst extractISOWeekData = simpleParse(\"weekYear\", \"weekNumber\", \"weekDay\");\nconst extractISOOrdinalData = simpleParse(\"year\", \"ordinal\");\nconst sqlYmdRegex = /(\\d{4})-(\\d\\d)-(\\d\\d)/; // dumbed-down version of the ISO one\nconst sqlTimeRegex = RegExp(\n  `${isoTimeBaseRegex.source} ?(?:${offsetRegex.source}|(${ianaRegex.source}))?`\n);\nconst sqlTimeExtensionRegex = RegExp(`(?: ${sqlTimeRegex.source})?`);\n\nfunction int(match, pos, fallback) {\n  const m = match[pos];\n  return isUndefined(m) ? fallback : parseInteger(m);\n}\n\nfunction extractISOYmd(match, cursor) {\n  const item = {\n    year: int(match, cursor),\n    month: int(match, cursor + 1, 1),\n    day: int(match, cursor + 2, 1),\n  };\n\n  return [item, null, cursor + 3];\n}\n\nfunction extractISOTime(match, cursor) {\n  const item = {\n    hours: int(match, cursor, 0),\n    minutes: int(match, cursor + 1, 0),\n    seconds: int(match, cursor + 2, 0),\n    milliseconds: parseMillis(match[cursor + 3]),\n  };\n\n  return [item, null, cursor + 4];\n}\n\nfunction extractISOOffset(match, cursor) {\n  const local = !match[cursor] && !match[cursor + 1],\n    fullOffset = signedOffset(match[cursor + 1], match[cursor + 2]),\n    zone = local ? null : FixedOffsetZone.instance(fullOffset);\n  return [{}, zone, cursor + 3];\n}\n\nfunction extractIANAZone(match, cursor) {\n  const zone = match[cursor] ? IANAZone.create(match[cursor]) : null;\n  return [{}, zone, cursor + 1];\n}\n\n// ISO time parsing\n\nconst isoTimeOnly = RegExp(`^T?${isoTimeBaseRegex.source}$`);\n\n// ISO duration parsing\n\nconst isoDuration =\n  /^-?P(?:(?:(-?\\d{1,20}(?:\\.\\d{1,20})?)Y)?(?:(-?\\d{1,20}(?:\\.\\d{1,20})?)M)?(?:(-?\\d{1,20}(?:\\.\\d{1,20})?)W)?(?:(-?\\d{1,20}(?:\\.\\d{1,20})?)D)?(?:T(?:(-?\\d{1,20}(?:\\.\\d{1,20})?)H)?(?:(-?\\d{1,20}(?:\\.\\d{1,20})?)M)?(?:(-?\\d{1,20})(?:[.,](-?\\d{1,20}))?S)?)?)$/;\n\nfunction extractISODuration(match) {\n  const [s, yearStr, monthStr, weekStr, dayStr, hourStr, minuteStr, secondStr, millisecondsStr] =\n    match;\n\n  const hasNegativePrefix = s[0] === \"-\";\n  const negativeSeconds = secondStr && secondStr[0] === \"-\";\n\n  const maybeNegate = (num, force = false) =>\n    num !== undefined && (force || (num && hasNegativePrefix)) ? -num : num;\n\n  return [\n    {\n      years: maybeNegate(parseFloating(yearStr)),\n      months: maybeNegate(parseFloating(monthStr)),\n      weeks: maybeNegate(parseFloating(weekStr)),\n      days: maybeNegate(parseFloating(dayStr)),\n      hours: maybeNegate(parseFloating(hourStr)),\n      minutes: maybeNegate(parseFloating(minuteStr)),\n      seconds: maybeNegate(parseFloating(secondStr), secondStr === \"-0\"),\n      milliseconds: maybeNegate(parseMillis(millisecondsStr), negativeSeconds),\n    },\n  ];\n}\n\n// These are a little braindead. EDT *should* tell us that we're in, say, America/New_York\n// and not just that we're in -240 *right now*. But since I don't think these are used that often\n// I'm just going to ignore that\nconst obsOffsets = {\n  GMT: 0,\n  EDT: -4 * 60,\n  EST: -5 * 60,\n  CDT: -5 * 60,\n  CST: -6 * 60,\n  MDT: -6 * 60,\n  MST: -7 * 60,\n  PDT: -7 * 60,\n  PST: -8 * 60,\n};\n\nfunction fromStrings(weekdayStr, yearStr, monthStr, dayStr, hourStr, minuteStr, secondStr) {\n  const result = {\n    year: yearStr.length === 2 ? untruncateYear(parseInteger(yearStr)) : parseInteger(yearStr),\n    month: English.monthsShort.indexOf(monthStr) + 1,\n    day: parseInteger(dayStr),\n    hour: parseInteger(hourStr),\n    minute: parseInteger(minuteStr),\n  };\n\n  if (secondStr) result.second = parseInteger(secondStr);\n  if (weekdayStr) {\n    result.weekday =\n      weekdayStr.length > 3\n        ? English.weekdaysLong.indexOf(weekdayStr) + 1\n        : English.weekdaysShort.indexOf(weekdayStr) + 1;\n  }\n\n  return result;\n}\n\n// RFC 2822/5322\nconst rfc2822 =\n  /^(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun),\\s)?(\\d{1,2})\\s(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\\s(\\d{2,4})\\s(\\d\\d):(\\d\\d)(?::(\\d\\d))?\\s(?:(UT|GMT|[ECMP][SD]T)|([Zz])|(?:([+-]\\d\\d)(\\d\\d)))$/;\n\nfunction extractRFC2822(match) {\n  const [\n      ,\n      weekdayStr,\n      dayStr,\n      monthStr,\n      yearStr,\n      hourStr,\n      minuteStr,\n      secondStr,\n      obsOffset,\n      milOffset,\n      offHourStr,\n      offMinuteStr,\n    ] = match,\n    result = fromStrings(weekdayStr, yearStr, monthStr, dayStr, hourStr, minuteStr, secondStr);\n\n  let offset;\n  if (obsOffset) {\n    offset = obsOffsets[obsOffset];\n  } else if (milOffset) {\n    offset = 0;\n  } else {\n    offset = signedOffset(offHourStr, offMinuteStr);\n  }\n\n  return [result, new FixedOffsetZone(offset)];\n}\n\nfunction preprocessRFC2822(s) {\n  // Remove comments and folding whitespace and replace multiple-spaces with a single space\n  return s\n    .replace(/\\([^()]*\\)|[\\n\\t]/g, \" \")\n    .replace(/(\\s\\s+)/g, \" \")\n    .trim();\n}\n\n// http date\n\nconst rfc1123 =\n    /^(Mon|Tue|Wed|Thu|Fri|Sat|Sun), (\\d\\d) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) (\\d{4}) (\\d\\d):(\\d\\d):(\\d\\d) GMT$/,\n  rfc850 =\n    /^(Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday), (\\d\\d)-(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)-(\\d\\d) (\\d\\d):(\\d\\d):(\\d\\d) GMT$/,\n  ascii =\n    /^(Mon|Tue|Wed|Thu|Fri|Sat|Sun) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) ( \\d|\\d\\d) (\\d\\d):(\\d\\d):(\\d\\d) (\\d{4})$/;\n\nfunction extractRFC1123Or850(match) {\n  const [, weekdayStr, dayStr, monthStr, yearStr, hourStr, minuteStr, secondStr] = match,\n    result = fromStrings(weekdayStr, yearStr, monthStr, dayStr, hourStr, minuteStr, secondStr);\n  return [result, FixedOffsetZone.utcInstance];\n}\n\nfunction extractASCII(match) {\n  const [, weekdayStr, monthStr, dayStr, hourStr, minuteStr, secondStr, yearStr] = match,\n    result = fromStrings(weekdayStr, yearStr, monthStr, dayStr, hourStr, minuteStr, secondStr);\n  return [result, FixedOffsetZone.utcInstance];\n}\n\nconst isoYmdWithTimeExtensionRegex = combineRegexes(isoYmdRegex, isoTimeExtensionRegex);\nconst isoWeekWithTimeExtensionRegex = combineRegexes(isoWeekRegex, isoTimeExtensionRegex);\nconst isoOrdinalWithTimeExtensionRegex = combineRegexes(isoOrdinalRegex, isoTimeExtensionRegex);\nconst isoTimeCombinedRegex = combineRegexes(isoTimeRegex);\n\nconst extractISOYmdTimeAndOffset = combineExtractors(\n  extractISOYmd,\n  extractISOTime,\n  extractISOOffset,\n  extractIANAZone\n);\nconst extractISOWeekTimeAndOffset = combineExtractors(\n  extractISOWeekData,\n  extractISOTime,\n  extractISOOffset,\n  extractIANAZone\n);\nconst extractISOOrdinalDateAndTime = combineExtractors(\n  extractISOOrdinalData,\n  extractISOTime,\n  extractISOOffset,\n  extractIANAZone\n);\nconst extractISOTimeAndOffset = combineExtractors(\n  extractISOTime,\n  extractISOOffset,\n  extractIANAZone\n);\n\n/*\n * @private\n */\n\nexport function parseISODate(s) {\n  return parse(\n    s,\n    [isoYmdWithTimeExtensionRegex, extractISOYmdTimeAndOffset],\n    [isoWeekWithTimeExtensionRegex, extractISOWeekTimeAndOffset],\n    [isoOrdinalWithTimeExtensionRegex, extractISOOrdinalDateAndTime],\n    [isoTimeCombinedRegex, extractISOTimeAndOffset]\n  );\n}\n\n// ISO Interval parsing\n\n// Note: Do not optimize the outer non-capturing group, it is necessary, because the\n// regex is combined with other regexes and contains |\nconst partialIsoIntervalEndDate = /(?:(?:(\\d\\d)-)?(\\d\\d)?|(?:W(\\d\\d)-)?(\\d)|(\\d{3}))/;\nconst isoIntervalEndDateTime = combineRegexes(partialIsoIntervalEndDate, isoTimeExtensionRegex);\n\nconst extractPartialIsoIntervalEndDate = simpleParse(\n  \"month\",\n  \"day\",\n  \"weekNumber\",\n  \"weekDay\",\n  \"ordinal\"\n);\n\nconst extractISOIntervalPartialDateAndTime = combineExtractors(\n  extractPartialIsoIntervalEndDate,\n  extractISOTime,\n  extractISOOffset,\n  extractIANAZone\n);\n\nexport function parseISOIntervalEnd(s) {\n  return parse(\n    s,\n    [isoIntervalEndDateTime, extractISOIntervalPartialDateAndTime],\n    [isoYmdWithTimeExtensionRegex, extractISOYmdTimeAndOffset],\n    [isoWeekWithTimeExtensionRegex, extractISOWeekTimeAndOffset],\n    [isoOrdinalWithTimeExtensionRegex, extractISOOrdinalDateAndTime],\n    [isoTimeCombinedRegex, extractISOTimeAndOffset]\n  );\n}\n\nexport function parseRFC2822Date(s) {\n  return parse(preprocessRFC2822(s), [rfc2822, extractRFC2822]);\n}\n\nexport function parseHTTPDate(s) {\n  return parse(\n    s,\n    [rfc1123, extractRFC1123Or850],\n    [rfc850, extractRFC1123Or850],\n    [ascii, extractASCII]\n  );\n}\n\nexport function parseISODuration(s) {\n  return parse(s, [isoDuration, extractISODuration]);\n}\n\nconst extractISOTimeOnly = combineExtractors(extractISOTime);\n\nexport function parseISOTimeOnly(s) {\n  return parse(s, [isoTimeOnly, extractISOTimeOnly]);\n}\n\nconst sqlYmdWithTimeExtensionRegex = combineRegexes(sqlYmdRegex, sqlTimeExtensionRegex);\nconst sqlTimeCombinedRegex = combineRegexes(sqlTimeRegex);\n\nconst extractISOTimeOffsetAndIANAZone = combineExtractors(\n  extractISOTime,\n  extractISOOffset,\n  extractIANAZone\n);\n\nexport function parseSQL(s) {\n  return parse(\n    s,\n    [sqlYmdWithTimeExtensionRegex, extractISOYmdTimeAndOffset],\n    [sqlTimeCombinedRegex, extractISOTimeOffsetAndIANAZone]\n  );\n}\n"
  },
  {
    "path": "src/impl/tokenParser.js",
    "content": "import { parseMillis, isUndefined, untruncateYear, signedOffset, hasOwnProperty } from \"./util.js\";\nimport Formatter from \"./formatter.js\";\nimport FixedOffsetZone from \"../zones/fixedOffsetZone.js\";\nimport IANAZone from \"../zones/IANAZone.js\";\nimport DateTime from \"../datetime.js\";\nimport { digitRegex, parseDigits } from \"./digits.js\";\nimport { ConflictingSpecificationError } from \"../errors.js\";\n\nconst MISSING_FTP = \"missing Intl.DateTimeFormat.formatToParts support\";\n\nfunction intUnit(regex, post = (i) => i) {\n  return { regex, deser: ([s]) => post(parseDigits(s)) };\n}\n\nconst NBSP = String.fromCharCode(160);\nconst spaceOrNBSP = `[ ${NBSP}]`;\nconst spaceOrNBSPRegExp = new RegExp(spaceOrNBSP, \"g\");\n\nfunction fixListRegex(s) {\n  // make dots optional and also make them literal\n  // make space and non breakable space characters interchangeable\n  return s.replace(/\\./g, \"\\\\.?\").replace(spaceOrNBSPRegExp, spaceOrNBSP);\n}\n\nfunction stripInsensitivities(s) {\n  return s\n    .replace(/\\./g, \"\") // ignore dots that were made optional\n    .replace(spaceOrNBSPRegExp, \" \") // interchange space and nbsp\n    .toLowerCase();\n}\n\nfunction oneOf(strings, startIndex) {\n  if (strings === null) {\n    return null;\n  } else {\n    return {\n      regex: RegExp(strings.map(fixListRegex).join(\"|\")),\n      deser: ([s]) =>\n        strings.findIndex((i) => stripInsensitivities(s) === stripInsensitivities(i)) + startIndex,\n    };\n  }\n}\n\nfunction offset(regex, groups) {\n  return { regex, deser: ([, h, m]) => signedOffset(h, m), groups };\n}\n\nfunction simple(regex) {\n  return { regex, deser: ([s]) => s };\n}\n\nfunction escapeToken(value) {\n  return value.replace(/[\\-\\[\\]{}()*+?.,\\\\\\^$|#\\s]/g, \"\\\\$&\");\n}\n\n/**\n * @param token\n * @param {Locale} loc\n */\nfunction unitForToken(token, loc) {\n  const one = digitRegex(loc),\n    two = digitRegex(loc, \"{2}\"),\n    three = digitRegex(loc, \"{3}\"),\n    four = digitRegex(loc, \"{4}\"),\n    six = digitRegex(loc, \"{6}\"),\n    oneOrTwo = digitRegex(loc, \"{1,2}\"),\n    oneToThree = digitRegex(loc, \"{1,3}\"),\n    oneToSix = digitRegex(loc, \"{1,6}\"),\n    oneToNine = digitRegex(loc, \"{1,9}\"),\n    twoToFour = digitRegex(loc, \"{2,4}\"),\n    fourToSix = digitRegex(loc, \"{4,6}\"),\n    literal = (t) => ({ regex: RegExp(escapeToken(t.val)), deser: ([s]) => s, literal: true }),\n    unitate = (t) => {\n      if (token.literal) {\n        return literal(t);\n      }\n      switch (t.val) {\n        // era\n        case \"G\":\n          return oneOf(loc.eras(\"short\"), 0);\n        case \"GG\":\n          return oneOf(loc.eras(\"long\"), 0);\n        // years\n        case \"y\":\n          return intUnit(oneToSix);\n        case \"yy\":\n          return intUnit(twoToFour, untruncateYear);\n        case \"yyyy\":\n          return intUnit(four);\n        case \"yyyyy\":\n          return intUnit(fourToSix);\n        case \"yyyyyy\":\n          return intUnit(six);\n        // months\n        case \"M\":\n          return intUnit(oneOrTwo);\n        case \"MM\":\n          return intUnit(two);\n        case \"MMM\":\n          return oneOf(loc.months(\"short\", true), 1);\n        case \"MMMM\":\n          return oneOf(loc.months(\"long\", true), 1);\n        case \"L\":\n          return intUnit(oneOrTwo);\n        case \"LL\":\n          return intUnit(two);\n        case \"LLL\":\n          return oneOf(loc.months(\"short\", false), 1);\n        case \"LLLL\":\n          return oneOf(loc.months(\"long\", false), 1);\n        // dates\n        case \"d\":\n          return intUnit(oneOrTwo);\n        case \"dd\":\n          return intUnit(two);\n        // ordinals\n        case \"o\":\n          return intUnit(oneToThree);\n        case \"ooo\":\n          return intUnit(three);\n        // time\n        case \"HH\":\n          return intUnit(two);\n        case \"H\":\n          return intUnit(oneOrTwo);\n        case \"hh\":\n          return intUnit(two);\n        case \"h\":\n          return intUnit(oneOrTwo);\n        case \"mm\":\n          return intUnit(two);\n        case \"m\":\n          return intUnit(oneOrTwo);\n        case \"q\":\n          return intUnit(oneOrTwo);\n        case \"qq\":\n          return intUnit(two);\n        case \"s\":\n          return intUnit(oneOrTwo);\n        case \"ss\":\n          return intUnit(two);\n        case \"S\":\n          return intUnit(oneToThree);\n        case \"SSS\":\n          return intUnit(three);\n        case \"u\":\n          return simple(oneToNine);\n        case \"uu\":\n          return simple(oneOrTwo);\n        case \"uuu\":\n          return intUnit(one);\n        // meridiem\n        case \"a\":\n          return oneOf(loc.meridiems(), 0);\n        // weekYear (k)\n        case \"kkkk\":\n          return intUnit(four);\n        case \"kk\":\n          return intUnit(twoToFour, untruncateYear);\n        // weekNumber (W)\n        case \"W\":\n          return intUnit(oneOrTwo);\n        case \"WW\":\n          return intUnit(two);\n        // weekdays\n        case \"E\":\n        case \"c\":\n          return intUnit(one);\n        case \"EEE\":\n          return oneOf(loc.weekdays(\"short\", false), 1);\n        case \"EEEE\":\n          return oneOf(loc.weekdays(\"long\", false), 1);\n        case \"ccc\":\n          return oneOf(loc.weekdays(\"short\", true), 1);\n        case \"cccc\":\n          return oneOf(loc.weekdays(\"long\", true), 1);\n        // offset/zone\n        case \"Z\":\n        case \"ZZ\":\n          return offset(new RegExp(`([+-]${oneOrTwo.source})(?::(${two.source}))?`), 2);\n        case \"ZZZ\":\n          return offset(new RegExp(`([+-]${oneOrTwo.source})(${two.source})?`), 2);\n        // we don't support ZZZZ (PST) or ZZZZZ (Pacific Standard Time) in parsing\n        // because we don't have any way to figure out what they are\n        case \"z\":\n          return simple(/[a-z_+-/]{1,256}?/i);\n        // this special-case \"token\" represents a place where a macro-token expanded into a white-space literal\n        // in this case we accept any non-newline white-space\n        case \" \":\n          return simple(/[^\\S\\n\\r]/);\n        default:\n          return literal(t);\n      }\n    };\n\n  const unit = unitate(token) || {\n    invalidReason: MISSING_FTP,\n  };\n\n  unit.token = token;\n\n  return unit;\n}\n\nconst partTypeStyleToTokenVal = {\n  year: {\n    \"2-digit\": \"yy\",\n    numeric: \"yyyyy\",\n  },\n  month: {\n    numeric: \"M\",\n    \"2-digit\": \"MM\",\n    short: \"MMM\",\n    long: \"MMMM\",\n  },\n  day: {\n    numeric: \"d\",\n    \"2-digit\": \"dd\",\n  },\n  weekday: {\n    short: \"EEE\",\n    long: \"EEEE\",\n  },\n  dayperiod: \"a\",\n  dayPeriod: \"a\",\n  hour12: {\n    numeric: \"h\",\n    \"2-digit\": \"hh\",\n  },\n  hour24: {\n    numeric: \"H\",\n    \"2-digit\": \"HH\",\n  },\n  minute: {\n    numeric: \"m\",\n    \"2-digit\": \"mm\",\n  },\n  second: {\n    numeric: \"s\",\n    \"2-digit\": \"ss\",\n  },\n  timeZoneName: {\n    long: \"ZZZZZ\",\n    short: \"ZZZ\",\n  },\n};\n\nfunction tokenForPart(part, formatOpts, resolvedOpts) {\n  const { type, value } = part;\n\n  if (type === \"literal\") {\n    const isSpace = /^\\s+$/.test(value);\n    return {\n      literal: !isSpace,\n      val: isSpace ? \" \" : value,\n    };\n  }\n\n  const style = formatOpts[type];\n\n  // The user might have explicitly specified hour12 or hourCycle\n  // if so, respect their decision\n  // if not, refer back to the resolvedOpts, which are based on the locale\n  let actualType = type;\n  if (type === \"hour\") {\n    if (formatOpts.hour12 != null) {\n      actualType = formatOpts.hour12 ? \"hour12\" : \"hour24\";\n    } else if (formatOpts.hourCycle != null) {\n      if (formatOpts.hourCycle === \"h11\" || formatOpts.hourCycle === \"h12\") {\n        actualType = \"hour12\";\n      } else {\n        actualType = \"hour24\";\n      }\n    } else {\n      // tokens only differentiate between 24 hours or not,\n      // so we do not need to check hourCycle here, which is less supported anyways\n      actualType = resolvedOpts.hour12 ? \"hour12\" : \"hour24\";\n    }\n  }\n  let val = partTypeStyleToTokenVal[actualType];\n  if (typeof val === \"object\") {\n    val = val[style];\n  }\n\n  if (val) {\n    return {\n      literal: false,\n      val,\n    };\n  }\n\n  return undefined;\n}\n\nfunction buildRegex(units) {\n  const re = units.map((u) => u.regex).reduce((f, r) => `${f}(${r.source})`, \"\");\n  return [`^${re}$`, units];\n}\n\nfunction match(input, regex, handlers) {\n  const matches = input.match(regex);\n\n  if (matches) {\n    const all = {};\n    let matchIndex = 1;\n    for (const i in handlers) {\n      if (hasOwnProperty(handlers, i)) {\n        const h = handlers[i],\n          groups = h.groups ? h.groups + 1 : 1;\n        if (!h.literal && h.token) {\n          all[h.token.val[0]] = h.deser(matches.slice(matchIndex, matchIndex + groups));\n        }\n        matchIndex += groups;\n      }\n    }\n    return [matches, all];\n  } else {\n    return [matches, {}];\n  }\n}\n\nfunction dateTimeFromMatches(matches) {\n  const toField = (token) => {\n    switch (token) {\n      case \"S\":\n        return \"millisecond\";\n      case \"s\":\n        return \"second\";\n      case \"m\":\n        return \"minute\";\n      case \"h\":\n      case \"H\":\n        return \"hour\";\n      case \"d\":\n        return \"day\";\n      case \"o\":\n        return \"ordinal\";\n      case \"L\":\n      case \"M\":\n        return \"month\";\n      case \"y\":\n        return \"year\";\n      case \"E\":\n      case \"c\":\n        return \"weekday\";\n      case \"W\":\n        return \"weekNumber\";\n      case \"k\":\n        return \"weekYear\";\n      case \"q\":\n        return \"quarter\";\n      default:\n        return null;\n    }\n  };\n\n  let zone = null;\n  let specificOffset;\n  if (!isUndefined(matches.z)) {\n    zone = IANAZone.create(matches.z);\n  }\n\n  if (!isUndefined(matches.Z)) {\n    if (!zone) {\n      zone = new FixedOffsetZone(matches.Z);\n    }\n    specificOffset = matches.Z;\n  }\n\n  if (!isUndefined(matches.q)) {\n    matches.M = (matches.q - 1) * 3 + 1;\n  }\n\n  if (!isUndefined(matches.h)) {\n    if (matches.h < 12 && matches.a === 1) {\n      matches.h += 12;\n    } else if (matches.h === 12 && matches.a === 0) {\n      matches.h = 0;\n    }\n  }\n\n  if (matches.G === 0 && matches.y) {\n    matches.y = -matches.y;\n  }\n\n  if (!isUndefined(matches.u)) {\n    matches.S = parseMillis(matches.u);\n  }\n\n  const vals = Object.keys(matches).reduce((r, k) => {\n    const f = toField(k);\n    if (f) {\n      r[f] = matches[k];\n    }\n\n    return r;\n  }, {});\n\n  return [vals, zone, specificOffset];\n}\n\nlet dummyDateTimeCache = null;\n\nfunction getDummyDateTime() {\n  if (!dummyDateTimeCache) {\n    dummyDateTimeCache = DateTime.fromMillis(1555555555555);\n  }\n\n  return dummyDateTimeCache;\n}\n\nfunction maybeExpandMacroToken(token, locale) {\n  if (token.literal) {\n    return token;\n  }\n\n  const formatOpts = Formatter.macroTokenToFormatOpts(token.val);\n  const tokens = formatOptsToTokens(formatOpts, locale);\n\n  if (tokens == null || tokens.includes(undefined)) {\n    return token;\n  }\n\n  return tokens;\n}\n\nexport function expandMacroTokens(tokens, locale) {\n  return Array.prototype.concat(...tokens.map((t) => maybeExpandMacroToken(t, locale)));\n}\n\n/**\n * @private\n */\n\nexport class TokenParser {\n  constructor(locale, format) {\n    this.locale = locale;\n    this.format = format;\n    this.tokens = expandMacroTokens(Formatter.parseFormat(format), locale);\n    this.units = this.tokens.map((t) => unitForToken(t, locale));\n    this.disqualifyingUnit = this.units.find((t) => t.invalidReason);\n\n    if (!this.disqualifyingUnit) {\n      const [regexString, handlers] = buildRegex(this.units);\n      this.regex = RegExp(regexString, \"i\");\n      this.handlers = handlers;\n    }\n  }\n\n  explainFromTokens(input) {\n    if (!this.isValid) {\n      return { input, tokens: this.tokens, invalidReason: this.invalidReason };\n    } else {\n      const [rawMatches, matches] = match(input, this.regex, this.handlers),\n        [result, zone, specificOffset] = matches\n          ? dateTimeFromMatches(matches)\n          : [null, null, undefined];\n      if (hasOwnProperty(matches, \"a\") && hasOwnProperty(matches, \"H\")) {\n        throw new ConflictingSpecificationError(\n          \"Can't include meridiem when specifying 24-hour format\"\n        );\n      }\n      return {\n        input,\n        tokens: this.tokens,\n        regex: this.regex,\n        rawMatches,\n        matches,\n        result,\n        zone,\n        specificOffset,\n      };\n    }\n  }\n\n  get isValid() {\n    return !this.disqualifyingUnit;\n  }\n\n  get invalidReason() {\n    return this.disqualifyingUnit ? this.disqualifyingUnit.invalidReason : null;\n  }\n}\n\nexport function explainFromTokens(locale, input, format) {\n  const parser = new TokenParser(locale, format);\n  return parser.explainFromTokens(input);\n}\n\nexport function parseFromTokens(locale, input, format) {\n  const { result, zone, specificOffset, invalidReason } = explainFromTokens(locale, input, format);\n  return [result, zone, specificOffset, invalidReason];\n}\n\nexport function formatOptsToTokens(formatOpts, locale) {\n  if (!formatOpts) {\n    return null;\n  }\n\n  const formatter = Formatter.create(locale, formatOpts);\n  const df = formatter.dtFormatter(getDummyDateTime());\n  const parts = df.formatToParts();\n  const resolvedOpts = df.resolvedOptions();\n  return parts.map((p) => tokenForPart(p, formatOpts, resolvedOpts));\n}\n"
  },
  {
    "path": "src/impl/util.js",
    "content": "/*\n  This is just a junk drawer, containing anything used across multiple classes.\n  Because Luxon is small(ish), this should stay small and we won't worry about splitting\n  it up into, say, parsingUtil.js and basicUtil.js and so on. But they are divided up by feature area.\n*/\n\nimport { InvalidArgumentError } from \"../errors.js\";\nimport Settings from \"../settings.js\";\nimport { dayOfWeek, isoWeekdayToLocal } from \"./conversions.js\";\n\n/**\n * @private\n */\n\n// TYPES\n\nexport function isUndefined(o) {\n  return typeof o === \"undefined\";\n}\n\nexport function isNumber(o) {\n  return typeof o === \"number\";\n}\n\nexport function isInteger(o) {\n  return typeof o === \"number\" && o % 1 === 0;\n}\n\nexport function isString(o) {\n  return typeof o === \"string\";\n}\n\nexport function isDate(o) {\n  return Object.prototype.toString.call(o) === \"[object Date]\";\n}\n\n// CAPABILITIES\n\nexport function hasRelative() {\n  try {\n    return typeof Intl !== \"undefined\" && !!Intl.RelativeTimeFormat;\n  } catch (e) {\n    return false;\n  }\n}\n\nexport function hasLocaleWeekInfo() {\n  try {\n    return (\n      typeof Intl !== \"undefined\" &&\n      !!Intl.Locale &&\n      (\"weekInfo\" in Intl.Locale.prototype || \"getWeekInfo\" in Intl.Locale.prototype)\n    );\n  } catch (e) {\n    return false;\n  }\n}\n\n// OBJECTS AND ARRAYS\n\nexport function maybeArray(thing) {\n  return Array.isArray(thing) ? thing : [thing];\n}\n\nexport function bestBy(arr, by, compare) {\n  if (arr.length === 0) {\n    return undefined;\n  }\n  return arr.reduce((best, next) => {\n    const pair = [by(next), next];\n    if (!best) {\n      return pair;\n    } else if (compare(best[0], pair[0]) === best[0]) {\n      return best;\n    } else {\n      return pair;\n    }\n  }, null)[1];\n}\n\nexport function pick(obj, keys) {\n  return keys.reduce((a, k) => {\n    a[k] = obj[k];\n    return a;\n  }, {});\n}\n\nexport function hasOwnProperty(obj, prop) {\n  return Object.prototype.hasOwnProperty.call(obj, prop);\n}\n\nexport function validateWeekSettings(settings) {\n  if (settings == null) {\n    return null;\n  } else if (typeof settings !== \"object\") {\n    throw new InvalidArgumentError(\"Week settings must be an object\");\n  } else {\n    if (\n      !integerBetween(settings.firstDay, 1, 7) ||\n      !integerBetween(settings.minimalDays, 1, 7) ||\n      !Array.isArray(settings.weekend) ||\n      settings.weekend.some((v) => !integerBetween(v, 1, 7))\n    ) {\n      throw new InvalidArgumentError(\"Invalid week settings\");\n    }\n    return {\n      firstDay: settings.firstDay,\n      minimalDays: settings.minimalDays,\n      weekend: Array.from(settings.weekend),\n    };\n  }\n}\n\n// NUMBERS AND STRINGS\n\nexport function integerBetween(thing, bottom, top) {\n  return isInteger(thing) && thing >= bottom && thing <= top;\n}\n\n// x % n but takes the sign of n instead of x\nexport function floorMod(x, n) {\n  return x - n * Math.floor(x / n);\n}\n\nexport function padStart(input, n = 2) {\n  const isNeg = input < 0;\n  let padded;\n  if (isNeg) {\n    padded = \"-\" + (\"\" + -input).padStart(n, \"0\");\n  } else {\n    padded = (\"\" + input).padStart(n, \"0\");\n  }\n  return padded;\n}\n\nexport function parseInteger(string) {\n  if (isUndefined(string) || string === null || string === \"\") {\n    return undefined;\n  } else {\n    return parseInt(string, 10);\n  }\n}\n\nexport function parseFloating(string) {\n  if (isUndefined(string) || string === null || string === \"\") {\n    return undefined;\n  } else {\n    return parseFloat(string);\n  }\n}\n\nexport function parseMillis(fraction) {\n  // Return undefined (instead of 0) in these cases, where fraction is not set\n  if (isUndefined(fraction) || fraction === null || fraction === \"\") {\n    return undefined;\n  } else {\n    const f = parseFloat(\"0.\" + fraction) * 1000;\n    return Math.floor(f);\n  }\n}\n\nexport function roundTo(number, digits, rounding = \"round\") {\n  const factor = 10 ** digits;\n  switch (rounding) {\n    case \"expand\":\n      return number > 0\n        ? Math.ceil(number * factor) / factor\n        : Math.floor(number * factor) / factor;\n    case \"trunc\":\n      return Math.trunc(number * factor) / factor;\n    case \"round\":\n      return Math.round(number * factor) / factor;\n    case \"floor\":\n      return Math.floor(number * factor) / factor;\n    case \"ceil\":\n      return Math.ceil(number * factor) / factor;\n    default:\n      throw new RangeError(`Value rounding ${rounding} is out of range`);\n  }\n}\n\n// DATE BASICS\n\nexport function isLeapYear(year) {\n  return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);\n}\n\nexport function daysInYear(year) {\n  return isLeapYear(year) ? 366 : 365;\n}\n\nexport function daysInMonth(year, month) {\n  const modMonth = floorMod(month - 1, 12) + 1,\n    modYear = year + (month - modMonth) / 12;\n\n  if (modMonth === 2) {\n    return isLeapYear(modYear) ? 29 : 28;\n  } else {\n    return [31, null, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][modMonth - 1];\n  }\n}\n\n// convert a calendar object to a local timestamp (epoch, but with the offset baked in)\nexport function objToLocalTS(obj) {\n  let d = Date.UTC(\n    obj.year,\n    obj.month - 1,\n    obj.day,\n    obj.hour,\n    obj.minute,\n    obj.second,\n    obj.millisecond\n  );\n\n  // for legacy reasons, years between 0 and 99 are interpreted as 19XX; revert that\n  if (obj.year < 100 && obj.year >= 0) {\n    d = new Date(d);\n    // set the month and day again, this is necessary because year 2000 is a leap year, but year 100 is not\n    // so if obj.year is in 99, but obj.day makes it roll over into year 100,\n    // the calculations done by Date.UTC are using year 2000 - which is incorrect\n    d.setUTCFullYear(obj.year, obj.month - 1, obj.day);\n  }\n  return +d;\n}\n\n// adapted from moment.js: https://github.com/moment/moment/blob/000ac1800e620f770f4eb31b5ae908f6167b0ab2/src/lib/units/week-calendar-utils.js\nfunction firstWeekOffset(year, minDaysInFirstWeek, startOfWeek) {\n  const fwdlw = isoWeekdayToLocal(dayOfWeek(year, 1, minDaysInFirstWeek), startOfWeek);\n  return -fwdlw + minDaysInFirstWeek - 1;\n}\n\nexport function weeksInWeekYear(weekYear, minDaysInFirstWeek = 4, startOfWeek = 1) {\n  const weekOffset = firstWeekOffset(weekYear, minDaysInFirstWeek, startOfWeek);\n  const weekOffsetNext = firstWeekOffset(weekYear + 1, minDaysInFirstWeek, startOfWeek);\n  return (daysInYear(weekYear) - weekOffset + weekOffsetNext) / 7;\n}\n\nexport function untruncateYear(year) {\n  if (year > 99) {\n    return year;\n  } else return year > Settings.twoDigitCutoffYear ? 1900 + year : 2000 + year;\n}\n\n// PARSING\n\nexport function parseZoneInfo(ts, offsetFormat, locale, timeZone = null) {\n  const date = new Date(ts),\n    intlOpts = {\n      hourCycle: \"h23\",\n      year: \"numeric\",\n      month: \"2-digit\",\n      day: \"2-digit\",\n      hour: \"2-digit\",\n      minute: \"2-digit\",\n    };\n\n  if (timeZone) {\n    intlOpts.timeZone = timeZone;\n  }\n\n  const modified = { timeZoneName: offsetFormat, ...intlOpts };\n\n  const parsed = new Intl.DateTimeFormat(locale, modified)\n    .formatToParts(date)\n    .find((m) => m.type.toLowerCase() === \"timezonename\");\n  return parsed ? parsed.value : null;\n}\n\n// signedOffset('-5', '30') -> -330\nexport function signedOffset(offHourStr, offMinuteStr) {\n  let offHour = parseInt(offHourStr, 10);\n\n  // don't || this because we want to preserve -0\n  if (Number.isNaN(offHour)) {\n    offHour = 0;\n  }\n\n  const offMin = parseInt(offMinuteStr, 10) || 0,\n    offMinSigned = offHour < 0 || Object.is(offHour, -0) ? -offMin : offMin;\n  return offHour * 60 + offMinSigned;\n}\n\n// COERCION\n\nexport function asNumber(value) {\n  const numericValue = Number(value);\n  if (typeof value === \"boolean\" || value === \"\" || !Number.isFinite(numericValue))\n    throw new InvalidArgumentError(`Invalid unit value ${value}`);\n  return numericValue;\n}\n\nexport function normalizeObject(obj, normalizer) {\n  const normalized = {};\n  for (const u in obj) {\n    if (hasOwnProperty(obj, u)) {\n      const v = obj[u];\n      if (v === undefined || v === null) continue;\n      normalized[normalizer(u)] = asNumber(v);\n    }\n  }\n  return normalized;\n}\n\n/**\n * Returns the offset's value as a string\n * @param {number} ts - Epoch milliseconds for which to get the offset\n * @param {string} format - What style of offset to return.\n *                          Accepts 'narrow', 'short', or 'techie'. Returning '+6', '+06:00', or '+0600' respectively\n * @return {string}\n */\nexport function formatOffset(offset, format) {\n  const hours = Math.trunc(Math.abs(offset / 60)),\n    minutes = Math.trunc(Math.abs(offset % 60)),\n    sign = offset >= 0 ? \"+\" : \"-\";\n\n  switch (format) {\n    case \"short\":\n      return `${sign}${padStart(hours, 2)}:${padStart(minutes, 2)}`;\n    case \"narrow\":\n      return `${sign}${hours}${minutes > 0 ? `:${minutes}` : \"\"}`;\n    case \"techie\":\n      return `${sign}${padStart(hours, 2)}${padStart(minutes, 2)}`;\n    default:\n      throw new RangeError(`Value format ${format} is out of range for property format`);\n  }\n}\n\nexport function timeObject(obj) {\n  return pick(obj, [\"hour\", \"minute\", \"second\", \"millisecond\"]);\n}\n"
  },
  {
    "path": "src/impl/zoneUtil.js",
    "content": "/**\n * @private\n */\n\nimport Zone from \"../zone.js\";\nimport IANAZone from \"../zones/IANAZone.js\";\nimport FixedOffsetZone from \"../zones/fixedOffsetZone.js\";\nimport InvalidZone from \"../zones/invalidZone.js\";\n\nimport { isUndefined, isString, isNumber } from \"./util.js\";\nimport SystemZone from \"../zones/systemZone.js\";\n\nexport function normalizeZone(input, defaultZone) {\n  let offset;\n  if (isUndefined(input) || input === null) {\n    return defaultZone;\n  } else if (input instanceof Zone) {\n    return input;\n  } else if (isString(input)) {\n    const lowered = input.toLowerCase();\n    if (lowered === \"default\") return defaultZone;\n    else if (lowered === \"local\" || lowered === \"system\") return SystemZone.instance;\n    else if (lowered === \"utc\" || lowered === \"gmt\") return FixedOffsetZone.utcInstance;\n    else return FixedOffsetZone.parseSpecifier(lowered) || IANAZone.create(input);\n  } else if (isNumber(input)) {\n    return FixedOffsetZone.instance(input);\n  } else if (typeof input === \"object\" && \"offset\" in input && typeof input.offset === \"function\") {\n    // This is dumb, but the instanceof check above doesn't seem to really work\n    // so we're duck checking it\n    return input;\n  } else {\n    return new InvalidZone(input);\n  }\n}\n"
  },
  {
    "path": "src/info.js",
    "content": "import DateTime from \"./datetime.js\";\nimport Settings from \"./settings.js\";\nimport Locale from \"./impl/locale.js\";\nimport IANAZone from \"./zones/IANAZone.js\";\nimport { normalizeZone } from \"./impl/zoneUtil.js\";\n\nimport { hasLocaleWeekInfo, hasRelative } from \"./impl/util.js\";\n\n/**\n * The Info class contains static methods for retrieving general time and date related data. For example, it has methods for finding out if a time zone has a DST, for listing the months in any supported locale, and for discovering which of Luxon features are available in the current environment.\n */\nexport default class Info {\n  /**\n   * Return whether the specified zone contains a DST.\n   * @param {string|Zone} [zone='local'] - Zone to check. Defaults to the environment's local zone.\n   * @return {boolean}\n   */\n  static hasDST(zone = Settings.defaultZone) {\n    const proto = DateTime.now().setZone(zone).set({ month: 12 });\n\n    return !zone.isUniversal && proto.offset !== proto.set({ month: 6 }).offset;\n  }\n\n  /**\n   * Return whether the specified zone is a valid IANA specifier.\n   * @param {string} zone - Zone to check\n   * @return {boolean}\n   */\n  static isValidIANAZone(zone) {\n    return IANAZone.isValidZone(zone);\n  }\n\n  /**\n   * Converts the input into a {@link Zone} instance.\n   *\n   * * If `input` is already a Zone instance, it is returned unchanged.\n   * * If `input` is a string containing a valid time zone name, a Zone instance\n   *   with that name is returned.\n   * * If `input` is a string that doesn't refer to a known time zone, a Zone\n   *   instance with {@link Zone#isValid} == false is returned.\n   * * If `input is a number, a Zone instance with the specified fixed offset\n   *   in minutes is returned.\n   * * If `input` is `null` or `undefined`, the default zone is returned.\n   * @param {string|Zone|number} [input] - the value to be converted\n   * @return {Zone}\n   */\n  static normalizeZone(input) {\n    return normalizeZone(input, Settings.defaultZone);\n  }\n\n  /**\n   * Get the weekday on which the week starts according to the given locale.\n   * @param {Object} opts - options\n   * @param {string} [opts.locale] - the locale code\n   * @param {string} [opts.locObj=null] - an existing locale object to use\n   * @returns {number} the start of the week, 1 for Monday through 7 for Sunday\n   */\n  static getStartOfWeek({ locale = null, locObj = null } = {}) {\n    return (locObj || Locale.create(locale)).getStartOfWeek();\n  }\n\n  /**\n   * Get the minimum number of days necessary in a week before it is considered part of the next year according\n   * to the given locale.\n   * @param {Object} opts - options\n   * @param {string} [opts.locale] - the locale code\n   * @param {string} [opts.locObj=null] - an existing locale object to use\n   * @returns {number}\n   */\n  static getMinimumDaysInFirstWeek({ locale = null, locObj = null } = {}) {\n    return (locObj || Locale.create(locale)).getMinDaysInFirstWeek();\n  }\n\n  /**\n   * Get the weekdays, which are considered the weekend according to the given locale\n   * @param {Object} opts - options\n   * @param {string} [opts.locale] - the locale code\n   * @param {string} [opts.locObj=null] - an existing locale object to use\n   * @returns {number[]} an array of weekdays, 1 for Monday through 7 for Sunday\n   */\n  static getWeekendWeekdays({ locale = null, locObj = null } = {}) {\n    // copy the array, because we cache it internally\n    return (locObj || Locale.create(locale)).getWeekendDays().slice();\n  }\n\n  /**\n   * Return an array of standalone month names.\n   * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat\n   * @param {string} [length='long'] - the length of the month representation, such as \"numeric\", \"2-digit\", \"narrow\", \"short\", \"long\"\n   * @param {Object} opts - options\n   * @param {string} [opts.locale] - the locale code\n   * @param {string} [opts.numberingSystem=null] - the numbering system\n   * @param {string} [opts.locObj=null] - an existing locale object to use\n   * @param {string} [opts.outputCalendar='gregory'] - the calendar\n   * @example Info.months()[0] //=> 'January'\n   * @example Info.months('short')[0] //=> 'Jan'\n   * @example Info.months('numeric')[0] //=> '1'\n   * @example Info.months('short', { locale: 'fr-CA' } )[0] //=> 'janv.'\n   * @example Info.months('numeric', { locale: 'ar' })[0] //=> '١'\n   * @example Info.months('long', { outputCalendar: 'islamic' })[0] //=> 'Rabiʻ I'\n   * @return {Array}\n   */\n  static months(\n    length = \"long\",\n    { locale = null, numberingSystem = null, locObj = null, outputCalendar = \"gregory\" } = {}\n  ) {\n    return (locObj || Locale.create(locale, numberingSystem, outputCalendar)).months(length);\n  }\n\n  /**\n   * Return an array of format month names.\n   * Format months differ from standalone months in that they're meant to appear next to the day of the month. In some languages, that\n   * changes the string.\n   * See {@link Info#months}\n   * @param {string} [length='long'] - the length of the month representation, such as \"numeric\", \"2-digit\", \"narrow\", \"short\", \"long\"\n   * @param {Object} opts - options\n   * @param {string} [opts.locale] - the locale code\n   * @param {string} [opts.numberingSystem=null] - the numbering system\n   * @param {string} [opts.locObj=null] - an existing locale object to use\n   * @param {string} [opts.outputCalendar='gregory'] - the calendar\n   * @return {Array}\n   */\n  static monthsFormat(\n    length = \"long\",\n    { locale = null, numberingSystem = null, locObj = null, outputCalendar = \"gregory\" } = {}\n  ) {\n    return (locObj || Locale.create(locale, numberingSystem, outputCalendar)).months(length, true);\n  }\n\n  /**\n   * Return an array of standalone week names.\n   * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat\n   * @param {string} [length='long'] - the length of the weekday representation, such as \"narrow\", \"short\", \"long\".\n   * @param {Object} opts - options\n   * @param {string} [opts.locale] - the locale code\n   * @param {string} [opts.numberingSystem=null] - the numbering system\n   * @param {string} [opts.locObj=null] - an existing locale object to use\n   * @example Info.weekdays()[0] //=> 'Monday'\n   * @example Info.weekdays('short')[0] //=> 'Mon'\n   * @example Info.weekdays('short', { locale: 'fr-CA' })[0] //=> 'lun.'\n   * @example Info.weekdays('short', { locale: 'ar' })[0] //=> 'الاثنين'\n   * @return {Array}\n   */\n  static weekdays(length = \"long\", { locale = null, numberingSystem = null, locObj = null } = {}) {\n    return (locObj || Locale.create(locale, numberingSystem, null)).weekdays(length);\n  }\n\n  /**\n   * Return an array of format week names.\n   * Format weekdays differ from standalone weekdays in that they're meant to appear next to more date information. In some languages, that\n   * changes the string.\n   * See {@link Info#weekdays}\n   * @param {string} [length='long'] - the length of the month representation, such as \"narrow\", \"short\", \"long\".\n   * @param {Object} opts - options\n   * @param {string} [opts.locale=null] - the locale code\n   * @param {string} [opts.numberingSystem=null] - the numbering system\n   * @param {string} [opts.locObj=null] - an existing locale object to use\n   * @return {Array}\n   */\n  static weekdaysFormat(\n    length = \"long\",\n    { locale = null, numberingSystem = null, locObj = null } = {}\n  ) {\n    return (locObj || Locale.create(locale, numberingSystem, null)).weekdays(length, true);\n  }\n\n  /**\n   * Return an array of meridiems.\n   * @param {Object} opts - options\n   * @param {string} [opts.locale] - the locale code\n   * @example Info.meridiems() //=> [ 'AM', 'PM' ]\n   * @example Info.meridiems({ locale: 'my' }) //=> [ 'နံနက်', 'ညနေ' ]\n   * @return {Array}\n   */\n  static meridiems({ locale = null } = {}) {\n    return Locale.create(locale).meridiems();\n  }\n\n  /**\n   * Return an array of eras, such as ['BC', 'AD']. The locale can be specified, but the calendar system is always Gregorian.\n   * @param {string} [length='short'] - the length of the era representation, such as \"short\" or \"long\".\n   * @param {Object} opts - options\n   * @param {string} [opts.locale] - the locale code\n   * @example Info.eras() //=> [ 'BC', 'AD' ]\n   * @example Info.eras('long') //=> [ 'Before Christ', 'Anno Domini' ]\n   * @example Info.eras('long', { locale: 'fr' }) //=> [ 'avant Jésus-Christ', 'après Jésus-Christ' ]\n   * @return {Array}\n   */\n  static eras(length = \"short\", { locale = null } = {}) {\n    return Locale.create(locale, null, \"gregory\").eras(length);\n  }\n\n  /**\n   * Return the set of available features in this environment.\n   * Some features of Luxon are not available in all environments. For example, on older browsers, relative time formatting support is not available. Use this function to figure out if that's the case.\n   * Keys:\n   * * `relative`: whether this environment supports relative time formatting\n   * * `localeWeek`: whether this environment supports different weekdays for the start of the week based on the locale\n   * @example Info.features() //=> { relative: false, localeWeek: true }\n   * @return {Object}\n   */\n  static features() {\n    return { relative: hasRelative(), localeWeek: hasLocaleWeekInfo() };\n  }\n}\n"
  },
  {
    "path": "src/interval.js",
    "content": "import DateTime, { friendlyDateTime, parseDataToDateTime } from \"./datetime.js\";\nimport Duration from \"./duration.js\";\nimport Settings from \"./settings.js\";\nimport { InvalidArgumentError, InvalidIntervalError } from \"./errors.js\";\nimport Invalid from \"./impl/invalid.js\";\nimport Formatter from \"./impl/formatter.js\";\nimport * as Formats from \"./impl/formats.js\";\nimport { parseISOIntervalEnd } from \"./impl/regexParser.js\";\n\nconst INVALID = \"Invalid Interval\";\n\n// checks if the start is equal to or before the end\nfunction validateStartEnd(start, end) {\n  if (!start || !start.isValid) {\n    return Interval.invalid(\"missing or invalid start\");\n  } else if (!end || !end.isValid) {\n    return Interval.invalid(\"missing or invalid end\");\n  } else if (end < start) {\n    return Interval.invalid(\n      \"end before start\",\n      `The end of an interval must be after its start, but you had start=${start.toISO()} and end=${end.toISO()}`\n    );\n  } else {\n    return null;\n  }\n}\n\n/**\n * An Interval object represents a half-open interval of time, where each endpoint is a {@link DateTime}. Conceptually, it's a container for those two endpoints, accompanied by methods for creating, parsing, interrogating, comparing, transforming, and formatting them.\n *\n * Here is a brief overview of the most commonly used methods and getters in Interval:\n *\n * * **Creation** To create an Interval, use {@link Interval.fromDateTimes}, {@link Interval.after}, {@link Interval.before}, or {@link Interval.fromISO}.\n * * **Accessors** Use {@link Interval#start} and {@link Interval#end} to get the start and end.\n * * **Interrogation** To analyze the Interval, use {@link Interval#count}, {@link Interval#length}, {@link Interval#hasSame}, {@link Interval#contains}, {@link Interval#isAfter}, or {@link Interval#isBefore}.\n * * **Transformation** To create other Intervals out of this one, use {@link Interval#set}, {@link Interval#splitAt}, {@link Interval#splitBy}, {@link Interval#divideEqually}, {@link Interval.merge}, {@link Interval.xor}, {@link Interval#union}, {@link Interval#intersection}, or {@link Interval#difference}.\n * * **Comparison** To compare this Interval to another one, use {@link Interval#equals}, {@link Interval#overlaps}, {@link Interval#abutsStart}, {@link Interval#abutsEnd}, {@link Interval#engulfs}\n * * **Output** To convert the Interval into other representations, see {@link Interval#toString}, {@link Interval#toLocaleString}, {@link Interval#toISO}, {@link Interval#toISODate}, {@link Interval#toISOTime}, {@link Interval#toFormat}, and {@link Interval#toDuration}.\n */\nexport default class Interval {\n  /**\n   * @private\n   */\n  constructor(config) {\n    /**\n     * @access private\n     */\n    this.s = config.start;\n    /**\n     * @access private\n     */\n    this.e = config.end;\n    /**\n     * @access private\n     */\n    this.invalid = config.invalid || null;\n    /**\n     * @access private\n     */\n    this.isLuxonInterval = true;\n  }\n\n  /**\n   * Create an invalid Interval.\n   * @param {string} reason - simple string of why this Interval is invalid. Should not contain parameters or anything else data-dependent\n   * @param {string} [explanation=null] - longer explanation, may include parameters and other useful debugging information\n   * @return {Interval}\n   */\n  static invalid(reason, explanation = null) {\n    if (!reason) {\n      throw new InvalidArgumentError(\"need to specify a reason the Interval is invalid\");\n    }\n\n    const invalid = reason instanceof Invalid ? reason : new Invalid(reason, explanation);\n\n    if (Settings.throwOnInvalid) {\n      throw new InvalidIntervalError(invalid);\n    } else {\n      return new Interval({ invalid });\n    }\n  }\n\n  /**\n   * Create an Interval from a start DateTime and an end DateTime. Inclusive of the start but not the end.\n   * @param {DateTime|Date|Object} start\n   * @param {DateTime|Date|Object} end\n   * @return {Interval}\n   */\n  static fromDateTimes(start, end) {\n    const builtStart = friendlyDateTime(start),\n      builtEnd = friendlyDateTime(end);\n\n    const validateError = validateStartEnd(builtStart, builtEnd);\n\n    if (validateError == null) {\n      return new Interval({\n        start: builtStart,\n        end: builtEnd,\n      });\n    } else {\n      return validateError;\n    }\n  }\n\n  /**\n   * Create an Interval from a start DateTime and a Duration to extend to.\n   * @param {DateTime|Date|Object} start\n   * @param {Duration|Object|number} duration - the length of the Interval.\n   * @return {Interval}\n   */\n  static after(start, duration) {\n    const dur = Duration.fromDurationLike(duration),\n      dt = friendlyDateTime(start);\n    return Interval.fromDateTimes(dt, dt.plus(dur));\n  }\n\n  /**\n   * Create an Interval from an end DateTime and a Duration to extend backwards to.\n   * @param {DateTime|Date|Object} end\n   * @param {Duration|Object|number} duration - the length of the Interval.\n   * @return {Interval}\n   */\n  static before(end, duration) {\n    const dur = Duration.fromDurationLike(duration),\n      dt = friendlyDateTime(end);\n    return Interval.fromDateTimes(dt.minus(dur), dt);\n  }\n\n  /**\n   * Create an Interval from an ISO 8601 string.\n   * Accepts `<start>/<end>`, `<start>/<duration>`, and `<duration>/<end>` formats.\n   * @param {string} text - the ISO string to parse\n   * @param {Object} [opts] - options to pass {@link DateTime#fromISO} and optionally {@link Duration#fromISO}\n   * @see https://en.wikipedia.org/wiki/ISO_8601#Time_intervals\n   * @return {Interval}\n   */\n  static fromISO(text, opts) {\n    const { zone, setZone, ...restOpts } = opts || {};\n    const [s, e] = (text || \"\").split(\"/\", 2);\n    if (s && e) {\n      let start, startIsValid;\n      try {\n        // we need to know the zone that was used in the string, so that we can\n        // default to it when parsing end, therefor use setZone: true\n        start = DateTime.fromISO(s, { ...restOpts, zone, setZone: true });\n        startIsValid = start.isValid;\n      } catch (e) {\n        startIsValid = false;\n      }\n\n      let end, endIsValid;\n      try {\n        const [vals, parsedZone] = parseISOIntervalEnd(e);\n        const endParseOpts = {\n          ...restOpts,\n          overrideNow: startIsValid ? start.valueOf() : null,\n          zone: startIsValid ? start.zone : zone,\n          setZone: true,\n        };\n        end = parseDataToDateTime(vals, parsedZone, endParseOpts, \"ISO 8601 Interval end\", e);\n        endIsValid = end.isValid;\n      } catch (e) {\n        endIsValid = false;\n      }\n\n      // if we overrode the user's choice for setZone earlier, make up for it now\n      if (startIsValid && !setZone) {\n        start = start.setZone(zone);\n      }\n      if (endIsValid && !setZone) {\n        end = end.setZone(zone);\n      }\n\n      if (startIsValid && endIsValid) {\n        return Interval.fromDateTimes(start, end);\n      }\n\n      if (startIsValid) {\n        const dur = Duration.fromISO(e, opts);\n        if (dur.isValid) {\n          return Interval.after(start, dur);\n        }\n      } else if (endIsValid) {\n        const dur = Duration.fromISO(s, opts);\n        if (dur.isValid) {\n          return Interval.before(end, dur);\n        }\n      }\n    }\n    return Interval.invalid(\"unparsable\", `the input \"${text}\" can't be parsed as ISO 8601`);\n  }\n\n  /**\n   * Check if an object is an Interval. Works across context boundaries\n   * @param {object} o\n   * @return {boolean}\n   */\n  static isInterval(o) {\n    return (o && o.isLuxonInterval) || false;\n  }\n\n  /**\n   * Returns the start of the Interval\n   * @type {DateTime}\n   */\n  get start() {\n    return this.isValid ? this.s : null;\n  }\n\n  /**\n   * Returns the end of the Interval. This is the first instant which is not part of the interval\n   * (Interval is half-open).\n   * @type {DateTime}\n   */\n  get end() {\n    return this.isValid ? this.e : null;\n  }\n\n  /**\n   * Returns the last DateTime included in the interval (since end is not part of the interval)\n   * @type {DateTime}\n   */\n  get lastDateTime() {\n    return this.isValid ? (this.e ? this.e.minus(1) : null) : null;\n  }\n\n  /**\n   * Returns whether this Interval's end is at least its start, meaning that the Interval isn't 'backwards'.\n   * @type {boolean}\n   */\n  get isValid() {\n    return this.invalidReason === null;\n  }\n\n  /**\n   * Returns an error code if this Interval is invalid, or null if the Interval is valid\n   * @type {string}\n   */\n  get invalidReason() {\n    return this.invalid ? this.invalid.reason : null;\n  }\n\n  /**\n   * Returns an explanation of why this Interval became invalid, or null if the Interval is valid\n   * @type {string}\n   */\n  get invalidExplanation() {\n    return this.invalid ? this.invalid.explanation : null;\n  }\n\n  /**\n   * Returns the length of the Interval in the specified unit.\n   * @param {string} unit - the unit (such as 'hours' or 'days') to return the length in.\n   * @return {number}\n   */\n  length(unit = \"milliseconds\") {\n    return this.isValid ? this.toDuration(...[unit]).get(unit) : NaN;\n  }\n\n  /**\n   * Returns the count of minutes, hours, days, months, or years included in the Interval, even in part.\n   * Unlike {@link Interval#length} this counts sections of the calendar, not periods of time, e.g. specifying 'day'\n   * asks 'what dates are included in this interval?', not 'how many days long is this interval?'\n   * @param {string} [unit='milliseconds'] - the unit of time to count.\n   * @param {Object} opts - options\n   * @param {boolean} [opts.useLocaleWeeks=false] - If true, use weeks based on the locale, i.e. use the locale-dependent start of the week; this operation will always use the locale of the start DateTime\n   * @return {number}\n   */\n  count(unit = \"milliseconds\", opts) {\n    if (!this.isValid) return NaN;\n    const start = this.start.startOf(unit, opts);\n    let end;\n    if (opts?.useLocaleWeeks) {\n      end = this.end.reconfigure({ locale: start.locale });\n    } else {\n      end = this.end;\n    }\n    end = end.startOf(unit, opts);\n    return Math.floor(end.diff(start, unit).get(unit)) + (end.valueOf() !== this.end.valueOf());\n  }\n\n  /**\n   * Returns whether this Interval's start and end are both in the same unit of time\n   * @param {string} unit - the unit of time to check sameness on\n   * @return {boolean}\n   */\n  hasSame(unit) {\n    return this.isValid ? this.isEmpty() || this.e.minus(1).hasSame(this.s, unit) : false;\n  }\n\n  /**\n   * Return whether this Interval has the same start and end DateTimes.\n   * @return {boolean}\n   */\n  isEmpty() {\n    return this.s.valueOf() === this.e.valueOf();\n  }\n\n  /**\n   * Return whether this Interval's start is after the specified DateTime.\n   * @param {DateTime} dateTime\n   * @return {boolean}\n   */\n  isAfter(dateTime) {\n    if (!this.isValid) return false;\n    return this.s > dateTime;\n  }\n\n  /**\n   * Return whether this Interval's end is before the specified DateTime.\n   * @param {DateTime} dateTime\n   * @return {boolean}\n   */\n  isBefore(dateTime) {\n    if (!this.isValid) return false;\n    return this.e <= dateTime;\n  }\n\n  /**\n   * Return whether this Interval contains the specified DateTime.\n   * @param {DateTime} dateTime\n   * @return {boolean}\n   */\n  contains(dateTime) {\n    if (!this.isValid) return false;\n    return this.s <= dateTime && this.e > dateTime;\n  }\n\n  /**\n   * \"Sets\" the start and/or end dates. Returns a newly-constructed Interval.\n   * @param {Object} values - the values to set\n   * @param {DateTime} values.start - the starting DateTime\n   * @param {DateTime} values.end - the ending DateTime\n   * @return {Interval}\n   */\n  set({ start, end } = {}) {\n    if (!this.isValid) return this;\n    return Interval.fromDateTimes(start || this.s, end || this.e);\n  }\n\n  /**\n   * Split this Interval at each of the specified DateTimes\n   * @param {...DateTime} dateTimes - the unit of time to count.\n   * @return {Array}\n   */\n  splitAt(...dateTimes) {\n    if (!this.isValid) return [];\n    const sorted = dateTimes\n        .map(friendlyDateTime)\n        .filter((d) => this.contains(d))\n        .sort((a, b) => a.toMillis() - b.toMillis()),\n      results = [];\n    let { s } = this,\n      i = 0;\n\n    while (s < this.e) {\n      const added = sorted[i] || this.e,\n        next = +added > +this.e ? this.e : added;\n      results.push(Interval.fromDateTimes(s, next));\n      s = next;\n      i += 1;\n    }\n\n    return results;\n  }\n\n  /**\n   * Split this Interval into smaller Intervals, each of the specified length.\n   * Left over time is grouped into a smaller interval\n   * @param {Duration|Object|number} duration - The length of each resulting interval.\n   * @return {Array}\n   */\n  splitBy(duration) {\n    const dur = Duration.fromDurationLike(duration);\n\n    if (!this.isValid || !dur.isValid || dur.as(\"milliseconds\") === 0) {\n      return [];\n    }\n\n    let { s } = this,\n      idx = 1,\n      next;\n\n    const results = [];\n    while (s < this.e) {\n      const added = this.start.plus(dur.mapUnits((x) => x * idx));\n      next = +added > +this.e ? this.e : added;\n      results.push(Interval.fromDateTimes(s, next));\n      s = next;\n      idx += 1;\n    }\n\n    return results;\n  }\n\n  /**\n   * Split this Interval into the specified number of smaller intervals.\n   * @param {number} numberOfParts - The number of Intervals to divide the Interval into.\n   * @return {Array}\n   */\n  divideEqually(numberOfParts) {\n    if (!this.isValid) return [];\n    return this.splitBy(this.length() / numberOfParts).slice(0, numberOfParts);\n  }\n\n  /**\n   * Return whether this Interval overlaps with the specified Interval\n   * @param {Interval} other\n   * @return {boolean}\n   */\n  overlaps(other) {\n    return this.e > other.s && this.s < other.e;\n  }\n\n  /**\n   * Return whether this Interval's end is adjacent to the specified Interval's start.\n   * @param {Interval} other\n   * @return {boolean}\n   */\n  abutsStart(other) {\n    if (!this.isValid) return false;\n    return +this.e === +other.s;\n  }\n\n  /**\n   * Return whether this Interval's start is adjacent to the specified Interval's end.\n   * @param {Interval} other\n   * @return {boolean}\n   */\n  abutsEnd(other) {\n    if (!this.isValid) return false;\n    return +other.e === +this.s;\n  }\n\n  /**\n   * Returns true if this Interval fully contains the specified Interval, specifically if the intersect (of this Interval and the other Interval) is equal to the other Interval; false otherwise.\n   * @param {Interval} other\n   * @return {boolean}\n   */\n  engulfs(other) {\n    if (!this.isValid) return false;\n    return this.s <= other.s && this.e >= other.e;\n  }\n\n  /**\n   * Return whether this Interval has the same start and end as the specified Interval.\n   * @param {Interval} other\n   * @return {boolean}\n   */\n  equals(other) {\n    if (!this.isValid || !other.isValid) {\n      return false;\n    }\n\n    return this.s.equals(other.s) && this.e.equals(other.e);\n  }\n\n  /**\n   * Return an Interval representing the intersection of this Interval and the specified Interval.\n   * Specifically, the resulting Interval has the maximum start time and the minimum end time of the two Intervals.\n   * Returns null if the intersection is empty, meaning, the intervals don't intersect.\n   * @param {Interval} other\n   * @return {Interval}\n   */\n  intersection(other) {\n    if (!this.isValid) return this;\n    const s = this.s > other.s ? this.s : other.s,\n      e = this.e < other.e ? this.e : other.e;\n\n    if (s >= e) {\n      return null;\n    } else {\n      return Interval.fromDateTimes(s, e);\n    }\n  }\n\n  /**\n   * Return an Interval representing the union of this Interval and the specified Interval.\n   * Specifically, the resulting Interval has the minimum start time and the maximum end time of the two Intervals.\n   * @param {Interval} other\n   * @return {Interval}\n   */\n  union(other) {\n    if (!this.isValid) return this;\n    const s = this.s < other.s ? this.s : other.s,\n      e = this.e > other.e ? this.e : other.e;\n    return Interval.fromDateTimes(s, e);\n  }\n\n  /**\n   * Merge an array of Intervals into an equivalent minimal set of Intervals.\n   * Combines overlapping and adjacent Intervals.\n   * The resulting array will contain the Intervals in ascending order, that is, starting with the earliest Interval\n   * and ending with the latest.\n   *\n   * @param {Array} intervals\n   * @return {Array}\n   */\n  static merge(intervals) {\n    const [found, final] = intervals\n      .sort((a, b) => a.s - b.s)\n      .reduce(\n        ([sofar, current], item) => {\n          if (!current) {\n            return [sofar, item];\n          } else if (current.overlaps(item) || current.abutsStart(item)) {\n            return [sofar, current.union(item)];\n          } else {\n            return [sofar.concat([current]), item];\n          }\n        },\n        [[], null]\n      );\n    if (final) {\n      found.push(final);\n    }\n    return found;\n  }\n\n  /**\n   * Return an array of Intervals representing the spans of time that only appear in one of the specified Intervals.\n   * @param {Array} intervals\n   * @return {Array}\n   */\n  static xor(intervals) {\n    let start = null,\n      currentCount = 0;\n    const results = [],\n      ends = intervals.map((i) => [\n        { time: i.s, type: \"s\" },\n        { time: i.e, type: \"e\" },\n      ]),\n      flattened = Array.prototype.concat(...ends),\n      arr = flattened.sort((a, b) => a.time - b.time);\n\n    for (const i of arr) {\n      currentCount += i.type === \"s\" ? 1 : -1;\n\n      if (currentCount === 1) {\n        start = i.time;\n      } else {\n        if (start && +start !== +i.time) {\n          results.push(Interval.fromDateTimes(start, i.time));\n        }\n\n        start = null;\n      }\n    }\n\n    return Interval.merge(results);\n  }\n\n  /**\n   * Return an Interval representing the span of time in this Interval that doesn't overlap with any of the specified Intervals.\n   * @param {...Interval} intervals\n   * @return {Array}\n   */\n  difference(...intervals) {\n    return Interval.xor([this].concat(intervals))\n      .map((i) => this.intersection(i))\n      .filter((i) => i && !i.isEmpty());\n  }\n\n  /**\n   * Returns a string representation of this Interval appropriate for debugging.\n   * @return {string}\n   */\n  toString() {\n    if (!this.isValid) return INVALID;\n    return `[${this.s.toISO()} – ${this.e.toISO()})`;\n  }\n\n  /**\n   * Returns a string representation of this Interval appropriate for the REPL.\n   * @return {string}\n   */\n  [Symbol.for(\"nodejs.util.inspect.custom\")]() {\n    if (this.isValid) {\n      return `Interval { start: ${this.s.toISO()}, end: ${this.e.toISO()} }`;\n    } else {\n      return `Interval { Invalid, reason: ${this.invalidReason} }`;\n    }\n  }\n\n  /**\n   * Returns a localized string representing this Interval. Accepts the same options as the\n   * Intl.DateTimeFormat constructor and any presets defined by Luxon, such as\n   * {@link DateTime.DATE_FULL} or {@link DateTime.TIME_SIMPLE}. The exact behavior of this method\n   * is browser-specific, but in general it will return an appropriate representation of the\n   * Interval in the assigned locale. Defaults to the system's locale if no locale has been\n   * specified.\n   * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat\n   * @param {Object} [formatOpts=DateTime.DATE_SHORT] - Either a DateTime preset or\n   * Intl.DateTimeFormat constructor options.\n   * @param {Object} opts - Options to override the configuration of the start DateTime.\n   * @example Interval.fromISO('2022-11-07T09:00Z/2022-11-08T09:00Z').toLocaleString(); //=> 11/7/2022 – 11/8/2022\n   * @example Interval.fromISO('2022-11-07T09:00Z/2022-11-08T09:00Z').toLocaleString(DateTime.DATE_FULL); //=> November 7 – 8, 2022\n   * @example Interval.fromISO('2022-11-07T09:00Z/2022-11-08T09:00Z').toLocaleString(DateTime.DATE_FULL, { locale: 'fr-FR' }); //=> 7–8 novembre 2022\n   * @example Interval.fromISO('2022-11-07T17:00Z/2022-11-07T19:00Z').toLocaleString(DateTime.TIME_SIMPLE); //=> 6:00 – 8:00 PM\n   * @example Interval.fromISO('2022-11-07T17:00Z/2022-11-07T19:00Z').toLocaleString({ weekday: 'short', month: 'short', day: '2-digit', hour: '2-digit', minute: '2-digit' }); //=> Mon, Nov 07, 6:00 – 8:00 p\n   * @return {string}\n   */\n  toLocaleString(formatOpts = Formats.DATE_SHORT, opts = {}) {\n    return this.isValid\n      ? Formatter.create(this.s.loc.clone(opts), formatOpts).formatInterval(this)\n      : INVALID;\n  }\n\n  /**\n   * Returns an ISO 8601-compliant string representation of this Interval.\n   * @see https://en.wikipedia.org/wiki/ISO_8601#Time_intervals\n   * @param {Object} opts - The same options as {@link DateTime#toISO}\n   * @return {string}\n   */\n  toISO(opts) {\n    if (!this.isValid) return INVALID;\n    return `${this.s.toISO(opts)}/${this.e.toISO(opts)}`;\n  }\n\n  /**\n   * Returns an ISO 8601-compliant string representation of date of this Interval.\n   * The time components are ignored.\n   * @see https://en.wikipedia.org/wiki/ISO_8601#Time_intervals\n   * @return {string}\n   */\n  toISODate() {\n    if (!this.isValid) return INVALID;\n    return `${this.s.toISODate()}/${this.e.toISODate()}`;\n  }\n\n  /**\n   * Returns an ISO 8601-compliant string representation of time of this Interval.\n   * The date components are ignored.\n   * @see https://en.wikipedia.org/wiki/ISO_8601#Time_intervals\n   * @param {Object} opts - The same options as {@link DateTime#toISO}\n   * @return {string}\n   */\n  toISOTime(opts) {\n    if (!this.isValid) return INVALID;\n    return `${this.s.toISOTime(opts)}/${this.e.toISOTime(opts)}`;\n  }\n\n  /**\n   * Returns a string representation of this Interval formatted according to the specified format\n   * string. **You may not want this.** See {@link Interval#toLocaleString} for a more flexible\n   * formatting tool.\n   * @param {string} dateFormat - The format string. This string formats the start and end time.\n   * See {@link DateTime#toFormat} for details.\n   * @param {Object} opts - Options.\n   * @param {string} [opts.separator =  ' – '] - A separator to place between the start and end\n   * representations.\n   * @return {string}\n   */\n  toFormat(dateFormat, { separator = \" – \" } = {}) {\n    if (!this.isValid) return INVALID;\n    return `${this.s.toFormat(dateFormat)}${separator}${this.e.toFormat(dateFormat)}`;\n  }\n\n  /**\n   * Return a Duration representing the time spanned by this interval.\n   * @param {string|string[]} [unit=['milliseconds']] - the unit or units (such as 'hours' or 'days') to include in the duration.\n   * @param {Object} opts - options that affect the creation of the Duration\n   * @param {string} [opts.conversionAccuracy='casual'] - the conversion system to use\n   * @example Interval.fromDateTimes(dt1, dt2).toDuration().toObject() //=> { milliseconds: 88489257 }\n   * @example Interval.fromDateTimes(dt1, dt2).toDuration('days').toObject() //=> { days: 1.0241812152777778 }\n   * @example Interval.fromDateTimes(dt1, dt2).toDuration(['hours', 'minutes']).toObject() //=> { hours: 24, minutes: 34.82095 }\n   * @example Interval.fromDateTimes(dt1, dt2).toDuration(['hours', 'minutes', 'seconds']).toObject() //=> { hours: 24, minutes: 34, seconds: 49.257 }\n   * @example Interval.fromDateTimes(dt1, dt2).toDuration('seconds').toObject() //=> { seconds: 88489.257 }\n   * @return {Duration}\n   */\n  toDuration(unit, opts) {\n    if (!this.isValid) {\n      return Duration.invalid(this.invalidReason);\n    }\n    return this.e.diff(this.s, unit, opts);\n  }\n\n  /**\n   * Run mapFn on the interval start and end, returning a new Interval from the resulting DateTimes\n   * @param {function} mapFn\n   * @return {Interval}\n   * @example Interval.fromDateTimes(dt1, dt2).mapEndpoints(endpoint => endpoint.toUTC())\n   * @example Interval.fromDateTimes(dt1, dt2).mapEndpoints(endpoint => endpoint.plus({ hours: 2 }))\n   */\n  mapEndpoints(mapFn) {\n    return Interval.fromDateTimes(mapFn(this.s), mapFn(this.e));\n  }\n}\n"
  },
  {
    "path": "src/luxon.js",
    "content": "import DateTime from \"./datetime.js\";\nimport Duration from \"./duration.js\";\nimport Interval from \"./interval.js\";\nimport Info from \"./info.js\";\nimport Zone from \"./zone.js\";\nimport FixedOffsetZone from \"./zones/fixedOffsetZone.js\";\nimport IANAZone from \"./zones/IANAZone.js\";\nimport InvalidZone from \"./zones/invalidZone.js\";\nimport SystemZone from \"./zones/systemZone.js\";\nimport Settings from \"./settings.js\";\n\nconst VERSION = \"3.7.2\";\n\nexport {\n  VERSION,\n  DateTime,\n  Duration,\n  Interval,\n  Info,\n  Zone,\n  FixedOffsetZone,\n  IANAZone,\n  InvalidZone,\n  SystemZone,\n  Settings,\n};\n"
  },
  {
    "path": "src/package.json",
    "content": "{\n  \"type\": \"module\",\n  \"version\": \"3.7.2\"\n}\n"
  },
  {
    "path": "src/settings.js",
    "content": "import SystemZone from \"./zones/systemZone.js\";\nimport IANAZone from \"./zones/IANAZone.js\";\nimport Locale from \"./impl/locale.js\";\nimport DateTime from \"./datetime.js\";\n\nimport { normalizeZone } from \"./impl/zoneUtil.js\";\nimport { validateWeekSettings } from \"./impl/util.js\";\nimport { resetDigitRegexCache } from \"./impl/digits.js\";\n\nlet now = () => Date.now(),\n  defaultZone = \"system\",\n  defaultLocale = null,\n  defaultNumberingSystem = null,\n  defaultOutputCalendar = null,\n  twoDigitCutoffYear = 60,\n  throwOnInvalid,\n  defaultWeekSettings = null;\n\n/**\n * 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.\n */\nexport default class Settings {\n  /**\n   * Get the callback for returning the current timestamp.\n   * @type {function}\n   */\n  static get now() {\n    return now;\n  }\n\n  /**\n   * Set the callback for returning the current timestamp.\n   * The function should return a number, which will be interpreted as an Epoch millisecond count\n   * @type {function}\n   * @example Settings.now = () => Date.now() + 3000 // pretend it is 3 seconds in the future\n   * @example Settings.now = () => 0 // always pretend it's Jan 1, 1970 at midnight in UTC time\n   */\n  static set now(n) {\n    now = n;\n  }\n\n  /**\n   * Set the default time zone to create DateTimes in. Does not affect existing instances.\n   * Use the value \"system\" to reset this value to the system's time zone.\n   * @type {string}\n   */\n  static set defaultZone(zone) {\n    defaultZone = zone;\n  }\n\n  /**\n   * Get the default time zone object currently used to create DateTimes. Does not affect existing instances.\n   * The default value is the system's time zone (the one set on the machine that runs this code).\n   * @type {Zone}\n   */\n  static get defaultZone() {\n    return normalizeZone(defaultZone, SystemZone.instance);\n  }\n\n  /**\n   * Get the default locale to create DateTimes with. Does not affect existing instances.\n   * @type {string}\n   */\n  static get defaultLocale() {\n    return defaultLocale;\n  }\n\n  /**\n   * Set the default locale to create DateTimes with. Does not affect existing instances.\n   * @type {string}\n   */\n  static set defaultLocale(locale) {\n    defaultLocale = locale;\n  }\n\n  /**\n   * Get the default numbering system to create DateTimes with. Does not affect existing instances.\n   * @type {string}\n   */\n  static get defaultNumberingSystem() {\n    return defaultNumberingSystem;\n  }\n\n  /**\n   * Set the default numbering system to create DateTimes with. Does not affect existing instances.\n   * @type {string}\n   */\n  static set defaultNumberingSystem(numberingSystem) {\n    defaultNumberingSystem = numberingSystem;\n  }\n\n  /**\n   * Get the default output calendar to create DateTimes with. Does not affect existing instances.\n   * @type {string}\n   */\n  static get defaultOutputCalendar() {\n    return defaultOutputCalendar;\n  }\n\n  /**\n   * Set the default output calendar to create DateTimes with. Does not affect existing instances.\n   * @type {string}\n   */\n  static set defaultOutputCalendar(outputCalendar) {\n    defaultOutputCalendar = outputCalendar;\n  }\n\n  /**\n   * @typedef {Object} WeekSettings\n   * @property {number} firstDay\n   * @property {number} minimalDays\n   * @property {number[]} weekend\n   */\n\n  /**\n   * @return {WeekSettings|null}\n   */\n  static get defaultWeekSettings() {\n    return defaultWeekSettings;\n  }\n\n  /**\n   * Allows overriding the default locale week settings, i.e. the start of the week, the weekend and\n   * how many days are required in the first week of a year.\n   * Does not affect existing instances.\n   *\n   * @param {WeekSettings|null} weekSettings\n   */\n  static set defaultWeekSettings(weekSettings) {\n    defaultWeekSettings = validateWeekSettings(weekSettings);\n  }\n\n  /**\n   * Get the cutoff year for whether a 2-digit year string is interpreted in the current or previous century. Numbers higher than the cutoff will be considered to mean 19xx and numbers lower or equal to the cutoff will be considered 20xx.\n   * @type {number}\n   */\n  static get twoDigitCutoffYear() {\n    return twoDigitCutoffYear;\n  }\n\n  /**\n   * Set the cutoff year for whether a 2-digit year string is interpreted in the current or previous century. Numbers higher than the cutoff will be considered to mean 19xx and numbers lower or equal to the cutoff will be considered 20xx.\n   * @type {number}\n   * @example Settings.twoDigitCutoffYear = 0 // all 'yy' are interpreted as 20th century\n   * @example Settings.twoDigitCutoffYear = 99 // all 'yy' are interpreted as 21st century\n   * @example Settings.twoDigitCutoffYear = 50 // '49' -> 2049; '50' -> 1950\n   * @example Settings.twoDigitCutoffYear = 1950 // interpreted as 50\n   * @example Settings.twoDigitCutoffYear = 2050 // ALSO interpreted as 50\n   */\n  static set twoDigitCutoffYear(cutoffYear) {\n    twoDigitCutoffYear = cutoffYear % 100;\n  }\n\n  /**\n   * Get whether Luxon will throw when it encounters invalid DateTimes, Durations, or Intervals\n   * @type {boolean}\n   */\n  static get throwOnInvalid() {\n    return throwOnInvalid;\n  }\n\n  /**\n   * Set whether Luxon will throw when it encounters invalid DateTimes, Durations, or Intervals\n   * @type {boolean}\n   */\n  static set throwOnInvalid(t) {\n    throwOnInvalid = t;\n  }\n\n  /**\n   * Reset Luxon's global caches. Should only be necessary in testing scenarios.\n   * @return {void}\n   */\n  static resetCaches() {\n    Locale.resetCache();\n    IANAZone.resetCache();\n    DateTime.resetCache();\n    resetDigitRegexCache();\n  }\n}\n"
  },
  {
    "path": "src/zone.js",
    "content": "import { ZoneIsAbstractError } from \"./errors.js\";\n\n/**\n * @interface\n */\nexport default class Zone {\n  /**\n   * The type of zone\n   * @abstract\n   * @type {string}\n   */\n  get type() {\n    throw new ZoneIsAbstractError();\n  }\n\n  /**\n   * The name of this zone.\n   * @abstract\n   * @type {string}\n   */\n  get name() {\n    throw new ZoneIsAbstractError();\n  }\n\n  /**\n   * The IANA name of this zone.\n   * Defaults to `name` if not overwritten by a subclass.\n   * @abstract\n   * @type {string}\n   */\n  get ianaName() {\n    return this.name;\n  }\n\n  /**\n   * Returns whether the offset is known to be fixed for the whole year.\n   * @abstract\n   * @type {boolean}\n   */\n  get isUniversal() {\n    throw new ZoneIsAbstractError();\n  }\n\n  /**\n   * Returns the offset's common name (such as EST) at the specified timestamp\n   * @abstract\n   * @param {number} ts - Epoch milliseconds for which to get the name\n   * @param {Object} opts - Options to affect the format\n   * @param {string} opts.format - What style of offset to return. Accepts 'long' or 'short'.\n   * @param {string} opts.locale - What locale to return the offset name in.\n   * @return {string}\n   */\n  offsetName(ts, opts) {\n    throw new ZoneIsAbstractError();\n  }\n\n  /**\n   * Returns the offset's value as a string\n   * @abstract\n   * @param {number} ts - Epoch milliseconds for which to get the offset\n   * @param {string} format - What style of offset to return.\n   *                          Accepts 'narrow', 'short', or 'techie'. Returning '+6', '+06:00', or '+0600' respectively\n   * @return {string}\n   */\n  formatOffset(ts, format) {\n    throw new ZoneIsAbstractError();\n  }\n\n  /**\n   * Return the offset in minutes for this zone at the specified timestamp.\n   * @abstract\n   * @param {number} ts - Epoch milliseconds for which to compute the offset\n   * @return {number}\n   */\n  offset(ts) {\n    throw new ZoneIsAbstractError();\n  }\n\n  /**\n   * Return whether this Zone is equal to another zone\n   * @abstract\n   * @param {Zone} otherZone - the zone to compare\n   * @return {boolean}\n   */\n  equals(otherZone) {\n    throw new ZoneIsAbstractError();\n  }\n\n  /**\n   * Return whether this Zone is valid.\n   * @abstract\n   * @type {boolean}\n   */\n  get isValid() {\n    throw new ZoneIsAbstractError();\n  }\n}\n"
  },
  {
    "path": "src/zones/IANAZone.js",
    "content": "import { formatOffset, parseZoneInfo, isUndefined, objToLocalTS } from \"../impl/util.js\";\nimport Zone from \"../zone.js\";\n\nconst dtfCache = new Map();\nfunction makeDTF(zoneName) {\n  let dtf = dtfCache.get(zoneName);\n  if (dtf === undefined) {\n    dtf = new Intl.DateTimeFormat(\"en-US\", {\n      hour12: false,\n      timeZone: zoneName,\n      year: \"numeric\",\n      month: \"2-digit\",\n      day: \"2-digit\",\n      hour: \"2-digit\",\n      minute: \"2-digit\",\n      second: \"2-digit\",\n      era: \"short\",\n    });\n    dtfCache.set(zoneName, dtf);\n  }\n  return dtf;\n}\n\nconst typeToPos = {\n  year: 0,\n  month: 1,\n  day: 2,\n  era: 3,\n  hour: 4,\n  minute: 5,\n  second: 6,\n};\n\nfunction hackyOffset(dtf, date) {\n  const formatted = dtf.format(date).replace(/\\u200E/g, \"\"),\n    parsed = /(\\d+)\\/(\\d+)\\/(\\d+) (AD|BC),? (\\d+):(\\d+):(\\d+)/.exec(formatted),\n    [, fMonth, fDay, fYear, fadOrBc, fHour, fMinute, fSecond] = parsed;\n  return [fYear, fMonth, fDay, fadOrBc, fHour, fMinute, fSecond];\n}\n\nfunction partsOffset(dtf, date) {\n  const formatted = dtf.formatToParts(date);\n  const filled = [];\n  for (let i = 0; i < formatted.length; i++) {\n    const { type, value } = formatted[i];\n    const pos = typeToPos[type];\n\n    if (type === \"era\") {\n      filled[pos] = value;\n    } else if (!isUndefined(pos)) {\n      filled[pos] = parseInt(value, 10);\n    }\n  }\n  return filled;\n}\n\nconst ianaZoneCache = new Map();\n/**\n * A zone identified by an IANA identifier, like America/New_York\n * @implements {Zone}\n */\nexport default class IANAZone extends Zone {\n  /**\n   * @param {string} name - Zone name\n   * @return {IANAZone}\n   */\n  static create(name) {\n    let zone = ianaZoneCache.get(name);\n    if (zone === undefined) {\n      ianaZoneCache.set(name, (zone = new IANAZone(name)));\n    }\n    return zone;\n  }\n\n  /**\n   * Reset local caches. Should only be necessary in testing scenarios.\n   * @return {void}\n   */\n  static resetCache() {\n    ianaZoneCache.clear();\n    dtfCache.clear();\n  }\n\n  /**\n   * 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.\n   * @param {string} s - The string to check validity on\n   * @example IANAZone.isValidSpecifier(\"America/New_York\") //=> true\n   * @example IANAZone.isValidSpecifier(\"Sport~~blorp\") //=> false\n   * @deprecated For backward compatibility, this forwards to isValidZone, better use `isValidZone()` directly instead.\n   * @return {boolean}\n   */\n  static isValidSpecifier(s) {\n    return this.isValidZone(s);\n  }\n\n  /**\n   * Returns whether the provided string identifies a real zone\n   * @param {string} zone - The string to check\n   * @example IANAZone.isValidZone(\"America/New_York\") //=> true\n   * @example IANAZone.isValidZone(\"Fantasia/Castle\") //=> false\n   * @example IANAZone.isValidZone(\"Sport~~blorp\") //=> false\n   * @return {boolean}\n   */\n  static isValidZone(zone) {\n    return IANAZone.normalizeZone(zone) != null;\n  }\n\n  /**\n   * Normalize the name of the provided IANA zone or return null\n   * if it is not a valid IANA zone.\n   * @param {string} zone - The string to normalize\n   * @example IANAZone.normalizeZone(\"America/New_York\") //=> \"America/New_York\"\n   * @example IANAZone.normalizeZone(\"america/NEw_York\") //=> \"America/New_York\"\n   * @example IANAZone.normalizeZone(\"EST5EDT\") //=> \"America/New_York\"\n   * @example IANAZone.normalizeZone(\"Fantasia/Castle\") //=> null\n   * @example IANAZone.normalizeZone(\"Sport~~blorp\") //=> null\n   * @return {string|null}\n   */\n  static normalizeZone(zone) {\n    if (!zone) {\n      return null;\n    }\n    try {\n      return new Intl.DateTimeFormat(\"en-US\", { timeZone: zone }).resolvedOptions().timeZone;\n    } catch (e) {\n      return null;\n    }\n  }\n\n  constructor(name) {\n    super();\n    const normalizedName = IANAZone.normalizeZone(name);\n    /** @private **/\n    this.valid = normalizedName != null;\n    // For backwards compatibility we only normalize in casing, otherwise would also normalize something like\n    // EST5EDT to America/New_York.\n    /** @private **/\n    this.zoneName =\n      normalizedName && normalizedName.toLowerCase() === name.toLowerCase() ? normalizedName : name;\n  }\n\n  /**\n   * The type of zone. `iana` for all instances of `IANAZone`.\n   * @override\n   * @type {string}\n   */\n  get type() {\n    return \"iana\";\n  }\n\n  /**\n   * The name of this zone (i.e. the IANA zone name).\n   * @override\n   * @type {string}\n   */\n  get name() {\n    return this.zoneName;\n  }\n\n  /**\n   * Returns whether the offset is known to be fixed for the whole year:\n   * Always returns false for all IANA zones.\n   * @override\n   * @type {boolean}\n   */\n  get isUniversal() {\n    return false;\n  }\n\n  /**\n   * Returns the offset's common name (such as EST) at the specified timestamp\n   * @override\n   * @param {number} ts - Epoch milliseconds for which to get the name\n   * @param {Object} opts - Options to affect the format\n   * @param {string} opts.format - What style of offset to return. Accepts 'long' or 'short'.\n   * @param {string} opts.locale - What locale to return the offset name in.\n   * @return {string}\n   */\n  offsetName(ts, { format, locale }) {\n    return parseZoneInfo(ts, format, locale, this.name);\n  }\n\n  /**\n   * Returns the offset's value as a string\n   * @override\n   * @param {number} ts - Epoch milliseconds for which to get the offset\n   * @param {string} format - What style of offset to return.\n   *                          Accepts 'narrow', 'short', or 'techie'. Returning '+6', '+06:00', or '+0600' respectively\n   * @return {string}\n   */\n  formatOffset(ts, format) {\n    return formatOffset(this.offset(ts), format);\n  }\n\n  /**\n   * Return the offset in minutes for this zone at the specified timestamp.\n   * @override\n   * @param {number} ts - Epoch milliseconds for which to compute the offset\n   * @return {number}\n   */\n  offset(ts) {\n    if (!this.valid) return NaN;\n    const date = new Date(ts);\n\n    if (isNaN(date)) return NaN;\n\n    const dtf = makeDTF(this.name);\n    let [year, month, day, adOrBc, hour, minute, second] = dtf.formatToParts\n      ? partsOffset(dtf, date)\n      : hackyOffset(dtf, date);\n\n    if (adOrBc === \"BC\") {\n      year = -Math.abs(year) + 1;\n    }\n\n    // because we're using hour12 and https://bugs.chromium.org/p/chromium/issues/detail?id=1025564&can=2&q=%2224%3A00%22%20datetimeformat\n    const adjustedHour = hour === 24 ? 0 : hour;\n\n    const asUTC = objToLocalTS({\n      year,\n      month,\n      day,\n      hour: adjustedHour,\n      minute,\n      second,\n      millisecond: 0,\n    });\n\n    let asTS = +date;\n    const over = asTS % 1000;\n    asTS -= over >= 0 ? over : 1000 + over;\n    return (asUTC - asTS) / (60 * 1000);\n  }\n\n  /**\n   * Return whether this Zone is equal to another zone\n   * @override\n   * @param {Zone} otherZone - the zone to compare\n   * @return {boolean}\n   */\n  equals(otherZone) {\n    return otherZone.type === \"iana\" && otherZone.name === this.name;\n  }\n\n  /**\n   * Return whether this Zone is valid.\n   * @override\n   * @type {boolean}\n   */\n  get isValid() {\n    return this.valid;\n  }\n}\n"
  },
  {
    "path": "src/zones/fixedOffsetZone.js",
    "content": "import { formatOffset, signedOffset } from \"../impl/util.js\";\nimport Zone from \"../zone.js\";\n\nlet singleton = null;\n\n/**\n * A zone with a fixed offset (meaning no DST)\n * @implements {Zone}\n */\nexport default class FixedOffsetZone extends Zone {\n  /**\n   * Get a singleton instance of UTC\n   * @return {FixedOffsetZone}\n   */\n  static get utcInstance() {\n    if (singleton === null) {\n      singleton = new FixedOffsetZone(0);\n    }\n    return singleton;\n  }\n\n  /**\n   * Get an instance with a specified offset\n   * @param {number} offset - The offset in minutes\n   * @return {FixedOffsetZone}\n   */\n  static instance(offset) {\n    return offset === 0 ? FixedOffsetZone.utcInstance : new FixedOffsetZone(offset);\n  }\n\n  /**\n   * Get an instance of FixedOffsetZone from a UTC offset string, like \"UTC+6\"\n   * @param {string} s - The offset string to parse\n   * @example FixedOffsetZone.parseSpecifier(\"UTC+6\")\n   * @example FixedOffsetZone.parseSpecifier(\"UTC+06\")\n   * @example FixedOffsetZone.parseSpecifier(\"UTC-6:00\")\n   * @return {FixedOffsetZone}\n   */\n  static parseSpecifier(s) {\n    if (s) {\n      const r = s.match(/^utc(?:([+-]\\d{1,2})(?::(\\d{2}))?)?$/i);\n      if (r) {\n        return new FixedOffsetZone(signedOffset(r[1], r[2]));\n      }\n    }\n    return null;\n  }\n\n  constructor(offset) {\n    super();\n    /** @private **/\n    this.fixed = offset;\n  }\n\n  /**\n   * The type of zone. `fixed` for all instances of `FixedOffsetZone`.\n   * @override\n   * @type {string}\n   */\n  get type() {\n    return \"fixed\";\n  }\n\n  /**\n   * The name of this zone.\n   * All fixed zones' names always start with \"UTC\" (plus optional offset)\n   * @override\n   * @type {string}\n   */\n  get name() {\n    return this.fixed === 0 ? \"UTC\" : `UTC${formatOffset(this.fixed, \"narrow\")}`;\n  }\n\n  /**\n   * The IANA name of this zone, i.e. `Etc/UTC` or `Etc/GMT+/-nn`\n   *\n   * @override\n   * @type {string}\n   */\n  get ianaName() {\n    if (this.fixed === 0) {\n      return \"Etc/UTC\";\n    } else {\n      return `Etc/GMT${formatOffset(-this.fixed, \"narrow\")}`;\n    }\n  }\n\n  /**\n   * Returns the offset's common name at the specified timestamp.\n   *\n   * For fixed offset zones this equals to the zone name.\n   * @override\n   */\n  offsetName() {\n    return this.name;\n  }\n\n  /**\n   * Returns the offset's value as a string\n   * @override\n   * @param {number} ts - Epoch milliseconds for which to get the offset\n   * @param {string} format - What style of offset to return.\n   *                          Accepts 'narrow', 'short', or 'techie'. Returning '+6', '+06:00', or '+0600' respectively\n   * @return {string}\n   */\n  formatOffset(ts, format) {\n    return formatOffset(this.fixed, format);\n  }\n\n  /**\n   * Returns whether the offset is known to be fixed for the whole year:\n   * Always returns true for all fixed offset zones.\n   * @override\n   * @type {boolean}\n   */\n  get isUniversal() {\n    return true;\n  }\n\n  /**\n   * Return the offset in minutes for this zone at the specified timestamp.\n   *\n   * For fixed offset zones, this is constant and does not depend on a timestamp.\n   * @override\n   * @return {number}\n   */\n  offset() {\n    return this.fixed;\n  }\n\n  /**\n   * Return whether this Zone is equal to another zone (i.e. also fixed and same offset)\n   * @override\n   * @param {Zone} otherZone - the zone to compare\n   * @return {boolean}\n   */\n  equals(otherZone) {\n    return otherZone.type === \"fixed\" && otherZone.fixed === this.fixed;\n  }\n\n  /**\n   * Return whether this Zone is valid:\n   * All fixed offset zones are valid.\n   * @override\n   * @type {boolean}\n   */\n  get isValid() {\n    return true;\n  }\n}\n"
  },
  {
    "path": "src/zones/invalidZone.js",
    "content": "import Zone from \"../zone.js\";\n\n/**\n * A zone that failed to parse. You should never need to instantiate this.\n * @implements {Zone}\n */\nexport default class InvalidZone extends Zone {\n  constructor(zoneName) {\n    super();\n    /**  @private */\n    this.zoneName = zoneName;\n  }\n\n  /** @override **/\n  get type() {\n    return \"invalid\";\n  }\n\n  /** @override **/\n  get name() {\n    return this.zoneName;\n  }\n\n  /** @override **/\n  get isUniversal() {\n    return false;\n  }\n\n  /** @override **/\n  offsetName() {\n    return null;\n  }\n\n  /** @override **/\n  formatOffset() {\n    return \"\";\n  }\n\n  /** @override **/\n  offset() {\n    return NaN;\n  }\n\n  /** @override **/\n  equals() {\n    return false;\n  }\n\n  /** @override **/\n  get isValid() {\n    return false;\n  }\n}\n"
  },
  {
    "path": "src/zones/systemZone.js",
    "content": "import { formatOffset, parseZoneInfo } from \"../impl/util.js\";\nimport Zone from \"../zone.js\";\n\nlet singleton = null;\n\n/**\n * Represents the local zone for this JavaScript environment.\n * @implements {Zone}\n */\nexport default class SystemZone extends Zone {\n  /**\n   * Get a singleton instance of the local zone\n   * @return {SystemZone}\n   */\n  static get instance() {\n    if (singleton === null) {\n      singleton = new SystemZone();\n    }\n    return singleton;\n  }\n\n  /** @override **/\n  get type() {\n    return \"system\";\n  }\n\n  /** @override **/\n  get name() {\n    return new Intl.DateTimeFormat().resolvedOptions().timeZone;\n  }\n\n  /** @override **/\n  get isUniversal() {\n    return false;\n  }\n\n  /** @override **/\n  offsetName(ts, { format, locale }) {\n    return parseZoneInfo(ts, format, locale);\n  }\n\n  /** @override **/\n  formatOffset(ts, format) {\n    return formatOffset(this.offset(ts), format);\n  }\n\n  /** @override **/\n  offset(ts) {\n    return -new Date(ts).getTimezoneOffset();\n  }\n\n  /** @override **/\n  equals(otherZone) {\n    return otherZone.type === \"system\";\n  }\n\n  /** @override **/\n  get isValid() {\n    return true;\n  }\n}\n"
  },
  {
    "path": "tasks/build.js",
    "content": "const rollup = require(\"rollup\"),\n  { babel } = require(\"@rollup/plugin-babel\"),\n  { terser } = require(\"rollup-plugin-terser\"),\n  { nodeResolve } = require(\"@rollup/plugin-node-resolve\"),\n  rollupCommonJS = require(\"@rollup/plugin-commonjs\"),\n  UglifyJS = require(\"uglify-js\"),\n  fs = require(\"fs\");\n\n// For some reason, the minifier is currently producing total giberrish, at least for the global build.\n// I've disabled it for now, and will simply uglify externally.\nconst TRUST_MINIFY = false;\n\nfunction rollupInputOpts(opts) {\n  const presetOpts = {\n    modules: false,\n    loose: true,\n  };\n\n  if (opts.target) {\n    presetOpts.targets = opts.target;\n  }\n\n  const inputOpts = {\n    input: opts.src || \"./src/luxon.js\",\n    onwarn: (warning) => {\n      // I don't care about these for now\n      if (warning.code !== \"CIRCULAR_DEPENDENCY\") {\n        console.warn(`(!) ${warning.message}`);\n      }\n    },\n\n    plugins: [\n      nodeResolve(),\n      rollupCommonJS({\n        include: \"node_modules/**\",\n      }),\n    ],\n  };\n\n  if (opts.compile || typeof opts.compile === \"undefined\") {\n    inputOpts.plugins.push(\n      babel({\n        babelrc: false,\n        presets: [[\"@babel/preset-env\", presetOpts]],\n        babelHelpers: \"bundled\",\n      })\n    );\n  }\n\n  if (opts.minify && TRUST_MINIFY) {\n    inputOpts.plugins.push(\n      terser({\n        comments: false,\n        mangle: {\n          topLevel: !opts.global,\n        },\n      })\n    );\n  }\n\n  return inputOpts;\n}\n\nfunction rollupOutputOpts(dest, opts) {\n  const outputOpts = {\n    file: `build/${dest}/${opts.filename || \"luxon.js\"}`,\n    format: opts.format,\n    sourcemap: true,\n  };\n\n  if (opts.name) {\n    outputOpts.name = opts.name;\n  }\n\n  return outputOpts;\n}\n\nasync function babelAndRollup(dest, opts) {\n  const inputOpts = rollupInputOpts(opts),\n    outputOpts = rollupOutputOpts(dest, opts),\n    bundle = await rollup.rollup(inputOpts);\n  await bundle.write(outputOpts);\n}\n\nasync function buildLibrary(dest, opts) {\n  console.log(\"Building\", dest);\n  const promises = [babelAndRollup(dest, opts)];\n\n  const filename = opts.filename || \"luxon.js\";\n  const minFilename = filename.replace(/\\.(m?js)$/, \".min.$1\");\n\n  if (opts.minify && TRUST_MINIFY) {\n    promises.push(\n      babelAndRollup(dest, {\n        ...opts,\n        minify: true,\n        filename: minFilename,\n      })\n    );\n  }\n\n  await Promise.all(promises);\n\n  if (opts.minify && !TRUST_MINIFY) {\n    const code = fs.readFileSync(`build/${dest}/${filename}`, \"utf8\"),\n      ugly = UglifyJS.minify(code, {\n        toplevel: !opts.global,\n        output: {\n          comments: false,\n        },\n        sourceMap: {\n          filename: `build/${dest}/${filename}`,\n        },\n      });\n    if (ugly.error) {\n      console.error(\"Error uglifying\", ugly.error);\n    } else {\n      fs.writeFileSync(`build/${dest}/${minFilename}`, ugly.code);\n      fs.writeFileSync(`build/${dest}/${minFilename}.map`, ugly.map);\n    }\n  }\n  console.log(\"Built\", dest);\n}\n\nconst browsersOld = \"last 2 major versions\";\n\nasync function global() {\n  await buildLibrary(\"global\", {\n    format: \"iife\",\n    global: true,\n    name: \"luxon\",\n    target: browsersOld,\n    minify: true,\n  });\n}\n\nasync function amd() {\n  await buildLibrary(\"amd\", {\n    format: \"amd\",\n    name: \"luxon\",\n    target: browsersOld,\n    minify: true,\n  });\n}\n\nasync function node() {\n  await buildLibrary(\"node\", { format: \"cjs\", target: \"node 12\" });\n}\n\nasync function cjsBrowser() {\n  await buildLibrary(\"cjs-browser\", { format: \"cjs\", target: browsersOld });\n}\n\nasync function es6() {\n  await buildLibrary(\"es6\", {\n    format: \"es\",\n    minify: true,\n    compile: false,\n    filename: \"luxon.mjs\",\n  });\n}\n\nasync function globalEs6() {\n  await buildLibrary(\"global-es6\", {\n    format: \"iife\",\n    name: \"luxon\",\n    compile: false,\n    global: true,\n  });\n}\n\nasync function buildAll() {\n  await Promise.all([node(), cjsBrowser(), es6(), amd(), global(), globalEs6()]);\n}\n\nmodule.exports = {\n  buildAll,\n  buildNode: node,\n  buildGlobal: global,\n};\n"
  },
  {
    "path": "tasks/buildAll.js",
    "content": "const { buildAll } = require(\"./build\");\nbuildAll().catch(console.error);\n"
  },
  {
    "path": "tasks/buildGlobal.js",
    "content": "const { buildGlobal } = require(\"./build\");\nbuildGlobal().catch(console.error);\n"
  },
  {
    "path": "tasks/buildNode.js",
    "content": "const { buildNode } = require(\"./build\");\nbuildNode().catch(console.error);\n"
  },
  {
    "path": "test/datetime/create.test.js",
    "content": "/* global test expect */\n\nimport { DateTime, Settings } from \"../../src/luxon\";\nimport Helpers, { supportsMinDaysInFirstWeek } from \"../helpers\";\n\nconst withDefaultLocale = Helpers.withDefaultLocale,\n  withDefaultNumberingSystem = Helpers.setUnset(\"defaultNumberingSystem\"),\n  withDefaultOutputCalendar = Helpers.setUnset(\"defaultOutputCalendar\"),\n  withthrowOnInvalid = Helpers.setUnset(\"throwOnInvalid\"),\n  withDefaultZone = Helpers.withDefaultZone,\n  withDefaultWeekSettings = Helpers.setUnset(\"defaultWeekSettings\");\n\n//------\n// .now()\n//------\ntest(\"DateTime.now has today's date\", () => {\n  const date = new Date(),\n    now = DateTime.now();\n  expect(now.toJSDate().getDate()).toBe(date.getDate());\n  // The two instants should be a few milliseconds apart\n  expect(Math.abs(now.valueOf() - date.valueOf()) < 1000).toBe(true);\n});\n\ntest(\"DateTime.now accepts the default locale\", () => {\n  withDefaultLocale(\"fr\", () => expect(DateTime.now().locale).toBe(\"fr\"));\n});\n\ntest(\"DateTime.now accepts the default numbering system\", () => {\n  withDefaultNumberingSystem(\"beng\", () => expect(DateTime.now().numberingSystem).toBe(\"beng\"));\n});\n\ntest(\"DateTime.now accepts the default output calendar\", () => {\n  withDefaultOutputCalendar(\"hebrew\", () => expect(DateTime.now().outputCalendar).toBe(\"hebrew\"));\n});\n\ntest(\"DateTime.now accepts the default time zone\", () => {\n  withDefaultZone(\"Europe/Paris\", () => expect(DateTime.now().zoneName).toBe(\"Europe/Paris\"));\n});\n\n//------\n// .local()\n//------\ntest(\"DateTime.local() has today's date\", () => {\n  const date = new Date(),\n    now = DateTime.local();\n  expect(now.toJSDate().getDate()).toBe(date.getDate());\n  // The two instants should be a few milliseconds apart\n  expect(Math.abs(now.valueOf() - date.valueOf()) < 1000).toBe(true);\n});\n\ntest(\"DateTime.local(2017) is the beginning of the year\", () => {\n  const dt = DateTime.local(2017);\n  expect(dt.year).toBe(2017);\n  expect(dt.month).toBe(1);\n  expect(dt.day).toBe(1);\n  expect(dt.hour).toBe(0);\n  expect(dt.minute).toBe(0);\n  expect(dt.second).toBe(0);\n  expect(dt.millisecond).toBe(0);\n});\n\ntest(\"DateTime.local(2017, 6) is the beginning of the month\", () => {\n  const dt = DateTime.local(2017, 6);\n  expect(dt.year).toBe(2017);\n  expect(dt.month).toBe(6);\n  expect(dt.day).toBe(1);\n  expect(dt.hour).toBe(0);\n  expect(dt.minute).toBe(0);\n  expect(dt.second).toBe(0);\n  expect(dt.millisecond).toBe(0);\n});\n\ntest(\"DateTime.local(2017, 6, 12) is the beginning of 6/12\", () => {\n  const dt = DateTime.local(2017, 6, 12);\n  expect(dt.year).toBe(2017);\n  expect(dt.month).toBe(6);\n  expect(dt.day).toBe(12);\n  expect(dt.hour).toBe(0);\n  expect(dt.minute).toBe(0);\n  expect(dt.second).toBe(0);\n  expect(dt.millisecond).toBe(0);\n});\n\ntest(\"DateTime.local(2017, 6, 12, 5) is the beginning of the hour\", () => {\n  const dt = DateTime.local(2017, 6, 12, 5);\n  expect(dt.year).toBe(2017);\n  expect(dt.month).toBe(6);\n  expect(dt.day).toBe(12);\n  expect(dt.hour).toBe(5);\n  expect(dt.minute).toBe(0);\n  expect(dt.second).toBe(0);\n  expect(dt.millisecond).toBe(0);\n});\n\ntest(\"DateTime.local(2017, 6, 12, 5, 25) is the beginning of the minute\", () => {\n  const dt = DateTime.local(2017, 6, 12, 5, 25);\n  expect(dt.year).toBe(2017);\n  expect(dt.month).toBe(6);\n  expect(dt.day).toBe(12);\n  expect(dt.hour).toBe(5);\n  expect(dt.minute).toBe(25);\n  expect(dt.second).toBe(0);\n  expect(dt.millisecond).toBe(0);\n});\n\ntest(\"DateTime.local(2017, 6, 12, 5, 25, 16) is the beginning of the second\", () => {\n  const dt = DateTime.local(2017, 6, 12, 5, 25, 16);\n  expect(dt.year).toBe(2017);\n  expect(dt.month).toBe(6);\n  expect(dt.day).toBe(12);\n  expect(dt.hour).toBe(5);\n  expect(dt.minute).toBe(25);\n  expect(dt.second).toBe(16);\n  expect(dt.millisecond).toBe(0);\n});\n\ntest(\"DateTime.local(2017, 6, 12, 5, 25, 16, 255) is right down to the millisecond\", () => {\n  const dt = DateTime.local(2017, 6, 12, 5, 25, 16, 255);\n  expect(dt.year).toBe(2017);\n  expect(dt.month).toBe(6);\n  expect(dt.day).toBe(12);\n  expect(dt.hour).toBe(5);\n  expect(dt.minute).toBe(25);\n  expect(dt.second).toBe(16);\n  expect(dt.millisecond).toBe(255);\n});\n\ntest(\"DateTime.local accepts the default locale\", () => {\n  withDefaultLocale(\"fr\", () => expect(DateTime.local().locale).toBe(\"fr\"));\n});\n\ntest(\"DateTime.local accepts the default numbering system\", () => {\n  withDefaultNumberingSystem(\"beng\", () => expect(DateTime.local().numberingSystem).toBe(\"beng\"));\n});\n\ntest(\"DateTime.local accepts the default output calendar\", () => {\n  withDefaultOutputCalendar(\"hebrew\", () => expect(DateTime.local().outputCalendar).toBe(\"hebrew\"));\n});\n\ntest(\"DateTime.local does not accept non-integer values\", () => {\n  const dt = DateTime.local(2017, 6.7, 12);\n  expect(dt.isValid).toBe(false);\n});\n\ntest(\"DateTime.local accepts the default time zone\", () => {\n  withDefaultZone(\"Europe/Paris\", () => expect(DateTime.local().zoneName).toBe(\"Europe/Paris\"));\n});\n\ntest(\"DateTime.local accepts an options hash in any position\", () => {\n  const options = {\n    zone: \"Europe/Paris\",\n    numberingSystem: \"beng\",\n    outputCalendar: \"islamic\",\n    locale: \"fr\",\n  };\n  const args = [\n    DateTime.local(options),\n    DateTime.local(2017, options),\n    DateTime.local(2017, 6, options),\n    DateTime.local(2017, 6, 12, options),\n    DateTime.local(2017, 6, 12, options),\n    DateTime.local(2017, 6, 12, 5, options),\n    DateTime.local(2017, 6, 12, 5, 25, options),\n    DateTime.local(2017, 6, 12, 5, 25, 16, options),\n    DateTime.local(2017, 6, 12, 5, 25, 16, 255, options),\n  ];\n\n  for (const i in args) {\n    const dt = args[i];\n    expect(dt.zoneName).toBe(\"Europe/Paris\");\n    expect(dt.numberingSystem).toBe(\"beng\");\n    expect(dt.outputCalendar).toBe(\"islamic\");\n    expect(dt.locale).toBe(\"fr\");\n  }\n});\n\n//------\n// .utc()\n//-------\ntest(\"DateTime.utc() is in utc\", () => {\n  const now = DateTime.utc();\n  expect(now.offset).toBe(0);\n});\n\ntest(\"DateTime.utc(2017) is the beginning of the year\", () => {\n  const dt = DateTime.utc(2017);\n  expect(dt.year).toBe(2017);\n  expect(dt.month).toBe(1);\n  expect(dt.day).toBe(1);\n  expect(dt.hour).toBe(0);\n  expect(dt.minute).toBe(0);\n  expect(dt.second).toBe(0);\n  expect(dt.millisecond).toBe(0);\n});\n\ntest(\"DateTime.utc(2017, 6) is the beginning of the month\", () => {\n  const dt = DateTime.utc(2017, 6);\n  expect(dt.year).toBe(2017);\n  expect(dt.month).toBe(6);\n  expect(dt.day).toBe(1);\n  expect(dt.hour).toBe(0);\n  expect(dt.minute).toBe(0);\n  expect(dt.second).toBe(0);\n  expect(dt.millisecond).toBe(0);\n});\n\ntest(\"DateTime.utc(2017, 6, 12) is the beginning of 6/12\", () => {\n  const dt = DateTime.utc(2017, 6, 12);\n  expect(dt.year).toBe(2017);\n  expect(dt.month).toBe(6);\n  expect(dt.day).toBe(12);\n  expect(dt.hour).toBe(0);\n  expect(dt.minute).toBe(0);\n  expect(dt.second).toBe(0);\n  expect(dt.millisecond).toBe(0);\n});\n\ntest(\"DateTime.utc(2017, 6, 12, 5) is the beginning of the hour\", () => {\n  const dt = DateTime.utc(2017, 6, 12, 5);\n  expect(dt.year).toBe(2017);\n  expect(dt.month).toBe(6);\n  expect(dt.day).toBe(12);\n  expect(dt.hour).toBe(5);\n  expect(dt.minute).toBe(0);\n  expect(dt.second).toBe(0);\n  expect(dt.millisecond).toBe(0);\n});\n\ntest(\"DateTime.utc(2017, 6, 12, 5, 25) is the beginning of the minute\", () => {\n  const dt = DateTime.utc(2017, 6, 12, 5, 25);\n  expect(dt.year).toBe(2017);\n  expect(dt.month).toBe(6);\n  expect(dt.day).toBe(12);\n  expect(dt.hour).toBe(5);\n  expect(dt.minute).toBe(25);\n  expect(dt.second).toBe(0);\n  expect(dt.millisecond).toBe(0);\n});\n\ntest(\"DateTime.utc(2017, 6, 12, 5, 25, 16) is the beginning of the second\", () => {\n  const dt = DateTime.utc(2017, 6, 12, 5, 25, 16);\n  expect(dt.year).toBe(2017);\n  expect(dt.month).toBe(6);\n  expect(dt.day).toBe(12);\n  expect(dt.hour).toBe(5);\n  expect(dt.minute).toBe(25);\n  expect(dt.second).toBe(16);\n  expect(dt.millisecond).toBe(0);\n});\n\ntest(\"DateTime.utc(2017, 6, 12, 5, 25, 16, 255) is right down to the millisecond\", () => {\n  const dt = DateTime.utc(2017, 6, 12, 5, 25, 16, 255);\n  expect(dt.year).toBe(2017);\n  expect(dt.month).toBe(6);\n  expect(dt.day).toBe(12);\n  expect(dt.hour).toBe(5);\n  expect(dt.minute).toBe(25);\n  expect(dt.second).toBe(16);\n  expect(dt.millisecond).toBe(255);\n});\n\ntest(\"DateTime.utc accepts the default locale\", () => {\n  withDefaultLocale(\"fr\", () => expect(DateTime.utc().locale).toBe(\"fr\"));\n});\n\ntest(\"DateTime.utc accepts an options hash in any position\", () => {\n  const options = {\n    numberingSystem: \"beng\",\n    outputCalendar: \"islamic\",\n    locale: \"fr\",\n  };\n  const args = [\n    DateTime.utc(options),\n    DateTime.utc(2017, options),\n    DateTime.utc(2017, 6, options),\n    DateTime.utc(2017, 6, 12, options),\n    DateTime.utc(2017, 6, 12, options),\n    DateTime.utc(2017, 6, 12, 5, options),\n    DateTime.utc(2017, 6, 12, 5, 25, options),\n    DateTime.utc(2017, 6, 12, 5, 25, 16, options),\n    DateTime.utc(2017, 6, 12, 5, 25, 16, 255, options),\n  ];\n\n  for (const i in args) {\n    const dt = args[i];\n    expect(dt.zoneName).toBe(\"UTC\");\n    expect(dt.numberingSystem).toBe(\"beng\");\n    expect(dt.outputCalendar).toBe(\"islamic\");\n    expect(dt.locale).toBe(\"fr\");\n  }\n});\n\n//------\n// .fromJSDate()\n//-------\ntest(\"DateTime.fromJSDate(date) clones the date\", () => {\n  const date = new Date(1982, 4, 25),\n    dateTime = DateTime.fromJSDate(date),\n    oldValue = dateTime.valueOf();\n\n  date.setDate(14);\n  expect(dateTime.toJSDate().valueOf()).toBe(oldValue);\n});\n\ntest(\"DateTime.fromJSDate(date) accepts a zone option\", () => {\n  const date = new Date(1982, 4, 25),\n    dateTime = DateTime.fromJSDate(date, { zone: \"America/Santiago\" });\n\n  expect(dateTime.toJSDate().valueOf()).toBe(date.valueOf());\n  expect(dateTime.zoneName).toBe(\"America/Santiago\");\n});\n\ntest(\"DateTime.fromJSDate(date) returns invalid for invalid values\", () => {\n  expect(DateTime.fromJSDate(\"\").isValid).toBe(false);\n  expect(DateTime.fromJSDate(new Date(\"\")).isValid).toBe(false);\n  expect(DateTime.fromJSDate(new Date().valueOf()).isValid).toBe(false);\n});\n\ntest(\"DateTime.fromJSDate accepts the default locale\", () => {\n  withDefaultLocale(\"fr\", () => expect(DateTime.fromJSDate(new Date()).locale).toBe(\"fr\"));\n});\n\ntest(\"DateTime.fromJSDate(date) throw errors for invalid values when throwOnInvalid is true\", () => {\n  withthrowOnInvalid(true, () => {\n    expect(() => DateTime.fromJSDate(\"\")).toThrow();\n    expect(() => DateTime.fromJSDate(new Date(\"\"))).toThrow();\n    expect(() => DateTime.fromJSDate(new Date().valueOf())).toThrow();\n    expect(() => DateTime.fromJSDate(new Date(), { zone: \"America/Blorp\" })).toThrow();\n    expect(() => DateTime.fromJSDate(\"2019-04-16T11:32:32Z\")).toThrow();\n  });\n});\n\n//------\n// .fromMillis()\n//-------\ntest(\"DateTime.fromMillis(ms) has a value of ms\", () => {\n  const bigValue = 391147200000;\n  expect(DateTime.fromMillis(bigValue).valueOf()).toBe(bigValue);\n\n  expect(DateTime.fromMillis(0).valueOf()).toBe(0);\n});\n\ntest(\"DateTime.fromMillis(ms) accepts a zone option\", () => {\n  const value = 391147200000,\n    dateTime = DateTime.fromMillis(value, { zone: \"America/Santiago\" });\n\n  expect(dateTime.valueOf()).toBe(value);\n  expect(dateTime.zoneName).toBe(\"America/Santiago\");\n});\n\ntest(\"DateTime.fromMillis accepts the default locale\", () => {\n  withDefaultLocale(\"fr\", () => expect(DateTime.fromMillis(391147200000).locale).toBe(\"fr\"));\n});\n\ntest(\"DateTime.fromMillis(ms) throws InvalidArgumentError for non-numeric input\", () => {\n  expect(() => DateTime.fromMillis(\"slurp\")).toThrow();\n});\n\ntest(\"DateTime.fromMillis(ms) does not accept out-of-bounds numbers\", () => {\n  expect(DateTime.fromMillis(-8.64e15 - 1).isValid).toBe(false);\n  expect(DateTime.fromMillis(8.64e15 + 1).isValid).toBe(false);\n});\n\ntest(\"DateTime.fromMillis(ms) does not accept non-finite numbers\", () => {\n  expect(DateTime.fromMillis(Infinity).isValid).toBe(false);\n  expect(DateTime.fromMillis(-Infinity).isValid).toBe(false);\n  expect(DateTime.fromMillis(NaN).isValid).toBe(false);\n});\n\n//------\n// .fromSeconds()\n//-------\ntest(\"DateTime.fromSeconds(seconds) has a value of 1000 * seconds\", () => {\n  const seconds = 391147200;\n  expect(DateTime.fromSeconds(seconds).valueOf()).toBe(1000 * seconds);\n\n  expect(DateTime.fromSeconds(0).valueOf()).toBe(0);\n});\n\ntest(\"DateTime.fromSeconds(ms) accepts a zone option\", () => {\n  const seconds = 391147200,\n    dateTime = DateTime.fromSeconds(seconds, { zone: \"America/Santiago\" });\n\n  expect(dateTime.valueOf()).toBe(1000 * seconds);\n  expect(dateTime.zoneName).toBe(\"America/Santiago\");\n});\n\ntest(\"DateTime.fromSeconds accepts the default locale\", () => {\n  withDefaultLocale(\"fr\", () => expect(DateTime.fromSeconds(391147200).locale).toBe(\"fr\"));\n});\n\ntest(\"DateTime.fromSeconds(seconds) throws InvalidArgumentError for non-numeric input\", () => {\n  expect(() => DateTime.fromSeconds(\"slurp\")).toThrow();\n});\n\ntest(\"DateTime.fromSeconds(seconds) does not accept out-of-bounds numbers\", () => {\n  expect(DateTime.fromSeconds(-8.64e12 - 1).isValid).toBe(false);\n  expect(DateTime.fromSeconds(8.64e12 + 1).isValid).toBe(false);\n});\n\ntest(\"DateTime.fromSeconds(seconds) does not accept non-finite numbers\", () => {\n  expect(DateTime.fromSeconds(Infinity).isValid).toBe(false);\n  expect(DateTime.fromSeconds(-Infinity).isValid).toBe(false);\n  expect(DateTime.fromSeconds(NaN).isValid).toBe(false);\n});\n\n//------\n// .fromObject()\n//-------\nconst baseObject = {\n  year: 1982,\n  month: 5,\n  day: 25,\n  hour: 9,\n  minute: 23,\n  second: 54,\n  millisecond: 123,\n};\n\ntest(\"DateTime.fromObject() sets all the fields\", () => {\n  const dateTime = DateTime.fromObject(baseObject);\n\n  expect(dateTime.isOffsetFixed).toBe(false);\n  expect(dateTime.year).toBe(1982);\n  expect(dateTime.month).toBe(5);\n  expect(dateTime.day).toBe(25);\n  expect(dateTime.hour).toBe(9);\n  expect(dateTime.minute).toBe(23);\n  expect(dateTime.second).toBe(54);\n  expect(dateTime.millisecond).toBe(123);\n});\n\ntest('DateTime.fromObject() accepts a zone option of \"utc\"', () => {\n  const dateTime = DateTime.fromObject(baseObject, { zone: \"utc\" });\n\n  expect(dateTime.isOffsetFixed).toBe(true);\n  expect(dateTime.year).toBe(1982);\n  expect(dateTime.month).toBe(5);\n  expect(dateTime.day).toBe(25);\n  expect(dateTime.hour).toBe(9);\n  expect(dateTime.minute).toBe(23);\n  expect(dateTime.second).toBe(54);\n  expect(dateTime.millisecond).toBe(123);\n});\n\ntest('DateTime.fromObject() accepts \"utc-8\" as the zone option', () => {\n  const dateTime = DateTime.fromObject(baseObject, { zone: \"utc-8\" });\n\n  expect(dateTime.isOffsetFixed).toBe(true);\n  expect(dateTime.offset).toBe(-8 * 60);\n  expect(dateTime.year).toBe(1982);\n  expect(dateTime.month).toBe(5);\n  expect(dateTime.day).toBe(25);\n  expect(dateTime.hour).toBe(9);\n  expect(dateTime.minute).toBe(23);\n  expect(dateTime.second).toBe(54);\n  expect(dateTime.millisecond).toBe(123);\n});\n\ntest('DateTime.fromObject() accepts \"America/Los_Angeles\" as the zone option', () => {\n  const dateTime = DateTime.fromObject(baseObject, { zone: \"America/Los_Angeles\" });\n\n  expect(dateTime.isOffsetFixed).toBe(false);\n  expect(dateTime.offset).toBe(-7 * 60);\n  expect(dateTime.year).toBe(1982);\n  expect(dateTime.month).toBe(5);\n  expect(dateTime.day).toBe(25);\n  expect(dateTime.hour).toBe(9);\n  expect(dateTime.minute).toBe(23);\n  expect(dateTime.second).toBe(54);\n  expect(dateTime.millisecond).toBe(123);\n});\n\ntest(\"DateTime.fromObject() accepts a Zone as the zone option\", () => {\n  const daylight = DateTime.fromObject(\n    { ...baseObject, month: 5 },\n    { zone: \"America/Los_Angeles\" }\n  );\n  const standard = DateTime.fromObject(\n    { ...baseObject, month: 12 },\n    { zone: \"America/Los_Angeles\" }\n  );\n\n  expect(daylight.isOffsetFixed).toBe(false);\n  expect(daylight.offset).toBe(-7 * 60);\n  expect(daylight.year).toBe(1982);\n  expect(daylight.month).toBe(5);\n  expect(daylight.day).toBe(25);\n  expect(daylight.hour).toBe(9);\n  expect(daylight.minute).toBe(23);\n  expect(daylight.second).toBe(54);\n  expect(daylight.millisecond).toBe(123);\n\n  expect(standard.isOffsetFixed).toBe(false);\n  expect(standard.offset).toBe(-8 * 60);\n  expect(standard.year).toBe(1982);\n  expect(standard.month).toBe(12);\n  expect(standard.day).toBe(25);\n  expect(standard.hour).toBe(9);\n  expect(standard.minute).toBe(23);\n  expect(standard.second).toBe(54);\n  expect(standard.millisecond).toBe(123);\n});\n\ntest(\"DateTime.fromObject() rejects invalid zones\", () => {\n  const dt = DateTime.fromObject({}, { zone: \"blorp\" });\n  expect(dt.isValid).toBe(false);\n  expect(dt.invalidReason).toBe(\"unsupported zone\");\n});\n\ntest(\"DateTime.fromObject() ignores the case of object keys\", () => {\n  const dt = DateTime.fromObject({ Year: 2019, MONTH: 4, daYs: 10 });\n  expect(dt.isValid).toBe(true);\n  expect(dt.year).toBe(2019);\n  expect(dt.month).toBe(4);\n  expect(dt.day).toBe(10);\n});\n\ntest(\"DateTime.fromObject() throws with invalid object key\", () => {\n  expect(() => DateTime.fromObject({ invalidUnit: 42 })).toThrow();\n});\n\ntest(\"DateTime.fromObject() throws with invalid value types\", () => {\n  expect(() => DateTime.fromObject({ year: \"blorp\" })).toThrow();\n  expect(() => DateTime.fromObject({ year: \"\" })).toThrow();\n  expect(() => DateTime.fromObject({ month: NaN })).toThrow();\n  expect(() => DateTime.fromObject({ month: Infinity })).toThrow();\n  expect(() => DateTime.fromObject({ month: -Infinity })).toThrow();\n  expect(() => DateTime.fromObject({ day: true })).toThrow();\n  expect(() => DateTime.fromObject({ day: false })).toThrow();\n  expect(() => DateTime.fromObject({ hour: {} })).toThrow();\n  expect(() => DateTime.fromObject({ hour: { unit: 42 } })).toThrow();\n});\n\ntest(\"DateTime.fromObject() reject invalid values\", () => {\n  expect(DateTime.fromObject({ ordinal: 5000 }).isValid).toBe(false);\n  expect(DateTime.fromObject({ minute: -6 }).isValid).toBe(false);\n  expect(DateTime.fromObject({ millisecond: new Date() }).isValid).toBe(false);\n});\n\ntest(\"DateTime.fromObject() defaults high-order values to the current date\", () => {\n  const dateTime = DateTime.fromObject({}),\n    now = DateTime.now();\n\n  expect(dateTime.year).toBe(now.year);\n  expect(dateTime.month).toBe(now.month);\n  expect(dateTime.day).toBe(now.day);\n});\n\ntest(\"DateTime.fromObject() defaults lower-order values to their minimums if a high-order value is set\", () => {\n  const dateTime = DateTime.fromObject({ year: 2017 });\n  expect(dateTime.year).toBe(2017);\n  expect(dateTime.month).toBe(1);\n  expect(dateTime.day).toBe(1);\n  expect(dateTime.hour).toBe(0);\n  expect(dateTime.minute).toBe(0);\n  expect(dateTime.second).toBe(0);\n  expect(dateTime.millisecond).toBe(0);\n});\n\ntest(\"DateTime.fromObject() w/weeks handles fully specified dates\", () => {\n  const dt = DateTime.fromObject({\n    weekYear: 2016,\n    weekNumber: 2,\n    weekday: 3,\n    hour: 9,\n    minute: 23,\n    second: 54,\n    millisecond: 123,\n  });\n  expect(dt.weekYear).toBe(2016);\n  expect(dt.weekNumber).toBe(2);\n  expect(dt.weekday).toBe(3);\n  expect(dt.year).toBe(2016);\n  expect(dt.month).toBe(1);\n  expect(dt.day).toBe(13);\n});\n\ntest(\"DateTime.fromObject() w/weekYears handles skew with Gregorian years\", () => {\n  let dt = DateTime.fromObject({ weekYear: 2015, weekNumber: 1, weekday: 3 });\n  expect(dt.weekYear).toBe(2015);\n  expect(dt.weekNumber).toBe(1);\n  expect(dt.weekday).toBe(3);\n  expect(dt.year).toBe(2014);\n  expect(dt.month).toBe(12);\n  expect(dt.day).toBe(31);\n\n  dt = DateTime.fromObject({ weekYear: 2009, weekNumber: 53, weekday: 5 });\n  expect(dt.weekYear).toBe(2009);\n  expect(dt.weekNumber).toBe(53);\n  expect(dt.weekday).toBe(5);\n  expect(dt.year).toBe(2010);\n  expect(dt.month).toBe(1);\n  expect(dt.day).toBe(1);\n});\n\ntest(\"DateTime.fromObject() w/weeks defaults high-order values to the current date\", () => {\n  const dt = DateTime.fromObject({ weekday: 2 }),\n    now = DateTime.now();\n\n  expect(dt.weekYear).toBe(now.weekYear);\n  expect(dt.weekNumber).toBe(now.weekNumber);\n  expect(dt.weekday).toBe(2);\n});\n\ntest(\"DateTime.fromObject() w/weeks defaults low-order values to their minimums\", () => {\n  const dt = DateTime.fromObject({ weekYear: 2016 });\n\n  expect(dt.weekYear).toBe(2016);\n  expect(dt.weekNumber).toBe(1);\n  expect(dt.weekday).toBe(1);\n  expect(dt.hour).toBe(0);\n  expect(dt.minute).toBe(0);\n  expect(dt.second).toBe(0);\n  expect(dt.millisecond).toBe(0);\n});\n\ntest(\"DateTime.fromObject() w/locale weeks defaults low-order values to their minimums\", () => {\n  const dt = DateTime.fromObject({ localWeekYear: 2016 }, { locale: \"en-US\" });\n\n  expect(dt.localWeekYear).toBe(2016);\n  expect(dt.localWeekNumber).toBe(1);\n  expect(dt.localWeekday).toBe(1);\n  expect(dt.hour).toBe(0);\n  expect(dt.minute).toBe(0);\n  expect(dt.second).toBe(0);\n  expect(dt.millisecond).toBe(0);\n});\n\ntest(\"DateTime.fromObject() w/locale weeks defaults high-order values to the current date\", () => {\n  const dt = DateTime.fromObject({ localWeekday: 2 }, { locale: \"en-US\" }),\n    now = DateTime.local({ locale: \"en-US\" });\n\n  expect(dt.localWeekYear).toBe(now.localWeekYear);\n  expect(dt.localWeekNumber).toBe(now.localWeekNumber);\n  expect(dt.localWeekday).toBe(2);\n});\n\ntest(\"DateTime.fromObject() w/locale weeks handles fully specified dates\", () => {\n  const dt = DateTime.fromObject(\n    {\n      localWeekYear: 2022,\n      localWeekNumber: 2,\n      localWeekday: 3,\n      hour: 9,\n      minute: 23,\n      second: 54,\n      millisecond: 123,\n    },\n    { locale: \"en-US\" }\n  );\n  expect(dt.localWeekYear).toBe(2022);\n  expect(dt.localWeekNumber).toBe(2);\n  expect(dt.localWeekday).toBe(3);\n  expect(dt.year).toBe(2022);\n  expect(dt.month).toBe(1);\n  expect(dt.day).toBe(supportsMinDaysInFirstWeek() ? 4 : 11);\n});\n\ntest(\"DateTime.fromObject() w/locale weeks handles fully specified dates with custom week settings\", () => {\n  withDefaultWeekSettings(\n    {\n      firstDay: 7,\n      minimalDays: 1,\n      weekend: [6, 7],\n    },\n    () => {\n      const dt = DateTime.fromObject(\n        {\n          localWeekYear: 2022,\n          localWeekNumber: 2,\n          localWeekday: 3,\n          hour: 9,\n          minute: 23,\n          second: 54,\n          millisecond: 123,\n        },\n        { locale: \"en-US\" }\n      );\n      expect(dt.localWeekYear).toBe(2022);\n      expect(dt.localWeekNumber).toBe(2);\n      expect(dt.localWeekday).toBe(3);\n      expect(dt.year).toBe(2022);\n      expect(dt.month).toBe(1);\n      expect(dt.day).toBe(4);\n    }\n  );\n});\n\ntest(\"DateTime.fromObject() w/localWeekYears handles skew with Gregorian years\", () => {\n  let dt = DateTime.fromObject(\n    { localWeekYear: 2022, localWeekNumber: 1, localWeekday: 1 },\n    { locale: \"en-US\" }\n  );\n  expect(dt.localWeekYear).toBe(2022);\n  expect(dt.localWeekNumber).toBe(1);\n  expect(dt.localWeekday).toBe(1);\n  expect(dt.year).toBe(supportsMinDaysInFirstWeek() ? 2021 : 2022);\n  expect(dt.month).toBe(supportsMinDaysInFirstWeek() ? 12 : 1);\n  expect(dt.day).toBe(supportsMinDaysInFirstWeek() ? 26 : 2);\n\n  dt = DateTime.fromObject(\n    { localWeekYear: 2009, localWeekNumber: 53, localWeekday: 5 },\n    { locale: \"de-DE\" }\n  );\n  expect(dt.localWeekYear).toBe(2009);\n  expect(dt.localWeekNumber).toBe(53);\n  expect(dt.localWeekday).toBe(5);\n  expect(dt.year).toBe(2010);\n  expect(dt.month).toBe(1);\n  expect(dt.day).toBe(1);\n});\n\ntest(\"DateTime.fromObject() w/localWeekYears handles skew with Gregorian years and custom week settings\", () => {\n  withDefaultWeekSettings(\n    {\n      firstDay: 7,\n      minimalDays: 1,\n      weekend: [6, 7],\n    },\n    () => {\n      let dt = DateTime.fromObject({ localWeekYear: 2022, localWeekNumber: 1, localWeekday: 1 });\n      expect(dt.localWeekYear).toBe(2022);\n      expect(dt.localWeekNumber).toBe(1);\n      expect(dt.localWeekday).toBe(1);\n      expect(dt.year).toBe(2021);\n      expect(dt.month).toBe(12);\n      expect(dt.day).toBe(26);\n    }\n  );\n});\n\ntest(\"DateTime.fromObject throws when both locale based weeks and ISO-weeks are specified\", () => {\n  expect(() => DateTime.fromObject({ localWeekYear: 2022, weekNumber: 12 })).toThrow();\n  expect(() => DateTime.fromObject({ localWeekYear: 2022, weekday: 2 })).toThrow();\n});\n\ntest(\"DateTime.fromObject() w/ordinals handles fully specified dates\", () => {\n  const dt = DateTime.fromObject({\n    year: 2016,\n    ordinal: 200,\n    hour: 9,\n    minute: 23,\n    second: 54,\n    millisecond: 123,\n  });\n  expect(dt.year).toBe(2016);\n  expect(dt.ordinal).toBe(200);\n  expect(dt.month).toBe(7);\n  expect(dt.day).toBe(18);\n});\n\ntest(\"DateTime.fromObject() w/ordinal defaults to the current year\", () => {\n  const dt = DateTime.fromObject({ ordinal: 200 }),\n    now = DateTime.now();\n  expect(dt.year).toBe(now.year);\n  expect(dt.ordinal).toBe(200);\n});\n\ntest(\"DateTime.fromObject() returns invalid for invalid values\", () => {\n  expect(DateTime.fromObject({ weekYear: 2017, weekNumber: 54 }).isValid).toBe(false);\n  expect(DateTime.fromObject({ weekYear: 2017, weekNumber: 3.6 }).isValid).toBe(false);\n  expect(DateTime.fromObject({ weekYear: 2017, weekNumber: 15, weekday: 0 }).isValid).toBe(false);\n});\n\ntest(\"DateTime.fromObject accepts the default locale\", () => {\n  withDefaultLocale(\"fr\", () => expect(DateTime.fromObject({}).locale).toBe(\"fr\"));\n});\n\ntest(\"DateTime.fromObject accepts really low year numbers\", () => {\n  const dt = DateTime.fromObject({ year: 5 });\n  expect(dt.year).toBe(5);\n  expect(dt.month).toBe(1);\n  expect(dt.day).toBe(1);\n});\n\ntest(\"DateTime.fromObject accepts really low year numbers with IANA zones\", () => {\n  const dt = DateTime.fromObject({ year: 5 }, { zone: \"America/New_York\" });\n  expect(dt.year).toBe(5);\n  expect(dt.month).toBe(1);\n  expect(dt.day).toBe(1);\n});\n\ntest(\"DateTime.fromObject accepts plurals and weird capitalization\", () => {\n  const dt = DateTime.fromObject({ Year: 2005, months: 12, dAy: 13 });\n  expect(dt.year).toBe(2005);\n  expect(dt.month).toBe(12);\n  expect(dt.day).toBe(13);\n});\n\ntest(\"DateTime.fromObject validates weekdays\", () => {\n  let dt = DateTime.fromObject({ year: 2005, months: 12, day: 13, weekday: 1 });\n  expect(dt.isValid).toBe(false);\n\n  dt = DateTime.fromObject({ year: 2005, months: 12, day: 13, weekday: 2 });\n  expect(dt.isValid).toBe(true);\n});\n\ntest(\"DateTime.fromObject accepts a locale\", () => {\n  const res = DateTime.fromObject({}, { locale: \"be\" });\n  expect(res.locale).toBe(\"be\");\n});\n\ntest(\"DateTime.fromObject accepts a locale with calendar and numbering identifiers\", () => {\n  const res = DateTime.fromObject({}, { locale: \"be-u-ca-coptic-nu-mong\" });\n  expect(res.locale).toBe(\"be-u-ca-coptic-nu-mong\");\n  expect(res.outputCalendar).toBe(\"coptic\");\n  expect(res.numberingSystem).toBe(\"mong\");\n});\n\ntest(\"DateTime.fromObject accepts a locale string with weird junk in it\", () => {\n  withDefaultLocale(\"en-US\", () => {\n    const res = DateTime.fromObject(\n      {},\n      {\n        locale: \"be-u-ca-coptic-ca-islamic\",\n      }\n    );\n\n    expect(res.locale).toBe(\"be-u-ca-coptic-ca-islamic\");\n\n    // \"coptic\" is right, but some versions of Node 10 give \"gregory\"\n    expect(res.outputCalendar === \"gregory\" || res.outputCalendar === \"coptic\").toBe(true);\n    expect(res.numberingSystem).toBe(\"latn\");\n  });\n});\n\ntest(\"DateTime.fromObject overrides the locale string with explicit settings\", () => {\n  const res = DateTime.fromObject(\n    {},\n    {\n      locale: \"be-u-ca-coptic-nu-mong\",\n      numberingSystem: \"thai\",\n      outputCalendar: \"islamic\",\n    }\n  );\n\n  expect(res.locale).toBe(\"be-u-ca-coptic-nu-mong\");\n  expect(res.outputCalendar).toBe(\"islamic\");\n  expect(res.numberingSystem).toBe(\"thai\");\n});\n\ntest(\"DateTime.fromObject handles null as a language tag\", () => {\n  withDefaultLocale(\"en-GB\", () => {\n    const res = DateTime.fromObject(\n      {},\n      {\n        locale: null,\n        numberingSystem: \"thai\",\n        outputCalendar: \"islamic\",\n      }\n    );\n\n    expect(res.locale).toBe(\"en-GB\");\n    expect(res.outputCalendar).toBe(\"islamic\");\n    expect(res.numberingSystem).toBe(\"thai\");\n  });\n});\n\ntest(\"DateTime.fromRFC2822 parses GMT correctly\", () => {\n  const dt = DateTime.fromRFC2822(\"25 Nov 2016 13:23:12 GMT\", { zone: \"UTC\" });\n  expect(dt.year).toBe(2016);\n  expect(dt.month).toBe(11);\n  expect(dt.day).toBe(25);\n  expect(dt.hour).toBe(13);\n  expect(dt.minute).toBe(23);\n  expect(dt.second).toBe(12);\n  expect(dt.millisecond).toBe(0);\n  expect(dt.offset).toBe(0);\n});\n\ntest(\"DateTime.fromRFC2822 parses Zulu correctly\", () => {\n  const dt = DateTime.fromRFC2822(\"25 Nov 2016 13:23 Z\", { zone: \"UTC\" });\n  expect(dt.year).toBe(2016);\n  expect(dt.month).toBe(11);\n  expect(dt.day).toBe(25);\n  expect(dt.hour).toBe(13);\n  expect(dt.minute).toBe(23);\n  expect(dt.second).toBe(0);\n  expect(dt.millisecond).toBe(0);\n  expect(dt.offset).toBe(0);\n});\n\ntest(\"DateTime.fromRFC2822 parses offset correctly\", () => {\n  const dt = DateTime.fromRFC2822(\"Fri, 25 Nov 2016 13:23:12 +0600\", {\n    zone: \"UTC\",\n  });\n  expect(dt.year).toBe(2016);\n  expect(dt.month).toBe(11);\n  expect(dt.day).toBe(25);\n  expect(dt.hour).toBe(7);\n  expect(dt.minute).toBe(23);\n  expect(dt.second).toBe(12);\n  expect(dt.millisecond).toBe(0);\n  expect(dt.offset).toBe(0);\n});\n\ntest(\"DateTime.fromRFC2822 is invalid when weekday is not consistent\", () => {\n  // Actually a Friday, not a Saturday\n  expect(DateTime.fromRFC2822(\"Sat, 25 Nov 2016 13:23:12 +0600\").isValid).toBe(false);\n});\n\ntest(\"DateTime.fromHTTP parses rfc1123\", () => {\n  const dt = DateTime.fromHTTP(\"Sun, 06 Nov 1994 08:49:37 GMT\", {\n    zone: \"UTC\",\n  });\n  expect(dt.year).toBe(1994);\n  expect(dt.month).toBe(11);\n  expect(dt.day).toBe(6);\n  expect(dt.hour).toBe(8);\n  expect(dt.minute).toBe(49);\n  expect(dt.second).toBe(37);\n  expect(dt.millisecond).toBe(0);\n  expect(dt.offset).toBe(0);\n});\n\ntest(\"DateTime.fromHTTP parses rfc850\", () => {\n  const dt = DateTime.fromHTTP(\"Sunday, 06-Nov-94 08:49:37 GMT\", {\n    zone: \"UTC\",\n  });\n  expect(dt.year).toBe(1994);\n  expect(dt.month).toBe(11);\n  expect(dt.day).toBe(6);\n  expect(dt.hour).toBe(8);\n  expect(dt.minute).toBe(49);\n  expect(dt.second).toBe(37);\n  expect(dt.millisecond).toBe(0);\n  expect(dt.offset).toBe(0);\n});\n\ntest(\"DateTime.fromHTTP parses ascii\", () => {\n  const dt = DateTime.fromHTTP(\"Sun Nov  6 08:49:37 1994\", { zone: \"UTC\" });\n  expect(dt.year).toBe(1994);\n  expect(dt.month).toBe(11);\n  expect(dt.day).toBe(6);\n  expect(dt.hour).toBe(8);\n  expect(dt.minute).toBe(49);\n  expect(dt.second).toBe(37);\n  expect(dt.millisecond).toBe(0);\n  expect(dt.offset).toBe(0);\n});\n\ntest(\"DateTime.fromHTTP is invalid when weekday is not consistent\", () => {\n  // Actually a Sunday, not a Saturday\n  expect(DateTime.fromRFC2822(\"Sat, 06 Nov 1994 08:49:37 GMT\").isValid).toBe(false);\n  expect(DateTime.fromRFC2822(\"Saturday, 06-Nov-94 08:49:37 GMT\").isValid).toBe(false);\n  expect(DateTime.fromRFC2822(\"Sat Nov  6 08:49:37 1994\").isValid).toBe(false);\n});\n\ntest(\"DateTime.fromObject takes a undefined to mean {}\", () => {\n  const res = DateTime.fromObject();\n  expect(res.year).toBe(new Date().getFullYear());\n});\n\ntest(\"private language subtags don't break unicode subtags\", () => {\n  const res = DateTime.fromObject(\n    {},\n    {\n      locale: \"be-u-ca-coptic-nu-mong-x-twain\",\n      numberingSystem: \"thai\",\n      outputCalendar: \"islamic\",\n    }\n  );\n\n  expect(res.locale).toBe(\"be-u-ca-coptic-nu-mong\");\n  expect(res.outputCalendar).toBe(\"islamic\");\n  expect(res.numberingSystem).toBe(\"thai\");\n});\n\ntest(\"DateTime.local works even after time zone change\", () => {\n  // This test catches errors produced when guessOffsetForZone produces wildy wrong guesses\n  // This guards against a regression by broken caching in that method\n  Settings.resetCaches();\n  withDefaultZone(\"America/Los_Angeles\", () => {\n    expect(DateTime.local(2024).year).toBe(2024);\n  });\n  withDefaultZone(\"America/Chicago\", () => {\n    const dateTime = DateTime.local(2024, 11, 3, 0, 5, 0);\n    expect(dateTime.zoneName).toBe(\"America/Chicago\");\n    expect(dateTime.toObject()).toEqual({\n      year: 2024,\n      month: 11,\n      day: 3,\n      hour: 0,\n      minute: 5,\n      second: 0,\n      millisecond: 0,\n    });\n  });\n});\n"
  },
  {
    "path": "test/datetime/degrade.test.js",
    "content": "/* global expect */\nimport { DateTime } from \"../../src/luxon\";\n\nconst Helpers = require(\"../helpers\");\n\nHelpers.withoutRTF(\"calling toRelative falls back to English\", () => {\n  expect(\n    DateTime.fromISO(\"2014-08-06\", { locale: \"fr\" }).toRelative({\n      base: DateTime.fromISO(\"1982-05-25\"),\n    })\n  ).toBe(\"in 32 years\");\n});\n"
  },
  {
    "path": "test/datetime/diff.test.js",
    "content": "/* global test expect */\n\nimport { DateTime } from \"../../src/luxon\";\n\nconst Helpers = require(\"../helpers\");\n\n//------\n// diff\n//-------\nconst diffFromObjs = (o1, o2, units) =>\n  DateTime.fromObject(o1).diff(DateTime.fromObject(o2), units);\nconst diffObjs = (o1, o2, units) => diffFromObjs(o1, o2, units).toObject();\n\ntest(\"DateTime#diff defaults to milliseconds\", () => {\n  expect(diffObjs({ year: 2017, millisecond: 12 }, { year: 2017 })).toEqual({\n    milliseconds: 12,\n  });\n  expect(diffFromObjs({ year: 2017 }, { year: 2017 }).milliseconds).toBe(0);\n});\n\ntest(\"DateTime#diff makes simple diffs\", () => {\n  expect(diffObjs({ year: 2017 }, { year: 2017 }, \"years\")).toEqual({ years: 0 });\n\n  expect(diffObjs({ year: 2017 }, { year: 2016 }, \"years\")).toEqual({\n    years: 1,\n  });\n\n  expect(diffObjs({ year: 2016, month: 4 }, { year: 2016, month: 1 }, \"quarters\")).toEqual({\n    quarters: 1,\n  });\n\n  expect(diffObjs({ year: 2017, month: 10 }, { year: 2017, month: 4 }, \"quarters\")).toEqual({\n    quarters: 2,\n  });\n\n  expect(\n    diffObjs({ year: 2016, month: 6, day: 28 }, { year: 2016, month: 5, day: 28 }, \"months\")\n  ).toEqual({ months: 1 });\n\n  expect(\n    diffObjs({ year: 2016, month: 6, day: 28 }, { year: 2016, month: 6, day: 25 }, \"days\")\n  ).toEqual({ days: 3 });\n\n  expect(\n    diffObjs({ year: 2016, month: 6, day: 1 }, { year: 2016, month: 5, day: 28 }, \"days\")\n  ).toEqual({ days: 4 });\n\n  expect(\n    diffObjs({ year: 2016, month: 6, day: 29 }, { year: 2016, month: 6, day: 1 }, \"weeks\")\n  ).toEqual({ weeks: 4 });\n\n  expect(\n    diffObjs({ year: 2016, month: 3, day: 3 }, { year: 2016, month: 2, day: 18 }, \"weeks\")\n  ).toEqual({ weeks: 2 });\n\n  expect(\n    diffObjs(\n      { year: 2016, month: 6, day: 28, hour: 13 },\n      { year: 2016, month: 6, day: 28, hour: 5 },\n      \"hours\"\n    )\n  ).toEqual({ hours: 8 });\n\n  expect(\n    diffObjs(\n      { year: 2016, month: 6, day: 28, hour: 13 },\n      { year: 2016, month: 6, day: 28, hour: 5 },\n      \"days\"\n    )\n  ).toEqual({ days: 1 / 3 });\n\n  expect(\n    diffObjs(\n      { year: 2016, month: 6, day: 28, hour: 13 },\n      { year: 2016, month: 6, day: 25, hour: 5 },\n      \"hours\"\n    )\n  ).toEqual({ hours: 24 * 3 + 8 });\n});\n\ntest(\"DateTime#diff accepts multiple units\", () => {\n  expect(\n    diffObjs(\n      { year: 2016, month: 3, day: 28, hour: 13, minute: 46 },\n      { year: 2016, month: 3, day: 16, hour: 5, second: 18 },\n      [\"days\", \"hours\", \"minutes\", \"seconds\"]\n    )\n  ).toEqual({ days: 12, hours: 8, minutes: 45, seconds: 42 });\n\n  expect(\n    diffObjs({ year: 2016, month: 3, day: 25 }, { year: 2016, month: 3, day: 1 }, [\"weeks\", \"days\"])\n  ).toEqual({ weeks: 3, days: 3 });\n\n  expect(\n    diffObjs({ year: 2016, month: 3, day: 28 }, { year: 2010, month: 3, day: 16 }, [\n      \"years\",\n      \"days\",\n    ])\n  ).toEqual({ years: 6, days: 12 });\n\n  expect(\n    diffObjs({ year: 2016, month: 3, day: 14 }, { year: 2010, month: 3, day: 16 }, [\n      \"years\",\n      \"days\",\n    ])\n  ).toEqual({ years: 5, days: 364 });\n\n  expect(\n    diffObjs({ year: 2015, month: 3, day: 14 }, { year: 2009, month: 3, day: 16 }, [\n      \"years\",\n      \"days\",\n    ])\n  ).toEqual({ years: 5, days: 363 });\n});\n\ntest(\"DateTime#diff handles unmatched units\", () => {\n  expect(\n    diffObjs(\n      { year: 2017, month: 6, day: 7, hour: 21 },\n      { year: 2017, month: 6, day: 1, hour: 22 },\n      [\"weeks\", \"days\", \"hours\"]\n    )\n  ).toEqual({ weeks: 0, days: 5, hours: 23 });\n\n  expect(\n    diffObjs(\n      { year: 2017, month: 6, day: 27, hour: 21 },\n      { year: 2017, month: 6, day: 26, hour: 22 },\n      [\"days\", \"hours\"]\n    )\n  ).toEqual({ days: 0, hours: 23 });\n\n  expect(\n    diffObjs(\n      { year: 2017, month: 6, day: 7, hour: 21 },\n      { year: 2017, month: 6, day: 1, hour: 22 },\n      [\"weeks\", \"hours\"]\n    )\n  ).toEqual({ weeks: 0, hours: 23 + 5 * 24 });\n});\n\ntest(\"DateTime#diff sets all its units to 0 if the duration is empty\", () => {\n  const t = DateTime.fromObject({ year: 2018, month: 11, day: 5, hour: 0 });\n  expect(t.diff(t).toObject()).toEqual({ milliseconds: 0 });\n  expect(t.diff(t, \"hours\").toObject()).toEqual({ hours: 0 });\n  expect(t.diff(t, \"days\").toObject()).toEqual({ days: 0 });\n});\n\ntest(\"DateTime#diff sets units to 0 if second object is slightly larger\", () => {\n  expect(\n    diffObjs(\n      { year: 2017, month: 6, day: 26, hour: 21, minute: 1, second: 2, millisecond: 1 },\n      { year: 2017, month: 6, day: 26, hour: 21, minute: 1, second: 2, millisecond: 2 },\n      [\"years\", \"months\", \"weeks\", \"days\", \"hours\", \"minutes\", \"seconds\", \"milliseconds\"]\n    )\n  ).toEqual({\n    years: 0,\n    months: 0,\n    weeks: 0,\n    days: 0,\n    hours: 0,\n    minutes: 0,\n    seconds: 0,\n    milliseconds: -1,\n  });\n});\n\ntest(\"DateTime#diff puts fractional parts in the lowest order unit\", () => {\n  expect(\n    diffObjs({ year: 2017, month: 7, day: 14 }, { year: 2016, month: 6, day: 16 }, [\n      \"years\",\n      \"months\",\n    ])\n  ).toEqual({ years: 1, months: 1 - 2 / 30 });\n});\n\ntest(\"DateTime#diff returns the fractional parts even when it can't find a whole unit\", () => {\n  expect(\n    diffObjs(\n      { year: 2017, month: 7, day: 14, hour: 6 },\n      { year: 2017, month: 7, day: 14, hour: 2 },\n      [\"days\"]\n    )\n  ).toEqual({ days: 1 / 6 });\n});\n\ntest(\"DateTime#diff is calendary for years, months, day\", () => {\n  // respecting the leap year\n  expect(\n    diffObjs({ year: 2016, month: 6, day: 14 }, { year: 2010, month: 6, day: 14 }, [\n      \"years\",\n      \"days\",\n    ])\n  ).toEqual({ years: 6, days: 0 });\n\n  expect(\n    diffObjs({ year: 2016, month: 3, day: 14 }, { year: 2010, month: 3, day: 16 }, [\n      \"years\",\n      \"days\",\n    ])\n  ).toEqual({ years: 5, days: 364 });\n\n  // ignores the DST, works in calendar days, not bubbled months\n  expect(\n    diffObjs({ year: 2016, month: 5, day: 14 }, { year: 2016, month: 2, day: 14 }, \"days\")\n  ).toEqual({ days: 90 });\n});\n\ntest(\"DateTime#diff handles fractional years as fractions of those specific years\", () => {\n  // the point here is that we're crossing the leap year\n  expect(\n    diffObjs({ year: 2020, month: 3, day: 27 }, { year: 2018, month: 3, day: 28 }, \"years\")\n  ).toEqual({ years: 1 + 365.0 / 366 });\n});\n\ntest(\"DateTime#diff handles fractional months as fractions of those specific months\", () => {\n  // The point here is that January has 31 days\n  expect(\n    diffObjs({ year: 2018, month: 2, day: 24 }, { year: 2017, month: 12, day: 25 }, \"months\")\n  ).toEqual({ months: 1 + 30.0 / 31 });\n});\n\ntest(\"DateTime#diff handles fractional weeks as fractions of those specific weeks\", () => {\n  // America/New_York has a fall back Nov 4, 2018 at 2:00\n  expect(\n    diffObjs(\n      { year: 2018, month: 11, day: 16, hour: 0 },\n      { year: 2018, month: 11, day: 2, hour: 1 },\n      \"weeks\"\n    )\n  ).toEqual({ weeks: 1 + 6.0 / 7 + 23.0 / 24 / 7 });\n});\n\ntest(\"DateTime#diff handles fractional days as fractions of those specific days\", () => {\n  // America/New_York has a fall back Nov 4, 2018 at 2:00\n  expect(\n    diffObjs(\n      { year: 2018, month: 11, day: 5, hour: 0 },\n      { year: 2018, month: 11, day: 3, hour: 1 },\n      \"days\"\n    )\n  ).toEqual({ days: 1 + 24 / 25 });\n});\n\ntest(\"DateTime#diff is precise for lower order units\", () => {\n  // spring forward skips one hour\n  expect(\n    diffObjs({ year: 2016, month: 5, day: 5 }, { year: 2016, month: 1, day: 1 }, \"hours\")\n  ).toEqual({ hours: 2999 });\n});\n\ntest(\"DateTime#diff passes through options\", () => {\n  const dt1 = DateTime.fromObject({ year: 2016, month: 5, day: 5 }),\n    dt2 = DateTime.fromObject({ year: 2016, month: 1, day: 1 }),\n    dur1 = dt1.diff(dt2, \"hours\", { conversionAccuracy: \"longterm\" }),\n    dur2 = dt1.diff(dt2, \"days\", { conversionAccuracy: \"longterm\" });\n  expect(dur1.conversionAccuracy).toBe(\"longterm\");\n  expect(dur2.conversionAccuracy).toBe(\"longterm\");\n});\n\ntest(\"DateTime#diff returns invalid Durations if the DateTimes are invalid\", () => {\n  const i = DateTime.invalid(\"because\");\n  expect(i.diff(DateTime.now()).isValid).toBe(false);\n  expect(DateTime.now().diff(i).isValid).toBe(false);\n});\n\ntest(\"DateTime#diff results in a duration with the same locale\", () => {\n  const dt1 = DateTime.fromObject(\n      {\n        year: 2016,\n        month: 5,\n        day: 5,\n      },\n      {\n        locale: \"fr\",\n        numberingSystem: \"mong\",\n      }\n    ),\n    dt2 = DateTime.fromObject(\n      {\n        year: 2016,\n        month: 1,\n        day: 1,\n      },\n      {\n        locale: \"es\",\n        numberingSystem: \"beng\",\n      }\n    ),\n    dur = dt1.diff(dt2);\n\n  expect(dur.locale).toBe(\"fr\");\n  expect(dur.numberingSystem).toBe(\"mong\");\n});\n\n// see https://github.com/moment/luxon/issues/487\ntest(\"DateTime#diff results works when needing to backtrack months\", () => {\n  const left = DateTime.fromJSDate(new Date(1554036127038)); // 2019-03-31T12:42:07.038Z\n  const right = DateTime.fromJSDate(new Date(1554122527128)); // 2019-04-01T12:42:07.128Z\n\n  const diff = right.diff(left, [\"months\", \"days\", \"hours\"]);\n  expect(diff.months).toBe(0);\n  expect(diff.days).toBe(1);\n});\n\n// see https://github.com/moment/luxon/issues/1301\ntest(\"DateTime#diff handles Feb-29 edge case logic for higher order units in a manner consistent with DateTime#plus\", () => {\n  const left = DateTime.fromISO(\"2020-02-29\");\n  const right = DateTime.fromISO(\"2021-04-01\");\n\n  const diff = right.diff(left, [\"years\", \"months\", \"days\"]);\n  expect(diff.days).toBe(3);\n  expect(left.plus(diff).equals(right)).toBe(true);\n});\n\n// see https://github.com/moment/luxon/issues/1165\ntest(\"DateTime#diff handles datetimes whose dayDiff is off by 2 days instead of the usual 1 due to zone differences\", () => {\n  // Notice how `end` is 5 days away when only looking at calendar days ignoring time zones,\n  // but actually the diff is 3 days and 23 hours (days are off by 2)\n  // because Europe/Madrid here has an offset of +02:00.\n  const start = DateTime.fromISO(\"2022-05-05T23:00\", { zone: \"UTC\" });\n  const end = DateTime.fromISO(\"2022-05-10T00:00\", { zone: \"Europe/Madrid\" });\n\n  const diff1 = end.diff(start, [\"days\", \"hours\", \"minutes\"]).toObject();\n  expect(diff1).toEqual({ days: 3, hours: 23, minutes: 0 });\n\n  const diff2 = end.diff(start, [\"days\"]);\n  expect(diff2.days).toBeCloseTo(3.958333, 6);\n});\n\n//------\n// diffNow\n//-------\n\nHelpers.withNow(\"DateTime#diffNow defaults to milliseconds\", DateTime.local(2017, 5, 15), () => {\n  const dt = DateTime.local(2014, 8, 6),\n    dur = dt.diffNow();\n  expect(dur.milliseconds).toBe(-87523200000);\n});\n\nHelpers.withNow(\"DateTime#diffNow accepts units\", DateTime.local(2017, 5, 15), () => {\n  const dt = DateTime.local(2014, 8, 6),\n    dur = dt.diffNow(\"days\");\n  expect(dur.days).toBe(-1013);\n});\n\nHelpers.withNow(\"DateTime#diffNow passes through options\", DateTime.local(2017, 5, 15), () => {\n  const dt = DateTime.local(2014, 8, 6),\n    dur = dt.diffNow(\"days\", { conversionAccuracy: \"longterm\" });\n  expect(dur.conversionAccuracy).toBe(\"longterm\");\n});\n\ntest(\"DateTime#diffNow returns invalid Durations if the DateTime is invalid\", () => {\n  const i = DateTime.invalid(\"because\");\n  expect(i.diffNow().isValid).toBe(false);\n});\n"
  },
  {
    "path": "test/datetime/dst.test.js",
    "content": "/* global test expect */\n\nimport { DateTime, Settings } from \"../../src/luxon\";\n\nconst dateTimeConstructors = {\n  fromObject: (year, month, day, hour) =>\n    DateTime.fromObject({ year, month, day, hour }, { zone: \"America/New_York\" }),\n  local: (year, month, day, hour) =>\n    DateTime.local(year, month, day, hour, { zone: \"America/New_York\" }),\n};\n\nfor (const [name, local] of Object.entries(dateTimeConstructors)) {\n  describe(`DateTime.${name}`, () => {\n    test(\"Hole dates are bumped forward\", () => {\n      const d = local(2017, 3, 12, 2);\n      expect(d.hour).toBe(3);\n      expect(d.offset).toBe(-4 * 60);\n    });\n\n    if (name == \"fromObject\") {\n      // this is questionable behavior, but I wanted to document it\n      test(\"Ambiguous dates pick the one with the current offset\", () => {\n        const oldSettings = Settings.now;\n        try {\n          Settings.now = () => 1495653314595; // May 24, 2017\n          let d = local(2017, 11, 5, 1);\n          expect(d.hour).toBe(1);\n          expect(d.offset).toBe(-4 * 60);\n\n          Settings.now = () => 1484456400000; // Jan 15, 2017\n          d = local(2017, 11, 5, 1);\n          expect(d.hour).toBe(1);\n          expect(d.offset).toBe(-5 * 60);\n        } finally {\n          Settings.now = oldSettings;\n        }\n      });\n    } else {\n      test(\"Ambiguous dates pick the one with the cached offset\", () => {\n        const oldSettings = Settings.now;\n        try {\n          Settings.resetCaches();\n          Settings.now = () => 1495653314595; // May 24, 2017\n          let d = local(2017, 11, 5, 1);\n          expect(d.hour).toBe(1);\n          expect(d.offset).toBe(-4 * 60);\n\n          Settings.now = () => 1484456400000; // Jan 15, 2017\n          d = local(2017, 11, 5, 1);\n          expect(d.hour).toBe(1);\n          expect(d.offset).toBe(-4 * 60);\n\n          Settings.resetCaches();\n\n          Settings.now = () => 1484456400000; // Jan 15, 2017\n          d = local(2017, 11, 5, 1);\n          expect(d.hour).toBe(1);\n          expect(d.offset).toBe(-5 * 60);\n\n          Settings.now = () => 1495653314595; // May 24, 2017\n          d = local(2017, 11, 5, 1);\n          expect(d.hour).toBe(1);\n          expect(d.offset).toBe(-5 * 60);\n        } finally {\n          Settings.now = oldSettings;\n        }\n      });\n    }\n\n    test(\"Adding an hour to land on the Spring Forward springs forward\", () => {\n      const d = local(2017, 3, 12, 1).plus({ hour: 1 });\n      expect(d.hour).toBe(3);\n      expect(d.offset).toBe(-4 * 60);\n    });\n\n    test(\"Subtracting an hour to land on the Spring Forward springs forward\", () => {\n      const d = local(2017, 3, 12, 3).minus({ hour: 1 });\n      expect(d.hour).toBe(1);\n      expect(d.offset).toBe(-5 * 60);\n    });\n\n    test(\"Adding an hour to land on the Fall Back falls back\", () => {\n      const d = local(2017, 11, 5, 0).plus({ hour: 2 });\n      expect(d.hour).toBe(1);\n      expect(d.offset).toBe(-5 * 60);\n    });\n\n    test(\"Subtracting an hour to land on the Fall Back falls back\", () => {\n      let d = local(2017, 11, 5, 3).minus({ hour: 2 });\n      expect(d.hour).toBe(1);\n      expect(d.offset).toBe(-5 * 60);\n\n      d = d.minus({ hour: 1 });\n      expect(d.hour).toBe(1);\n      expect(d.offset).toBe(-4 * 60);\n    });\n\n    test(\"Changing a calendar date to land on a hole bumps forward\", () => {\n      let d = local(2017, 3, 11, 2).plus({ day: 1 });\n      expect(d.hour).toBe(3);\n      expect(d.offset).toBe(-4 * 60);\n\n      d = local(2017, 3, 13, 2).minus({ day: 1 });\n      expect(d.hour).toBe(3);\n      expect(d.offset).toBe(-4 * 60);\n    });\n\n    test(\"Changing a calendar date to land on an ambiguous time chooses the closest one\", () => {\n      let d = local(2017, 11, 4, 1).plus({ day: 1 });\n      expect(d.hour).toBe(1);\n      expect(d.offset).toBe(-4 * 60);\n\n      d = local(2017, 11, 6, 1).minus({ day: 1 });\n      expect(d.hour).toBe(1);\n      expect(d.offset).toBe(-5 * 60);\n    });\n\n    test(\"Start of a 0:00->1:00 DST day is 1:00\", () => {\n      const d = DateTime.fromObject(\n        {\n          year: 2017,\n          month: 10,\n          day: 15,\n        },\n        {\n          zone: \"America/Sao_Paulo\",\n        }\n      ).startOf(\"day\");\n      expect(d.day).toBe(15);\n      expect(d.hour).toBe(1);\n      expect(d.minute).toBe(0);\n      expect(d.second).toBe(0);\n    });\n\n    test(\"End of a 0:00->1:00 DST day is 23:59\", () => {\n      const d = DateTime.fromObject(\n        {\n          year: 2017,\n          month: 10,\n          day: 15,\n        },\n        {\n          zone: \"America/Sao_Paulo\",\n        }\n      ).endOf(\"day\");\n      expect(d.day).toBe(15);\n      expect(d.hour).toBe(23);\n      expect(d.minute).toBe(59);\n      expect(d.second).toBe(59);\n    });\n  });\n}\n\ndescribe(\"DateTime.local() with offset caching\", () => {\n  const edtTs = 1495653314000; // May 24, 2017 15:15:14 -0400\n  const estTs = 1484456400000; // Jan 15, 2017 00:00 -0500\n\n  const edtDate = [2017, 5, 24, 15, 15, 14, 0];\n  const estDate = [2017, 1, 15, 0, 0, 0, 0];\n\n  const timestamps = { EDT: edtTs, EST: estTs };\n  const dates = { EDT: edtDate, EST: estDate };\n  const zoneObj = { zone: \"America/New_York\" };\n\n  for (const [cacheName, cacheTs] of Object.entries(timestamps)) {\n    for (const [nowName, nowTs] of Object.entries(timestamps)) {\n      for (const [dateName, date] of Object.entries(dates)) {\n        test(`cache = ${cacheName}, now = ${nowName}, date = ${dateName}`, () => {\n          const oldSettings = Settings.now;\n          try {\n            Settings.now = () => cacheTs;\n            Settings.resetCaches();\n            // load cache\n            DateTime.local(2020, 1, 1, 0, zoneObj);\n\n            Settings.now = () => nowTs;\n            const dt = DateTime.local(...date, zoneObj);\n            expect(dt.toMillis()).toBe(timestamps[dateName]);\n            expect(dt.year).toBe(date[0]);\n            expect(dt.month).toBe(date[1]);\n            expect(dt.day).toBe(date[2]);\n            expect(dt.hour).toBe(date[3]);\n            expect(dt.minute).toBe(date[4]);\n            expect(dt.second).toBe(date[5]);\n          } finally {\n            Settings.now = oldSettings;\n          }\n        });\n      }\n    }\n  }\n});\n"
  },
  {
    "path": "test/datetime/equality.test.js",
    "content": "/* global test expect */\n\nimport { DateTime } from \"../../src/luxon\";\n\ntest(\"equals self\", () => {\n  const l = DateTime.now();\n  expect(l.equals(l)).toBe(true);\n});\n\ntest(\"equals identically constructed\", () => {\n  const l1 = DateTime.local(2017, 5, 15),\n    l2 = DateTime.local(2017, 5, 15);\n  expect(l1.equals(l2)).toBe(true);\n});\n\ntest(\"does not equal a different zone\", () => {\n  const l1 = DateTime.local(2017, 5, 15).setZone(\"America/New_York\"),\n    l2 = DateTime.local(2017, 5, 15).setZone(\"America/Los_Angeles\");\n  expect(l1.equals(l2)).toBe(false);\n});\n\ntest(\"does not equal an invalid DateTime\", () => {\n  const l1 = DateTime.local(2017, 5, 15),\n    l2 = DateTime.invalid(\"whatever\");\n  expect(l1.equals(l2)).toBe(false);\n});\n\ntest(\"does not equal a different locale\", () => {\n  const l1 = DateTime.local(2017, 5, 15),\n    l2 = DateTime.local(2017, 5, 15).setLocale(\"fr\");\n  expect(l1.equals(l2)).toBe(false);\n});\n\ntest(\"does not equal a different numbering system\", () => {\n  const l1 = DateTime.local(2017, 5, 15),\n    l2 = DateTime.local(2017, 5, 15).reconfigure({ numberingSystem: \"beng\" });\n  expect(l1.equals(l2)).toBe(false);\n});\n\ntest(\"does not equal a different output calendar\", () => {\n  const l1 = DateTime.local(2017, 5, 15),\n    l2 = DateTime.local(2017, 5, 15).reconfigure({ outputCalendar: \"islamic\" });\n  expect(l1.equals(l2)).toBe(false);\n});\n"
  },
  {
    "path": "test/datetime/format.test.js",
    "content": "/* global test expect */\n\nimport { DateTime, Zone, FixedOffsetZone } from \"../../src/luxon\";\nimport { InvalidUnitError } from \"../../src/errors\";\n\nconst dtMaker = () =>\n    DateTime.fromObject(\n      {\n        year: 1982,\n        month: 5,\n        day: 25,\n        hour: 9,\n        minute: 23,\n        second: 54,\n        millisecond: 123,\n      },\n      {\n        zone: \"utc\",\n      }\n    ),\n  dt = dtMaker(),\n  invalid = DateTime.invalid(\"because\");\n\nclass CustomZone extends Zone {\n  constructor(name, offset) {\n    super();\n    this._name = name;\n    this._offset = offset;\n  }\n\n  get isUniversal() {\n    return true;\n  }\n\n  get isValid() {\n    return true;\n  }\n\n  get name() {\n    return this._name;\n  }\n\n  get type() {\n    return \"custom\";\n  }\n\n  equals(zone) {\n    return zone instanceof CustomZone && zone._name === this._name && zone._offset === this._offset;\n  }\n\n  offset(_ms) {\n    return this._offset;\n  }\n\n  offsetName(_ms, { format }) {\n    if (format === \"short\") {\n      return this._name.substring(0, 4);\n    } else {\n      return this._name;\n    }\n  }\n\n  formatOffset(...args) {\n    return FixedOffsetZone.prototype.formatOffset(...args);\n  }\n}\n\n//------\n// #toJSON()\n//------\ntest(\"DateTime#toJSON() just does toISO\", () => {\n  expect(dt.toJSON()).toBe(\"1982-05-25T09:23:54.123Z\");\n});\n\n//------\n// #toISO()\n//------\ntest(\"DateTime#toISO() shows 'Z' for UTC\", () => {\n  expect(dt.toISO()).toBe(\"1982-05-25T09:23:54.123Z\");\n});\n\ntest(\"DateTime#toISO() shows the offset, unless explicitly asked\", () => {\n  const offsetted = dt.toUTC(-6 * 60);\n  expect(offsetted.toISO()).toBe(\"1982-05-25T03:23:54.123-06:00\");\n  expect(offsetted.toISO({ includeOffset: false })).toBe(\"1982-05-25T03:23:54.123\");\n});\n\ntest(\"DateTime#toISO() supports the 'basic' format\", () => {\n  expect(dt.toISO({ format: \"basic\" })).toBe(\"19820525T092354.123Z\");\n});\n\ntest(\"DateTime#toISO() suppresses [milli]seconds\", () => {\n  const noZeroMilliseconds = { suppressMilliseconds: true };\n  expect(dt.toISO(noZeroMilliseconds)).toBe(\"1982-05-25T09:23:54.123Z\");\n  expect(dt.set({ millisecond: 0 }).toISO(noZeroMilliseconds)).toBe(\"1982-05-25T09:23:54Z\");\n\n  const noZeroSeconds = { suppressSeconds: true, suppressMilliseconds: true };\n  expect(dt.set({ millisecond: 0 }).toISO(noZeroSeconds)).toBe(\"1982-05-25T09:23:54Z\");\n  expect(dt.set({ seconds: 0, milliseconds: 0 }).toISO(noZeroSeconds)).toBe(\"1982-05-25T09:23Z\");\n\n  const suppressOnlySeconds = { suppressSeconds: true };\n  expect(dt.set({ seconds: 0 }).toISO(suppressOnlySeconds)).toBe(\"1982-05-25T09:23:00.123Z\");\n  expect(dt.set({ seconds: 0, milliseconds: 0 }).toISO(suppressOnlySeconds)).toBe(\n    \"1982-05-25T09:23Z\"\n  );\n});\n\ntest(\"DateTime#toISO() returns null for invalid DateTimes\", () => {\n  expect(invalid.toISO()).toBe(null);\n});\n\n// #724, Firefox specific issue, offset prints as '-05:50.60000000000002'\ntest(\"DateTime#toISO() rounds fractional timezone minute offsets\", () => {\n  expect(DateTime.fromMillis(-62090696591000).setZone(\"America/Chicago\").toISO()).toBe(\n    \"0002-06-04T10:26:13.000-05:50\"\n  );\n});\n\ntest(\"DateTime#toISO() handles long gregorian format\", () => {\n  const negativeYear = dt.set({ year: 12345 });\n  expect(negativeYear.toISO()).toBe(\"+012345-05-25T09:23:54.123Z\");\n});\n\ntest(\"DateTime#toISO() handles negative years\", () => {\n  const negativeYear = dt.set({ year: -12345 });\n  expect(negativeYear.toISO()).toBe(\"-012345-05-25T09:23:54.123Z\");\n});\n\ntest(\"DateTime#toISO() default to Z when timezone is 00:00\", () => {\n  const negativeYear = dt.setZone(\"utc\");\n  expect(negativeYear.toISO()).toBe(\"1982-05-25T09:23:54.123Z\");\n});\n\ntest(\"DateTime#toISO() renders 00:00 for non-offset but non utc datetimes\", () => {\n  const negativeYear = dt.setZone(\"Africa/Abidjan\");\n  expect(negativeYear.toISO()).toBe(\"1982-05-25T09:23:54.123+00:00\");\n});\n\ntest(\"DateTime#toISO() supports the extendedZone option\", () => {\n  let zoned = dt.setZone(\"America/Chicago\");\n  expect(zoned.toISO({ extendedZone: true })).toBe(\n    \"1982-05-25T04:23:54.123-05:00[America/Chicago]\"\n  );\n  expect(zoned.toISO({ includeOffset: false, extendedZone: true })).toBe(\n    \"1982-05-25T04:23:54.123[America/Chicago]\"\n  );\n\n  zoned = dt.setZone(\"UTC+6\");\n  expect(zoned.toISO({ extendedZone: true })).toBe(\"1982-05-25T15:23:54.123+06:00[Etc/GMT-6]\");\n  expect(zoned.toISO({ includeOffset: false, extendedZone: true })).toBe(\n    \"1982-05-25T15:23:54.123[Etc/GMT-6]\"\n  );\n\n  zoned = dt.setZone(\"UTC\");\n  // note no Z\n  expect(zoned.toISO({ extendedZone: true })).toBe(\"1982-05-25T09:23:54.123+00:00[Etc/UTC]\");\n  expect(zoned.toISO({ includeOffset: false, extendedZone: true })).toBe(\n    \"1982-05-25T09:23:54.123[Etc/UTC]\"\n  );\n});\n\ntest(\"DateTime#toISO({precision}) truncates time to desired precision\", () => {\n  expect(dt.toISO({ precision: \"millisecond\" })).toBe(\"1982-05-25T09:23:54.123Z\");\n  expect(dt.toISO({ precision: \"second\" })).toBe(\"1982-05-25T09:23:54Z\");\n  expect(dt.toISO({ precision: \"minute\" })).toBe(\"1982-05-25T09:23Z\");\n  expect(dt.toISO({ precision: \"hours\" })).toBe(\"1982-05-25T09Z\");\n  expect(dt.toISO({ precision: \"days\" })).toBe(\"1982-05-25Z\");\n  expect(dt.toISO({ precision: \"months\" })).toBe(\"1982-05Z\");\n  expect(dt.toISO({ precision: \"years\" })).toBe(\"1982Z\");\n});\n\ntest(\"DateTime#toISO({precision}) throws when the precision is invalid\", () => {\n  expect(() => dt.toISO({ precision: \"ms\" })).toThrow(InvalidUnitError);\n  expect(() => dt.toISO({ precision: \"xxx\" })).toThrow(InvalidUnitError);\n  expect(() => dt.toISO({ precision: null })).toThrow(TypeError);\n});\n\n//------\n// #toISODate()\n//------\ntest(\"DateTime#toISODate() returns ISO 8601 date\", () => {\n  expect(dt.toISODate()).toBe(\"1982-05-25\");\n});\n\ntest(\"DateTime#toISODate() is local to the zone\", () => {\n  expect(dt.toUTC(-10 * 60).toISODate()).toBe(\"1982-05-24\");\n});\n\ntest(\"DateTime#toISODate() can output the basic format\", () => {\n  expect(dt.toISODate({ format: \"basic\" })).toBe(\"19820525\");\n});\n\ntest(\"DateTime#toISODate() returns null for invalid DateTimes\", () => {\n  expect(invalid.toISODate()).toBe(null);\n});\n\ntest(\"DateTime#toISODate() returns ISO 8601 date in format [±YYYYY]\", () => {\n  expect(\n    DateTime.fromObject({ year: 118040, month: 5, day: 25 }, { zone: \"utc\" }).toISODate()\n  ).toBe(\"+118040-05-25\");\n  expect(\n    DateTime.fromObject({ year: -118040, month: 5, day: 25 }, { zone: \"utc\" }).toISODate()\n  ).toBe(\"-118040-05-25\");\n});\n\ntest(\"DateTime#toISODate() correctly pads negative years\", () => {\n  expect(DateTime.fromObject({ year: -1, month: 1, day: 1 }, { zone: \"utc\" }).toISODate()).toBe(\n    \"-000001-01-01\"\n  );\n  expect(DateTime.fromObject({ year: -10, month: 1, day: 1 }, { zone: \"utc\" }).toISODate()).toBe(\n    \"-000010-01-01\"\n  );\n});\n\ntest(\"DateTime#toISODate({precision}) truncates time to desired precision\", () => {\n  expect(dt.toISODate({ precision: \"millisecond\" })).toBe(\"1982-05-25\");\n  expect(dt.toISODate({ precision: \"second\" })).toBe(\"1982-05-25\");\n  expect(dt.toISODate({ precision: \"minute\" })).toBe(\"1982-05-25\");\n  expect(dt.toISODate({ precision: \"hours\" })).toBe(\"1982-05-25\");\n  expect(dt.toISODate({ precision: \"days\" })).toBe(\"1982-05-25\");\n  expect(dt.toISODate({ precision: \"months\" })).toBe(\"1982-05\");\n  expect(dt.toISODate({ precision: \"years\" })).toBe(\"1982\");\n});\n\ntest(\"DateTime#toISODate({precision}) throws when the precision is invalid\", () => {\n  expect(() => dt.toISODate({ precision: \"ms\" })).toThrow(InvalidUnitError);\n  expect(() => dt.toISODate({ precision: \"xxx\" })).toThrow(InvalidUnitError);\n  expect(() => dt.toISODate({ precision: null })).toThrow(TypeError);\n});\n\n//------\n// #toISOWeekDate()\n//------\ntest(\"DateTime#toISOWeekDate() returns ISO 8601 date\", () => {\n  expect(dt.toISOWeekDate()).toBe(\"1982-W21-2\");\n});\n\ntest(\"DateTime#toISOWeekDate() returns null for invalid DateTimes\", () => {\n  expect(invalid.toISOWeekDate()).toBe(null);\n});\n\n//------\n// #toISOTime()\n//------\ntest(\"DateTime#toISOTime() returns an ISO 8601 date\", () => {\n  expect(dt.toISOTime()).toBe(\"09:23:54.123Z\");\n});\n\ntest(\"DateTime#toISOTime() won't suppress seconds by default\", () => {\n  expect(dt.startOf(\"minute\").toISOTime()).toBe(\"09:23:00.000Z\");\n});\n\ntest(\"DateTime#toISOTime() won't suppress milliseconds by default\", () => {\n  expect(dt.startOf(\"second\").toISOTime()).toBe(\"09:23:54.000Z\");\n});\n\ntest(\"DateTime#toISOTime({suppressMilliseconds: true}) won't suppress milliseconds if they're nonzero\", () => {\n  expect(dt.toISOTime({ suppressMilliseconds: true })).toBe(\"09:23:54.123Z\");\n});\n\ntest(\"DateTime#toISOTime({suppressMilliseconds: true}) will suppress milliseconds if they're zero\", () => {\n  expect(dt.set({ millisecond: 0 }).toISOTime({ suppressMilliseconds: true })).toBe(\"09:23:54Z\");\n});\n\ntest(\"DateTime#toISOTime({suppressSeconds: true}) won't suppress milliseconds if they're nonzero\", () => {\n  expect(dt.toISOTime({ suppressSeconds: true })).toBe(\"09:23:54.123Z\");\n});\n\ntest(\"DateTime#toISOTime({suppressSeconds: true}) will suppress milliseconds if they're zero\", () => {\n  expect(dt.set({ second: 0, millisecond: 0 }).toISOTime({ suppressSeconds: true })).toBe(\"09:23Z\");\n});\n\ntest(\"DateTime#toISOTime() handles other offsets\", () => {\n  expect(dt.setZone(\"America/New_York\").toISOTime()).toBe(\"05:23:54.123-04:00\");\n});\n\ntest(\"DateTime#toISOTime() can omit the offset\", () => {\n  expect(dt.toISOTime({ includeOffset: false })).toBe(\"09:23:54.123\");\n});\n\ntest(\"DateTime#toISOTime() can output the basic format\", () => {\n  expect(dt.toISOTime({ format: \"basic\" })).toBe(\"092354.123Z\");\n  expect(dt.setZone(\"America/New_York\").toISOTime({ format: \"basic\" })).toBe(\"052354.123-0400\");\n  const dt2 = dt.set({ second: 0, millisecond: 0 });\n  expect(dt2.toISOTime({ format: \"basic\", suppressMilliseconds: true })).toBe(\"092300Z\");\n  expect(dt2.toISOTime({ format: \"basic\", suppressSeconds: true })).toBe(\"0923Z\");\n});\n\ntest(\"DateTime#toISOTime can include the prefix\", () => {\n  expect(dt.toISOTime({ includePrefix: true })).toBe(\"T09:23:54.123Z\");\n  expect(dt.toISOTime({ format: \"basic\", includePrefix: true })).toBe(\"T092354.123Z\");\n});\n\ntest(\"DateTime#toISOTime() returns null for invalid DateTimes\", () => {\n  expect(invalid.toISOTime()).toBe(null);\n});\n\ntest(\"DateTime#toISOTime({precision}) truncates time to desired precision\", () => {\n  expect(dt.toISOTime({ precision: \"millisecond\" })).toBe(\"09:23:54.123Z\");\n  expect(dt.toISOTime({ precision: \"second\" })).toBe(\"09:23:54Z\");\n  expect(dt.toISOTime({ precision: \"minute\" })).toBe(\"09:23Z\");\n  expect(dt.toISOTime({ precision: \"hours\" })).toBe(\"09Z\");\n  expect(dt.toISOTime({ precision: \"days\" })).toBe(\"Z\");\n  expect(dt.toISOTime({ precision: \"months\" })).toBe(\"Z\");\n  expect(dt.toISOTime({ precision: \"years\" })).toBe(\"Z\");\n});\n\ntest(\"DateTime#toISOTime({precision}) throws when the precision is invalid\", () => {\n  expect(() => dt.toISOTime({ precision: \"ms\" })).toThrow(InvalidUnitError);\n  expect(() => dt.toISOTime({ precision: \"xxx\" })).toThrow(InvalidUnitError);\n  expect(() => dt.toISOTime({ precision: null })).toThrow(TypeError);\n});\n\ntest(\"DateTime#toISOTime({precision, suppressSeconds}) suppresses when precision is > 'hour'\", () => {\n  const dt2 = dt.set({ second: 0, millisecond: 0 });\n  expect(dt2.toISOTime({ precision: \"year\", suppressSeconds: true })).toBe(\"Z\");\n  expect(dt2.toISOTime({ precision: \"month\", suppressSeconds: true })).toBe(\"Z\");\n  expect(dt2.toISOTime({ precision: \"day\", suppressSeconds: true })).toBe(\"Z\");\n  expect(dt2.toISOTime({ precision: \"hour\", suppressSeconds: true })).toBe(\"09Z\");\n  expect(dt2.toISOTime({ precision: \"minute\", suppressSeconds: true })).toBe(\"09:23Z\");\n  expect(dt2.toISOTime({ precision: \"second\", suppressSeconds: true })).toBe(\"09:23Z\");\n  expect(dt2.toISOTime({ precision: \"millisecond\", suppressSeconds: true })).toBe(\"09:23Z\");\n});\n\ntest(\"DateTime#toISOTime({precision, suppressMilliseconds}) suppresses when precision is > 'minute'\", () => {\n  const dt2 = dt.set({ millisecond: 0 });\n  expect(dt2.toISOTime({ precision: \"year\", suppressSeconds: true })).toBe(\"Z\");\n  expect(dt2.toISOTime({ precision: \"month\", suppressSeconds: true })).toBe(\"Z\");\n  expect(dt2.toISOTime({ precision: \"day\", suppressSeconds: true })).toBe(\"Z\");\n  expect(dt2.toISOTime({ precision: \"hour\", suppressMilliseconds: true })).toBe(\"09Z\");\n  expect(dt2.toISOTime({ precision: \"minute\", suppressMilliseconds: true })).toBe(\"09:23Z\");\n  expect(dt2.toISOTime({ precision: \"second\", suppressMilliseconds: true })).toBe(\"09:23:54Z\");\n  expect(dt2.toISOTime({ precision: \"millisecond\", suppressMilliseconds: true })).toBe(\"09:23:54Z\");\n});\n\n//------\n// #toRFC2822()\n//------\n\ntest(\"DateTime#toRFC2822() returns an RFC 2822 date\", () => {\n  expect(dt.toUTC().toRFC2822()).toBe(\"Tue, 25 May 1982 09:23:54 +0000\");\n  expect(dt.setZone(\"America/New_York\").toRFC2822()).toBe(\"Tue, 25 May 1982 05:23:54 -0400\");\n  expect(dt.set({ hour: 15 }).toRFC2822()).toBe(\"Tue, 25 May 1982 15:23:54 +0000\");\n});\n\ntest(\"DateTime#toRFC2822() returns null for invalid DateTimes\", () => {\n  expect(invalid.toRFC2822()).toBe(null);\n});\n\n//------\n// #toHTTP()\n//------\n\ntest(\"DateTime#toHTTP() returns an RFC 1123 date\", () => {\n  expect(dt.toUTC().toHTTP()).toBe(\"Tue, 25 May 1982 09:23:54 GMT\");\n  expect(dt.setZone(\"America/New_York\").toHTTP()).toBe(\"Tue, 25 May 1982 09:23:54 GMT\");\n  expect(dt.plus({ hours: 10 }).toHTTP()).toBe(\"Tue, 25 May 1982 19:23:54 GMT\");\n});\n\ntest(\"DateTime#toHTTP() returns null for invalid DateTimes\", () => {\n  expect(invalid.toHTTP()).toBe(null);\n});\n\n//------\n// #toSQLDate()\n//------\n\ntest(\"DateTime#toSQLDate() returns SQL date\", () => {\n  expect(dt.toUTC().toSQLDate()).toBe(\"1982-05-25\");\n  expect(dt.setZone(\"America/New_York\").toSQLDate()).toBe(\"1982-05-25\");\n});\n\ntest(\"DateTime#toSQLDate() returns null for invalid DateTimes\", () => {\n  expect(invalid.toSQLDate()).toBe(null);\n});\n\n//------\n// #toSQLTime()\n//------\n\ntest(\"DateTime#toSQLTime() returns SQL time\", () => {\n  expect(dt.toUTC().toSQLTime()).toBe(\"09:23:54.123 Z\");\n  expect(dt.setZone(\"America/New_York\").toSQLTime()).toBe(\"05:23:54.123 -04:00\");\n});\n\ntest(\"DateTime#toSQLTime() accepts an includeOffset option\", () => {\n  expect(dt.toUTC().toSQLTime({ includeOffset: false })).toBe(\"09:23:54.123\");\n  expect(dt.setZone(\"America/New_York\").toSQLTime({ includeOffset: false })).toBe(\"05:23:54.123\");\n});\n\ntest(\"DateTime#toSQLTime() accepts an includeOffsetSpace option\", () => {\n  expect(dt.setZone(\"America/New_York\").toSQLTime({ includeOffsetSpace: false })).toBe(\n    \"05:23:54.123-04:00\"\n  );\n});\n\ntest(\"DateTime#toSQLTime() accepts an includeZone option\", () => {\n  expect(dt.toUTC().toSQLTime({ includeZone: true })).toBe(\"09:23:54.123 UTC\");\n  expect(dt.setZone(\"America/New_York\").toSQLTime({ includeZone: true })).toBe(\n    \"05:23:54.123 America/New_York\"\n  );\n});\n\ntest(\"DateTime#toSQLTime() returns null for invalid DateTimes\", () => {\n  expect(invalid.toSQLTime()).toBe(null);\n});\n\n//------\n// #toSQL()\n//------\n\ntest(\"DateTime#toSQL() returns SQL date time\", () => {\n  expect(dt.toUTC().toSQL()).toBe(\"1982-05-25 09:23:54.123 Z\");\n  expect(dt.setZone(\"America/New_York\").toSQL()).toBe(\"1982-05-25 05:23:54.123 -04:00\");\n});\n\ntest(\"DateTime#toSQL() accepts space option\", () => {\n  expect(dt.setZone(\"America/New_York\").toSQL({ includeOffsetSpace: false })).toBe(\n    \"1982-05-25 05:23:54.123-04:00\"\n  );\n});\n\ntest(\"DateTime#toSQL() accepts an includeOffset option\", () => {\n  expect(dt.toUTC().toSQL({ includeOffset: false })).toBe(\"1982-05-25 09:23:54.123\");\n  expect(dt.setZone(\"America/New_York\").toSQL({ includeOffset: false })).toBe(\n    \"1982-05-25 05:23:54.123\"\n  );\n});\n\ntest(\"DateTime#toSQL() accepts an includeZone option\", () => {\n  expect(dt.toUTC().toSQL({ includeZone: true })).toBe(\"1982-05-25 09:23:54.123 UTC\");\n  expect(dt.setZone(\"America/New_York\").toSQL({ includeZone: true })).toBe(\n    \"1982-05-25 05:23:54.123 America/New_York\"\n  );\n});\n\ntest(\"DateTime#toSQL() returns null for invalid DateTimes\", () => {\n  expect(invalid.toSQL()).toBe(null);\n});\n\n//------\n// #toString()\n//-------\ntest(\"DateTime#toString() returns the ISO time\", () => {\n  expect(dt.toUTC(-6 * 60).toString()).toBe(\"1982-05-25T03:23:54.123-06:00\");\n});\n\ntest(\"DateTime#toString() returns something different for invalid DateTimes\", () => {\n  expect(invalid.toString()).toBe(\"Invalid DateTime\");\n});\n\n//------\n// #toLocaleString()\n//-------\ntest(\"DateTime#toLocaleString returns a sensible string by default\", () => {\n  expect(dt.reconfigure({ locale: \"en-US\" }).toLocaleString()).toBe(\"5/25/1982\");\n});\n\ntest(\"DateTime#toLocaleString lets the locale set the numbering system\", () => {\n  expect(dt.reconfigure({ locale: \"ja-JP\" }).toLocaleString({ hour: \"numeric\" })).toBe(\"9時\");\n});\n\ntest(\"DateTime#toLocaleString accepts locale settings from the dateTime\", () => {\n  expect(dt.reconfigure({ locale: \"be\" }).toLocaleString()).toBe(\"25.5.1982\");\n});\n\ntest(\"DateTime#toLocaleString accepts numbering system settings from the dateTime\", () => {\n  expect(dt.reconfigure({ numberingSystem: \"beng\" }).toLocaleString()).toBe(\"৫/২৫/১৯৮২\");\n});\n\ntest(\"DateTime#toLocaleString accepts output calendar settings from the dateTime\", () => {\n  expect(dt.reconfigure({ outputCalendar: \"islamic\" }).toLocaleString()).toBe(\"8/2/1402 AH\");\n});\n\ntest(\"DateTime#toLocaleString accepts options to the formatter\", () => {\n  expect(dt.toLocaleString({ weekday: \"short\" }).indexOf(\"Tue\") >= 0).toBeTruthy();\n});\n\ntest(\"DateTime#toLocaleString can override the dateTime's locale\", () => {\n  expect(dt.reconfigure({ locale: \"be\" }).toLocaleString({}, { locale: \"fr\" })).toBe(\"25/05/1982\");\n});\n\ntest(\"DateTime#toLocaleString can override the dateTime's numbering system\", () => {\n  expect(\n    dt.reconfigure({ numberingSystem: \"beng\" }).toLocaleString({ numberingSystem: \"mong\" })\n  ).toBe(\"᠕/᠒᠕/᠑᠙᠘᠒\");\n});\n\ntest(\"DateTime#toLocaleString can override the dateTime's output calendar\", () => {\n  expect(\n    dt.reconfigure({ outputCalendar: \"islamic\" }).toLocaleString({}, { outputCalendar: \"coptic\" })\n  ).toBe(\"9/17/1698 ERA1\");\n});\n\ntest(\"DateTime#toLocaleString() returns something different for invalid DateTimes\", () => {\n  expect(invalid.toLocaleString()).toBe(\"Invalid DateTime\");\n});\n\ntest(\"DateTime#toLocaleString() shows things in the right IANA zone\", () => {\n  expect(dt.setZone(\"America/New_York\").toLocaleString(DateTime.DATETIME_SHORT)).toBe(\n    \"5/25/1982, 5:23 AM\"\n  );\n});\n\ntest(\"DateTime#toLocaleString() shows things in the right fixed-offset zone\", () => {\n  expect(dt.setZone(\"UTC-8\").toLocaleString(DateTime.DATETIME_SHORT)).toBe(\"5/25/1982, 1:23 AM\");\n});\n\ntest(\"DateTime#toLocaleString() shows things in the right fixed-offset zone when showing the zone\", () => {\n  expect(dt.setZone(\"UTC-8\").toLocaleString(DateTime.DATETIME_FULL)).toBe(\n    \"May 25, 1982 at 1:23 AM GMT-8\"\n  );\n});\n\ntest(\"DateTime#toLocaleString() shows things with UTC if fixed-offset zone with 0 offset is used\", () => {\n  expect(dt.setZone(\"UTC\").toLocaleString(DateTime.DATETIME_FULL)).toBe(\n    \"May 25, 1982 at 9:23 AM UTC\"\n  );\n});\n\ntest(\"DateTime#toLocaleString() does the best it can with unsupported fixed-offset zone when showing the zone\", () => {\n  expect(dt.setZone(\"UTC+4:30\").toLocaleString(DateTime.DATETIME_FULL)).toBe(\n    \"May 25, 1982 at 1:53\\u202FPM UTC+4:30\"\n  );\n});\n\ntest(\"DateTime#toLocaleString() does the best it can with unsupported fixed-offset zone with timeStyle full\", () => {\n  expect(dt.setZone(\"UTC+4:30\").toLocaleString({ timeStyle: \"full\" })).toBe(\n    \"1:53:54\\u202FPM UTC+4:30\"\n  );\n});\n\ntest(\"DateTime#toLocaleString() shows things in the right custom zone\", () => {\n  expect(dt.setZone(new CustomZone(\"CUSTOM\", 30)).toLocaleString(DateTime.DATETIME_SHORT)).toBe(\n    \"5/25/1982, 9:53\\u202FAM\"\n  );\n});\n\ntest(\"DateTime#toLocaleString() shows things in the right custom zone when showing the zone\", () => {\n  expect(dt.setZone(new CustomZone(\"CUSTOM\", 30)).toLocaleString(DateTime.DATETIME_FULL)).toBe(\n    \"May 25, 1982 at 9:53\\u202FAM CUST\"\n  );\n});\n\ntest(\"DateTime#toLocaleString() shows things in the right custom zone with timeStyle full\", () => {\n  expect(dt.setZone(new CustomZone(\"CUSTOM\", 30)).toLocaleString({ timeStyle: \"full\" })).toBe(\n    \"9:53:54\\u202FAM CUSTOM\"\n  );\n});\n\ntest(\"DateTime#toLocaleString uses locale-appropriate time formats\", () => {\n  expect(dt.reconfigure({ locale: \"en-US\" }).toLocaleString(DateTime.TIME_SIMPLE)).toBe(\"9:23 AM\");\n  expect(dt.reconfigure({ locale: \"en-US\" }).toLocaleString(DateTime.TIME_24_SIMPLE)).toBe(\"09:23\");\n\n  // France has 24-hour time by default\n  expect(dt.reconfigure({ locale: \"fr\" }).toLocaleString(DateTime.TIME_SIMPLE)).toBe(\"09:23\");\n  expect(dt.reconfigure({ locale: \"fr\" }).toLocaleString(DateTime.TIME_24_SIMPLE)).toBe(\"09:23\");\n\n  // For whatever reason, Spain doesn't prefix with \"0\"\n  expect(dt.reconfigure({ locale: \"es\" }).toLocaleString(DateTime.TIME_SIMPLE)).toBe(\"9:23\");\n  expect(dt.reconfigure({ locale: \"es\" }).toLocaleString(DateTime.TIME_24_SIMPLE)).toBe(\"9:23\");\n});\n\ntest(\"DateTime#toLocaleString() respects language tags\", () => {\n  expect(dt.reconfigure({ locale: \"en-US-u-hc-h23\" }).toLocaleString(DateTime.TIME_SIMPLE)).toBe(\n    \"09:23\"\n  );\n});\n\ntest(\"DateTime#toLocaleString() accepts a zone even when the zone is set\", () => {\n  expect(\n    dt.toLocaleString({\n      hour: \"numeric\",\n      minute: \"numeric\",\n      timeZoneName: \"short\",\n      timeZone: \"America/Los_Angeles\",\n    })\n  ).toBe(\"2:23 AM PDT\");\n});\n\n//------\n// #resolvedLocaleOpts()\n//------\ntest(\"DateTime#resolvedLocaleOpts returns a thing\", () => {\n  const res = DateTime.now().resolvedLocaleOptions();\n\n  expect(res.outputCalendar).toBeDefined();\n  expect(res.locale).toBeDefined();\n  expect(res.numberingSystem).toBeDefined();\n});\n\ntest(\"DateTime#resolvedLocaleOpts reflects changes to the locale\", () => {\n  const res = DateTime.now()\n    .reconfigure({\n      locale: \"be\",\n      numberingSystem: \"mong\",\n      outputCalendar: \"coptic\",\n    })\n    .resolvedLocaleOptions();\n\n  expect(res.locale).toBe(\"be-u-ca-coptic-nu-mong\");\n  expect(res.outputCalendar).toBe(\"coptic\");\n  expect(res.numberingSystem).toBe(\"mong\");\n});\n\ntest(\"DateTime#resolvedLocaleOpts can override with options\", () => {\n  const res = DateTime.now().resolvedLocaleOptions({\n    locale: \"be\",\n    numberingSystem: \"mong\",\n    outputCalendar: \"coptic\",\n  });\n\n  expect(res.locale).toBe(\"be-u-ca-coptic-nu-mong\");\n  expect(res.outputCalendar).toBe(\"coptic\");\n  expect(res.numberingSystem).toBe(\"mong\");\n});\n\n//------\n// #toLocaleParts()\n//------\n\ntest(\"DateTime#toLocaleParts returns a en-US by default\", () => {\n  expect(dt.reconfigure({ locale: \"en-US\" }).toLocaleParts()).toEqual([\n    { type: \"month\", value: \"5\" },\n    { type: \"literal\", value: \"/\" },\n    { type: \"day\", value: \"25\" },\n    { type: \"literal\", value: \"/\" },\n    { type: \"year\", value: \"1982\" },\n  ]);\n});\n\ntest(\"DateTime#toLocaleParts accepts locale settings from the dateTime\", () => {\n  expect(dt.reconfigure({ locale: \"be\" }).toLocaleParts()).toEqual([\n    { type: \"day\", value: \"25\" },\n    { type: \"literal\", value: \".\" },\n    { type: \"month\", value: \"5\" },\n    { type: \"literal\", value: \".\" },\n    { type: \"year\", value: \"1982\" },\n  ]);\n});\n\ntest(\"DateTime#toLocaleParts can override the dateTime's locale\", () => {\n  expect(dt.reconfigure({ locale: \"be\" }).toLocaleParts({ locale: \"fr\" })).toEqual([\n    { type: \"day\", value: \"25\" },\n    { type: \"literal\", value: \"/\" },\n    { type: \"month\", value: \"05\" },\n    { type: \"literal\", value: \"/\" },\n    { type: \"year\", value: \"1982\" },\n  ]);\n});\n\ntest(\"DateTime#toLocaleParts accepts date formatting options\", () => {\n  expect(dt.toLocaleParts(DateTime.TIME_24_SIMPLE)).toEqual([\n    { type: \"hour\", value: \"09\" },\n    { type: \"literal\", value: \":\" },\n    { type: \"minute\", value: \"23\" },\n  ]);\n});\n\ntest(\"DateTime#toLocaleParts returns empty for invalid DateTimes\", () => {\n  expect(invalid.toLocaleParts()).toEqual([]);\n});\n"
  },
  {
    "path": "test/datetime/getters.test.js",
    "content": "/* global test expect */\n\nimport { DateTime } from \"../../src/luxon\";\nimport Settings from \"../../src/settings\";\n\nconst dateTime = DateTime.fromJSDate(new Date(1982, 4, 25, 9, 23, 54, 123)),\n  utc = DateTime.fromMillis(Date.UTC(1982, 4, 25, 9, 23, 54, 123)).toUTC(),\n  inv = DateTime.invalid(\"I said so\");\n\n//------\n// year/month/day/hour/minute/second/millisecond\n//------\ntest(\"DateTime#year returns the year\", () => {\n  expect(dateTime.year).toBe(1982);\n  expect(utc.year).toBe(1982);\n  expect(inv.year).toBeFalsy();\n});\n\ntest(\"DateTime#month returns the (1-indexed) month\", () => {\n  expect(dateTime.month).toBe(5);\n  expect(utc.month).toBe(5);\n  expect(inv.month).toBeFalsy();\n});\n\ntest(\"DateTime#day returns the day\", () => {\n  expect(dateTime.day).toBe(25);\n  expect(utc.day).toBe(25);\n  expect(inv.day).toBeFalsy();\n});\n\ntest(\"DateTime#hour returns the hour\", () => {\n  expect(dateTime.hour).toBe(9);\n  expect(utc.hour).toBe(9);\n  expect(inv.hour).toBeFalsy();\n});\n\ntest(\"DateTime#minute returns the minute\", () => {\n  expect(dateTime.minute).toBe(23);\n  expect(utc.minute).toBe(23);\n  expect(inv.minute).toBeFalsy();\n});\n\ntest(\"DateTime#second returns the second\", () => {\n  expect(dateTime.second).toBe(54);\n  expect(utc.second).toBe(54);\n  expect(inv.second).toBeFalsy();\n});\n\ntest(\"DateTime#millisecond returns the millisecond\", () => {\n  expect(dateTime.millisecond).toBe(123);\n  expect(utc.millisecond).toBe(123);\n  expect(inv.millisecond).toBeFalsy();\n});\n\n//------\n// weekYear/weekNumber/weekday\n//------\ntest(\"DateTime#weekYear returns the weekYear\", () => {\n  expect(dateTime.weekYear).toBe(1982);\n  // test again bc caching\n  expect(dateTime.weekYear).toBe(1982);\n});\n\ntest(\"DateTime#weekNumber returns the weekNumber\", () => {\n  expect(dateTime.weekNumber).toBe(21);\n  // test again bc caching\n  expect(dateTime.weekNumber).toBe(21);\n});\n\ntest(\"DateTime#weekday returns the weekday\", () => {\n  expect(dateTime.weekday).toBe(2);\n  // test again bc caching\n  expect(dateTime.weekday).toBe(2);\n});\n\ntest(\"DateTime#weekday returns the weekday for older dates\", () => {\n  const dt = DateTime.fromObject({ year: 43, month: 4, day: 4 });\n  expect(dt.weekday).toBe(6);\n});\n\n//------\n// weekdayShort/weekdayLong\n//------\ntest(\"DateTime#weekdayShort returns the short human readable weekday for en-US locale\", () => {\n  expect(dateTime.setLocale(\"en-US\").weekdayShort).toBe(\"Tue\");\n});\n\ntest(\"DateTime#weekdayLong returns the human readable weekday for en-US locale\", () => {\n  expect(dateTime.setLocale(\"en-US\").weekdayLong).toBe(\"Tuesday\");\n});\n\ntest(\"DateTime#weekdayShort returns the short human readable weekday for fr locale\", () => {\n  expect(dateTime.setLocale(\"fr\").weekdayShort).toBe(\"mar.\");\n});\n\ntest(\"DateTime#weekdayLong returns the human readable weekday for fr locale\", () => {\n  expect(dateTime.setLocale(\"fr\").weekdayLong).toBe(\"mardi\");\n});\n\ntest(\"DateTime#weekdayShort returns null for invalid DateTimes\", () => {\n  expect(inv.weekdayShort).toBe(null);\n});\n\ntest(\"DateTime#weekdayLong returns null for invalid DateTimes\", () => {\n  expect(inv.weekdayLong).toBe(null);\n});\n\n//------\n// monthShort/monthLong\n//------\ntest(\"DateTime#monthShort returns the short human readable month\", () => {\n  expect(dateTime.setLocale(\"en-US\").monthShort).toBe(\"May\");\n});\n\ntest(\"DateTime#monthLong returns the human readable month\", () => {\n  expect(dateTime.setLocale(\"en-US\").monthLong).toBe(\"May\");\n});\n\ntest(\"DateTime#monthShort returns the short human readable month\", () => {\n  expect(dateTime.minus({ months: 1 }).setLocale(\"en-US\").monthShort).toBe(\"Apr\");\n});\n\ntest(\"DateTime#monthLong returns the human readable month\", () => {\n  expect(dateTime.minus({ months: 1 }).setLocale(\"en-US\").monthLong).toBe(\"April\");\n});\n\ntest(\"DateTime#monthShort returns the short human readable month for fr locale\", () => {\n  expect(dateTime.minus({ months: 1 }).setLocale(\"fr\").monthShort).toBe(\"avr.\");\n});\n\ntest(\"DateTime#monthLong returns the human readable month for fr locale\", () => {\n  expect(dateTime.minus({ months: 1 }).setLocale(\"fr\").monthLong).toBe(\"avril\");\n});\n\ntest(\"DateTime#monthLong returns null for invalid DateTimes\", () => {\n  expect(inv.monthLong).toBe(null);\n});\n\ntest(\"DateTime#monthShort returns null for invalid DateTimes\", () => {\n  expect(inv.monthShort).toBe(null);\n});\n\n//------\n// ordinal\n//------\n\ntest(\"DateTime#ordinal returns the ordinal\", () => {\n  expect(dateTime.ordinal).toBe(145);\n});\n\ntest(\"DateTime#ordinal returns NaN for invalid DateTimes\", () => {\n  expect(inv.ordinal).toBeFalsy();\n});\n\n//------\n// get\n//------\ntest(\"DateTime#get can retrieve any unit\", () => {\n  expect(dateTime.get(\"ordinal\")).toBe(145);\n  expect(dateTime.get(\"year\")).toBe(1982);\n  expect(dateTime.get(\"weekNumber\")).toBe(21);\n});\n\ntest(\"DateTime#get returns undefined for invalid units\", () => {\n  expect(dateTime.get(\"plurp\")).toBeUndefined();\n});\n\n//------\n// locale\n//------\ntest(\"DateTime#locale returns the locale\", () => {\n  const dt = DateTime.now().reconfigure({ locale: \"be\" });\n  expect(dt.locale).toBe(\"be\");\n});\n\n//------\n// zone/zoneName\n//------\ntest(\"DateTime#zone returns the time zone\", () => {\n  expect(dateTime.zone).toBe(Settings.defaultZone);\n});\n\ntest(\"DateTime#zoneName returns the name of the time zone\", () => {\n  expect(dateTime.zoneName).toBe(Settings.defaultZone.name);\n});\n\n//------\n// Misc\n//------\ntest(\"Invalid DateTimes have unhelpful getters\", () => {\n  const i = DateTime.invalid(\"because\");\n  expect(i.year).toBeFalsy();\n  expect(i.month).toBeFalsy();\n  expect(i.day).toBeFalsy();\n  expect(i.hour).toBeFalsy();\n  expect(i.minute).toBeFalsy();\n  expect(i.second).toBeFalsy();\n  expect(i.millisecond).toBeFalsy();\n  expect(i.weekYear).toBeFalsy();\n  expect(i.weekNumber).toBeFalsy();\n  expect(i.weekday).toBeFalsy();\n});\n"
  },
  {
    "path": "test/datetime/info.test.js",
    "content": "/* global test expect */\n\nimport { DateTime } from \"../../src/luxon\";\n\nconst dateTime = DateTime.fromJSDate(new Date(1982, 4, 25, 9, 23, 54, 123));\n\n//------\n// #toObject\n//-------\ntest(\"DateTime#toObject returns the object\", () => {\n  expect(dateTime.toObject()).toEqual({\n    year: 1982,\n    month: 5,\n    day: 25,\n    hour: 9,\n    minute: 23,\n    second: 54,\n    millisecond: 123,\n  });\n});\n\ntest(\"DateTime#toObject accepts a flag to return config\", () => {\n  expect(dateTime.toObject({ includeConfig: true })).toEqual({\n    year: 1982,\n    month: 5,\n    day: 25,\n    hour: 9,\n    minute: 23,\n    second: 54,\n    millisecond: 123,\n    locale: \"en-US\",\n    numberingSystem: null,\n    outputCalendar: null,\n  });\n});\n\ntest(\"DateTime#toObject returns an empty object for invalid DateTimes\", () => {\n  expect(DateTime.invalid(\"because\").toObject()).toEqual({});\n});\n"
  },
  {
    "path": "test/datetime/invalid.test.js",
    "content": "/* global test expect */\n\nimport { DateTime, Settings } from \"../../src/luxon\";\n\nconst organic1 = DateTime.utc(2014, 13, 33),\n  // not an actual Wednesday\n  organic2 = DateTime.fromObject({ weekday: 3, year: 1982, month: 5, day: 25 }, { zone: \"UTC\" }),\n  organic3 = DateTime.fromObject({ year: 1982, month: 5, day: 25, hour: 27 }),\n  organic4 = DateTime.fromObject(\n    { year: 1982, month: 5, day: 25, hour: 2 },\n    { zone: \"America/Lasers\" }\n  );\n\ntest(\"Explicitly invalid dates are invalid\", () => {\n  const dt = DateTime.invalid(\"just because\", \"seriously, just because\");\n  expect(dt.isValid).toBe(false);\n  expect(dt.invalidReason).toBe(\"just because\");\n  expect(dt.invalidExplanation).toBe(\"seriously, just because\");\n});\n\ntest(\"Invalid creations are invalid\", () => {\n  expect(organic1.isValid).toBe(false);\n  expect(organic2.isValid).toBe(false);\n  expect(organic3.isValid).toBe(false);\n});\n\ntest(\"invalid zones result in invalid dates\", () => {\n  expect(DateTime.now().setZone(\"America/Lasers\").isValid).toBe(false);\n  expect(DateTime.now().setZone(\"America/Lasers\").invalidReason).toBe(\"unsupported zone\");\n\n  expect(DateTime.local({ zone: \"America/Lasers\" }).isValid).toBe(false);\n  expect(DateTime.local({ zone: \"America/Lasers\" }).invalidReason).toBe(\"unsupported zone\");\n\n  expect(DateTime.local(1982, { zone: \"America/Lasers\" }).isValid).toBe(false);\n  expect(DateTime.local(1982, { zone: \"America/Lasers\" }).invalidReason).toBe(\"unsupported zone\");\n\n  expect(DateTime.fromJSDate(new Date(), { zone: \"America/Lasers\" }).isValid).toBe(false);\n  expect(DateTime.fromJSDate(new Date(), { zone: \"America/Lasers\" }).invalidReason).toBe(\n    \"unsupported zone\"\n  );\n\n  expect(DateTime.fromMillis(0, { zone: \"America/Lasers\" }).isValid).toBe(false);\n  expect(DateTime.fromMillis(0, { zone: \"America/Lasers\" }).invalidReason).toBe(\"unsupported zone\");\n});\n\ntest(\"Invalid DateTimes tell you why\", () => {\n  expect(organic1.invalidReason).toBe(\"unit out of range\");\n  expect(organic2.invalidReason).toBe(\"mismatched weekday\");\n  expect(organic3.invalidReason).toBe(\"unit out of range\");\n  expect(organic4.invalidReason).toBe(\"unsupported zone\");\n});\n\ntest(\"Invalid DateTimes can provide an extended explanation\", () => {\n  expect(organic1.invalidExplanation).toBe(\n    \"you specified 13 (of type number) as a month, which is invalid\"\n  );\n  expect(organic2.invalidExplanation).toBe(\n    \"you can't specify both a weekday of 3 and a date of 1982-05-25T00:00:00.000Z\"\n  );\n  expect(organic3.invalidExplanation).toBe(\n    \"you specified 27 (of type number) as a hour, which is invalid\"\n  );\n});\n\ntest(\"Invalid DateTimes return invalid Dates\", () => {\n  expect(organic1.toJSDate().valueOf()).toBe(NaN);\n});\n\ntest(\"Diffing invalid DateTimes creates invalid Durations\", () => {\n  expect(organic1.diff(DateTime.now()).isValid).toBe(false);\n  expect(DateTime.now().diff(organic1).isValid).toBe(false);\n});\n\ntest(\"throwOnInvalid throws\", () => {\n  try {\n    Settings.throwOnInvalid = true;\n    expect(() =>\n      DateTime.fromObject({\n        weekday: 3,\n        year: 1982,\n        month: 5,\n        day: 25,\n      })\n    ).toThrow();\n  } finally {\n    Settings.throwOnInvalid = false;\n  }\n});\n\ntest(\"DateTime.invalid throws if you don't provide a reason\", () => {\n  expect(() => DateTime.invalid()).toThrow();\n});\n\ntest(\"throwOnInvalid throws if year is too big\", () => {\n  try {\n    Settings.throwOnInvalid = true;\n    expect(() =>\n      DateTime.fromObject({\n        year: 9999999,\n        month: 5,\n        day: 25,\n      })\n    ).toThrow();\n  } finally {\n    Settings.throwOnInvalid = false;\n  }\n});\n"
  },
  {
    "path": "test/datetime/localeWeek.test.js",
    "content": "/* global test expect */\n\nimport { DateTime, Info } from \"../../src/luxon\";\nimport Helpers, { supportsMinDaysInFirstWeek } from \"../helpers\";\n\nconst withDefaultWeekSettings = Helpers.setUnset(\"defaultWeekSettings\");\n\n//------\n// .startOf() with useLocaleWeeks\n//------\ntest(\"startOf(week) with useLocaleWeeks adheres to the locale\", () => {\n  const dt = DateTime.fromISO(\"2023-06-14T13:00:00Z\", { setZone: true });\n  expect(\n    dt.reconfigure({ locale: \"de-DE\" }).startOf(\"week\", { useLocaleWeeks: true }).toISO()\n  ).toBe(\"2023-06-12T00:00:00.000Z\");\n  expect(\n    dt.reconfigure({ locale: \"en-US\" }).startOf(\"week\", { useLocaleWeeks: true }).toISO()\n  ).toBe(\"2023-06-11T00:00:00.000Z\");\n});\n\ntest(\"startOf(week) with useLocaleWeeks handles crossing into the previous year\", () => {\n  const dt = DateTime.fromISO(\"2023-01-01T13:00:00Z\", { setZone: true });\n  expect(\n    dt.reconfigure({ locale: \"de-DE\" }).startOf(\"week\", { useLocaleWeeks: true }).toISO()\n  ).toBe(\"2022-12-26T00:00:00.000Z\");\n});\n\n//------\n// .endOf() with useLocaleWeeks\n//------\ntest(\"endOf(week) with useLocaleWeeks adheres to the locale\", () => {\n  const dt = DateTime.fromISO(\"2023-06-14T13:00:00Z\", { setZone: true });\n  expect(dt.reconfigure({ locale: \"de-DE\" }).endOf(\"week\", { useLocaleWeeks: true }).toISO()).toBe(\n    \"2023-06-18T23:59:59.999Z\"\n  );\n  expect(dt.reconfigure({ locale: \"en-US\" }).endOf(\"week\", { useLocaleWeeks: true }).toISO()).toBe(\n    \"2023-06-17T23:59:59.999Z\"\n  );\n});\n\ntest(\"endOf(week) with useLocaleWeeks handles crossing into the next year\", () => {\n  const dt = DateTime.fromISO(\"2022-12-31T13:00:00Z\", { setZone: true });\n  expect(dt.reconfigure({ locale: \"de-DE\" }).endOf(\"week\", { useLocaleWeeks: true }).toISO()).toBe(\n    \"2023-01-01T23:59:59.999Z\"\n  );\n});\n\n//------\n// .hasSame() with useLocaleWeeks\n//------\ntest(\"hasSame(week) with useLocaleWeeks adheres to the locale\", () => {\n  const dt1 = DateTime.fromISO(\"2023-06-11T03:00:00Z\", { setZone: true, locale: \"en-US\" });\n  const dt2 = DateTime.fromISO(\"2023-06-14T03:00:00Z\", { setZone: true, locale: \"en-US\" });\n  expect(dt1.hasSame(dt2, \"week\", { useLocaleWeeks: true })).toBe(true);\n\n  const dt3 = DateTime.fromISO(\"2023-06-14T03:00:00Z\", { setZone: true, locale: \"en-US\" });\n  const dt4 = DateTime.fromISO(\"2023-06-18T03:00:00Z\", { setZone: true, locale: \"en-US\" });\n  expect(dt3.hasSame(dt4, \"week\", { useLocaleWeeks: true })).toBe(false);\n});\n\ntest(\"hasSame(week) with useLocaleWeeks ignores the locale of otherDateTime\", () => {\n  const dt1 = DateTime.fromISO(\"2023-06-11T03:00:00Z\", { setZone: true, locale: \"en-US\" });\n  const dt2 = DateTime.fromISO(\"2023-06-14T03:00:00Z\", { setZone: true, locale: \"de-DE\" });\n  expect(dt1.hasSame(dt2, \"week\", { useLocaleWeeks: true })).toBe(true);\n  expect(dt2.hasSame(dt1, \"week\", { useLocaleWeeks: true })).toBe(false);\n});\n\n//------\n// .isWeekend\n//------\n\nconst week = [\n  \"2023-07-31T00:00:00Z\", // Monday\n  \"2023-08-01T00:00:00Z\",\n  \"2023-08-02T00:00:00Z\",\n  \"2023-08-03T00:00:00Z\",\n  \"2023-08-04T00:00:00Z\",\n  \"2023-08-05T00:00:00Z\",\n  \"2023-08-06T00:00:00Z\", // Sunday\n];\ntest(\"isWeekend in locale en-US reports Saturday and Sunday as weekend\", () => {\n  const dates = week.map(\n    (iso) => DateTime.fromISO(iso, { setZone: true, locale: \"en-US\" }).isWeekend\n  );\n  expect(dates).toStrictEqual([false, false, false, false, false, true, true]);\n});\n\ntest(\"isWeekend in locale he reports Friday and Saturday as weekend\", () => {\n  const dates = week.map((iso) => DateTime.fromISO(iso, { setZone: true, locale: \"he\" }).isWeekend);\n  expect(dates).toStrictEqual([false, false, false, false, true, true, false]);\n});\n\n//------\n// .localWeekNumber / .localWeekYear\n//------\ndescribe(\"localWeekNumber in locale de-DE\", () => {\n  test(\"Jan  1 2012 should be week 52, year 2011\", () => {\n    const dt = DateTime.fromISO(\"2012-01-01\", { locale: \"de-DE\" });\n    expect(dt.localWeekNumber).toBe(52);\n    expect(dt.localWeekYear).toBe(2011);\n  });\n  test(\"Jan  2 2012 should be week 1, year 2012\", () => {\n    const dt = DateTime.fromISO(\"2012-01-02\", { locale: \"de-DE\" });\n    expect(dt.localWeekNumber).toBe(1);\n    expect(dt.localWeekYear).toBe(2012);\n  });\n  test(\"Jan  8 2012 should be week 1, year 2012\", () => {\n    const dt = DateTime.fromISO(\"2012-01-08\", { locale: \"de-DE\" });\n    expect(dt.localWeekNumber).toBe(1);\n    expect(dt.localWeekYear).toBe(2012);\n  });\n  test(\"Jan  9 2012 should be week 2, year 2012\", () => {\n    const dt = DateTime.fromISO(\"2012-01-09\", { locale: \"de-DE\" });\n    expect(dt.localWeekNumber).toBe(2);\n    expect(dt.localWeekYear).toBe(2012);\n  });\n  test(\"Jan  15 2012 should be week 2, year 2012\", () => {\n    const dt = DateTime.fromISO(\"2012-01-15\", { locale: \"de-DE\" });\n    expect(dt.localWeekNumber).toBe(2);\n    expect(dt.localWeekYear).toBe(2012);\n  });\n});\n\ndescribe(\"localWeekNumber in locale en-US\", () => {\n  test(\"Jan  1 2012 should be week 1, year 2012\", () => {\n    const dt = DateTime.fromISO(\"2012-01-01\", { locale: \"en-US\" });\n    expect(dt.localWeekNumber).toBe(1);\n    expect(dt.localWeekYear).toBe(2012);\n  });\n  test(\"Jan  7 2012 should be week 1, year 2012\", () => {\n    const dt = DateTime.fromISO(\"2012-01-07\", { locale: \"en-US\" });\n    expect(dt.localWeekNumber).toBe(1);\n    expect(dt.localWeekYear).toBe(2012);\n  });\n  test(\"Jan  8 2012 should be week 2, year 2012\", () => {\n    const dt = DateTime.fromISO(\"2012-01-08\", { locale: \"en-US\" });\n    expect(dt.localWeekNumber).toBe(2);\n    expect(dt.localWeekYear).toBe(2012);\n  });\n  test(\"Jan  14 2012 should be week 2, year 2012\", () => {\n    const dt = DateTime.fromISO(\"2012-01-14\", { locale: \"en-US\" });\n    expect(dt.localWeekNumber).toBe(2);\n    expect(dt.localWeekYear).toBe(2012);\n  });\n  test(\"Jan  15 2012 should be week 3, year 2012\", () => {\n    const dt = DateTime.fromISO(\"2012-01-15\", { locale: \"en-US\" });\n    expect(dt.localWeekNumber).toBe(3);\n    expect(dt.localWeekYear).toBe(2012);\n  });\n});\n\n//------\n// .localWeekday\n//------\ndescribe(\"localWeekday in locale en-US\", () => {\n  test(\"Sunday should be reported as the 1st day of the week\", () => {\n    const dt = DateTime.fromISO(\"2023-08-06\", { locale: \"en-US\" });\n    expect(dt.localWeekday).toBe(1);\n  });\n  test(\"Monday should be reported as the 2nd day of the week\", () => {\n    const dt = DateTime.fromISO(\"2023-08-07\", { locale: \"en-US\" });\n    expect(dt.localWeekday).toBe(2);\n  });\n  test(\"Tuesday should be reported as the 3rd day of the week\", () => {\n    const dt = DateTime.fromISO(\"2023-08-08\", { locale: \"en-US\" });\n    expect(dt.localWeekday).toBe(3);\n  });\n  test(\"Wednesday should be reported as the 4th day of the week\", () => {\n    const dt = DateTime.fromISO(\"2023-08-09\", { locale: \"en-US\" });\n    expect(dt.localWeekday).toBe(4);\n  });\n  test(\"Thursday should be reported as the 5th day of the week\", () => {\n    const dt = DateTime.fromISO(\"2023-08-10\", { locale: \"en-US\" });\n    expect(dt.localWeekday).toBe(5);\n  });\n  test(\"Friday should be reported as the 6th day of the week\", () => {\n    const dt = DateTime.fromISO(\"2023-08-11\", { locale: \"en-US\" });\n    expect(dt.localWeekday).toBe(6);\n  });\n  test(\"Saturday should be reported as the 7th day of the week\", () => {\n    const dt = DateTime.fromISO(\"2023-08-12\", { locale: \"en-US\" });\n    expect(dt.localWeekday).toBe(7);\n  });\n});\n\ndescribe(\"localWeekday in locale de-DE\", () => {\n  test(\"Monday should be reported as the 1st day of the week\", () => {\n    const dt = DateTime.fromISO(\"2023-08-07\", { locale: \"de-DE\" });\n    expect(dt.localWeekday).toBe(1);\n  });\n  test(\"Tuesday should be reported as the 2nd day of the week\", () => {\n    const dt = DateTime.fromISO(\"2023-08-08\", { locale: \"de-DE\" });\n    expect(dt.localWeekday).toBe(2);\n  });\n  test(\"Wednesday should be reported as the 3rd day of the week\", () => {\n    const dt = DateTime.fromISO(\"2023-08-09\", { locale: \"de-DE\" });\n    expect(dt.localWeekday).toBe(3);\n  });\n  test(\"Thursday should be reported as the 4th day of the week\", () => {\n    const dt = DateTime.fromISO(\"2023-08-10\", { locale: \"de-DE\" });\n    expect(dt.localWeekday).toBe(4);\n  });\n  test(\"Friday should be reported as the 5th day of the week\", () => {\n    const dt = DateTime.fromISO(\"2023-08-11\", { locale: \"de-DE\" });\n    expect(dt.localWeekday).toBe(5);\n  });\n  test(\"Saturday should be reported as the 6th day of the week\", () => {\n    const dt = DateTime.fromISO(\"2023-08-12\", { locale: \"de-DE\" });\n    expect(dt.localWeekday).toBe(6);\n  });\n  test(\"Sunday should be reported as the 7th day of the week\", () => {\n    const dt = DateTime.fromISO(\"2023-08-13\", { locale: \"de-DE\" });\n    expect(dt.localWeekday).toBe(7);\n  });\n});\n\ndescribe(\"weeksInLocalWeekYear\", () => {\n  test(\"2018 should have 53 weeks in en-US\", () => {\n    expect(DateTime.local(2018, 6, 1, { locale: \"en-US\" }).weeksInLocalWeekYear).toBe(52);\n  });\n  test(\"2022 should have 53 weeks in en-US\", () => {\n    expect(DateTime.local(2022, 6, 1, { locale: \"en-US\" }).weeksInLocalWeekYear).toBe(\n      supportsMinDaysInFirstWeek() ? 53 : 52\n    );\n  });\n  test(\"2022 should have 52 weeks in de-DE\", () => {\n    expect(DateTime.local(2022, 6, 1, { locale: \"de-DE\" }).weeksInLocalWeekYear).toBe(52);\n  });\n  test(\"2020 should have 53 weeks in de-DE\", () => {\n    expect(DateTime.local(2020, 6, 1, { locale: \"de-DE\" }).weeksInLocalWeekYear).toBe(53);\n  });\n  test(\"2018 should have 52 weeks with minDays 1, start 7\", () => {\n    withDefaultWeekSettings(\n      {\n        minimalDays: 1,\n        firstDay: 7,\n        weekend: [6, 7],\n      },\n      () => {\n        expect(DateTime.local(2018, 6, 1).weeksInLocalWeekYear).toBe(52);\n      }\n    );\n  });\n  test(\"2022 should have 53 weeks with minDays 1, start 7\", () => {\n    withDefaultWeekSettings(\n      {\n        minimalDays: 1,\n        firstDay: 7,\n        weekend: [6, 7],\n      },\n      () => {\n        expect(DateTime.local(2022, 6, 1).weeksInLocalWeekYear).toBe(53);\n      }\n    );\n  });\n});\n\ndescribe(\"Week settings can be overridden\", () => {\n  test(\"Overridden week info should be reported by Info\", () => {\n    withDefaultWeekSettings({ firstDay: 3, minimalDays: 5, weekend: [4, 6] }, () => {\n      expect(Info.getStartOfWeek()).toBe(3);\n      expect(Info.getMinimumDaysInFirstWeek()).toBe(5);\n      expect(Info.getWeekendWeekdays()).toEqual([4, 6]);\n    });\n  });\n\n  test(\"Overridden week info should be reported by DateTime#isWeekend\", () => {\n    withDefaultWeekSettings({ firstDay: 7, minimalDays: 1, weekend: [1, 3] }, () => {\n      expect(DateTime.local(2022, 1, 31).isWeekend).toBe(true);\n      expect(DateTime.local(2022, 2, 1).isWeekend).toBe(false);\n      expect(DateTime.local(2022, 2, 2).isWeekend).toBe(true);\n      expect(DateTime.local(2022, 2, 3).isWeekend).toBe(false);\n      expect(DateTime.local(2022, 2, 4).isWeekend).toBe(false);\n      expect(DateTime.local(2022, 2, 5).isWeekend).toBe(false);\n      expect(DateTime.local(2022, 2, 6).isWeekend).toBe(false);\n    });\n  });\n  test(\"Overridden week info should be respected by DateTime accessors\", () => {\n    withDefaultWeekSettings({ firstDay: 7, minimalDays: 1, weekend: [6, 7] }, () => {\n      const dt = DateTime.local(2022, 1, 1, { locale: \"de-DE\" });\n      expect(dt.localWeekday).toBe(7);\n      expect(dt.localWeekNumber).toBe(1);\n      expect(dt.localWeekYear).toBe(2022);\n    });\n  });\n  test(\"Overridden week info should be respected by DateTime#set\", () => {\n    withDefaultWeekSettings({ firstDay: 7, minimalDays: 1, weekend: [6, 7] }, () => {\n      const dt = DateTime.local(2022, 1, 1, { locale: \"de-DE\" });\n      const modified = dt.set({ localWeekday: 1 });\n      expect(modified.year).toBe(2021);\n      expect(modified.month).toBe(12);\n      expect(modified.day).toBe(26);\n    });\n  });\n});\n"
  },
  {
    "path": "test/datetime/many.test.js",
    "content": "/* global test expect */\n\nimport { DateTime } from \"../../src/luxon\";\n\n//------\n// min\n//-------\ntest(\"DateTime.min returns the only dateTime if solo\", () => {\n  const m = DateTime.min(DateTime.fromJSDate(new Date(1982, 5, 25)));\n  expect(m).toBeTruthy();\n  expect(m.valueOf()).toBe(DateTime.fromJSDate(new Date(1982, 5, 25)).valueOf());\n});\n\ntest(\"DateTime.min returns the min dateTime\", () => {\n  const m = DateTime.min(\n    DateTime.fromJSDate(new Date(1982, 4, 25)),\n    DateTime.fromJSDate(new Date(1982, 3, 25)),\n    DateTime.fromJSDate(new Date(1982, 3, 26))\n  );\n  expect(m.valueOf()).toBe(DateTime.fromJSDate(new Date(1982, 3, 25)).valueOf());\n});\n\ntest(\"DateTime.min returns undefined if no argument\", () => {\n  const m = DateTime.min();\n  expect(m).toBeUndefined();\n});\n\ntest(\"DateTime.min is stable\", () => {\n  const m = DateTime.min(\n    DateTime.fromJSDate(new Date(1982, 4, 25)),\n    DateTime.fromJSDate(new Date(1982, 3, 25)).reconfigure({ locale: \"en-GB\" }),\n    DateTime.fromJSDate(new Date(1982, 3, 25)).reconfigure({ locale: \"en-US\" })\n  );\n  expect(m.locale).toBe(\"en-GB\");\n});\n\ntest(\"DateTime.min throws if you don't pass it DateTimes\", () => {\n  const dt = DateTime.fromJSDate(new Date(1982, 2, 25));\n  const notADt = \"flob\";\n\n  expect(() => DateTime.min(dt, notADt)).toThrow();\n  expect(() => DateTime.min(notADt)).toThrow();\n  expect(() => DateTime.min(notADt, notADt)).toThrow();\n});\n\n//------\n// max\n//-------\ntest(\"DateTime.max returns the only dateTime if solo\", () => {\n  const m = DateTime.max(DateTime.fromJSDate(new Date(1982, 5, 25)));\n  expect(m).toBeTruthy();\n  expect(m.valueOf()).toBe(DateTime.fromJSDate(new Date(1982, 5, 25)).valueOf());\n});\n\ntest(\"DateTime.max returns the max dateTime\", () => {\n  const m = DateTime.max(\n    DateTime.fromJSDate(new Date(1982, 5, 25)),\n    DateTime.fromJSDate(new Date(1982, 3, 25)),\n    DateTime.fromJSDate(new Date(1982, 3, 26))\n  );\n  expect(m.valueOf()).toBe(DateTime.fromJSDate(new Date(1982, 5, 25)).valueOf());\n});\n\ntest(\"DateTime.max returns undefined if no argument\", () => {\n  const m = DateTime.max();\n  expect(m).toBeUndefined();\n});\n\ntest(\"DateTime.max is stable\", () => {\n  const m = DateTime.max(\n    DateTime.fromJSDate(new Date(1982, 2, 25)),\n    DateTime.fromJSDate(new Date(1982, 3, 25)).reconfigure({ locale: \"en-GB\" }),\n    DateTime.fromJSDate(new Date(1982, 3, 25)).reconfigure({ locale: \"en-US\" })\n  );\n  expect(m.locale).toBe(\"en-GB\");\n});\n\ntest(\"DateTime.max throws if you don't pass it DateTimes\", () => {\n  const dt = DateTime.fromJSDate(new Date(1982, 2, 25));\n  const notADt = \"flob\";\n\n  expect(() => DateTime.max(dt, notADt)).toThrow();\n  expect(() => DateTime.max(notADt)).toThrow();\n  expect(() => DateTime.max(notADt, notADt)).toThrow();\n});\n"
  },
  {
    "path": "test/datetime/math.test.js",
    "content": "/* global test expect */\n\nimport { DateTime, Duration } from \"../../src/luxon\";\nimport { casualMatrix } from \"../../src/duration\";\n\nfunction createDateTime() {\n  return DateTime.fromObject({\n    year: 2010,\n    month: 2,\n    day: 3,\n\n    hour: 4,\n    minute: 5,\n    second: 6,\n    millisecond: 7,\n  });\n}\n\n//------\n// #plus()\n//------\ntest(\"DateTime#plus({ years: 1 }) adds a year\", () => {\n  const i = createDateTime().plus({ years: 1 });\n  expect(i.year).toBe(2011);\n});\n\ntest(\"DateTime#plus({quarter: 1}) adds a quarter\", () => {\n  const i = createDateTime().plus({ quarters: 1 });\n  expect(i.quarter).toBe(2);\n  expect(i.month).toBe(5);\n});\n\ntest(\"DateTime#plus({ months: 1 }) at the end of the month\", () => {\n  const i = DateTime.fromISO(\"2018-01-31T10:00\"),\n    later = i.plus({ months: 1 });\n  expect(later.day).toBe(28);\n  expect(later.month).toBe(2);\n});\n\ntest(\"DateTime#plus({ months: 1 }) at the end of the month in a leap year\", () => {\n  const i = DateTime.fromISO(\"2016-01-31T10:00\"),\n    later = i.plus({ months: 1 });\n  expect(later.day).toBe(29);\n  expect(later.month).toBe(2);\n});\n\ntest(\"DateTime#plus({ months: 13 }) at the end of the month\", () => {\n  const i = DateTime.fromISO(\"2015-01-31T10:00\"),\n    later = i.plus({ months: 13 });\n  expect(later.day).toBe(29);\n  expect(later.month).toBe(2);\n  expect(later.year).toBe(2016);\n});\n\ntest(\"DateTime#plus({ days: 1 }) keeps the same time across a DST\", () => {\n  const i = DateTime.fromISO(\"2016-03-12T10:00\", {\n      zone: \"America/Los_Angeles\",\n    }),\n    later = i.plus({ days: 1 });\n  expect(later.day).toBe(13);\n  expect(later.hour).toBe(10);\n});\n\ntest(\"DateTime#plus({ hours: 24 }) gains an hour to spring forward\", () => {\n  const i = DateTime.fromISO(\"2016-03-12T10:00\", {\n      zone: \"America/Los_Angeles\",\n    }),\n    later = i.plus({ hours: 24 });\n  expect(later.day).toBe(13);\n  expect(later.hour).toBe(11);\n});\n\n// #669\ntest(\"DateTime#plus({ days:0, hours: 24 }) gains an hour to spring forward\", () => {\n  const i = DateTime.fromISO(\"2016-03-12T10:00\", {\n      zone: \"America/Los_Angeles\",\n    }),\n    later = i.plus({ days: 0, hours: 24 });\n  expect(later.day).toBe(13);\n  expect(later.hour).toBe(11);\n});\n\ntest(\"DateTime#plus(Duration) adds the right amount of time\", () => {\n  const i = DateTime.fromISO(\"2016-03-12T10:13\"),\n    later = i.plus(Duration.fromObject({ day: 1, hour: 3, minute: 28 }));\n  expect(later.day).toBe(13);\n  expect(later.hour).toBe(13);\n  expect(later.minute).toBe(41);\n});\n\ntest(\"DateTime#plus(multiple) adds the right amount of time\", () => {\n  const i = DateTime.fromISO(\"2016-03-12T10:13\"),\n    later = i.plus({ days: 1, hours: 3, minutes: 28 });\n  expect(later.day).toBe(13);\n  expect(later.hour).toBe(13);\n  expect(later.minute).toBe(41);\n});\n\ntest(\"DateTime#plus maintains invalidity\", () => {\n  expect(DateTime.invalid(\"because\").plus({ day: 1 }).isValid).toBe(false);\n});\n\ntest(\"DateTime#plus works across the 100 barrier\", () => {\n  const d = DateTime.fromISO(\"0099-12-31\").plus({ day: 2 });\n  expect(d.year).toBe(100);\n  expect(d.month).toBe(1);\n  expect(d.day).toBe(2);\n});\n\ntest(\"DateTime#plus works across the 100 barrier when passing through February\", () => {\n  const d = DateTime.fromISO(\"0099-12-31\").plus({ day: 61 });\n  expect(d.year).toBe(100);\n  expect(d.month).toBe(3);\n  expect(d.day).toBe(2);\n});\n\ntest(\"DateTime#plus renders invalid when out of max. datetime range using days\", () => {\n  const d = DateTime.utc(1970, 1, 1, 0, 0, 0, 0).plus({ day: 1e8 + 1 });\n  expect(d.isValid).toBe(false);\n});\n\ntest(\"DateTime#plus renders invalid when out of max. datetime range using seconds\", () => {\n  const d = DateTime.utc(1970, 1, 1, 0, 0, 0, 0).plus({ second: 1e8 * 24 * 60 * 60 + 1 });\n  expect(d.isValid).toBe(false);\n});\n\ntest(\"DateTime#plus renders invalid when out of max. datetime range using IANAZone\", () => {\n  const d = DateTime.utc(1970, 1, 1, 0, 0, 0, 0)\n    .setZone(\"America/Los_Angeles\")\n    .plus({ second: 1e8 * 24 * 60 * 60 + 1 });\n  expect(d.isValid).toBe(false);\n});\n\ntest(\"DateTime#plus handles fractional days\", () => {\n  const d = DateTime.fromISO(\"2016-01-31T10:00\");\n  expect(d.plus({ days: 0.8 })).toEqual(d.plus({ hours: (24 * 4) / 5 }));\n  expect(d.plus({ days: 6.8 })).toEqual(d.plus({ days: 6, hours: (24 * 4) / 5 }));\n  expect(d.plus({ days: 6.8, milliseconds: 17 })).toEqual(\n    d.plus({ days: 6, milliseconds: 0.8 * 24 * 60 * 60 * 1000 + 17 })\n  );\n});\n\ntest(\"DateTime#plus handles fractional large units\", () => {\n  const units = [\"weeks\", \"months\", \"quarters\", \"years\"];\n\n  for (const unit of units) {\n    const d = DateTime.fromISO(\"2016-01-31T10:00\");\n    expect(d.plus({ [unit]: 8.7 })).toEqual(\n      d.plus({\n        [unit]: 8,\n        milliseconds: Duration.fromObject({ [unit]: 0.7 }).as(\"milliseconds\"),\n      })\n    );\n  }\n});\n\n// #645\ntest(\"DateTime#plus supports positive and negative duration units\", () => {\n  const d = DateTime.fromISO(\"2020-01-08T12:34\");\n  expect(d.plus({ months: 1, days: -1 })).toEqual(d.plus({ months: 1 }).plus({ days: -1 }));\n  expect(d.plus({ years: 4, days: -1 })).toEqual(d.plus({ years: 4 }).plus({ days: -1 }));\n  expect(d.plus({ years: 0.5, days: -1.5 })).toEqual(d.plus({ years: 0.5 }).plus({ days: -1.5 }));\n});\n\n//------\n// #minus()\n//------\ntest(\"DateTime#minus({ years: 1 }) subtracts a year\", () => {\n  const dt = createDateTime().minus({ years: 1 });\n  expect(dt.year).toBe(2009);\n});\n\ntest(\"DateTime#minus({ quarters: 1 }) subtracts a quarter\", () => {\n  const dt = createDateTime().minus({ quarters: 1 });\n  expect(dt.year).toBe(2009);\n  expect(dt.quarter).toBe(4);\n  expect(dt.month).toBe(11);\n});\n\ntest(\"DateTime#minus({ months: 1 }) at the end of the month\", () => {\n  const i = DateTime.fromISO(\"2018-03-31T10:00\"),\n    earlier = i.minus({ months: 1 });\n  expect(earlier.day).toBe(28);\n  expect(earlier.month).toBe(2);\n});\n\ntest(\"DateTime#minus({ months: 1 }) at the end of the month in a leap year\", () => {\n  const i = DateTime.fromISO(\"2016-03-31T10:00\"),\n    earlier = i.minus({ months: 1 });\n  expect(earlier.day).toBe(29);\n  expect(earlier.month).toBe(2);\n});\n\ntest(\"DateTime#minus({ months: 13 }) at the end of the month\", () => {\n  const i = DateTime.fromISO(\"2017-03-31T10:00\"),\n    earlier = i.minus({ months: 13 });\n  expect(earlier.day).toBe(29);\n  expect(earlier.month).toBe(2);\n  expect(earlier.year).toBe(2016);\n});\n\ntest(\"DateTime#minus maintains invalidity\", () => {\n  expect(DateTime.invalid(\"because\").minus({ day: 1 }).isValid).toBe(false);\n});\n\ntest(\"DateTime#minus works across the 100 barrier\", () => {\n  const d = DateTime.fromISO(\"0100-01-02\").minus({ day: 2 });\n  expect(d.year).toBe(99);\n  expect(d.month).toBe(12);\n  expect(d.day).toBe(31);\n});\n\ntest(\"DateTime#minus renders invalid when out of max. datetime range using days\", () => {\n  const d = DateTime.utc(1970, 1, 1, 0, 0, 0, 0).minus({ day: 1e8 + 1 });\n  expect(d.isValid).toBe(false);\n});\n\ntest(\"DateTime#minus renders invalid when out of max. datetime range using seconds\", () => {\n  const d = DateTime.utc(1970, 1, 1, 0, 0, 0, 0).minus({ second: 1e8 * 24 * 60 * 60 + 1 });\n  expect(d.isValid).toBe(false);\n});\n\ntest(\"DateTime#plus renders invalid when out of max. datetime range using IANAZone\", () => {\n  const d = DateTime.utc(1970, 1, 1, 0, 0, 0, 0)\n    .setZone(\"America/Los_Angeles\")\n    .minus({ second: 1e8 * 24 * 60 * 60 + 1 });\n  expect(d.isValid).toBe(false);\n});\n\ntest(\"DateTime#minus handles fractional days\", () => {\n  const d = DateTime.fromISO(\"2016-01-31T10:00\");\n  expect(d.minus({ days: 0.8 })).toEqual(d.minus({ hours: (24 * 4) / 5 }));\n  expect(d.minus({ days: 6.8 })).toEqual(d.minus({ days: 6, hours: (24 * 4) / 5 }));\n  expect(d.minus({ days: 6.8, milliseconds: 17 })).toEqual(\n    d.minus({ days: 6, milliseconds: 0.8 * 24 * 60 * 60 * 1000 + 17 })\n  );\n});\n\ntest(\"DateTime#minus handles fractional large units\", () => {\n  const units = [\"weeks\", \"months\", \"quarters\", \"years\"];\n\n  for (const unit of units) {\n    const d = DateTime.fromISO(\"2016-01-31T10:00\");\n    expect(d.minus({ [unit]: 8.7 })).toEqual(\n      d.minus({\n        [unit]: 8,\n        milliseconds: Duration.fromObject({ [unit]: 0.7 }).as(\"milliseconds\"),\n      })\n    );\n  }\n});\n\n// #645\ntest(\"DateTime#minus supports positive and negative duration units\", () => {\n  const d = DateTime.fromISO(\"2020-01-08T12:34\");\n  expect(d.minus({ months: 1, days: -1 })).toEqual(d.minus({ months: 1 }).minus({ days: -1 }));\n  expect(d.minus({ years: 4, days: -1 })).toEqual(d.minus({ years: 4 }).minus({ days: -1 }));\n  expect(d.minus({ years: 0.5, days: -1.5 })).toEqual(\n    d.minus({ years: 0.5 }).minus({ days: -1.5 })\n  );\n});\n\n//------\n// #startOf()\n//------\ntest(\"DateTime#startOf('year') goes to the start of the year\", () => {\n  const dt = createDateTime().startOf(\"year\");\n\n  expect(dt.year).toBe(2010);\n  expect(dt.month).toBe(1);\n  expect(dt.day).toBe(1);\n  expect(dt.hour).toBe(0);\n  expect(dt.minute).toBe(0);\n  expect(dt.second).toBe(0);\n  expect(dt.millisecond).toBe(0);\n});\n\ntest(\"DateTime#startOf('quarter') goes to the start of the quarter\", () => {\n  const monthToQuarterStart = (month, quarterStart) => {\n    const dt = DateTime.fromObject({\n      year: 2017,\n      month,\n      day: 10,\n      hour: 4,\n      minute: 5,\n      second: 6,\n      millisecond: 7,\n    }).startOf(\"quarter\");\n\n    expect(dt.year).toBe(2017);\n    expect(dt.month).toBe(quarterStart);\n    expect(dt.day).toBe(1);\n    expect(dt.hour).toBe(0);\n    expect(dt.minute).toBe(0);\n    expect(dt.second).toBe(0);\n    expect(dt.millisecond).toBe(0);\n  };\n\n  monthToQuarterStart(1, 1);\n  monthToQuarterStart(2, 1);\n  monthToQuarterStart(3, 1);\n  monthToQuarterStart(4, 4);\n  monthToQuarterStart(5, 4);\n  monthToQuarterStart(6, 4);\n  monthToQuarterStart(7, 7);\n  monthToQuarterStart(8, 7);\n  monthToQuarterStart(9, 7);\n  monthToQuarterStart(10, 10);\n  monthToQuarterStart(11, 10);\n  monthToQuarterStart(12, 10);\n});\n\ntest(\"DateTime#startOf('month') goes to the start of the month\", () => {\n  const dt = createDateTime().startOf(\"month\");\n\n  expect(dt.year).toBe(2010);\n  expect(dt.month).toBe(2);\n  expect(dt.day).toBe(1);\n  expect(dt.hour).toBe(0);\n  expect(dt.minute).toBe(0);\n  expect(dt.second).toBe(0);\n  expect(dt.millisecond).toBe(0);\n});\n\ntest(\"DateTime#startOf('day') goes to the start of the day\", () => {\n  const dt = createDateTime().startOf(\"day\");\n\n  expect(dt.year).toBe(2010);\n  expect(dt.month).toBe(2);\n  expect(dt.day).toBe(3);\n  expect(dt.hour).toBe(0);\n  expect(dt.minute).toBe(0);\n  expect(dt.second).toBe(0);\n  expect(dt.millisecond).toBe(0);\n});\n\ntest(\"DateTime#startOf('hour') goes to the start of the hour\", () => {\n  const dt = createDateTime().startOf(\"hour\");\n\n  expect(dt.year).toBe(2010);\n  expect(dt.month).toBe(2);\n  expect(dt.day).toBe(3);\n  expect(dt.hour).toBe(4);\n  expect(dt.minute).toBe(0);\n  expect(dt.second).toBe(0);\n  expect(dt.millisecond).toBe(0);\n});\n\ntest(\"DateTime#startOf('minute') goes to the start of the minute\", () => {\n  const dt = createDateTime().startOf(\"minute\");\n\n  expect(dt.year).toBe(2010);\n  expect(dt.month).toBe(2);\n  expect(dt.day).toBe(3);\n  expect(dt.hour).toBe(4);\n  expect(dt.minute).toBe(5);\n  expect(dt.second).toBe(0);\n  expect(dt.millisecond).toBe(0);\n});\n\ntest(\"DateTime#startOf('second') goes to the start of the second\", () => {\n  const dt = createDateTime().startOf(\"second\");\n\n  expect(dt.year).toBe(2010);\n  expect(dt.month).toBe(2);\n  expect(dt.day).toBe(3);\n  expect(dt.hour).toBe(4);\n  expect(dt.minute).toBe(5);\n  expect(dt.second).toBe(6);\n  expect(dt.millisecond).toBe(0);\n});\n\ntest(\"DateTime#startOf('week') goes to the start of the week\", () => {\n  // using a different day so that it doesn't end up as the first of the month\n  const dt = DateTime.fromISO(\"2016-03-12T10:00\").startOf(\"week\");\n\n  expect(dt.year).toBe(2016);\n  expect(dt.month).toBe(3);\n  expect(dt.day).toBe(7);\n  expect(dt.hour).toBe(0);\n  expect(dt.minute).toBe(0);\n  expect(dt.second).toBe(0);\n  expect(dt.millisecond).toBe(0);\n});\n\ntest(\"DateTime#startOf maintains invalidity\", () => {\n  expect(DateTime.invalid(\"because\").startOf(\"day\").isValid).toBe(false);\n});\n\ntest(\"DateTime#startOf throws on invalid units\", () => {\n  expect(() => DateTime.fromISO(\"2016-03-12T10:00\").startOf(\"splork\")).toThrow();\n  expect(() => DateTime.fromISO(\"2016-03-12T10:00\").startOf(\"\")).toThrow();\n});\n\n//------\n// #endOf()\n//------\ntest(\"DateTime#endOf('year') goes to the start of the year\", () => {\n  const dt = createDateTime().endOf(\"year\");\n\n  expect(dt.year).toBe(2010);\n  expect(dt.month).toBe(12);\n  expect(dt.day).toBe(31);\n  expect(dt.hour).toBe(23);\n  expect(dt.minute).toBe(59);\n  expect(dt.second).toBe(59);\n  expect(dt.millisecond).toBe(999);\n});\n\ntest(\"DateTime#endOf('quarter') goes to the end of the quarter\", () => {\n  const dt = createDateTime().endOf(\"quarter\");\n\n  expect(dt.year).toBe(2010);\n  expect(dt.month).toBe(3);\n  expect(dt.day).toBe(31);\n  expect(dt.hour).toBe(23);\n  expect(dt.minute).toBe(59);\n  expect(dt.second).toBe(59);\n  expect(dt.millisecond).toBe(999);\n});\n\ntest(\"DateTime#endOf('quarter') goes to the end of the quarter in December\", () => {\n  const monthToQuarterEnd = (month, endMonth) => {\n    const dt = DateTime.fromObject({\n      year: 2017,\n      month,\n      day: 10,\n      hour: 4,\n      minute: 5,\n      second: 6,\n      millisecond: 7,\n    }).endOf(\"quarter\");\n\n    expect(dt.year).toBe(2017);\n    expect(dt.month).toBe(endMonth);\n    expect(dt.day).toBe(dt.endOf(\"month\").day);\n    expect(dt.hour).toBe(23);\n    expect(dt.minute).toBe(59);\n    expect(dt.second).toBe(59);\n    expect(dt.millisecond).toBe(999);\n  };\n\n  monthToQuarterEnd(1, 3);\n  monthToQuarterEnd(2, 3);\n  monthToQuarterEnd(3, 3);\n  monthToQuarterEnd(4, 6);\n  monthToQuarterEnd(5, 6);\n  monthToQuarterEnd(6, 6);\n  monthToQuarterEnd(7, 9);\n  monthToQuarterEnd(8, 9);\n  monthToQuarterEnd(9, 9);\n  monthToQuarterEnd(10, 12);\n  monthToQuarterEnd(11, 12);\n  monthToQuarterEnd(12, 12);\n});\n\ntest(\"DateTime#endOf('month') goes to the start of the month\", () => {\n  const dt = createDateTime().endOf(\"month\");\n\n  expect(dt.year).toBe(2010);\n  expect(dt.month).toBe(2);\n  expect(dt.day).toBe(28);\n  expect(dt.hour).toBe(23);\n  expect(dt.minute).toBe(59);\n  expect(dt.second).toBe(59);\n  expect(dt.millisecond).toBe(999);\n});\n\ntest(\"DateTime#endOf('day') goes to the start of the day\", () => {\n  const dt = createDateTime().endOf(\"day\");\n\n  expect(dt.year).toBe(2010);\n  expect(dt.month).toBe(2);\n  expect(dt.day).toBe(3);\n  expect(dt.hour).toBe(23);\n  expect(dt.minute).toBe(59);\n  expect(dt.second).toBe(59);\n  expect(dt.millisecond).toBe(999);\n});\n\ntest(\"DateTime#endOf('hour') goes to the start of the hour\", () => {\n  const dt = createDateTime().endOf(\"hour\");\n\n  expect(dt.year).toBe(2010);\n  expect(dt.month).toBe(2);\n  expect(dt.day).toBe(3);\n  expect(dt.hour).toBe(4);\n  expect(dt.minute).toBe(59);\n  expect(dt.second).toBe(59);\n  expect(dt.millisecond).toBe(999);\n});\n\ntest(\"DateTime#endOf('minute') goes to the start of the minute\", () => {\n  const dt = createDateTime().endOf(\"minute\");\n\n  expect(dt.year).toBe(2010);\n  expect(dt.month).toBe(2);\n  expect(dt.day).toBe(3);\n  expect(dt.hour).toBe(4);\n  expect(dt.minute).toBe(5);\n  expect(dt.second).toBe(59);\n  expect(dt.millisecond).toBe(999);\n});\n\ntest(\"DateTime#endOf('second') goes to the start of the second\", () => {\n  const dt = createDateTime().endOf(\"second\");\n\n  expect(dt.year).toBe(2010);\n  expect(dt.month).toBe(2);\n  expect(dt.day).toBe(3);\n  expect(dt.hour).toBe(4);\n  expect(dt.minute).toBe(5);\n  expect(dt.second).toBe(6);\n  expect(dt.millisecond).toBe(999);\n});\n\ntest(\"DateTime#endOf('week') goes to the end of the week\", () => {\n  // using a different day so that it doesn't end up as the first of the month\n  const dt = DateTime.fromISO(\"2016-03-12T10:00\").endOf(\"week\");\n\n  expect(dt.year).toBe(2016);\n  expect(dt.month).toBe(3);\n  expect(dt.day).toBe(13);\n  expect(dt.hour).toBe(23);\n  expect(dt.minute).toBe(59);\n  expect(dt.second).toBe(59);\n  expect(dt.millisecond).toBe(999);\n});\n\ntest(\"DateTime#endOf maintains invalidity\", () => {\n  expect(DateTime.invalid(\"because\").endOf(\"day\").isValid).toBe(false);\n});\n\ntest(\"DateTime#endOf throws on invalid units\", () => {\n  expect(() => DateTime.fromISO(\"2016-03-12T10:00\").endOf(\"splork\")).toThrow();\n});\n"
  },
  {
    "path": "test/datetime/misc.test.js",
    "content": "/* global test expect */\nimport { DateTime } from \"../../src/luxon\";\n\n// you hate to see a class like this, but here we are\n\n//------\n// #hasSame()\n//------\n\ntest(\"DateTime#hasSame() can use milliseconds for exact comparisons\", () => {\n  const dt = DateTime.now();\n  expect(dt.hasSame(dt, \"millisecond\")).toBe(true);\n  expect(dt.hasSame(dt.reconfigure({ locale: \"fr\" }), \"millisecond\")).toBe(true);\n  expect(dt.hasSame(dt.plus({ milliseconds: 1 }), \"millisecond\")).toBe(false);\n});\n\ntest(\"DateTime#hasSame() checks the unit\", () => {\n  const dt = DateTime.now();\n  expect(dt.hasSame(dt, \"day\")).toBe(true);\n  expect(dt.hasSame(dt.startOf(\"day\"), \"day\")).toBe(true);\n  expect(dt.hasSame(dt.plus({ days: 1 }), \"days\")).toBe(false);\n});\n\ntest(\"DateTime#hasSame() checks high-order units\", () => {\n  const dt1 = DateTime.fromISO(\"2001-02-03\");\n  const dt2 = DateTime.fromISO(\"2001-05-03\");\n  expect(dt1.hasSame(dt2, \"year\")).toBe(true);\n  expect(dt1.hasSame(dt2, \"month\")).toBe(false);\n  // Even when days are equal, return false when a higher-order unit differs.\n  expect(dt1.hasSame(dt2, \"day\")).toBe(false);\n});\n\n// #584\ntest(\"DateTime#hasSame() ignores time offsets and is symmetric\", () => {\n  const d1 = DateTime.fromISO(\"2019-10-02T01:02:03.045+03:00\", {\n    zone: \"Europe/Helsinki\",\n  });\n  const d2 = DateTime.fromISO(\"2019-10-02T01:02:03.045-05:00\", {\n    zone: \"America/Chicago\",\n  });\n\n  expect(d1.hasSame(d2, \"day\")).toBe(true);\n  expect(d2.hasSame(d1, \"day\")).toBe(true);\n  expect(d1.hasSame(d2, \"hour\")).toBe(true);\n  expect(d2.hasSame(d1, \"hour\")).toBe(true);\n  expect(d1.hasSame(d2, \"second\")).toBe(true);\n  expect(d2.hasSame(d1, \"second\")).toBe(true);\n  expect(d1.hasSame(d2, \"millisecond\")).toBe(true);\n  expect(d2.hasSame(d1, \"millisecond\")).toBe(true);\n});\n\ntest(\"DateTime#hasSame() returns false for invalid DateTimes\", () => {\n  const dt = DateTime.now(),\n    invalid = DateTime.invalid(\"because\");\n  expect(dt.hasSame(invalid, \"day\")).toBe(false);\n  expect(invalid.hasSame(invalid, \"day\")).toBe(false);\n  expect(invalid.hasSame(dt, \"day\")).toBe(false);\n});\n\n//------\n// #until()\n//------\n\ntest(\"DateTime#until() creates an Interval\", () => {\n  const dt = DateTime.now(),\n    other = dt.plus({ days: 1 }),\n    i = dt.until(other);\n\n  expect(i.start).toBe(dt);\n  expect(i.end).toBe(other);\n});\n\ntest(\"DateTime#until() creates an invalid Interval out of an invalid DateTime\", () => {\n  const dt = DateTime.now(),\n    invalid = DateTime.invalid(\"because\");\n\n  expect(invalid.until(invalid).isValid).toBe(false);\n  expect(invalid.until(dt).isValid).toBe(false);\n  expect(dt.until(invalid).isValid).toBe(false);\n});\n\n//------\n// #isInLeapYear\n//------\ntest(\"DateTime#isInLeapYear returns the whether the DateTime's year is in a leap year\", () => {\n  expect(DateTime.local(2017, 5, 25).isInLeapYear).toBe(false);\n  expect(DateTime.local(2020, 5, 25).isInLeapYear).toBe(true);\n});\n\ntest(\"DateTime#isInLeapYear returns false for invalid DateTimes\", () => {\n  expect(DateTime.invalid(\"because\").isInLeapYear).toBe(false);\n});\n\n//------\n// #daysInYear\n//------\ntest(\"DateTime#daysInYear returns the number of days in the DateTime's year\", () => {\n  expect(DateTime.local(2017, 5, 25).daysInYear).toBe(365);\n  expect(DateTime.local(2020, 5, 25).daysInYear).toBe(366);\n});\n\ntest(\"DateTime#daysInYear returns NaN for invalid DateTimes\", () => {\n  expect(DateTime.invalid(\"because\").daysInYear).toBeFalsy();\n});\n\n//------\n// #daysInMonth\n//------\ntest(\"DateTime#daysInMonth returns the number of days in the DateTime's month\", () => {\n  expect(DateTime.local(2017, 3, 10).daysInMonth).toBe(31);\n  expect(DateTime.local(2017, 6, 10).daysInMonth).toBe(30);\n  expect(DateTime.local(2017, 2, 10).daysInMonth).toBe(28);\n  expect(DateTime.local(2020, 2, 10).daysInMonth).toBe(29);\n});\n\ntest(\"DateTime#daysInMonth returns NaN for invalid DateTimes\", () => {\n  expect(DateTime.invalid(\"because\").daysInMonth).toBeFalsy();\n});\n\n//------\n// #weeksInWeekYear\n//------\ntest(\"DateTime#weeksInWeekYear returns the number of days in the DateTime's year\", () => {\n  expect(DateTime.local(2004, 5, 25).weeksInWeekYear).toBe(53);\n  expect(DateTime.local(2017, 5, 25).weeksInWeekYear).toBe(52);\n  expect(DateTime.local(2020, 5, 25).weeksInWeekYear).toBe(53);\n});\n\ntest(\"DateTime#weeksInWeekYear returns NaN for invalid DateTimes\", () => {\n  expect(DateTime.invalid(\"because\").weeksInWeekYear).toBeFalsy();\n});\n"
  },
  {
    "path": "test/datetime/proto.test.js",
    "content": "/* global test expect */\nimport { DateTime } from \"../../src/luxon\";\n\ntest(\"DateTime prototype properties should not throw when accessed\", () => {\n  const d = DateTime.now();\n  expect(() =>\n    Object.getOwnPropertyNames(Object.getPrototypeOf(d)).forEach(\n      (name) => Object.getPrototypeOf(d)[name]\n    )\n  ).not.toThrow();\n});\n"
  },
  {
    "path": "test/datetime/reconfigure.test.js",
    "content": "/* global test expect */\n\nimport { DateTime } from \"../../src/luxon\";\n\nconst dt = DateTime.fromObject(\n  {},\n  {\n    locale: \"fr\",\n    numberingSystem: \"beng\",\n    outputCalendar: \"coptic\",\n  }\n);\n\n//------\n// #reconfigure()\n//------\ntest(\"DateTime#reconfigure() sets the locale\", () => {\n  const recon = dt.reconfigure({ locale: \"it\" });\n  expect(recon.locale).toBe(\"it\");\n  expect(recon.numberingSystem).toBe(\"beng\");\n  expect(recon.outputCalendar).toBe(\"coptic\");\n});\n\ntest(\"DateTime#reconfigure() sets the outputCalendar\", () => {\n  const recon = dt.reconfigure({ outputCalendar: \"ethioaa\" });\n  expect(recon.locale).toBe(\"fr\");\n  expect(recon.numberingSystem).toBe(\"beng\");\n  expect(recon.outputCalendar).toBe(\"ethioaa\");\n});\n\ntest(\"DateTime#reconfigure() sets the numberingSystem\", () => {\n  const recon = dt.reconfigure({ numberingSystem: \"thai\" });\n  expect(recon.locale).toBe(\"fr\");\n  expect(recon.numberingSystem).toBe(\"thai\");\n  expect(recon.outputCalendar).toBe(\"coptic\");\n});\n\ntest(\"DateTime#reconfigure() with no arguments no opts\", () => {\n  const recon = dt.reconfigure();\n  expect(recon.locale).toBe(\"fr\");\n  expect(recon.numberingSystem).toBe(\"beng\");\n  expect(recon.outputCalendar).toBe(\"coptic\");\n});\n"
  },
  {
    "path": "test/datetime/regexParse.test.js",
    "content": "/* global test expect */\n\nimport { DateTime } from \"../../src/luxon\";\nimport { withNow } from \"../helpers\";\n\n//------\n// .fromISO\n//-------\n\ntest(\"DateTime.fromISO() parses as local by default\", () => {\n  const dt = DateTime.fromISO(\"2016-05-25T09:08:34.123\");\n  expect(dt.toObject()).toEqual({\n    year: 2016,\n    month: 5,\n    day: 25,\n    hour: 9,\n    minute: 8,\n    second: 34,\n    millisecond: 123,\n  });\n});\n\ntest(\"DateTime.fromISO() uses the offset provided, but keeps the dateTime as local\", () => {\n  const dt = DateTime.fromISO(\"2016-05-25T09:08:34.123+06:00\");\n  expect(dt.toUTC().toObject()).toEqual({\n    year: 2016,\n    month: 5,\n    day: 25,\n    hour: 3,\n    minute: 8,\n    second: 34,\n    millisecond: 123,\n  });\n});\n\ntest(\"DateTime.fromISO() uses the Z if provided, but keeps the dateTime as local\", () => {\n  const dt = DateTime.fromISO(\"2016-05-25T09:08:34.123Z\");\n  expect(dt.toUTC().toObject()).toEqual({\n    year: 2016,\n    month: 5,\n    day: 25,\n    hour: 9,\n    minute: 8,\n    second: 34,\n    millisecond: 123,\n  });\n});\n\ntest(\"DateTime.fromISO() optionally adopts the UTC offset provided\", () => {\n  let dt = DateTime.fromISO(\"2016-05-25T09:08:34.123+06:00\", { setZone: true });\n  expect(dt.zone.name).toBe(\"UTC+6\");\n  expect(dt.toObject()).toEqual({\n    year: 2016,\n    month: 5,\n    day: 25,\n    hour: 9,\n    minute: 8,\n    second: 34,\n    millisecond: 123,\n  });\n\n  dt = DateTime.fromISO(\"1983-10-14T13:30Z\", { setZone: true });\n  expect(dt.zone.name).toBe(\"UTC\");\n  expect(dt.offset).toBe(0);\n  expect(dt.toObject()).toEqual({\n    year: 1983,\n    month: 10,\n    day: 14,\n    hour: 13,\n    minute: 30,\n    second: 0,\n    millisecond: 0,\n  });\n\n  // #1610 - lowercase z\n  dt = DateTime.fromISO(\"1983-10-14T13:30z\", { setZone: true });\n  expect(dt.zone.name).toBe(\"UTC\");\n  expect(dt.offset).toBe(0);\n  expect(dt.toObject()).toEqual({\n    year: 1983,\n    month: 10,\n    day: 14,\n    hour: 13,\n    minute: 30,\n    second: 0,\n    millisecond: 0,\n  });\n\n  // #580\n  dt = DateTime.fromISO(\"2016-05-25T09:08:34.123-00:30\", { setZone: true });\n  expect(dt.zone.name).toBe(\"UTC-0:30\");\n  expect(dt.toObject()).toEqual({\n    year: 2016,\n    month: 5,\n    day: 25,\n    hour: 9,\n    minute: 8,\n    second: 34,\n    millisecond: 123,\n  });\n});\n\ntest(\"DateTime.fromISO() can optionally specify a zone\", () => {\n  let dt = DateTime.fromISO(\"2016-05-25T09:08:34.123\", { zone: \"utc\" });\n  expect(dt.offset).toEqual(0);\n  expect(dt.toObject()).toEqual({\n    year: 2016,\n    month: 5,\n    day: 25,\n    hour: 9,\n    minute: 8,\n    second: 34,\n    millisecond: 123,\n  });\n\n  dt = DateTime.fromISO(\"2016-05-25T09:08:34.123+06:00\", { zone: \"utc\" });\n  expect(dt.offset).toEqual(0);\n  expect(dt.toObject()).toEqual({\n    year: 2016,\n    month: 5,\n    day: 25,\n    hour: 3,\n    minute: 8,\n    second: 34,\n    millisecond: 123,\n  });\n});\n\nconst isSame = (s, expected) => expect(DateTime.fromISO(s).toObject()).toEqual(expected);\n\ntest(\"DateTime.fromISO() accepts both capital T and lowercase t\", () => {\n  // #1610\n  const dt = DateTime.fromISO(\"2016-05-25T09:08:34.123+06:00\");\n  isSame(\"2016-05-25t09:08:34.123+06:00\", dt.toObject());\n});\n\ntest(\"DateTime.fromISO() accepts both capital Z and lowercase z\", () => {\n  // #1610\n  const dt = DateTime.fromISO(\"2016-05-25T09:08:34.123Z\");\n  isSame(\"2016-05-25T09:08:34.123z\", dt.toObject());\n});\n\ntest(\"DateTime.fromISO() accepts just the year\", () => {\n  isSame(\"2016\", {\n    year: 2016,\n    month: 1,\n    day: 1,\n    hour: 0,\n    minute: 0,\n    second: 0,\n    millisecond: 0,\n  });\n});\n\ntest(\"DateTime.fromISO() accepts year-month\", () => {\n  isSame(\"2016-05\", {\n    year: 2016,\n    month: 5,\n    day: 1,\n    hour: 0,\n    minute: 0,\n    second: 0,\n    millisecond: 0,\n  });\n});\n\ntest(\"DateTime.fromISO() accepts yearmonth\", () => {\n  isSame(\"201605\", {\n    year: 2016,\n    month: 5,\n    day: 1,\n    hour: 0,\n    minute: 0,\n    second: 0,\n    millisecond: 0,\n  });\n});\n\ntest(\"DateTime.fromISO() accepts year-month-day\", () => {\n  isSame(\"2016-05-25\", {\n    year: 2016,\n    month: 5,\n    day: 25,\n    hour: 0,\n    minute: 0,\n    second: 0,\n    millisecond: 0,\n  });\n});\n\ntest(\"DateTime.fromISO() accepts yearmonthday\", () => {\n  isSame(\"20160525\", {\n    year: 2016,\n    month: 5,\n    day: 25,\n    hour: 0,\n    minute: 0,\n    second: 0,\n    millisecond: 0,\n  });\n});\n\ntest(\"DateTime.fromISO() accepts extend years\", () => {\n  isSame(\"+002016-05-25\", {\n    year: 2016,\n    month: 5,\n    day: 25,\n    hour: 0,\n    minute: 0,\n    second: 0,\n    millisecond: 0,\n  });\n\n  isSame(\"-002016-05-25\", {\n    year: -2016,\n    month: 5,\n    day: 25,\n    hour: 0,\n    minute: 0,\n    second: 0,\n    millisecond: 0,\n  });\n});\n\ntest(\"DateTime.fromISO() accepts year-month-dayThour\", () => {\n  isSame(\"2016-05-25T09\", {\n    year: 2016,\n    month: 5,\n    day: 25,\n    hour: 9,\n    minute: 0,\n    second: 0,\n    millisecond: 0,\n  });\n});\n\ntest(\"DateTime.fromISO() accepts year-month-dayThour:minute\", () => {\n  isSame(\"2016-05-25T09:24\", {\n    year: 2016,\n    month: 5,\n    day: 25,\n    hour: 9,\n    minute: 24,\n    second: 0,\n    millisecond: 0,\n  });\n\n  isSame(\"2016-05-25T0924\", {\n    year: 2016,\n    month: 5,\n    day: 25,\n    hour: 9,\n    minute: 24,\n    second: 0,\n    millisecond: 0,\n  });\n});\n\ntest(\"DateTime.fromISO() accepts year-month-dayThour:minute:second\", () => {\n  isSame(\"2016-05-25T09:24:15\", {\n    year: 2016,\n    month: 5,\n    day: 25,\n    hour: 9,\n    minute: 24,\n    second: 15,\n    millisecond: 0,\n  });\n\n  isSame(\"2016-05-25T092415\", {\n    year: 2016,\n    month: 5,\n    day: 25,\n    hour: 9,\n    minute: 24,\n    second: 15,\n    millisecond: 0,\n  });\n});\n\ntest(\"DateTime.fromISO() accepts year-month-dayThour:minute:second.millisecond\", () => {\n  isSame(\"2016-05-25T09:24:15.123\", {\n    year: 2016,\n    month: 5,\n    day: 25,\n    hour: 9,\n    minute: 24,\n    second: 15,\n    millisecond: 123,\n  });\n\n  isSame(\"2016-05-25T092415.123\", {\n    year: 2016,\n    month: 5,\n    day: 25,\n    hour: 9,\n    minute: 24,\n    second: 15,\n    millisecond: 123,\n  });\n\n  isSame(\"2016-05-25T09:24:15,123\", {\n    year: 2016,\n    month: 5,\n    day: 25,\n    hour: 9,\n    minute: 24,\n    second: 15,\n    millisecond: 123,\n  });\n\n  isSame(\"2016-05-25T09:24:15.1239999\", {\n    year: 2016,\n    month: 5,\n    day: 25,\n    hour: 9,\n    minute: 24,\n    second: 15,\n    millisecond: 123,\n  });\n\n  isSame(\"2016-05-25T09:24:15.023\", {\n    year: 2016,\n    month: 5,\n    day: 25,\n    hour: 9,\n    minute: 24,\n    second: 15,\n    millisecond: 23,\n  });\n\n  // we round down always\n  isSame(\"2016-05-25T09:24:15.3456\", {\n    year: 2016,\n    month: 5,\n    day: 25,\n    hour: 9,\n    minute: 24,\n    second: 15,\n    millisecond: 345,\n  });\n\n  isSame(\"2016-05-25T09:24:15.999999\", {\n    year: 2016,\n    month: 5,\n    day: 25,\n    hour: 9,\n    minute: 24,\n    second: 15,\n    millisecond: 999,\n  });\n\n  // Support up to 20 digits\n  isSame(\"2016-05-25T09:24:15.12345678901234567890123456789\", {\n    year: 2016,\n    month: 5,\n    day: 25,\n    hour: 9,\n    minute: 24,\n    second: 15,\n    millisecond: 123,\n  });\n\n  isSame(\"2016-05-25T09:24:15.1\", {\n    year: 2016,\n    month: 5,\n    day: 25,\n    hour: 9,\n    minute: 24,\n    second: 15,\n    millisecond: 100,\n  });\n});\n\ntest(\"DateTime.fromISO() accepts year-week\", () => {\n  isSame(\"2016-W21\", {\n    year: 2016,\n    month: 5,\n    day: 23,\n    hour: 0,\n    minute: 0,\n    second: 0,\n    millisecond: 0,\n  });\n});\n\ntest(\"DateTime.fromISO() accepts year-week-day\", () => {\n  isSame(\"2016-W21-3\", {\n    year: 2016,\n    month: 5,\n    day: 25,\n    hour: 0,\n    minute: 0,\n    second: 0,\n    millisecond: 0,\n  });\n\n  isSame(\"2016W213\", {\n    year: 2016,\n    month: 5,\n    day: 25,\n    hour: 0,\n    minute: 0,\n    second: 0,\n    millisecond: 0,\n  });\n});\n\ntest(\"DateTime.fromISO() accepts year-week-dayTtime\", () => {\n  isSame(\"2016-W21-3T09:24:15.123\", {\n    year: 2016,\n    month: 5,\n    day: 25,\n    hour: 9,\n    minute: 24,\n    second: 15,\n    millisecond: 123,\n  });\n\n  isSame(\"2016W213T09:24:15.123\", {\n    year: 2016,\n    month: 5,\n    day: 25,\n    hour: 9,\n    minute: 24,\n    second: 15,\n    millisecond: 123,\n  });\n});\n\ntest(\"DateTime.fromISO() accepts year-ordinal\", () => {\n  isSame(\"2016-200\", {\n    year: 2016,\n    month: 7,\n    day: 18,\n    hour: 0,\n    minute: 0,\n    second: 0,\n    millisecond: 0,\n  });\n\n  isSame(\"2016200\", {\n    year: 2016,\n    month: 7,\n    day: 18,\n    hour: 0,\n    minute: 0,\n    second: 0,\n    millisecond: 0,\n  });\n});\n\ntest(\"DateTime.fromISO() accepts year-ordinalTtime\", () => {\n  isSame(\"2016-200T09:24:15.123\", {\n    year: 2016,\n    month: 7,\n    day: 18,\n    hour: 9,\n    minute: 24,\n    second: 15,\n    millisecond: 123,\n  });\n});\n\ntest(\"DateTime.fromISO() accepts year-ordinalTtime+offset\", () => {\n  const dt = DateTime.fromISO(\"2016-200T09:24:15.123+0600\", { setZone: true });\n  expect(dt.zone.name).toBe(\"UTC+6\");\n  expect(dt.toObject()).toEqual({\n    year: 2016,\n    month: 7,\n    day: 18,\n    hour: 9,\n    minute: 24,\n    second: 15,\n    millisecond: 123,\n  });\n});\n\ntest(\"DateTime.fromISO() accepts hour:minute:second.millisecond\", () => {\n  const { year, month, day } = DateTime.now();\n  isSame(\"09:24:15.123\", {\n    year,\n    month,\n    day,\n    hour: 9,\n    minute: 24,\n    second: 15,\n    millisecond: 123,\n  });\n});\n\ntest(\"DateTime.fromISO() accepts hour:minute:second,millisecond\", () => {\n  const { year, month, day } = DateTime.now();\n  isSame(\"09:24:15,123\", {\n    year,\n    month,\n    day,\n    hour: 9,\n    minute: 24,\n    second: 15,\n    millisecond: 123,\n  });\n});\n\ntest(\"DateTime.fromISO() accepts hour:minute:second\", () => {\n  const { year, month, day } = DateTime.now();\n  isSame(\"09:24:15\", {\n    year,\n    month,\n    day,\n    hour: 9,\n    minute: 24,\n    second: 15,\n    millisecond: 0,\n  });\n});\n\ntest(\"DateTime.fromISO() accepts hour:minute\", () => {\n  const { year, month, day } = DateTime.now();\n  isSame(\"09:24\", {\n    year,\n    month,\n    day,\n    hour: 9,\n    minute: 24,\n    second: 0,\n    millisecond: 0,\n  });\n});\n\ntest(\"DateTime.fromISO() accepts hour:minute\", () => {\n  const { year, month, day } = DateTime.now();\n  isSame(\"09:24\", {\n    year,\n    month,\n    day,\n    hour: 9,\n    minute: 24,\n    second: 0,\n    millisecond: 0,\n  });\n});\n\ntest(\"DateTime.fromISO() accepts 24:00\", () => {\n  isSame(\"2018-01-04T24:00\", {\n    year: 2018,\n    month: 1,\n    day: 5,\n    hour: 0,\n    minute: 0,\n    second: 0,\n    millisecond: 0,\n  });\n});\n\ntest(\"DateTime.fromISO() doesn't accept 24:23\", () => {\n  expect(DateTime.fromISO(\"2018-05-25T24:23\").isValid).toBe(false);\n});\n\ntest(\"DateTime.fromISO() accepts extended zones\", () => {\n  let dt = DateTime.fromISO(\"2016-05-14T10:23:54[Europe/Paris]\", {\n    setZone: true,\n  });\n  expect(dt.isValid).toBe(true);\n  expect(dt.zoneName).toBe(\"Europe/Paris\");\n  expect(dt.toObject()).toEqual({\n    year: 2016,\n    month: 5,\n    day: 14,\n    hour: 10,\n    minute: 23,\n    second: 54,\n    millisecond: 0,\n  });\n\n  dt = DateTime.fromISO(\"2016-05-14T10:23:54[UTC]\", { setZone: true });\n  expect(dt.isValid).toBe(true);\n  expect(dt.zoneName).toBe(\"UTC\");\n  expect(dt.offset).toBe(0);\n  expect(dt.toObject()).toEqual({\n    year: 2016,\n    month: 5,\n    day: 14,\n    hour: 10,\n    minute: 23,\n    second: 54,\n    millisecond: 0,\n  });\n\n  dt = DateTime.fromISO(\"2016-05-14T10:23:54[Etc/UTC]\", { setZone: true });\n  expect(dt.isValid).toBe(true);\n  expect(dt.zoneName).toBe(\"Etc/UTC\");\n  expect(dt.offset).toBe(0);\n  expect(dt.toObject()).toEqual({\n    year: 2016,\n    month: 5,\n    day: 14,\n    hour: 10,\n    minute: 23,\n    second: 54,\n    millisecond: 0,\n  });\n});\n\ntest(\"DateTime.fromISO() accepts extended zones and offsets\", () => {\n  let dt = DateTime.fromISO(\"2016-05-14T10:23:54+01:00[Europe/Paris]\", {\n    setZone: true,\n  });\n  expect(dt.isValid).toBe(true);\n  expect(dt.zoneName).toBe(\"Europe/Paris\");\n  expect(dt.toObject()).toEqual({\n    year: 2016,\n    month: 5,\n    day: 14,\n    hour: 10,\n    minute: 23,\n    second: 54,\n    millisecond: 0,\n  });\n\n  dt = DateTime.fromISO(\"2016-05-14T10:23:54+00:00[Etc/UTC]\", { setZone: true });\n  expect(dt.isValid).toBe(true);\n  expect(dt.zoneName).toBe(\"Etc/UTC\");\n  expect(dt.offset).toBe(0);\n  expect(dt.toObject()).toEqual({\n    year: 2016,\n    month: 5,\n    day: 14,\n    hour: 10,\n    minute: 23,\n    second: 54,\n    millisecond: 0,\n  });\n});\n\ntest(\"DateTime.fromISO() accepts extended zones on bare times\", () => {\n  const { year, month, day } = DateTime.now().setZone(\"Europe/Paris\");\n  let dt = DateTime.fromISO(\"10:23:54[Europe/Paris]\", {\n    setZone: true,\n  });\n  expect(dt.isValid).toBe(true);\n  expect(dt.zoneName).toBe(\"Europe/Paris\");\n  expect(dt.toObject()).toEqual({\n    year,\n    month,\n    day,\n    hour: 10,\n    minute: 23,\n    second: 54,\n    millisecond: 0,\n  });\n});\n\nwithNow(\n  \"DateTime.fromISO() accepts extended zones on bare times when UTC and zone are in different days\",\n  DateTime.fromISO(\"2023-11-20T23:30:00.000Z\"),\n  () => {\n    const { year, month, day } = DateTime.now().setZone(\"Europe/Paris\");\n    let dt = DateTime.fromISO(\"10:23:54[Europe/Paris]\", {\n      setZone: true,\n    });\n    expect(dt.isValid).toBe(true);\n    expect(dt.zoneName).toBe(\"Europe/Paris\");\n    expect(dt.toObject()).toEqual({\n      year,\n      month,\n      day,\n      hour: 10,\n      minute: 23,\n      second: 54,\n      millisecond: 0,\n    });\n  }\n);\n\ntest(\"DateTime.fromISO() accepts some technically incorrect stuff\", () => {\n  // these are formats that aren't technically valid but we parse anyway.\n  // Testing them more to document them than anything else\n  isSame(\"2016-05-25T0924:15.123\", {\n    year: 2016,\n    month: 5,\n    day: 25,\n    hour: 9,\n    minute: 24,\n    second: 15,\n    millisecond: 123,\n  });\n\n  isSame(\"2016-05-25T09:2415.123\", {\n    year: 2016,\n    month: 5,\n    day: 25,\n    hour: 9,\n    minute: 24,\n    second: 15,\n    millisecond: 123,\n  });\n\n  isSame(\"2016-W213\", {\n    year: 2016,\n    month: 5,\n    day: 25,\n    hour: 0,\n    minute: 0,\n    second: 0,\n    millisecond: 0,\n  });\n});\n\ntest(\"DateTime.fromISO() rejects poop\", () => {\n  const rejects = (s) => expect(DateTime.fromISO(s).isValid).toBeFalsy();\n\n  rejects(null);\n  rejects(\"\");\n  rejects(\" \");\n  rejects(\"2016-1\");\n  rejects(\"2016-1-15\");\n  rejects(\"2016-01-5\");\n  rejects(\"2016-01-00\");\n  rejects(\"2016-00-01\");\n  rejects(\"2016-05-25 08:34:34\");\n  rejects(\"2016-05-25Q08:34:34\");\n  rejects(\"2016-05-25T8:04:34\");\n  rejects(\"2016-05-25T08:4:34\");\n  rejects(\"2016-05-25T08:04:4\");\n  rejects(\"2016-05-25T:03:4\");\n  rejects(\"2016-05-25T08::4\");\n  rejects(\"2016-W32-02\");\n});\n\n//------\n// .fromRFC2822\n//-------\n\ntest(\"DateTime.fromRFC2822() accepts full format\", () => {\n  const dt = DateTime.fromRFC2822(\"Tue, 01 Nov 2016 13:23:12 +0630\");\n  expect(dt.isValid).toBe(true);\n  expect(dt.toUTC().toObject()).toEqual({\n    year: 2016,\n    month: 11,\n    day: 1,\n    hour: 6,\n    minute: 53,\n    second: 12,\n    millisecond: 0,\n  });\n});\n\ntest(\"DateTime.fromRFC2822 parses a range of dates\", () => {\n  const testCases = {\n    \"Sun, 12 Apr 2015 05:06:07 GMT\": [2015, 4, 12, 5, 6, 7],\n    \"Tue, 01 Nov 2016 01:23:45 +0000\": [2016, 11, 1, 1, 23, 45],\n    \"Tue, 01 Nov 16 04:23:45 Z\": [2016, 11, 1, 4, 23, 45],\n    \"01 Nov 2016 05:23:45 z\": [2016, 11, 1, 5, 23, 45],\n    \"Mon, 02 Jan 2017 06:00:00 -0800\": [2017, 1, 2, 6 + 8, 0, 0],\n    \"Mon, 02 Jan 2017 06:00:00 +0800\": [2017, 1, 1, 22, 0, 0],\n    \"Mon, 02 Jan 2017 06:00:00 +0330\": [2017, 1, 2, 2, 30, 0],\n    \"Mon, 02 Jan 2017 06:00:00 -0330\": [2017, 1, 2, 9, 30, 0],\n    \"Mon, 02 Jan 2017 06:00:00 PST\": [2017, 1, 2, 6 + 8, 0, 0],\n    \"Mon, 02 Jan 2017 06:00:00 PDT\": [2017, 1, 2, 6 + 7, 0, 0],\n  };\n\n  for (const testString in testCases) {\n    if (Object.prototype.hasOwnProperty.call(testCases, testString)) {\n      const expected = testCases[testString],\n        r = DateTime.fromRFC2822(testString).toUTC(),\n        actual = [r.year, r.month, r.day, r.hour, r.minute, r.second];\n      expect(expected).toEqual(actual);\n    }\n  }\n});\n\ntest(\"DateTime.fromRFC2822() rejects incorrect days of the week\", () => {\n  const dt = DateTime.fromRFC2822(\"Wed, 01 Nov 2016 13:23:12 +0600\");\n  expect(dt.isValid).toBe(false);\n});\n\ntest(\"DateTime.fromRFC2822() can elide the day of the week\", () => {\n  const dt = DateTime.fromRFC2822(\"01 Nov 2016 13:23:12 +0600\");\n  expect(dt.isValid).toBe(true);\n  expect(dt.toUTC().toObject()).toEqual({\n    year: 2016,\n    month: 11,\n    day: 1,\n    hour: 7,\n    minute: 23,\n    second: 12,\n    millisecond: 0,\n  });\n});\n\ntest(\"DateTime.fromRFC2822() can elide seconds\", () => {\n  const dt = DateTime.fromRFC2822(\"01 Nov 2016 13:23 +0600\");\n  expect(dt.isValid).toBe(true);\n  expect(dt.toUTC().toObject()).toEqual({\n    year: 2016,\n    month: 11,\n    day: 1,\n    hour: 7,\n    minute: 23,\n    second: 0,\n    millisecond: 0,\n  });\n});\n\ntest(\"DateTime.fromRFC2822() can use Z\", () => {\n  const dt = DateTime.fromRFC2822(\"01 Nov 2016 13:23:12 Z\");\n  expect(dt.isValid).toBe(true);\n  expect(dt.toUTC().toObject()).toEqual({\n    year: 2016,\n    month: 11,\n    day: 1,\n    hour: 13,\n    minute: 23,\n    second: 12,\n    millisecond: 0,\n  });\n});\n\ntest(\"DateTime.fromRFC2822() can use a weird subset of offset abbreviations\", () => {\n  const dt = DateTime.fromRFC2822(\"01 Nov 2016 13:23:12 EST\");\n  expect(dt.isValid).toBe(true);\n  expect(dt.toUTC().toObject()).toEqual({\n    year: 2016,\n    month: 11,\n    day: 1,\n    hour: 18,\n    minute: 23,\n    second: 12,\n    millisecond: 0,\n  });\n});\n\n//------\n// .fromHTTP\n//-------\n\ntest(\"DateTime.fromHTTP() can parse RFC 1123\", () => {\n  const dt = DateTime.fromHTTP(\"Sun, 06 Nov 1994 08:49:37 GMT\");\n  expect(dt.isValid).toBe(true);\n  expect(dt.toUTC().toObject()).toEqual({\n    year: 1994,\n    month: 11,\n    day: 6,\n    hour: 8,\n    minute: 49,\n    second: 37,\n    millisecond: 0,\n  });\n});\n\ntest(\"DateTime.fromHTTP() can parse RFC 850\", () => {\n  const dt = DateTime.fromHTTP(\"Sunday, 06-Nov-94 08:49:37 GMT\");\n  expect(dt.isValid).toBe(true);\n  expect(dt.toUTC().toObject()).toEqual({\n    year: 1994,\n    month: 11,\n    day: 6,\n    hour: 8,\n    minute: 49,\n    second: 37,\n    millisecond: 0,\n  });\n});\n\ntest(\"DateTime.fromHTTP() can parse RFC 850 on Wednesday\", () => {\n  const dt = DateTime.fromHTTP(\"Wednesday, 29-Jun-22 08:49:37 GMT\");\n  expect(dt.isValid).toBe(true);\n  expect(dt.toUTC().toObject()).toEqual({\n    year: 2022,\n    month: 6,\n    day: 29,\n    hour: 8,\n    minute: 49,\n    second: 37,\n    millisecond: 0,\n  });\n});\n\ntest(\"DateTime.fromHTTP() can parse ASCII dates with one date digit\", () => {\n  const dt = DateTime.fromHTTP(\"Sun Nov  6 08:49:37 1994\");\n  expect(dt.isValid).toBe(true);\n  expect(dt.toUTC().toObject()).toEqual({\n    year: 1994,\n    month: 11,\n    day: 6,\n    hour: 8,\n    minute: 49,\n    second: 37,\n    millisecond: 0,\n  });\n});\n\ntest(\"DateTime.fromHTTP() can parse ASCII dates with two date digits\", () => {\n  const dt = DateTime.fromHTTP(\"Wed Nov 16 08:49:37 1994\");\n  expect(dt.isValid).toBe(true);\n  expect(dt.toUTC().toObject()).toEqual({\n    year: 1994,\n    month: 11,\n    day: 16,\n    hour: 8,\n    minute: 49,\n    second: 37,\n    millisecond: 0,\n  });\n});\n\n//------\n// .fromSQL\n//-------\n\ntest(\"DateTime.fromSQL() can parse SQL dates\", () => {\n  const dt = DateTime.fromSQL(\"2016-05-14\");\n  expect(dt.isValid).toBe(true);\n  expect(dt.toObject()).toEqual({\n    year: 2016,\n    month: 5,\n    day: 14,\n    hour: 0,\n    minute: 0,\n    second: 0,\n    millisecond: 0,\n  });\n});\n\ntest(\"DateTime.fromSQL() can parse SQL times\", () => {\n  const dt = DateTime.fromSQL(\"04:12:00.123\");\n  expect(dt.isValid).toBe(true);\n  const now = new Date();\n  expect(dt.toObject()).toEqual({\n    year: now.getFullYear(),\n    month: now.getMonth() + 1,\n    day: now.getDate(),\n    hour: 4,\n    minute: 12,\n    second: 0,\n    millisecond: 123,\n  });\n});\n\ntest(\"DateTime.fromSQL() handles times without fractional seconds\", () => {\n  const dt = DateTime.fromSQL(\"04:12:00\");\n  expect(dt.isValid).toBe(true);\n  const now = new Date();\n  expect(dt.toObject()).toEqual({\n    year: now.getFullYear(),\n    month: now.getMonth() + 1,\n    day: now.getDate(),\n    hour: 4,\n    minute: 12,\n    second: 0,\n    millisecond: 0,\n  });\n});\n\ntest(\"DateTime.fromSQL() can parse SQL datetimes with sub-millisecond precision\", () => {\n  let dt = DateTime.fromSQL(\"2016-05-14 10:23:54.2346\");\n  expect(dt.isValid).toBe(true);\n  expect(dt.toObject()).toEqual({\n    year: 2016,\n    month: 5,\n    day: 14,\n    hour: 10,\n    minute: 23,\n    second: 54,\n    millisecond: 234,\n  });\n\n  dt = DateTime.fromSQL(\"2016-05-14 10:23:54.2341\");\n  expect(dt.isValid).toBe(true);\n  expect(dt.toObject()).toEqual({\n    year: 2016,\n    month: 5,\n    day: 14,\n    hour: 10,\n    minute: 23,\n    second: 54,\n    millisecond: 234,\n  });\n});\n\ntest(\"DateTime.fromSQL() handles deciseconds in SQL datetimes\", () => {\n  const dt = DateTime.fromSQL(\"2016-05-14 10:23:54.1\");\n  expect(dt.isValid).toBe(true);\n  expect(dt.toObject()).toEqual({\n    year: 2016,\n    month: 5,\n    day: 14,\n    hour: 10,\n    minute: 23,\n    second: 54,\n    millisecond: 100,\n  });\n});\n\ntest(\"DateTime.fromSQL() handles datetimes without fractional seconds\", () => {\n  const dt = DateTime.fromSQL(\"2016-05-14 10:23:54\");\n  expect(dt.isValid).toBe(true);\n  expect(dt.toObject()).toEqual({\n    year: 2016,\n    month: 5,\n    day: 14,\n    hour: 10,\n    minute: 23,\n    second: 54,\n    millisecond: 0,\n  });\n});\n\ntest(\"DateTime.fromSQL() accepts a zone to default to\", () => {\n  const dt = DateTime.fromSQL(\"2016-05-14 10:23:54.023\", { zone: \"utc\" });\n  expect(dt.isValid).toBe(true);\n  expect(dt.offset).toBe(0);\n  expect(dt.toObject()).toEqual({\n    year: 2016,\n    month: 5,\n    day: 14,\n    hour: 10,\n    minute: 23,\n    second: 54,\n    millisecond: 23,\n  });\n});\n\ntest(\"DateTime.fromSQL() can parse an optional offset\", () => {\n  let dt = DateTime.fromSQL(\"2016-05-14 10:23:54.023 +06:00\");\n  expect(dt.isValid).toBe(true);\n  expect(dt.toUTC().toObject()).toEqual({\n    year: 2016,\n    month: 5,\n    day: 14,\n    hour: 4,\n    minute: 23,\n    second: 54,\n    millisecond: 23,\n  });\n\n  // no space before the zone\n  dt = DateTime.fromSQL(\"2016-05-14 10:23:54.023+06:00\");\n  expect(dt.isValid).toBe(true);\n  expect(dt.toUTC().toObject()).toEqual({\n    year: 2016,\n    month: 5,\n    day: 14,\n    hour: 4,\n    minute: 23,\n    second: 54,\n    millisecond: 23,\n  });\n\n  // no milliseconds\n  dt = DateTime.fromSQL(\"2016-05-14 10:23:54 +06:00\");\n  expect(dt.isValid).toBe(true);\n  expect(dt.toUTC().toObject()).toEqual({\n    year: 2016,\n    month: 5,\n    day: 14,\n    hour: 4,\n    minute: 23,\n    second: 54,\n    millisecond: 0,\n  });\n});\n\ntest(\"DateTime.fromSQL() can parse an optional zone\", () => {\n  let dt = DateTime.fromSQL(\"2016-05-14 10:23:54 Europe/Paris\", {\n    setZone: true,\n  });\n  expect(dt.isValid).toBe(true);\n  expect(dt.zoneName).toBe(\"Europe/Paris\");\n  expect(dt.toObject()).toEqual({\n    year: 2016,\n    month: 5,\n    day: 14,\n    hour: 10,\n    minute: 23,\n    second: 54,\n    millisecond: 0,\n  });\n\n  dt = DateTime.fromSQL(\"2016-05-14 10:23:54 UTC\", { setZone: true });\n  expect(dt.isValid).toBe(true);\n  expect(dt.zoneName).toBe(\"UTC\");\n  expect(dt.offset).toBe(0);\n  expect(dt.toObject()).toEqual({\n    year: 2016,\n    month: 5,\n    day: 14,\n    hour: 10,\n    minute: 23,\n    second: 54,\n    millisecond: 0,\n  });\n});\n"
  },
  {
    "path": "test/datetime/relative.test.js",
    "content": "import DateTime from \"../../src/datetime\";\n\nconst Helpers = require(\"../helpers\");\n\n/* global expect test */\n\n//------\n// #toRelative()\n//-------\n\ntest(\"DateTime#toRelative works down through the units\", () => {\n  const base = DateTime.fromObject({ year: 1983, month: 10, day: 14 });\n  expect(base.plus({ minutes: 1 }).toRelative({ base })).toBe(\"in 1 minute\");\n  expect(base.plus({ minutes: 5 }).toRelative({ base })).toBe(\"in 5 minutes\");\n  expect(base.plus({ minutes: 65 }).toRelative({ base })).toBe(\"in 1 hour\");\n  expect(base.plus({ minutes: 165 }).toRelative({ base })).toBe(\"in 2 hours\");\n  expect(base.plus({ hours: 24 }).toRelative({ base })).toBe(\"in 1 day\");\n  expect(base.plus({ days: 3 }).toRelative({ base })).toBe(\"in 3 days\");\n  expect(base.plus({ months: 5 }).toRelative({ base })).toBe(\"in 5 months\");\n  expect(base.plus({ months: 15 }).toRelative({ base })).toBe(\"in 1 year\");\n\n  expect(base.minus({ minutes: 1 }).toRelative({ base })).toBe(\"1 minute ago\");\n  expect(base.minus({ minutes: 5 }).toRelative({ base })).toBe(\"5 minutes ago\");\n  expect(base.minus({ minutes: 65 }).toRelative({ base })).toBe(\"1 hour ago\");\n  expect(base.minus({ minutes: 165 }).toRelative({ base })).toBe(\"2 hours ago\");\n  expect(base.minus({ hours: 24 }).toRelative({ base })).toBe(\"1 day ago\");\n  expect(base.minus({ days: 3 }).toRelative({ base })).toBe(\"3 days ago\");\n  expect(base.minus({ months: 5 }).toRelative({ base })).toBe(\"5 months ago\");\n  expect(base.minus({ months: 15 }).toRelative({ base })).toBe(\"1 year ago\");\n});\n\ntest(\"DateTime#toRelative allows padding\", () => {\n  const base = DateTime.fromObject({ year: 1983, month: 10, day: 14 });\n  expect(base.endOf(\"day\").toRelative({ base, padding: 10 })).toBe(\"in 1 day\");\n  expect(base.minus({ days: 1, milliseconds: -1 }).toRelative({ base, padding: 10 })).toBe(\n    \"1 day ago\"\n  );\n});\n\ntest(\"DateTime#toRelative takes a round argument\", () => {\n  const base = DateTime.fromObject({ year: 1983, month: 10, day: 14 });\n  expect(base.plus({ months: 15 }).toRelative({ base, round: false })).toBe(\"in 1.25 years\");\n  expect(base.minus({ months: 15 }).toRelative({ base, round: false })).toBe(\"1.25 years ago\");\n});\n\ntest(\"DateTime#toRelative takes a rounding argument\", () => {\n  const base = DateTime.fromObject({ year: 1983, month: 10, day: 14 });\n  expect(base.plus({ hours: 2, milliseconds: -1 }).toRelative({ base, rounding: \"expand\" })).toBe(\n    \"in 2 hours\"\n  );\n  expect(base.plus({ hours: 2, milliseconds: 1 }).toRelative({ base, rounding: \"expand\" })).toBe(\n    \"in 3 hours\"\n  );\n  expect(base.minus({ hours: 2, milliseconds: -1 }).toRelative({ base, rounding: \"expand\" })).toBe(\n    \"2 hours ago\"\n  );\n  expect(base.minus({ hours: 2, milliseconds: 1 }).toRelative({ base, rounding: \"expand\" })).toBe(\n    \"3 hours ago\"\n  );\n\n  expect(base.plus({ hours: 2, milliseconds: -1 }).toRelative({ base, rounding: \"trunc\" })).toBe(\n    \"in 1 hour\"\n  );\n  expect(base.plus({ hours: 2, milliseconds: 1 }).toRelative({ base, rounding: \"trunc\" })).toBe(\n    \"in 2 hours\"\n  );\n  expect(base.minus({ hours: 2, milliseconds: -1 }).toRelative({ base, rounding: \"trunc\" })).toBe(\n    \"1 hour ago\"\n  );\n  expect(base.minus({ hours: 2, milliseconds: 1 }).toRelative({ base, rounding: \"trunc\" })).toBe(\n    \"2 hours ago\"\n  );\n\n  expect(base.plus({ hours: 2, milliseconds: -1 }).toRelative({ base, rounding: \"round\" })).toBe(\n    \"in 2 hours\"\n  );\n  expect(base.plus({ hours: 2, milliseconds: 1 }).toRelative({ base, rounding: \"round\" })).toBe(\n    \"in 2 hours\"\n  );\n  expect(base.minus({ hours: 2, milliseconds: -1 }).toRelative({ base, rounding: \"round\" })).toBe(\n    \"2 hours ago\"\n  );\n  expect(base.minus({ hours: 2, milliseconds: 1 }).toRelative({ base, rounding: \"round\" })).toBe(\n    \"2 hours ago\"\n  );\n\n  expect(base.plus({ hours: 2, milliseconds: -1 }).toRelative({ base, rounding: \"floor\" })).toBe(\n    \"in 1 hour\"\n  );\n  expect(base.plus({ hours: 2, milliseconds: 1 }).toRelative({ base, rounding: \"floor\" })).toBe(\n    \"in 2 hours\"\n  );\n  expect(base.minus({ hours: 2, milliseconds: -1 }).toRelative({ base, rounding: \"floor\" })).toBe(\n    \"2 hours ago\"\n  );\n  expect(base.minus({ hours: 2, milliseconds: 1 }).toRelative({ base, rounding: \"floor\" })).toBe(\n    \"3 hours ago\"\n  );\n\n  expect(base.plus({ hours: 2, milliseconds: -1 }).toRelative({ base, rounding: \"ceil\" })).toBe(\n    \"in 2 hours\"\n  );\n  expect(base.plus({ hours: 2, milliseconds: 1 }).toRelative({ base, rounding: \"ceil\" })).toBe(\n    \"in 3 hours\"\n  );\n  expect(base.minus({ hours: 2, milliseconds: -1 }).toRelative({ base, rounding: \"ceil\" })).toBe(\n    \"1 hour ago\"\n  );\n  expect(base.minus({ hours: 2, milliseconds: 1 }).toRelative({ base, rounding: \"ceil\" })).toBe(\n    \"2 hours ago\"\n  );\n});\n\ntest(\"DateTime#toRelative takes a round and a rounding argument\", () => {\n  const base = DateTime.fromObject({ year: 1983, month: 10, day: 14 });\n  expect(\n    base.plus({ hours: 2, milliseconds: -1 }).toRelative({ base, round: false, rounding: \"expand\" })\n  ).toBe(\"in 2 hours\");\n  expect(\n    base.plus({ hours: 2, milliseconds: 1 }).toRelative({ base, round: false, rounding: \"expand\" })\n  ).toBe(\"in 2.01 hours\");\n  expect(\n    base\n      .minus({ hours: 2, milliseconds: -1 })\n      .toRelative({ base, round: false, rounding: \"expand\" })\n  ).toBe(\"2 hours ago\");\n  expect(\n    base.minus({ hours: 2, milliseconds: 1 }).toRelative({ base, round: false, rounding: \"expand\" })\n  ).toBe(\"2.01 hours ago\");\n\n  expect(\n    base.plus({ hours: 2, milliseconds: -1 }).toRelative({ base, round: false, rounding: \"trunc\" })\n  ).toBe(\"in 1.99 hours\");\n  expect(\n    base.plus({ hours: 2, milliseconds: 1 }).toRelative({ base, round: false, rounding: \"trunc\" })\n  ).toBe(\"in 2 hours\");\n  expect(\n    base.minus({ hours: 2, milliseconds: -1 }).toRelative({ base, round: false, rounding: \"trunc\" })\n  ).toBe(\"1.99 hours ago\");\n  expect(\n    base.minus({ hours: 2, milliseconds: 1 }).toRelative({ base, round: false, rounding: \"trunc\" })\n  ).toBe(\"2 hours ago\");\n\n  expect(\n    base.plus({ hours: 2, milliseconds: -1 }).toRelative({ base, round: false, rounding: \"round\" })\n  ).toBe(\"in 2 hours\");\n  expect(\n    base.plus({ hours: 2, milliseconds: 1 }).toRelative({ base, round: false, rounding: \"round\" })\n  ).toBe(\"in 2 hours\");\n  expect(\n    base.minus({ hours: 2, milliseconds: -1 }).toRelative({ base, round: false, rounding: \"round\" })\n  ).toBe(\"2 hours ago\");\n  expect(\n    base.minus({ hours: 2, milliseconds: 1 }).toRelative({ base, round: false, rounding: \"round\" })\n  ).toBe(\"2 hours ago\");\n\n  expect(\n    base.plus({ hours: 2, milliseconds: -1 }).toRelative({ base, round: false, rounding: \"floor\" })\n  ).toBe(\"in 1.99 hours\");\n  expect(\n    base.plus({ hours: 2, milliseconds: 1 }).toRelative({ base, round: false, rounding: \"floor\" })\n  ).toBe(\"in 2 hours\");\n  expect(\n    base.minus({ hours: 2, milliseconds: -1 }).toRelative({ base, round: false, rounding: \"floor\" })\n  ).toBe(\"2 hours ago\");\n  expect(\n    base.minus({ hours: 2, milliseconds: 1 }).toRelative({ base, round: false, rounding: \"floor\" })\n  ).toBe(\"2.01 hours ago\");\n\n  expect(\n    base.plus({ hours: 2, milliseconds: -1 }).toRelative({ base, round: false, rounding: \"ceil\" })\n  ).toBe(\"in 2 hours\");\n  expect(\n    base.plus({ hours: 2, milliseconds: 1 }).toRelative({ base, round: false, rounding: \"ceil\" })\n  ).toBe(\"in 2.01 hours\");\n  expect(\n    base.minus({ hours: 2, milliseconds: -1 }).toRelative({ base, round: false, rounding: \"ceil\" })\n  ).toBe(\"1.99 hours ago\");\n  expect(\n    base.minus({ hours: 2, milliseconds: 1 }).toRelative({ base, round: false, rounding: \"ceil\" })\n  ).toBe(\"2 hours ago\");\n});\n\ntest(\"DateTime#toRelative takes a unit argument\", () => {\n  const base = DateTime.fromObject({ year: 2018, month: 10, day: 14 }, { zone: \"UTC\" });\n  expect(base.plus({ months: 15 }).toRelative({ base, unit: \"months\" })).toBe(\"in 15 months\");\n  expect(base.minus({ months: 15 }).toRelative({ base, unit: \"months\" })).toBe(\"15 months ago\");\n  expect(base.plus({ months: 3 }).toRelative({ base, unit: \"years\", round: false })).toBe(\n    \"in 0.25 years\"\n  );\n  expect(base.minus({ months: 3 }).toRelative({ base, unit: \"years\", round: false })).toBe(\n    \"0.25 years ago\"\n  );\n  expect(base.minus({ seconds: 30 }).toRelative({ base, unit: [\"days\", \"hours\", \"minutes\"] })).toBe(\n    \"0 minutes ago\"\n  );\n  expect(base.minus({ seconds: 1 }).toRelative({ base, unit: \"minutes\" })).toBe(\"0 minutes ago\");\n  expect(base.plus({ seconds: 1 }).toRelative({ base, unit: \"minutes\" })).toBe(\"in 0 minutes\");\n  expect(\n    base.plus({ seconds: 30 }).toRelative({\n      base,\n      unit: [\"days\", \"hours\", \"minutes\"],\n    })\n  ).toBe(\"in 0 minutes\");\n  expect(\n    base.plus({ years: 2 }).toRelative({\n      base,\n      unit: [\"days\", \"hours\", \"minutes\"],\n    })\n  ).toBe(\"in 731 days\");\n});\n\ntest(\"DateTime#toRelative always rounds toward 0\", () => {\n  const base = DateTime.fromObject({ year: 1983, month: 10, day: 14 });\n  expect(base.endOf(\"day\").toRelative({ base })).toBe(\"in 23 hours\");\n  expect(base.minus({ days: 1, milliseconds: -1 }).toRelative({ base })).toBe(\"23 hours ago\");\n});\n\ntest(\"DateTime#toRelative uses the absolute time\", () => {\n  const base = DateTime.fromObject({ year: 1983, month: 10, day: 14, hour: 23, minute: 59 });\n  const end = DateTime.fromObject({ year: 1983, month: 10, day: 15, hour: 0, minute: 3 });\n  expect(end.toRelative({ base })).toBe(\"in 4 minutes\");\n  expect(base.toRelative({ base: end })).toBe(\"4 minutes ago\");\n});\n\nHelpers.withoutRTF(\"DateTime#toRelative works without RTF\", () => {\n  const base = DateTime.fromObject({ year: 2019, month: 12, day: 25 });\n\n  expect(base.plus({ months: 1 }).toRelative({ base })).toBe(\"in 1 month\");\n  expect(base.plus({ months: 1 }).toRelative({ base, style: \"narrow\" })).toBe(\"in 1 mo.\");\n  expect(base.plus({ months: 1 }).toRelative({ base, unit: \"days\" })).toBe(\"in 31 days\");\n  expect(base.plus({ months: 1 }).toRelative({ base, style: \"short\", unit: \"days\" })).toBe(\n    \"in 31 days\"\n  );\n  expect(base.plus({ months: 1, days: 2 }).toRelative({ base, round: false })).toBe(\n    \"in 1.06 months\"\n  );\n});\n\nHelpers.withoutRTF(\"DateTime#toRelative falls back to English\", () => {\n  const base = DateTime.fromObject({ year: 2019, month: 12, day: 25 });\n  expect(base.setLocale(\"fr\").plus({ months: 1 }).toRelative({ base })).toBe(\"in 1 month\");\n});\n\ntest(\"DateTime#toRelative returns null when used on an invalid date\", () => {\n  expect(DateTime.invalid(\"not valid\").toRelative()).toBe(null);\n});\n\n//------\n// #toRelativeCalendar()\n//-------\n\ntest(\"DateTime#toRelativeCalendar uses the calendar\", () => {\n  const base = DateTime.fromObject({ year: 1983, month: 10, day: 14, hour: 23, minute: 59 });\n  const end = DateTime.fromObject({ year: 1983, month: 10, day: 15, hour: 0, minute: 3 });\n  expect(end.toRelativeCalendar({ base })).toBe(\"tomorrow\");\n});\n\nHelpers.withNow(\n  \"DateTime#toRelativeCalendar picks the correct unit with no options\",\n  DateTime.fromObject({ year: 1983, month: 10, day: 14 }),\n  () => {\n    const now = DateTime.now();\n    expect(now.plus({ days: 1 }).toRelativeCalendar()).toBe(\"tomorrow\");\n  }\n);\n\nHelpers.withNow(\n  \"DateTime#toRelativeCalendar picks the correct unit with no options at last day of month\",\n  DateTime.fromObject({ year: 1983, month: 10, day: 31 }),\n  () => {\n    const now = DateTime.now();\n    expect(now.plus({ days: 1 }).toRelativeCalendar()).toBe(\"next month\");\n  }\n);\n\nHelpers.withNow(\n  \"DateTime#toRelativeCalendar picks the correct unit with no options at least day of year\",\n  DateTime.fromObject({ year: 1983, month: 12, day: 31 }),\n  () => {\n    const now = DateTime.now();\n    expect(now.plus({ days: 1 }).toRelativeCalendar()).toBe(\"next year\");\n  }\n);\n\ntest(\"DateTime#toRelativeCalendar returns null when used on an invalid date\", () => {\n  expect(DateTime.invalid(\"not valid\").toRelativeCalendar()).toBe(null);\n});\n\ntest(\"DateTime#toRelativeCalendar works down through the units\", () => {\n  const base = DateTime.fromObject({ year: 1983, month: 10, day: 14, hour: 12 });\n  expect(base.plus({ minutes: 1 }).toRelativeCalendar({ base })).toBe(\"today\");\n  expect(base.plus({ minutes: 5 }).toRelativeCalendar({ base })).toBe(\"today\");\n  expect(base.plus({ minutes: 65 }).toRelativeCalendar({ base })).toBe(\"today\");\n  expect(base.plus({ hours: 13 }).toRelativeCalendar({ base })).toBe(\"tomorrow\");\n  expect(base.plus({ days: 3 }).toRelativeCalendar({ base })).toBe(\"in 3 days\");\n  expect(base.plus({ months: 1 }).toRelativeCalendar({ base })).toBe(\"next month\");\n  expect(base.plus({ months: 5 }).toRelativeCalendar({ base })).toBe(\"next year\");\n  expect(base.plus({ months: 15 }).toRelativeCalendar({ base })).toBe(\"in 2 years\");\n\n  expect(base.minus({ minutes: 1 }).toRelativeCalendar({ base })).toBe(\"today\");\n  expect(base.minus({ minutes: 5 }).toRelativeCalendar({ base })).toBe(\"today\");\n  expect(base.minus({ minutes: 65 }).toRelativeCalendar({ base })).toBe(\"today\");\n  expect(base.minus({ hours: 24 }).toRelativeCalendar({ base })).toBe(\"yesterday\");\n  expect(base.minus({ days: 3 }).toRelativeCalendar({ base })).toBe(\"3 days ago\");\n  expect(base.minus({ months: 1 }).toRelativeCalendar({ base })).toBe(\"last month\");\n  expect(base.minus({ months: 5 }).toRelativeCalendar({ base })).toBe(\"5 months ago\");\n  expect(base.minus({ months: 15 }).toRelativeCalendar({ base })).toBe(\"last year\");\n});\n\ntest(\"DateTime#toRelativeCalendar takes a unit argument\", () => {\n  const base = DateTime.fromObject({ year: 1983, month: 10, day: 14, hour: 12 }),\n    target = base.plus({ months: 3 });\n  expect(target.toRelativeCalendar({ base, unit: \"months\" })).toBe(\"in 3 months\");\n});\n\nHelpers.withoutRTF(\"DateTime#toRelativeCalendar works without RTF\", () => {\n  const base = DateTime.fromObject({ year: 2019, month: 10, day: 25 });\n  expect(base.plus({ months: 1 }).toRelativeCalendar({ base })).toBe(\"next month\");\n});\n\nHelpers.withoutRTF(\"DateTime#toRelativeCalendar falls back to English\", () => {\n  const base = DateTime.fromObject({ year: 2019, month: 12, day: 25 });\n  expect(base.setLocale(\"fr\").plus({ months: 1 }).toRelativeCalendar({ base })).toBe(\"next year\");\n});\n\ntest(\"DateTime#toRelativeCalendar works down through the units for different zone than local\", () => {\n  const target = DateTime.now().setZone(`UTC+3`),\n    target1 = target.plus({ days: 1 }),\n    target2 = target1.plus({ days: 1 }),\n    target3 = target2.plus({ days: 1 }),\n    options = { unit: \"days\" };\n\n  expect(target.toRelativeCalendar(options)).toBe(\"today\");\n  expect(target1.toRelativeCalendar(options)).toBe(\"tomorrow\");\n  expect(target2.toRelativeCalendar(options)).toBe(\"in 2 days\");\n  expect(target3.toRelativeCalendar(options)).toBe(\"in 3 days\");\n});\n\ntest(\"DateTime#toRelative works down through the units for different zone than local\", () => {\n  const base = DateTime.now().setZone(`UTC+3`);\n\n  expect(base.plus({ minutes: 65 }).toRelative()).toBe(\"in 1 hour\");\n  expect(base.plus({ minutes: 165 }).toRelative()).toBe(\"in 2 hours\");\n  expect(base.plus({ hours: 25 }).toRelative()).toBe(\"in 1 day\");\n  expect(base.plus({ months: 15 }).toRelative()).toBe(\"in 1 year\");\n\n  expect(base.minus({ minutes: 65 }).toRelative()).toBe(\"1 hour ago\");\n  expect(base.minus({ minutes: 165 }).toRelative()).toBe(\"2 hours ago\");\n  expect(base.minus({ hours: 25 }).toRelative()).toBe(\"1 day ago\");\n  expect(base.minus({ months: 15 }).toRelative()).toBe(\"1 year ago\");\n});\n"
  },
  {
    "path": "test/datetime/set.test.js",
    "content": "/* global test expect */\n\nimport { DateTime } from \"../../src/luxon\";\nimport Helpers, { supportsMinDaysInFirstWeek } from \"../helpers\";\n\nconst withDefaultWeekSettings = Helpers.setUnset(\"defaultWeekSettings\");\n\nconst dtFactory = () => DateTime.fromJSDate(new Date(1982, 4, 25, 9, 23, 54, 123));\nconst dt = dtFactory();\n\n//------\n// year/month/day/hour/minute/second/millisecond\n//-------\ntest(\"DateTime#set() sets Gregorian fields\", () => {\n  expect(dt.set({ year: 2012 }).year).toBe(2012);\n  expect(dt.set({ month: 2 }).month).toBe(2);\n  expect(dt.set({ month: 2 }).hour).toBe(9); // this will cross a DST for many people\n  expect(dt.set({ day: 5 }).day).toBe(5);\n  expect(dt.set({ hour: 4 }).hour).toBe(4);\n  expect(dt.set({ minute: 16 }).minute).toBe(16);\n  expect(dt.set({ second: 45 }).second).toBe(45);\n  expect(dt.set({ millisecond: 86 }).millisecond).toBe(86);\n});\n\ntest(\"DateTime#set({ month }) doesn't go to the wrong month\", () => {\n  const end = DateTime.fromJSDate(new Date(1983, 4, 31)),\n    moved = end.set({ month: 4 });\n  expect(moved.month).toBe(4);\n  expect(moved.day).toBe(30);\n});\n\ntest(\"DateTime#set({ year }) doesn't wrap leap years\", () => {\n  const end = DateTime.fromJSDate(new Date(2012, 1, 29)),\n    moved = end.set({ year: 2013 });\n  expect(moved.month).toBe(2);\n  expect(moved.day).toBe(28);\n});\n\n//------\n// weekYear/weekNumber/weekday\n//------\n\ntest(\"DateTime#set({ weekYear }) sets the date to the same weekNumber/weekday of the target weekYear\", () => {\n  const modified = dt.set({ weekYear: 2017 });\n  expect(modified.weekday).toBe(2); // still tuesday\n  expect(modified.weekNumber).toBe(21);\n  expect(modified.year).toBe(2017);\n  expect(modified.month).toBe(5);\n  expect(modified.day).toBe(23); // 2017-W21-2 is the 23\n  expect(modified.hour).toBe(9);\n  expect(modified.minute).toBe(23);\n  expect(modified.second).toBe(54);\n  expect(modified.millisecond).toBe(123);\n});\n\ntest(\"DateTime#set({ weekNumber }) sets the date to the same weekday of the target weekNumber\", () => {\n  const modified = dt.set({ weekNumber: 2 });\n  expect(modified.weekday).toBe(2); // still tuesday\n  expect(modified.year).toBe(1982);\n  expect(modified.month).toBe(1);\n  expect(modified.day).toBe(12);\n  expect(modified.hour).toBe(9);\n  expect(modified.minute).toBe(23);\n  expect(modified.second).toBe(54);\n  expect(modified.millisecond).toBe(123);\n});\n\ntest(\"DateTime#set({ weekday }) sets the weekday to this week's matching day\", () => {\n  const modified = dt.set({ weekday: 1 });\n  expect(modified.weekday).toBe(1);\n  expect(modified.year).toBe(1982);\n  expect(modified.month).toBe(5);\n  expect(modified.day).toBe(24); // monday is the previous day\n  expect(modified.hour).toBe(9);\n  expect(modified.minute).toBe(23);\n  expect(modified.second).toBe(54);\n  expect(modified.millisecond).toBe(123);\n});\n\ntest(\"DateTime#set({ weekday }) handles week year edge cases\", () => {\n  const endOfWeekIs = (startISO, expectedISO) => {\n    const start = DateTime.fromISO(startISO);\n    const expected = DateTime.fromISO(expectedISO);\n    expect(start.set({ weekday: 7 })).toEqual(expected);\n  };\n\n  endOfWeekIs(\"2016-01-02\", \"2016-01-03\");\n  endOfWeekIs(\"2016-12-29\", \"2017-01-01\");\n  endOfWeekIs(\"2021-01-01\", \"2021-01-03\");\n  endOfWeekIs(\"2028-01-01\", \"2028-01-02\");\n});\n\n//------\n// locale-based week units\n//------\n\ntest(\"DateTime#set({ localWeekday }) sets the weekday to this week's matching day based on the locale (en-US)\", () => {\n  const modified = dt.reconfigure({ locale: \"en-US\" }).set({ localWeekday: 1 });\n  expect(modified.localWeekday).toBe(1);\n  expect(modified.weekday).toBe(7);\n  expect(modified.year).toBe(1982);\n  expect(modified.month).toBe(5);\n  expect(modified.day).toBe(23);\n  expect(modified.hour).toBe(9);\n  expect(modified.minute).toBe(23);\n  expect(modified.second).toBe(54);\n  expect(modified.millisecond).toBe(123);\n});\n\ntest(\"DateTime#set({ localWeekday }) sets the weekday to this week's matching day based on the locale (de-DE)\", () => {\n  const modified = dt.reconfigure({ locale: \"de-DE\" }).set({ localWeekday: 1 });\n  expect(modified.localWeekday).toBe(1);\n  expect(modified.weekday).toBe(1);\n  expect(modified.year).toBe(1982);\n  expect(modified.month).toBe(5);\n  expect(modified.day).toBe(24);\n  expect(modified.hour).toBe(9);\n  expect(modified.minute).toBe(23);\n  expect(modified.second).toBe(54);\n  expect(modified.millisecond).toBe(123);\n});\n\ntest(\"DateTime#set({ localWeekday }) handles crossing over into the previous year\", () => {\n  const modified = DateTime.local(2022, 1, 1, 9, 23, 54, 123, { locale: \"en-US\" }).set({\n    localWeekday: 2,\n  });\n  expect(modified.localWeekday).toBe(2);\n  expect(modified.weekday).toBe(1);\n  expect(modified.year).toBe(2021);\n  expect(modified.month).toBe(12);\n  expect(modified.day).toBe(27);\n  expect(modified.hour).toBe(9);\n  expect(modified.minute).toBe(23);\n  expect(modified.second).toBe(54);\n  expect(modified.millisecond).toBe(123);\n});\n\ntest(\"DateTime#set({ localWeekday }) handles crossing over into the previous year\", () => {\n  const modified = DateTime.local(2022, 1, 1, 9, 23, 54, 123, { locale: \"en-US\" }).set({\n    localWeekday: 2,\n  });\n  expect(modified.localWeekday).toBe(2);\n  expect(modified.weekday).toBe(1);\n  expect(modified.year).toBe(2021);\n  expect(modified.month).toBe(12);\n  expect(modified.day).toBe(27);\n  expect(modified.hour).toBe(9);\n  expect(modified.minute).toBe(23);\n  expect(modified.second).toBe(54);\n  expect(modified.millisecond).toBe(123);\n});\n\ntest(\"DateTime#set({ localWeekNumber }) sets the date to the same weekday of the target weekNumber (en-US)\", () => {\n  const modified = dt.reconfigure({ locale: \"en-US\" }).set({ localWeekNumber: 2 });\n  expect(modified.weekday).toBe(2); // still tuesday\n  expect(modified.localWeekNumber).toBe(2);\n  expect(modified.year).toBe(1982);\n  expect(modified.month).toBe(1);\n  expect(modified.day).toBe(supportsMinDaysInFirstWeek() ? 5 : 12);\n  expect(modified.hour).toBe(9);\n  expect(modified.minute).toBe(23);\n  expect(modified.second).toBe(54);\n  expect(modified.millisecond).toBe(123);\n});\n\ntest(\"DateTime#set({ localWeekNumber }) sets the date to the same weekday of the target weekNumber (de-DE)\", () => {\n  const modified = dt.reconfigure({ locale: \"de-DE\" }).set({ localWeekNumber: 2 });\n  expect(modified.weekday).toBe(2); // still tuesday\n  expect(modified.localWeekNumber).toBe(2);\n  expect(modified.year).toBe(1982);\n  expect(modified.month).toBe(1);\n  expect(modified.day).toBe(12);\n  expect(modified.hour).toBe(9);\n  expect(modified.minute).toBe(23);\n  expect(modified.second).toBe(54);\n  expect(modified.millisecond).toBe(123);\n});\n\ntest(\"DateTime#set({ localWeekNumber }) sets the date to the same weekday of the target weekNumber (custom weekSettings)\", () => {\n  withDefaultWeekSettings(\n    {\n      firstDay: 7,\n      weekend: [6, 7],\n      minimalDays: 1,\n    },\n    () => {\n      const modified = dtFactory().set({ localWeekNumber: 2 });\n      expect(modified.weekday).toBe(2); // still tuesday\n      expect(modified.localWeekNumber).toBe(2);\n      expect(modified.year).toBe(1982);\n      expect(modified.month).toBe(1);\n      expect(modified.day).toBe(5);\n      expect(modified.hour).toBe(9);\n      expect(modified.minute).toBe(23);\n      expect(modified.second).toBe(54);\n      expect(modified.millisecond).toBe(123);\n    }\n  );\n});\n\ntest(\"DateTime#set({ localWeekYear }) sets the date to the same weekNumber/weekday of the target weekYear (en-US)\", () => {\n  const modified = dt.reconfigure({ locale: \"en-US\" }).set({ localWeekYear: 2017 });\n  expect(modified.localWeekday).toBe(3); // still tuesday\n  expect(modified.localWeekNumber).toBe(supportsMinDaysInFirstWeek() ? 22 : 21); // still week 22\n  expect(modified.localWeekYear).toBe(2017);\n  expect(modified.year).toBe(2017);\n  expect(modified.month).toBe(5);\n  expect(modified.day).toBe(supportsMinDaysInFirstWeek() ? 30 : 23); // 2017-W22-3 is the 30\n  expect(modified.hour).toBe(9);\n  expect(modified.minute).toBe(23);\n  expect(modified.second).toBe(54);\n  expect(modified.millisecond).toBe(123);\n});\n\ntest(\"DateTime#set({ localWeekNumber }) sets the date to the same weekday of the target weekNumber (custom weekSettings)\", () => {\n  withDefaultWeekSettings(\n    {\n      firstDay: 7,\n      weekend: [6, 7],\n      minimalDays: 1,\n    },\n    () => {\n      const modified = dtFactory().set({ localWeekNumber: 2 });\n      expect(modified.weekday).toBe(2); // still tuesday\n      expect(modified.localWeekNumber).toBe(2);\n      expect(modified.year).toBe(1982);\n      expect(modified.month).toBe(1);\n      expect(modified.day).toBe(5);\n      expect(modified.hour).toBe(9);\n      expect(modified.minute).toBe(23);\n      expect(modified.second).toBe(54);\n      expect(modified.millisecond).toBe(123);\n    }\n  );\n});\n\ntest(\"DateTime#set({ localWeekYear }) sets the date to the same weekNumber/weekday of the target weekYear (de-DE)\", () => {\n  const modified = dt.reconfigure({ locale: \"de-DE\" }).set({ localWeekYear: 2017 });\n  expect(modified.localWeekday).toBe(2); // still tuesday\n  expect(modified.localWeekNumber).toBe(21); // still week 21\n  expect(modified.localWeekYear).toBe(2017);\n  expect(modified.year).toBe(2017);\n  expect(modified.month).toBe(5);\n  expect(modified.day).toBe(23); // 2017-W21-2 is the 30\n  expect(modified.hour).toBe(9);\n  expect(modified.minute).toBe(23);\n  expect(modified.second).toBe(54);\n  expect(modified.millisecond).toBe(123);\n});\n\n//------\n// year/ordinal\n//------\ntest(\"DateTime#set({ ordinal }) sets the date to the ordinal within the current year\", () => {\n  const modified = dt.set({ ordinal: 200 });\n  expect(modified.year).toBe(1982);\n  expect(modified.month).toBe(7);\n  expect(modified.day).toBe(19);\n  expect(modified.hour).toBe(9);\n  expect(modified.minute).toBe(23);\n  expect(modified.second).toBe(54);\n  expect(modified.millisecond).toBe(123);\n});\n\n//------\n// set multiple things\n//------\n\ntest(\"DateTime.set does units in increasing size\", () => {\n  const modified = dt.set({ day: 31, month: 3 });\n  expect(modified.month).toBe(3);\n  expect(modified.day).toBe(31);\n});\n\n//------\n// set invalid things\n//------\ntest(\"DateTime#set throws for invalid units\", () => {\n  expect(() => dt.set({ glorb: 200 })).toThrow();\n});\n\ntest(\"DateTime#set throws for metadata\", () => {\n  expect(() => dt.set({ zone: \"UTC\" })).toThrow();\n  expect(() => dt.set({ locale: \"be\" })).toThrow();\n  expect(() => dt.set({ invalid: true })).toThrow();\n});\n\ntest(\"DateTime#set throws for mixing incompatible units\", () => {\n  expect(() => dt.set({ year: 2020, weekNumber: 22 })).toThrow();\n  expect(() => dt.set({ ordinal: 200, weekNumber: 22 })).toThrow();\n  expect(() => dt.set({ ordinal: 200, month: 8 })).toThrow();\n  expect(() => dt.set({ year: 2020, localWeekNumber: 22 })).toThrow();\n  expect(() => dt.set({ ordinal: 200, localWeekNumber: 22 })).toThrow();\n  expect(() => dt.set({ weekday: 2, localWeekNumber: 22 })).toThrow();\n  expect(() => dt.set({ weekday: 2, localWeekYear: 2022 })).toThrow();\n});\n\ntest(\"DateTime#set maintains invalidity\", () => {\n  expect(DateTime.invalid(\"because\").set({ ordinal: 200 }).isValid).toBe(false);\n});\n"
  },
  {
    "path": "test/datetime/toFormat.test.js",
    "content": "/* global test expect */\n\nimport { DateTime } from \"../../src/luxon\";\n\nconst dt = DateTime.fromObject(\n  {\n    year: 1982,\n    month: 5,\n    day: 25,\n    hour: 9,\n    minute: 23,\n    second: 54,\n    millisecond: 123,\n  },\n  {\n    zone: \"utc\",\n  }\n);\nconst ny = dt.setZone(\"America/New_York\", { keepLocalTime: true });\n\n//------\n// #toFormat()\n//------\n\ntest(\"DateTime#toFormat accepts the locale from the DateTime or the options\", () => {\n  expect(dt.setLocale(\"fr\").toFormat(\"LLLL\")).toBe(\"mai\");\n  expect(dt.toFormat(\"LLLL\", { locale: \"fr\" })).toBe(\"mai\");\n  expect(dt.setLocale(\"pt\").toFormat(\"LLLL\", { locale: \"fr\" })).toBe(\"mai\");\n});\n\ntest(\"DateTime#toFormat('u') returns fractional seconds\", () => {\n  expect(dt.toFormat(\"u\")).toBe(\"123\");\n  expect(dt.set({ millisecond: 82 }).toFormat(\"u\")).toBe(\"082\");\n  expect(dt.set({ millisecond: 2 }).toFormat(\"u\")).toBe(\"002\");\n  expect(dt.set({ millisecond: 80 }).toFormat(\"u\")).toBe(\"080\"); // I think this is OK\n});\n\ntest(\"DateTime#toFormat('uu') returns fractional seconds as two digits\", () => {\n  expect(dt.toFormat(\"uu\")).toBe(\"12\");\n  expect(dt.set({ millisecond: 82 }).toFormat(\"uu\")).toBe(\"08\");\n  expect(dt.set({ millisecond: 789 }).toFormat(\"uu\")).toBe(\"78\");\n});\n\ntest(\"DateTime#toFormat('uuu') returns fractional seconds as one digit\", () => {\n  expect(dt.toFormat(\"uuu\")).toBe(\"1\");\n  expect(dt.set({ millisecond: 82 }).toFormat(\"uuu\")).toBe(\"0\");\n  expect(dt.set({ millisecond: 789 }).toFormat(\"uuu\")).toBe(\"7\");\n});\n\ntest(\"DateTime#toFormat('S') returns the millisecond\", () => {\n  expect(dt.toFormat(\"S\")).toBe(\"123\");\n  expect(dt.reconfigure({ locale: \"bn\" }).toFormat(\"S\")).toBe(\"১২৩\");\n  expect(dt.toFormat(\"S\")).toBe(\"123\");\n  expect(dt.set({ millisecond: 82 }).toFormat(\"S\")).toBe(\"82\");\n});\n\ntest(\"DateTime#toFormat('SSS') returns padded the millisecond\", () => {\n  expect(dt.toFormat(\"SSS\")).toBe(\"123\");\n  expect(dt.reconfigure({ locale: \"bn\" }).toFormat(\"SSS\")).toBe(\"১২৩\");\n  expect(dt.set({ millisecond: 82 }).toFormat(\"SSS\")).toBe(\"082\");\n});\n\ntest(\"DateTime#toFormat('s') returns the second\", () => {\n  expect(dt.toFormat(\"s\")).toBe(\"54\");\n  expect(dt.reconfigure({ locale: \"bn\" }).toFormat(\"s\")).toBe(\"৫৪\");\n  expect(dt.set({ second: 6 }).toFormat(\"s\")).toBe(\"6\");\n});\n\ntest(\"DateTime#toFormat('ss') returns the padded second\", () => {\n  expect(dt.toFormat(\"ss\")).toBe(\"54\");\n  expect(dt.reconfigure({ locale: \"bn\" }).toFormat(\"ss\")).toBe(\"৫৪\");\n  expect(dt.set({ second: 6 }).toFormat(\"ss\")).toBe(\"06\");\n});\n\ntest(\"DateTime#toFormat('m') returns the minute\", () => {\n  expect(dt.toFormat(\"m\")).toBe(\"23\");\n  expect(dt.reconfigure({ locale: \"bn\" }).toFormat(\"m\")).toBe(\"২৩\");\n  expect(dt.set({ minute: 6 }).toFormat(\"m\")).toBe(\"6\");\n});\n\ntest(\"DateTime#toFormat('mm') returns the padded minute\", () => {\n  expect(dt.toFormat(\"mm\")).toBe(\"23\");\n  expect(dt.reconfigure({ locale: \"bn\" }).toFormat(\"mm\")).toBe(\"২৩\");\n  expect(dt.set({ minute: 6 }).toFormat(\"mm\")).toBe(\"06\");\n});\n\ntest(\"DateTime#toFormat('h') returns the hours\", () => {\n  expect(dt.toFormat(\"h\")).toBe(\"9\");\n  expect(dt.reconfigure({ locale: \"bn\" }).toFormat(\"h\")).toBe(\"৯\");\n  expect(dt.set({ hour: 0 }).toFormat(\"h\")).toBe(\"12\");\n  expect(dt.set({ hour: 24 }).toFormat(\"h\")).toBe(\"12\");\n  expect(dt.set({ hour: 12 }).toFormat(\"h\")).toBe(\"12\");\n  expect(dt.set({ hour: 13 }).toFormat(\"h\")).toBe(\"1\");\n});\n\ntest(\"DateTime#toFormat('hh') returns the padded hour (12-hour time)\", () => {\n  expect(dt.toFormat(\"hh\")).toBe(\"09\");\n  expect(dt.reconfigure({ locale: \"bn\" }).toFormat(\"hh\")).toBe(\"০৯\");\n  expect(dt.set({ hour: 0 }).toFormat(\"h\")).toBe(\"12\");\n  expect(dt.set({ hour: 24 }).toFormat(\"h\")).toBe(\"12\");\n  expect(dt.set({ hour: 12 }).toFormat(\"hh\")).toBe(\"12\");\n  expect(dt.set({ hour: 13 }).toFormat(\"hh\")).toBe(\"01\");\n});\n\ntest(\"DateTime#toFormat('H') returns the hour (24-hour time)\", () => {\n  expect(dt.toFormat(\"H\")).toBe(\"9\");\n  expect(dt.reconfigure({ locale: \"bn\" }).toFormat(\"H\")).toBe(\"৯\");\n  expect(dt.set({ hour: 12 }).toFormat(\"H\")).toBe(\"12\");\n  expect(dt.set({ hour: 13 }).toFormat(\"H\")).toBe(\"13\");\n});\n\ntest(\"DateTime#toFormat('HH') returns the padded hour (24-hour time)\", () => {\n  expect(dt.toFormat(\"HH\")).toBe(\"09\");\n  expect(dt.reconfigure({ locale: \"bn\" }).toFormat(\"HH\")).toBe(\"০৯\");\n  expect(dt.set({ hour: 12 }).toFormat(\"HH\")).toBe(\"12\");\n  expect(dt.set({ hour: 13 }).toFormat(\"HH\")).toBe(\"13\");\n});\n\ntest(\"DateTime#toFormat('Z') returns the narrow offset\", () => {\n  expect(dt.toUTC(360).toFormat(\"Z\")).toBe(\"+6\");\n  expect(dt.toUTC(390).toFormat(\"Z\")).toBe(\"+6:30\");\n  expect(dt.toUTC(-360).toFormat(\"Z\")).toBe(\"-6\");\n  expect(dt.toUTC(-390).toFormat(\"Z\")).toBe(\"-6:30\");\n  expect(dt.toUTC().toFormat(\"Z\")).toBe(\"+0\");\n});\n\ntest(\"DateTime#toFormat('ZZ') returns the padded offset\", () => {\n  expect(dt.toUTC(360).toFormat(\"ZZ\")).toBe(\"+06:00\");\n  expect(dt.toUTC(390).toFormat(\"ZZ\")).toBe(\"+06:30\");\n  expect(dt.toUTC(-360).toFormat(\"ZZ\")).toBe(\"-06:00\");\n  expect(dt.toUTC(-390).toFormat(\"ZZ\")).toBe(\"-06:30\");\n  expect(dt.toUTC().toFormat(\"ZZ\")).toBe(\"+00:00\");\n});\n\ntest(\"DateTime#toFormat('ZZZ') returns a numerical offset\", () => {\n  expect(dt.toUTC(360).toFormat(\"ZZZ\")).toBe(\"+0600\");\n  expect(dt.toUTC(390).toFormat(\"ZZZ\")).toBe(\"+0630\");\n  expect(dt.toUTC(-360).toFormat(\"ZZZ\")).toBe(\"-0600\");\n  expect(dt.toUTC(-390).toFormat(\"ZZZ\")).toBe(\"-0630\");\n  expect(dt.toUTC().toFormat(\"ZZZ\")).toBe(\"+0000\");\n});\n\ntest(\"DateTime#toFormat('ZZZZ') returns the short offset name\", () => {\n  const zoned = dt.setZone(\"America/Los_Angeles\");\n  expect(zoned.toFormat(\"ZZZZ\")).toBe(\"PDT\");\n  expect(dt.toUTC().toFormat(\"ZZZZ\")).toBe(\"UTC\");\n});\n\ntest(\"DateTime#toFormat('ZZZZZ') returns the full offset name\", () => {\n  const zoned = dt.setZone(\"America/Los_Angeles\");\n  expect(zoned.toFormat(\"ZZZZZ\")).toBe(\"Pacific Daylight Time\");\n  expect(dt.toUTC().toFormat(\"ZZZZZ\")).toBe(\"UTC\");\n});\n\ntest(\"DateTime#toFormat('z') returns the zone name\", () => {\n  const zoned = dt.setZone(\"America/Los_Angeles\");\n  expect(zoned.toFormat(\"z\")).toBe(\"America/Los_Angeles\");\n\n  const utc = dt.toUTC();\n  expect(utc.toFormat(\"z\")).toBe(\"UTC\");\n});\n\ntest(\"DateTime#toFormat('a') returns the meridiem\", () => {\n  expect(dt.toFormat(\"a\")).toBe(\"AM\");\n  expect(dt.reconfigure({ locale: \"my\" }).toFormat(\"a\")).toBe(\"နံနက်\");\n  expect(dt.set({ hour: 13 }).toFormat(\"a\")).toBe(\"PM\");\n  expect(dt.set({ hour: 13 }).reconfigure({ locale: \"my\" }).toFormat(\"a\")).toBe(\"ညနေ\");\n});\n\ntest(\"DateTime#toFormat('d') returns the day\", () => {\n  expect(dt.toFormat(\"d\")).toBe(\"25\");\n  expect(dt.set({ day: 1 }).toFormat(\"d\")).toBe(\"1\");\n});\n\ntest(\"DateTime#toFormat('dd') returns the padded day\", () => {\n  expect(dt.toFormat(\"dd\")).toBe(\"25\");\n  expect(dt.set({ day: 1 }).toFormat(\"dd\")).toBe(\"01\");\n});\n\ntest(\"DateTime#toFormat('E' || 'c') returns weekday number\", () => {\n  expect(dt.toFormat(\"E\")).toBe(\"2\");\n  expect(dt.toFormat(\"c\")).toBe(\"2\");\n});\n\ntest(\"DateTime#toFormat('EEE') returns short format weekday name\", () => {\n  expect(dt.toFormat(\"EEE\")).toBe(\"Tue\");\n  expect(dt.reconfigure({ locale: \"de\" }).toFormat(\"EEE\")).toBe(\"Di.\");\n});\n\ntest(\"DateTime#toFormat('ccc') returns short standalone weekday name\", () => {\n  expect(dt.toFormat(\"ccc\")).toBe(\"Tue\");\n  expect(dt.reconfigure({ locale: \"de\" }).toFormat(\"ccc\")).toBe(\"Di\");\n});\n\ntest(\"DateTime#toFormat('EEEE') returns the full format weekday name\", () => {\n  expect(dt.toFormat(\"EEEE\")).toBe(\"Tuesday\");\n});\n\ntest(\"DateTime#toFormat('cccc') returns the full standalone weekday name\", () => {\n  expect(dt.toFormat(\"cccc\")).toBe(\"Tuesday\");\n});\n\ntest(\"DateTime#toFormat('EEEEE' || 'ccccc') returns narrow weekday name\", () => {\n  expect(dt.toFormat(\"EEEEE\")).toBe(\"T\");\n  expect(dt.toFormat(\"ccccc\")).toBe(\"T\");\n});\n\ntest(\"DateTime#toFormat('M' || 'L') return the month number\", () => {\n  expect(dt.toFormat(\"M\")).toBe(\"5\");\n  expect(dt.toFormat(\"L\")).toBe(\"5\");\n});\n\ntest(\"DateTime#toFormat('MM' || 'LL') return the padded month number\", () => {\n  expect(dt.toFormat(\"MM\")).toBe(\"05\");\n});\n\ntest(\"DateTime#toFormat('MMM') returns the short format month name\", () => {\n  expect(dt.toFormat(\"MMM\")).toBe(\"May\");\n  expect(dt.reconfigure({ locale: \"de\" }).toFormat(\"MMM\")).toBe(\"Mai\");\n  expect(dt.set({ month: 8 }).toFormat(\"MMM\")).toBe(\"Aug\");\n});\n\ntest(\"DateTime#toFormat('LLL') returns the short standalone month name\", () => {\n  expect(dt.toFormat(\"LLL\")).toBe(\"May\");\n  expect(dt.reconfigure({ locale: \"de\" }).toFormat(\"LLL\")).toBe(\"Mai\");\n  expect(dt.set({ month: 8 }).toFormat(\"LLL\")).toBe(\"Aug\");\n});\n\ntest(\"DateTime#toFormat('MMMM') returns the full format month name\", () => {\n  expect(dt.toFormat(\"MMMM\")).toBe(\"May\");\n  expect(dt.set({ month: 8 }).toFormat(\"MMMM\")).toBe(\"August\");\n  expect(dt.set({ month: 8 }).reconfigure({ locale: \"ru\" }).toFormat(\"MMMM\")).toBe(\"августа\");\n});\n\ntest(\"DateTime#toFormat('LLLL') returns the full standalone month name\", () => {\n  expect(dt.toFormat(\"LLLL\")).toBe(\"May\");\n  expect(dt.set({ month: 8 }).toFormat(\"LLLL\")).toBe(\"August\");\n});\n\ntest(\"DateTime#toFormat('MMMMM' || 'LLLLL') returns the narrow month name\", () => {\n  expect(dt.toFormat(\"MMMMM\")).toBe(\"M\");\n  expect(dt.toFormat(\"LLLLL\")).toBe(\"M\");\n});\n\ntest(\"DateTime#toFormat('y') returns the full year\", () => {\n  expect(dt.toFormat(\"y\")).toBe(\"1982\");\n  expect(dt.reconfigure({ locale: \"bn\" }).toFormat(\"y\")).toBe(\"১৯৮২\");\n  expect(dt.set({ year: 3 }).toFormat(\"y\")).toBe(\"3\");\n});\n\ntest(\"DateTime#toFormat('yy') returns the two-digit year\", () => {\n  expect(dt.toFormat(\"yy\")).toBe(\"82\");\n  expect(dt.reconfigure({ locale: \"bn\" }).toFormat(\"yy\")).toBe(\"৮২\");\n  expect(dt.set({ year: 3 }).toFormat(\"yy\")).toBe(\"03\");\n});\n\ntest(\"DateTime#toFormat('yyyy') returns the padded full year\", () => {\n  expect(dt.toFormat(\"yyyy\")).toBe(\"1982\");\n  expect(dt.reconfigure({ locale: \"bn\" }).toFormat(\"yyyy\")).toBe(\"১৯৮২\");\n  expect(dt.set({ year: 3 }).toFormat(\"yyyy\")).toBe(\"0003\");\n  expect(dt.set({ year: 3 }).reconfigure({ locale: \"bn\" }).toFormat(\"yyyy\")).toBe(\"০০০৩\");\n});\n\ntest(\"DateTime#toFormat('yyyy') returns the padded full year\", () => {\n  const bigDt = DateTime.fromObject({ year: 36000 });\n  expect(bigDt.toFormat(\"yyyy\")).toBe(\"36000\");\n\n  const lilDt = DateTime.fromObject({ year: 17 });\n  expect(lilDt.toFormat(\"yyyy\")).toBe(\"0017\");\n});\n\ntest(\"DateTime#toFormat('yyyyyy') returns the padded extended year\", () => {\n  const hugeDt = DateTime.fromObject({ year: 136000 });\n  expect(hugeDt.toFormat(\"yyyyyy\")).toBe(\"136000\");\n\n  const bigDt = DateTime.fromObject({ year: 36000 });\n  expect(bigDt.toFormat(\"yyyyyy\")).toBe(\"036000\");\n\n  expect(dt.toFormat(\"yyyyyy\")).toBe(\"001982\");\n\n  const lilDt = DateTime.fromObject({ year: 17 });\n  expect(lilDt.toFormat(\"yyyyyy\")).toBe(\"000017\");\n});\n\ntest(\"DateTime#toFormat('G') returns the short era\", () => {\n  expect(dt.toFormat(\"G\")).toBe(\"AD\");\n  expect(dt.reconfigure({ locale: \"de\" }).toFormat(\"G\")).toBe(\"n. Chr.\");\n  expect(dt.set({ year: -21 }).toFormat(\"G\")).toBe(\"BC\");\n  expect(dt.set({ year: -21 }).reconfigure({ locale: \"de\" }).toFormat(\"G\")).toBe(\"v. Chr.\");\n});\n\ntest(\"DateTime#toFormat('GG') returns the full era\", () => {\n  expect(dt.toFormat(\"GG\")).toBe(\"Anno Domini\");\n  expect(dt.set({ year: -21 }).toFormat(\"GG\")).toBe(\"Before Christ\");\n});\n\ntest(\"DateTime#toFormat('GGGGG') returns the narrow era\", () => {\n  expect(dt.toFormat(\"GGGGG\")).toBe(\"A\");\n  expect(dt.set({ year: -21 }).toFormat(\"GGGGG\")).toBe(\"B\");\n});\n\ntest(\"DateTime#toFormat('W') returns the week number\", () => {\n  expect(dt.toFormat(\"W\")).toBe(\"21\");\n  expect(dt.set({ weekNumber: 5 }).toFormat(\"W\")).toBe(\"5\");\n});\n\ntest(\"DateTime#toFormat('WW') returns the padded week number\", () => {\n  expect(dt.toFormat(\"WW\")).toBe(\"21\");\n  expect(dt.set({ weekNumber: 5 }).toFormat(\"WW\")).toBe(\"05\");\n});\n\ntest(\"DateTime#toFormat('kk') returns the abbreviated week year\", () => {\n  expect(dt.toFormat(\"kk\")).toBe(\"82\");\n});\n\ntest(\"DateTime#toFormat('kkkk') returns the full week year\", () => {\n  expect(dt.toFormat(\"kkkk\")).toBe(\"1982\");\n});\n\ntest(\"DateTime#toFormat('o') returns an unpadded ordinal\", () => {\n  expect(dt.toFormat(\"o\")).toBe(\"145\");\n  expect(dt.set({ month: 1, day: 13 }).toFormat(\"o\")).toBe(\"13\");\n  expect(dt.set({ month: 1, day: 8 }).toFormat(\"o\")).toBe(\"8\");\n});\n\ntest(\"DateTime#toFormat('ooo') returns an unpadded ordinal\", () => {\n  expect(dt.toFormat(\"ooo\")).toBe(\"145\");\n  expect(dt.set({ month: 1, day: 13 }).toFormat(\"ooo\")).toBe(\"013\");\n  expect(dt.set({ month: 1, day: 8 }).toFormat(\"ooo\")).toBe(\"008\");\n});\n\ntest(\"DateTime#toFormat('q') returns an unpadded quarter\", () => {\n  expect(dt.toFormat(\"q\")).toBe(\"2\");\n  expect(dt.set({ month: 2 }).toFormat(\"q\")).toBe(\"1\");\n});\n\ntest(\"DateTime#toFormat('qq') returns a padded quarter\", () => {\n  expect(dt.toFormat(\"qq\")).toBe(\"02\");\n  expect(dt.set({ month: 2 }).toFormat(\"qq\")).toBe(\"01\");\n});\n\ntest(\"DateTime#toFormat('D') returns a short date representation\", () => {\n  expect(dt.toFormat(\"D\")).toBe(\"5/25/1982\");\n  expect(dt.reconfigure({ locale: \"fr\" }).toFormat(\"D\")).toBe(\"25/05/1982\");\n});\n\ntest(\"DateTime#toFormat('DD') returns a medium date representation\", () => {\n  expect(dt.toFormat(\"DD\")).toBe(\"May 25, 1982\");\n  expect(dt.set({ month: 8 }).toFormat(\"DD\")).toBe(\"Aug 25, 1982\");\n  expect(dt.reconfigure({ locale: \"fr\" }).toFormat(\"DD\")).toBe(\"25 mai 1982\");\n  expect(dt.set({ month: 2 }).reconfigure({ locale: \"fr\" }).toFormat(\"DD\")).toBe(\"25 févr. 1982\");\n});\n\ntest(\"DateTime#toFormat('DDD') returns a long date representation\", () => {\n  expect(dt.toFormat(\"DDD\")).toBe(\"May 25, 1982\");\n  expect(dt.set({ month: 8 }).toFormat(\"DDD\")).toBe(\"August 25, 1982\");\n  expect(dt.reconfigure({ locale: \"fr\" }).toFormat(\"DDD\")).toBe(\"25 mai 1982\");\n  expect(dt.set({ month: 2 }).reconfigure({ locale: \"fr\" }).toFormat(\"DDD\")).toBe(\n    \"25 février 1982\"\n  );\n});\n\ntest(\"DateTime#toFormat('DDDD') returns a long date representation\", () => {\n  expect(dt.toFormat(\"DDDD\")).toBe(\"Tuesday, May 25, 1982\");\n  expect(dt.set({ month: 8 }).toFormat(\"DDDD\")).toBe(\"Wednesday, August 25, 1982\");\n  expect(dt.reconfigure({ locale: \"fr\" }).toFormat(\"DDDD\")).toBe(\"mardi 25 mai 1982\");\n  expect(dt.set({ month: 2 }).reconfigure({ locale: \"fr\" }).toFormat(\"DDDD\")).toBe(\n    \"jeudi 25 février 1982\"\n  );\n});\n\ntest(\"DateTime#toFormat('t') returns a short time representation\", () => {\n  expect(dt.toFormat(\"t\")).toBe(\"9:23 AM\");\n  expect(dt.set({ hour: 13 }).toFormat(\"t\")).toBe(\"1:23 PM\");\n  expect(dt.reconfigure({ locale: \"fr\" }).toFormat(\"t\")).toBe(\"09:23\");\n  expect(dt.set({ hour: 13 }).reconfigure({ locale: \"fr\" }).toFormat(\"t\")).toBe(\"13:23\");\n});\n\ntest(\"DateTime#toFormat('T') returns a short 24-hour time representation\", () => {\n  expect(dt.toFormat(\"T\")).toBe(\"09:23\");\n  expect(dt.set({ hour: 13 }).toFormat(\"T\")).toBe(\"13:23\");\n  expect(dt.reconfigure({ locale: \"fr\" }).toFormat(\"T\")).toBe(\"09:23\");\n  expect(dt.set({ hour: 13 }).reconfigure({ locale: \"fr\" }).toFormat(\"T\")).toBe(\"13:23\");\n});\n\ntest(\"DateTime#toFormat('tt') returns a medium time representation\", () => {\n  expect(dt.toFormat(\"tt\")).toBe(\"9:23:54 AM\");\n  expect(dt.set({ hour: 13 }).toFormat(\"tt\")).toBe(\"1:23:54 PM\");\n  expect(dt.reconfigure({ locale: \"fr\" }).toFormat(\"tt\")).toBe(\"09:23:54\");\n  expect(dt.set({ hour: 13 }).reconfigure({ locale: \"fr\" }).toFormat(\"tt\")).toBe(\"13:23:54\");\n});\n\ntest(\"DateTime#toFormat('TT') returns a medium 24-hour time representation\", () => {\n  expect(dt.toFormat(\"TT\")).toBe(\"09:23:54\");\n  expect(dt.set({ hour: 13 }).toFormat(\"TT\")).toBe(\"13:23:54\");\n  expect(dt.reconfigure({ locale: \"fr\" }).toFormat(\"TT\")).toBe(\"09:23:54\");\n  expect(dt.set({ hour: 13 }).reconfigure({ locale: \"fr\" }).toFormat(\"TT\")).toBe(\"13:23:54\");\n});\n\ntest(\"DateTime#toFormat('ttt') returns a medium time representation\", () => {\n  // these seem to fail on Travis\n  // expect(dt.toFormat('ttt')).toBe('9:23:54 AM GMT');\n  // expect(dt.set({ hour: 13 }).toFormat('ttt')).toBe('1:23:54 PM GMT');\n  // expect(dt.reconfigure({ locale: 'fr' }).toFormat('ttt')).toBe('09:23:54 UTC');\n  // expect(dt.set({ hour: 13 }).reconfigure({ locale: 'fr' }).toFormat('ttt')).toBe('13:23:54 UTC');\n});\n\ntest(\"DateTime#toFormat('TTT') returns a medium time representation\", () => {\n  // these seem to fail on Travis\n  // expect(dt.toFormat('TTT')).toBe('09:23:54 GMT');\n  // expect(dt.set({ hour: 13 }).toFormat('TTT')).toBe('13:23:54 GMT');\n  // expect(dt.reconfigure({locale: 'fr' }).toFormat('TTT')).toBe('09:23:54 UTC');\n  // expect(dt.set({hour: 13 }).reconfigure({ locale: 'fr' }).toFormat('TTT')).toBe('13:23:54 UTC');\n});\n\ntest(\"DateTime#toFormat('f') returns a short date/time representation without seconds\", () => {\n  expect(dt.toFormat(\"f\").replace(/\\s+/g, \" \")).toBe(\"5/25/1982, 9:23 AM\");\n  expect(dt.set({ hour: 13 }).toFormat(\"f\").replace(/\\s+/g, \" \")).toBe(\"5/25/1982, 1:23 PM\");\n  expect(dt.reconfigure({ locale: \"fr\" }).toFormat(\"f\")).toBe(\"25/05/1982 09:23\");\n  expect(dt.set({ hour: 13 }).reconfigure({ locale: \"fr\" }).toFormat(\"f\")).toBe(\"25/05/1982 13:23\");\n});\n\ntest(\"DateTime#toFormat('ff') returns a medium date/time representation without seconds\", () => {\n  expect(dt.toFormat(\"ff\").replace(/\\s+/g, \" \")).toBe(\"May 25, 1982, 9:23 AM\");\n  expect(dt.set({ hour: 13 }).toFormat(\"ff\").replace(/\\s+/g, \" \")).toBe(\"May 25, 1982, 1:23 PM\");\n  expect(dt.set({ month: 8 }).toFormat(\"ff\").replace(/\\s+/g, \" \")).toBe(\"Aug 25, 1982, 9:23 AM\");\n  expect(dt.reconfigure({ locale: \"fr\" }).toFormat(\"ff\")).toBe(\"25 mai 1982, 09:23\");\n  expect(dt.set({ month: 2 }).reconfigure({ locale: \"fr\" }).toFormat(\"ff\")).toBe(\n    \"25 févr. 1982, 09:23\"\n  );\n  expect(dt.set({ hour: 13 }).reconfigure({ locale: \"fr\" }).toFormat(\"ff\")).toBe(\n    \"25 mai 1982, 13:23\"\n  );\n});\n\ntest(\"DateTime#toFormat('fff') returns a medium date/time representation without seconds\", () => {\n  expect(ny.toFormat(\"fff\")).toBe(\"May 25, 1982 at 9:23 AM EDT\");\n  expect(ny.set({ hour: 13 }).toFormat(\"fff\")).toBe(\"May 25, 1982 at 1:23 PM EDT\");\n  expect(ny.set({ month: 8 }).toFormat(\"fff\")).toBe(\"August 25, 1982 at 9:23 AM EDT\");\n  expect(ny.reconfigure({ locale: \"fr\" }).toFormat(\"fff\")).toBe(\"25 mai 1982 à 09:23 UTC−4\");\n  expect(ny.set({ month: 2 }).reconfigure({ locale: \"fr\" }).toFormat(\"fff\")).toBe(\n    \"25 février 1982 à 09:23 UTC−5\"\n  );\n  expect(ny.set({ hour: 13 }).reconfigure({ locale: \"fr\" }).toFormat(\"fff\")).toBe(\n    \"25 mai 1982 à 13:23 UTC−4\"\n  );\n});\n\ntest(\"DateTime#toFormat('ffff') returns a long date/time representation without seconds\", () => {\n  expect(ny.toFormat(\"ffff\")).toBe(\"Tuesday, May 25, 1982 at 9:23 AM Eastern Daylight Time\");\n  expect(ny.set({ hour: 13 }).toFormat(\"ffff\")).toBe(\n    \"Tuesday, May 25, 1982 at 1:23 PM Eastern Daylight Time\"\n  );\n  expect(ny.set({ month: 2 }).toFormat(\"ffff\")).toBe(\n    \"Thursday, February 25, 1982 at 9:23 AM Eastern Standard Time\"\n  );\n  expect(ny.reconfigure({ locale: \"fr\" }).toFormat(\"ffff\")).toBe(\n    \"mardi 25 mai 1982 à 09:23 heure d’été de l’Est nord-américain\"\n  );\n  expect(ny.set({ month: 2 }).reconfigure({ locale: \"fr\" }).toFormat(\"ffff\")).toBe(\n    \"jeudi 25 février 1982 à 09:23 heure normale de l’Est nord-américain\"\n  );\n  expect(ny.set({ hour: 13 }).reconfigure({ locale: \"fr\" }).toFormat(\"ffff\")).toBe(\n    \"mardi 25 mai 1982 à 13:23 heure d’été de l’Est nord-américain\"\n  );\n});\n\ntest(\"DateTime#toFormat('F') returns a short date/time representation with seconds\", () => {\n  expect(dt.toFormat(\"F\").replace(/\\s+/g, \" \")).toBe(\"5/25/1982, 9:23:54 AM\");\n  expect(dt.set({ hour: 13 }).toFormat(\"F\").replace(/\\s+/g, \" \")).toBe(\"5/25/1982, 1:23:54 PM\");\n  expect(dt.reconfigure({ locale: \"fr\" }).toFormat(\"F\")).toBe(\"25/05/1982 09:23:54\");\n  expect(dt.set({ hour: 13 }).reconfigure({ locale: \"fr\" }).toFormat(\"F\")).toBe(\n    \"25/05/1982 13:23:54\"\n  );\n});\n\ntest(\"DateTime#toFormat('FF') returns a medium date/time representation with seconds\", () => {\n  expect(dt.toFormat(\"FF\").replace(/\\s+/g, \" \")).toBe(\"May 25, 1982, 9:23:54 AM\");\n  expect(dt.set({ hour: 13 }).toFormat(\"FF\").replace(/\\s+/g, \" \")).toBe(\"May 25, 1982, 1:23:54 PM\");\n  expect(dt.set({ month: 8 }).toFormat(\"FF\").replace(/\\s+/g, \" \")).toBe(\"Aug 25, 1982, 9:23:54 AM\");\n  expect(dt.reconfigure({ locale: \"fr\" }).toFormat(\"FF\")).toBe(\"25 mai 1982, 09:23:54\");\n  expect(dt.set({ month: 2 }).reconfigure({ locale: \"fr\" }).toFormat(\"FF\")).toBe(\n    \"25 févr. 1982, 09:23:54\"\n  );\n  expect(dt.set({ hour: 13 }).reconfigure({ locale: \"fr\" }).toFormat(\"FF\")).toBe(\n    \"25 mai 1982, 13:23:54\"\n  );\n});\n\ntest(\"DateTime#toFormat('FFF') returns a medium date/time representation without seconds\", () => {\n  expect(ny.toFormat(\"FFF\")).toBe(\"May 25, 1982 at 9:23:54 AM EDT\");\n  expect(ny.set({ hour: 13 }).toFormat(\"FFF\")).toBe(\"May 25, 1982 at 1:23:54 PM EDT\");\n  expect(ny.set({ month: 8 }).toFormat(\"FFF\")).toBe(\"August 25, 1982 at 9:23:54 AM EDT\");\n  expect(ny.reconfigure({ locale: \"fr\" }).toFormat(\"FFF\")).toBe(\"25 mai 1982 à 9:23:54 UTC−4\");\n  expect(ny.set({ month: 2 }).reconfigure({ locale: \"fr\" }).toFormat(\"FFF\")).toBe(\n    \"25 février 1982 à 9:23:54 UTC−5\"\n  );\n  expect(ny.set({ hour: 13 }).reconfigure({ locale: \"fr\" }).toFormat(\"FFF\")).toBe(\n    \"25 mai 1982 à 13:23:54 UTC−4\"\n  );\n});\n\ntest(\"DateTime#toFormat('FFFF') returns a long date/time representation without seconds\", () => {\n  expect(ny.toFormat(\"FFFF\")).toBe(\"Tuesday, May 25, 1982 at 9:23:54 AM Eastern Daylight Time\");\n  expect(ny.set({ hour: 13 }).toFormat(\"FFFF\")).toBe(\n    \"Tuesday, May 25, 1982 at 1:23:54 PM Eastern Daylight Time\"\n  );\n  expect(ny.set({ month: 2 }).toFormat(\"FFFF\")).toBe(\n    \"Thursday, February 25, 1982 at 9:23:54 AM Eastern Standard Time\"\n  );\n  expect(ny.reconfigure({ locale: \"fr\" }).toFormat(\"FFFF\")).toBe(\n    \"mardi 25 mai 1982 à 9:23:54 heure d’été de l’Est nord-américain\"\n  );\n  expect(ny.set({ month: 2 }).reconfigure({ locale: \"fr\" }).toFormat(\"FFFF\")).toBe(\n    \"jeudi 25 février 1982 à 9:23:54 heure normale de l’Est nord-américain\"\n  );\n  expect(ny.set({ hour: 13 }).reconfigure({ locale: \"fr\" }).toFormat(\"FFFF\")).toBe(\n    \"mardi 25 mai 1982 à 13:23:54 heure d’été de l’Est nord-américain\"\n  );\n});\n\ntest(\"DateTime#toFormat returns a full formatted string\", () => {\n  expect(dt.toFormat(\"MM/yyyy GG\")).toBe(\"05/1982 Anno Domini\");\n});\n\ntest(\"DateTime#toFormat() accepts literals in single quotes\", () => {\n  expect(dt.toFormat(\"dd/MM/yyyy 'at' hh:mm\")).toBe(\"25/05/1982 at 09:23\");\n  expect(dt.toFormat(\"MMdd'T'hh\")).toBe(\"0525T09\");\n});\n\ntest(\"DateTime#toFormat allows escaping of single quotes\", () => {\n  expect(dt.toFormat(\"dd/MM/yyyy 'at' ''hh:mm''\")).toBe(\"25/05/1982 at '09:23'\");\n});\n\ntest(\"DateTime#toFormat() uses the numbering system\", () => {\n  expect(dt.reconfigure({ numberingSystem: \"beng\" }).toFormat(\"S\")).toBe(\"১২৩\");\n  expect(dt.toFormat(\"S\", { numberingSystem: \"beng\" })).toBe(\"১২৩\");\n});\n\ntest(\"DateTime#toFormat() uses the output calendar\", () => {\n  expect(dt.reconfigure({ outputCalendar: \"islamic\" }).toFormat(\"MMMM yyyy\")).toBe(\"Shaʻban 1402\");\n  expect(dt.toFormat(\"MMMM yyyy\", { outputCalendar: \"islamic\" })).toBe(\"Shaʻban 1402\");\n});\n\ntest(\"DateTime#toFormat() returns something different for invalid DateTimes\", () => {\n  expect(DateTime.invalid(\"because\").toFormat(\"dd MM yyyy\")).toBe(\"Invalid DateTime\");\n});\n\ntest(\"DateTime#toFormat('X') returns a Unix timestamp in seconds\", () => {\n  expect(dt.toFormat(\"X\")).toBe(\"391166634\");\n});\n\ntest(\"DateTime#toFormat('X') rounds down\", () => {\n  expect(dt.plus(500).toFormat(\"X\")).toBe(\"391166634\");\n});\n\ntest(\"DateTime#toFormat('x') returns a Unix timestamp in milliseconds\", () => {\n  expect(dt.toFormat(\"x\")).toBe(\"391166634123\");\n});\n\ntest(\"DateTime#toFormat('n')\", () => {\n  expect(DateTime.fromISO(\"2012-01-01\", { locale: \"de-DE\" }).toFormat(\"n\")).toBe(\"52\");\n  expect(DateTime.fromISO(\"2012-01-01\", { locale: \"en-US\" }).toFormat(\"n\")).toBe(\"1\");\n});\n\ntest(\"DateTime#toFormat('nn')\", () => {\n  expect(DateTime.fromISO(\"2012-01-01\", { locale: \"de-DE\" }).toFormat(\"nn\")).toBe(\"52\");\n  expect(DateTime.fromISO(\"2012-01-01\", { locale: \"en-US\" }).toFormat(\"nn\")).toBe(\"01\");\n});\n\ntest(\"DateTime#toFormat('ii')\", () => {\n  expect(DateTime.fromISO(\"2012-01-01\", { locale: \"de-DE\" }).toFormat(\"ii\")).toBe(\"11\");\n  expect(DateTime.fromISO(\"2012-01-01\", { locale: \"en-US\" }).toFormat(\"ii\")).toBe(\"12\");\n});\n\ntest(\"DateTime#toFormat('iiii')\", () => {\n  expect(DateTime.fromISO(\"2012-01-01\", { locale: \"de-DE\" }).toFormat(\"iiii\")).toBe(\"2011\");\n  expect(DateTime.fromISO(\"2012-01-01\", { locale: \"en-US\" }).toFormat(\"iiii\")).toBe(\"2012\");\n});\n"
  },
  {
    "path": "test/datetime/tokenParse.test.js",
    "content": "/* global test expect */\nimport { DateTime } from \"../../src/luxon\";\nimport Helpers, { cldrMajorVersion } from \"../helpers\";\nimport Settings from \"../../src/settings\";\nimport { ConflictingSpecificationError } from \"../../src/errors\";\n\n//------\n// .fromFormat\n//-------\ntest(\"DateTime.fromFormat() parses basic times\", () => {\n  const i = DateTime.fromFormat(\"1982/05/25 09:10:11.445\", \"yyyy/MM/dd HH:mm:ss.SSS\");\n  expect(i.year).toBe(1982);\n  expect(i.month).toBe(5);\n  expect(i.day).toBe(25);\n  expect(i.hour).toBe(9);\n  expect(i.minute).toBe(10);\n  expect(i.second).toBe(11);\n  expect(i.millisecond).toBe(445);\n});\n\ntest(\"DateTime.fromFormat() yields Invalid reason 'unparseable' for incompatible formats\", () => {\n  const i = DateTime.fromFormat(\"Mar 3, 2020\", \"MMM dd, yyyy\");\n  expect(i.invalid).not.toBeNull;\n  expect(i.invalid.reason).toEqual(\"unparsable\");\n});\n\ntest(\"DateTime.fromFormat() parses with variable-length input\", () => {\n  let i = DateTime.fromFormat(\"1982/05/03 09:07:05.004\", \"y/M/d H:m:s.S\");\n  expect(i.year).toBe(1982);\n  expect(i.month).toBe(5);\n  expect(i.day).toBe(3);\n  expect(i.hour).toBe(9);\n  expect(i.minute).toBe(7);\n  expect(i.second).toBe(5);\n  expect(i.millisecond).toBe(4);\n\n  i = DateTime.fromFormat(\"82/5/3 9:7:5.4\", \"yy/M/d H:m:s.S\");\n  expect(i.year).toBe(1982);\n  expect(i.month).toBe(5);\n  expect(i.day).toBe(3);\n  expect(i.hour).toBe(9);\n  expect(i.minute).toBe(7);\n  expect(i.second).toBe(5);\n  expect(i.millisecond).toBe(4);\n});\n\ntest(\"DateTime.fromFormat() parses meridiems\", () => {\n  let i = DateTime.fromFormat(\"1982/05/25 9 PM\", \"yyyy/MM/dd h a\");\n  expect(i.year).toBe(1982);\n  expect(i.month).toBe(5);\n  expect(i.day).toBe(25);\n  expect(i.hour).toBe(21);\n\n  i = DateTime.fromFormat(\"1982/05/25 9 AM\", \"yyyy/MM/dd h a\");\n  expect(i.year).toBe(1982);\n  expect(i.month).toBe(5);\n  expect(i.day).toBe(25);\n  expect(i.hour).toBe(9);\n\n  i = DateTime.fromFormat(\"1982/05/25 12 AM\", \"yyyy/MM/dd h a\");\n  expect(i.year).toBe(1982);\n  expect(i.month).toBe(5);\n  expect(i.day).toBe(25);\n  expect(i.hour).toBe(0);\n\n  i = DateTime.fromFormat(\"1982/05/25 12 PM\", \"yyyy/MM/dd h a\");\n  expect(i.year).toBe(1982);\n  expect(i.month).toBe(5);\n  expect(i.day).toBe(25);\n  expect(i.hour).toBe(12);\n});\n\ntest(\"DateTime.fromFormat() throws if you specify meridiem with 24-hour time\", () => {\n  expect(() => DateTime.fromFormat(\"930PM\", \"Hmma\")).toThrow(ConflictingSpecificationError);\n});\n\n// #714\ntest(\"DateTime.fromFormat() makes dots optional and handles non breakable spaces\", () => {\n  function parseMeridiem(input, isAM) {\n    const d = DateTime.fromFormat(input, \"hh:mm a\");\n    expect(d.hour).toBe(isAM ? 10 : 22);\n    expect(d.minute).toBe(45);\n    expect(d.second).toBe(0);\n  }\n\n  // Meridiem for this locale is \"a. m.\" or \"p. m.\", with a non breakable space\n  Helpers.withDefaultLocale(\"es-ES\", () => {\n    parseMeridiem(\"10:45 a. m.\", true);\n    parseMeridiem(\"10:45 a. m\", true);\n    parseMeridiem(\"10:45 a m.\", true);\n    parseMeridiem(\"10:45 a m\", true);\n\n    parseMeridiem(\"10:45 p. m.\", false);\n    parseMeridiem(\"10:45 p. m\", false);\n    parseMeridiem(\"10:45 p m.\", false);\n    parseMeridiem(\"10:45 p m\", false);\n\n    const nbsp = String.fromCharCode(160);\n\n    parseMeridiem(`10:45 a.${nbsp}m.`, true);\n    parseMeridiem(`10:45 a.${nbsp}m`, true);\n    parseMeridiem(`10:45 a${nbsp}m.`, true);\n    parseMeridiem(`10:45 a${nbsp}m`, true);\n\n    parseMeridiem(`10:45 p.${nbsp}m.`, false);\n    parseMeridiem(`10:45 p.${nbsp}m`, false);\n    parseMeridiem(`10:45 p${nbsp}m.`, false);\n    parseMeridiem(`10:45 p${nbsp}m`, false);\n  });\n});\n\ntest(\"DateTime.fromFormat() parses variable-digit years\", () => {\n  expect(DateTime.fromFormat(\"\", \"y\").isValid).toBe(false);\n  expect(DateTime.fromFormat(\"2\", \"y\").year).toBe(2);\n  expect(DateTime.fromFormat(\"22\", \"y\").year).toBe(22);\n  expect(DateTime.fromFormat(\"222\", \"y\").year).toBe(222);\n  expect(DateTime.fromFormat(\"2222\", \"y\").year).toBe(2222);\n  expect(DateTime.fromFormat(\"22222\", \"y\").year).toBe(22222);\n  expect(DateTime.fromFormat(\"222222\", \"y\").year).toBe(222222);\n  expect(DateTime.fromFormat(\"2222222\", \"y\").isValid).toBe(false);\n});\n\ntest(\"DateTime.fromFormat() with yyyyy optionally parses extended years\", () => {\n  expect(DateTime.fromFormat(\"222\", \"yyyyy\").isValid).toBe(false);\n  expect(DateTime.fromFormat(\"2222\", \"yyyyy\").year).toBe(2222);\n  expect(DateTime.fromFormat(\"22222\", \"yyyyy\").year).toBe(22222);\n  expect(DateTime.fromFormat(\"222222\", \"yyyyy\").year).toBe(222222);\n  expect(DateTime.fromFormat(\"2222222\", \"yyyyy\").isValid).toBe(false);\n});\n\ntest(\"DateTime.fromFormat() with yyyyyy strictly parses extended years\", () => {\n  expect(DateTime.fromFormat(\"2222\", \"yyyyyy\").isValid).toBe(false);\n  expect(DateTime.fromFormat(\"222222\", \"yyyyyy\").year).toBe(222222);\n  expect(DateTime.fromFormat(\"022222\", \"yyyyyy\").year).toBe(22222);\n  expect(DateTime.fromFormat(\"2222222\", \"yyyyyy\").isValid).toBe(false);\n});\n\ntest(\"DateTime.fromFormat() defaults yy to the right century\", () => {\n  expect(DateTime.fromFormat(\"60\", \"yy\").year).toBe(2060);\n  expect(DateTime.fromFormat(\"61\", \"yy\").year).toBe(1961);\n  expect(DateTime.fromFormat(\"1960\", \"yy\").year).toBe(1960);\n});\n\ntest(\"DateTime.fromFormat() respects Settings.twoDigitCutoffYear when parsing yy to the right century\", () => {\n  const oldTwoDigitCutoffYear = Settings.twoDigitCutoffYear;\n\n  try {\n    Settings.twoDigitCutoffYear = 50;\n    expect(DateTime.fromFormat(\"50\", \"yy\").year).toBe(2050);\n    expect(DateTime.fromFormat(\"51\", \"yy\").year).toBe(1951);\n    expect(DateTime.fromFormat(\"1950\", \"yy\").year).toBe(1950);\n  } finally {\n    Settings.twoDigitCutoffYear = oldTwoDigitCutoffYear;\n  }\n});\n\ntest(\"DateTime.fromFormat() parses hours\", () => {\n  expect(DateTime.fromFormat(\"5\", \"h\").hour).toBe(5);\n  expect(DateTime.fromFormat(\"12\", \"h\").hour).toBe(12);\n  expect(DateTime.fromFormat(\"05\", \"hh\").hour).toBe(5);\n  expect(DateTime.fromFormat(\"12\", \"hh\").hour).toBe(12);\n  expect(DateTime.fromFormat(\"5\", \"H\").hour).toBe(5);\n  expect(DateTime.fromFormat(\"13\", \"H\").hour).toBe(13);\n  expect(DateTime.fromFormat(\"05\", \"HH\").hour).toBe(5);\n  expect(DateTime.fromFormat(\"13\", \"HH\").hour).toBe(13);\n});\n\ntest(\"DateTime.fromFormat() parses milliseconds\", () => {\n  expect(DateTime.fromFormat(\"1\", \"S\").millisecond).toBe(1);\n  expect(DateTime.fromFormat(\"12\", \"S\").millisecond).toBe(12);\n  expect(DateTime.fromFormat(\"123\", \"S\").millisecond).toBe(123);\n  expect(DateTime.fromFormat(\"1234\", \"S\").isValid).toBe(false);\n\n  expect(DateTime.fromFormat(\"1\", \"S\").millisecond).toBe(1);\n  expect(DateTime.fromFormat(\"12\", \"S\").millisecond).toBe(12);\n  expect(DateTime.fromFormat(\"123\", \"S\").millisecond).toBe(123);\n\n  expect(DateTime.fromFormat(\"1\", \"SSS\").isValid).toBe(false);\n  expect(DateTime.fromFormat(\"12\", \"SSS\").isValid).toBe(false);\n  expect(DateTime.fromFormat(\"123\", \"SSS\").millisecond).toBe(123);\n  expect(DateTime.fromFormat(\"023\", \"SSS\").millisecond).toBe(23);\n  expect(DateTime.fromFormat(\"1234\", \"SSS\").isValid).toBe(false);\n});\n\ntest(\"DateTime.fromFormat() parses fractional seconds\", () => {\n  expect(DateTime.fromFormat(\"1\", \"u\").millisecond).toBe(100);\n  expect(DateTime.fromFormat(\"12\", \"u\").millisecond).toBe(120);\n  expect(DateTime.fromFormat(\"123\", \"u\").millisecond).toBe(123);\n  expect(DateTime.fromFormat(\"023\", \"u\").millisecond).toBe(23);\n  expect(DateTime.fromFormat(\"003\", \"u\").millisecond).toBe(3);\n  expect(DateTime.fromFormat(\"1234\", \"u\").millisecond).toBe(123);\n  expect(DateTime.fromFormat(\"1235\", \"u\").millisecond).toBe(123);\n\n  expect(DateTime.fromFormat(\"1\", \"uu\").millisecond).toBe(100);\n  expect(DateTime.fromFormat(\"12\", \"uu\").millisecond).toBe(120);\n  expect(DateTime.fromFormat(\"02\", \"uu\").millisecond).toBe(20);\n  expect(DateTime.fromFormat(\"-33\", \"uu\").isValid).toBe(false);\n\n  expect(DateTime.fromFormat(\"1\", \"uuu\").millisecond).toBe(100);\n  expect(DateTime.fromFormat(\"-2\", \"uuu\").isValid).toBe(false);\n});\n\ntest(\"DateTime.fromFormat() parses weekdays\", () => {\n  expect(DateTime.fromFormat(\"5\", \"E\").weekday).toBe(5);\n  expect(DateTime.fromFormat(\"5\", \"c\").weekday).toBe(5);\n\n  expect(DateTime.fromFormat(\"Fri\", \"EEE\").weekday).toBe(5);\n  expect(DateTime.fromFormat(\"Fri\", \"ccc\").weekday).toBe(5);\n\n  expect(DateTime.fromFormat(\"Friday\", \"EEEE\").weekday).toBe(5);\n  expect(DateTime.fromFormat(\"Friday\", \"cccc\").weekday).toBe(5);\n});\n\ntest(\"DateTime.fromFormat() parses eras\", () => {\n  let dt = DateTime.fromFormat(\"0206 AD\", \"yyyy G\");\n  expect(dt.year).toEqual(206);\n\n  dt = DateTime.fromFormat(\"0206 BC\", \"yyyy G\");\n  expect(dt.year).toEqual(-206);\n\n  dt = DateTime.fromFormat(\"0206 Before Christ\", \"yyyy GG\");\n  expect(dt.year).toEqual(-206);\n});\n\ntest(\"DateTime.fromFormat() parses variable-length days\", () => {\n  let i = DateTime.fromFormat(\"Mar 3, 2020\", \"MMM d, yyyy\");\n  expect(i.day).toBe(3);\n\n  i = DateTime.fromFormat(\"Mar 13, 2020\", \"MMM d, yyyy\");\n  expect(i.day).toBe(13);\n});\n\ntest(\"DateTime.fromFormat() parses fixed-length days\", () => {\n  let i = DateTime.fromFormat(\"Mar 03, 2020\", \"MMM dd, yyyy\");\n  expect(i.day).toBe(3);\n\n  i = DateTime.fromFormat(\"Mar 13, 2020\", \"MMM dd, yyyy\");\n  expect(i.day).toBe(13);\n});\n\ntest(\"DateTime.fromFormat() parses standalone month names\", () => {\n  let i = DateTime.fromFormat(\"May 25 1982\", \"LLLL dd yyyy\");\n  expect(i.year).toBe(1982);\n  expect(i.month).toBe(5);\n  expect(i.day).toBe(25);\n\n  i = DateTime.fromFormat(\"Sep 25 1982\", \"LLL dd yyyy\");\n  expect(i.year).toBe(1982);\n  expect(i.month).toBe(9);\n  expect(i.day).toBe(25);\n\n  i = DateTime.fromFormat(\"5 25 1982\", \"L dd yyyy\");\n  expect(i.year).toBe(1982);\n  expect(i.month).toBe(5);\n  expect(i.day).toBe(25);\n\n  i = DateTime.fromFormat(\"05 25 1982\", \"LL dd yyyy\");\n  expect(i.year).toBe(1982);\n  expect(i.month).toBe(5);\n  expect(i.day).toBe(25);\n\n  i = DateTime.fromFormat(\"mai 25 1982\", \"LLLL dd yyyy\", { locale: \"fr\" });\n  expect(i.year).toBe(1982);\n  expect(i.month).toBe(5);\n  expect(i.day).toBe(25);\n\n  i = DateTime.fromFormat(\"janv. 25 1982\", \"LLL dd yyyy\", { locale: \"fr\" });\n  expect(i.year).toBe(1982);\n  expect(i.month).toBe(1);\n  expect(i.day).toBe(25);\n});\n\ntest(\"DateTime.fromFormat() parses format month names\", () => {\n  let i = DateTime.fromFormat(\"May 25 1982\", \"MMMM dd yyyy\");\n  expect(i.year).toBe(1982);\n  expect(i.month).toBe(5);\n  expect(i.day).toBe(25);\n\n  i = DateTime.fromFormat(\"Sep 25 1982\", \"MMM dd yyyy\");\n  expect(i.year).toBe(1982);\n  expect(i.month).toBe(9);\n  expect(i.day).toBe(25);\n\n  i = DateTime.fromFormat(\"5 25 1982\", \"M dd yyyy\");\n  expect(i.year).toBe(1982);\n  expect(i.month).toBe(5);\n  expect(i.day).toBe(25);\n\n  i = DateTime.fromFormat(\"05 25 1982\", \"MM dd yyyy\");\n  expect(i.year).toBe(1982);\n  expect(i.month).toBe(5);\n  expect(i.day).toBe(25);\n\n  i = DateTime.fromFormat(\"mai 25 1982\", \"MMMM dd yyyy\", { locale: \"fr\" });\n  expect(i.year).toBe(1982);\n  expect(i.month).toBe(5);\n  expect(i.day).toBe(25);\n\n  i = DateTime.fromFormat(\"janv. 25 1982\", \"MMM dd yyyy\", { locale: \"fr\" });\n  expect(i.year).toBe(1982);\n  expect(i.month).toBe(1);\n  expect(i.day).toBe(25);\n});\n\ntest(\"DateTime.fromFormat() parses quarters\", () => {\n  const i = DateTime.fromFormat(\"1982Q2\", \"yyyy'Q'q\");\n  expect(i.year).toBe(1982);\n  expect(i.month).toBe(4);\n  expect(i.quarter).toBe(2);\n  expect(DateTime.fromFormat(\"2019Q1\", \"yyyy'Q'q\").month).toBe(1);\n  expect(DateTime.fromFormat(\"2019Q2\", \"yyyy'Q'q\").month).toBe(4);\n  expect(DateTime.fromFormat(\"2019Q3\", \"yyyy'Q'q\").month).toBe(7);\n  expect(DateTime.fromFormat(\"2019Q4\", \"yyyy'Q'q\").month).toBe(10);\n  expect(DateTime.fromFormat(\"2019Q01\", \"yyyy'Q'qq\").month).toBe(1);\n  expect(DateTime.fromFormat(\"2019Q02\", \"yyyy'Q'qq\").month).toBe(4);\n  expect(DateTime.fromFormat(\"2019Q03\", \"yyyy'Q'qq\").month).toBe(7);\n  expect(DateTime.fromFormat(\"2019Q04\", \"yyyy'Q'qq\").month).toBe(10);\n});\n\ntest(\"DateTime.fromFormat() makes trailing periods in month names optional\", () => {\n  const i = DateTime.fromFormat(\"janv 25 1982\", \"LLL dd yyyy\", {\n    locale: \"fr\",\n  });\n  expect(i.year).toBe(1982);\n  expect(i.month).toBe(1);\n  expect(i.day).toBe(25);\n});\n\ntest(\"DateTime.fromFormat() does not match arbitrary stuff with those periods\", () => {\n  const i = DateTime.fromFormat(\"janvQ 25 1982\", \"LLL dd yyyy\", {\n    locale: \"fr\",\n  });\n  expect(i.isValid).toBe(false);\n});\n\ntest(\"DateTime.fromFormat() uses case-insensitive matching\", () => {\n  const i = DateTime.fromFormat(\"Janv. 25 1982\", \"LLL dd yyyy\", {\n    locale: \"fr\",\n  });\n  expect(i.year).toBe(1982);\n  expect(i.month).toBe(1);\n  expect(i.day).toBe(25);\n});\n\ntest(\"DateTime.fromFormat() parses offsets\", () => {});\n\ntest(\"DateTime.fromFormat() validates weekday numbers\", () => {\n  let d = DateTime.fromFormat(\"2, 05/25/1982\", \"E, LL/dd/yyyy\");\n  expect(d.year).toBe(1982);\n  expect(d.month).toBe(5);\n  expect(d.day).toBe(25);\n\n  d = DateTime.fromFormat(\"1, 05/25/1982\", \"E, LL/dd/yyyy\");\n  expect(d.isValid).toBeFalsy();\n});\n\ntest(\"DateTime.fromFormat() validates weekday names\", () => {\n  let d = DateTime.fromFormat(\"Tuesday, 05/25/1982\", \"EEEE, LL/dd/yyyy\");\n  expect(d.isValid).toBe(true);\n  expect(d.year).toBe(1982);\n  expect(d.month).toBe(5);\n  expect(d.day).toBe(25);\n\n  d = DateTime.fromFormat(\"Monday, 05/25/1982\", \"EEEE, LL/dd/yyyy\");\n  expect(d.isValid).toBeFalsy();\n\n  d = DateTime.fromFormat(\"mardi, 05/25/1982\", \"EEEE, LL/dd/yyyy\", {\n    locale: \"fr\",\n  });\n  expect(d.isValid).toBe(true);\n  expect(d.year).toBe(1982);\n  expect(d.month).toBe(5);\n  expect(d.day).toBe(25);\n});\n\ntest(\"DateTime.fromFormat() defaults weekday to this week\", () => {\n  const d = DateTime.fromFormat(\"Monday\", \"EEEE\"),\n    now = DateTime.now();\n  expect(d.weekYear).toBe(now.weekYear);\n  expect(d.weekNumber).toBe(now.weekNumber);\n  expect(d.weekday).toBe(1);\n\n  const d2 = DateTime.fromFormat(\"3\", \"E\");\n  expect(d2.weekYear).toBe(now.weekYear);\n  expect(d2.weekNumber).toBe(now.weekNumber);\n  expect(d2.weekday).toBe(3);\n});\n\ntest(\"DateTime.fromFormat() parses ordinals\", () => {\n  let d = DateTime.fromFormat(\"2016 200\", \"yyyy ooo\");\n  expect(d.year).toBe(2016);\n  expect(d.ordinal).toBe(200);\n\n  d = DateTime.fromFormat(\"2016 200\", \"yyyy ooo\");\n  expect(d.year).toBe(2016);\n  expect(d.ordinal).toBe(200);\n\n  d = DateTime.fromFormat(\"2016 016\", \"yyyy ooo\");\n  expect(d.year).toBe(2016);\n  expect(d.ordinal).toBe(16);\n\n  d = DateTime.fromFormat(\"2016 200\", \"yyyy o\");\n  expect(d.year).toBe(2016);\n  expect(d.ordinal).toBe(200);\n\n  d = DateTime.fromFormat(\"2016 16\", \"yyyy o\");\n  expect(d.year).toBe(2016);\n  expect(d.ordinal).toBe(16);\n});\n\ntest(\"DateTime.fromFormat() throws on mixed units\", () => {\n  expect(() => {\n    DateTime.fromFormat(\"2017 34\", \"yyyy WW\");\n  }).toThrow();\n\n  expect(() => {\n    DateTime.fromFormat(\"2017 05 340\", \"yyyy MM ooo\");\n  }).toThrow();\n});\n\ntest(\"DateTime.fromFormat() accepts weekYear by itself\", () => {\n  let d = DateTime.fromFormat(\"2004\", \"kkkk\");\n  expect(d.weekYear).toBe(2004);\n  expect(d.weekNumber).toBe(1);\n  expect(d.weekday).toBe(1);\n\n  d = DateTime.fromFormat(\"04\", \"kk\");\n  expect(d.weekYear).toBe(2004);\n  expect(d.weekNumber).toBe(1);\n  expect(d.weekday).toBe(1);\n});\n\ntest(\"DateTime.fromFormat() defaults kk to the right century\", () => {\n  expect(DateTime.fromFormat(\"60\", \"kk\").weekYear).toBe(2060);\n  expect(DateTime.fromFormat(\"61\", \"kk\").weekYear).toBe(1961);\n  expect(DateTime.fromFormat(\"1960\", \"kk\").weekYear).toBe(1960);\n});\n\ntest(\"DateTime.fromFormat() respects Settings.twoDigitCutoffYear when parsing two digit weekYear to the right century\", () => {\n  const oldTwoDigitCutoffYear = Settings.twoDigitCutoffYear;\n\n  try {\n    Settings.twoDigitCutoffYear = 50;\n    expect(DateTime.fromFormat(\"50\", \"kk\").weekYear).toBe(2050);\n    expect(DateTime.fromFormat(\"51\", \"kk\").weekYear).toBe(1951);\n    expect(DateTime.fromFormat(\"1950\", \"kk\").weekYear).toBe(1950);\n  } finally {\n    Settings.twoDigitCutoffYear = oldTwoDigitCutoffYear;\n  }\n});\n\ntest(\"DateTime.fromFormat() accepts weekNumber by itself\", () => {\n  const now = DateTime.now();\n\n  let d = DateTime.fromFormat(\"17\", \"WW\");\n  expect(d.weekYear).toBe(now.weekYear);\n  expect(d.weekNumber).toBe(17);\n  expect(d.weekday).toBe(1);\n\n  d = DateTime.fromFormat(\"17\", \"W\");\n  expect(d.weekYear).toBe(now.weekYear);\n  expect(d.weekNumber).toBe(17);\n  expect(d.weekday).toBe(1);\n});\n\ntest(\"DateTime.fromFormat() accepts weekYear/weekNumber/weekday\", () => {\n  const d = DateTime.fromFormat(\"2004 17 2\", \"kkkk WW E\");\n  expect(d.weekYear).toBe(2004);\n  expect(d.weekNumber).toBe(17);\n  expect(d.weekday).toBe(2);\n});\n\ntest(\"DateTime.fromFormat() allows regex content\", () => {\n  const d = DateTime.fromFormat(\"Monday\", \"EEEE\"),\n    now = DateTime.now();\n  expect(d.weekYear).toBe(now.weekYear);\n  expect(d.weekNumber).toBe(now.weekNumber);\n  expect(d.weekday).toBe(1);\n});\n\ntest(\"DateTime.fromFormat() allows literals\", () => {\n  const i = DateTime.fromFormat(\"1982/05/25 hello 09:10:11.445\", \"yyyy/MM/dd 'hello' HH:mm:ss.SSS\");\n\n  expect(i.year).toBe(1982);\n  expect(i.month).toBe(5);\n  expect(i.day).toBe(25);\n  expect(i.hour).toBe(9);\n  expect(i.minute).toBe(10);\n  expect(i.second).toBe(11);\n  expect(i.millisecond).toBe(445);\n});\n\ntest(\"DateTime.fromFormat() returns invalid when unparsed\", () => {\n  expect(DateTime.fromFormat(\"Splurk\", \"EEEE\").isValid).toBe(false);\n});\n\ntest(\"DateTime.fromFormat() returns invalid when quarter value is not valid\", () => {\n  expect(DateTime.fromFormat(\"2019Qaa\", \"yyyy'Q'qq\").isValid).toBe(false);\n  expect(DateTime.fromFormat(\"2019Q00\", \"yyyy'Q'qq\").isValid).toBe(false);\n  expect(DateTime.fromFormat(\"2019Q0\", \"yyyy'Q'q\").isValid).toBe(false);\n  expect(DateTime.fromFormat(\"2019Q1\", \"yyyy'Q'q\").isValid).toBe(true);\n  expect(DateTime.fromFormat(\"2019Q5\", \"yyyy'Q'q\").isValid).toBe(false);\n});\n\ntest(\"DateTime.fromFormat() returns invalid for out-of-range values\", () => {\n  const rejects = (s, fmt, opts = {}) =>\n    expect(DateTime.fromFormat(s, fmt, opts).isValid).toBeFalsy();\n\n  rejects(\"8, 05/25/1982\", \"E, MM/dd/yyyy\", { locale: \"fr\" });\n  rejects(\"Tuesday, 05/25/1982\", \"EEEE, MM/dd/yyyy\", { locale: \"fr\" });\n  rejects(\"Giberish, 05/25/1982\", \"EEEE, MM/dd/yyyy\");\n  rejects(\"14/25/1982\", \"MM/dd/yyyy\");\n  rejects(\"05/46/1982\", \"MM/dd/yyyy\");\n});\n\ntest(\"DateTime.fromFormat() accepts a zone argument\", () => {\n  const d = DateTime.fromFormat(\"1982/05/25 09:10:11.445\", \"yyyy/MM/dd HH:mm:ss.SSS\", {\n    zone: \"Asia/Tokyo\",\n  });\n  expect(d.zoneName).toBe(\"Asia/Tokyo\");\n  expect(d.offset).toBe(9 * 60);\n  expect(d.year).toBe(1982);\n  expect(d.month).toBe(5);\n  expect(d.day).toBe(25);\n  expect(d.hour).toBe(9);\n  expect(d.minute).toBe(10);\n  expect(d.second).toBe(11);\n  expect(d.millisecond).toBe(445);\n});\n\ntest(\"DateTime.fromFormat() parses IANA zones\", () => {\n  let d = DateTime.fromFormat(\n    \"1982/05/25 09:10:11.445 Asia/Tokyo\",\n    \"yyyy/MM/dd HH:mm:ss.SSS z\"\n  ).toUTC();\n  expect(d.isValid).toBe(true);\n  expect(d.offset).toBe(0);\n  expect(d.hour).toBe(0);\n  expect(d.minute).toBe(10);\n\n  d = DateTime.fromFormat(\"1982/05/25 09:10:11.445 UTC\", \"yyyy/MM/dd HH:mm:ss.SSS z\").toUTC();\n  expect(d.isValid).toBe(true);\n  expect(d.offset).toBe(0);\n  expect(d.hour).toBe(9);\n  expect(d.minute).toBe(10);\n});\n\ntest(\"DateTime.fromFormat() with setZone parses IANA zones and sets it\", () => {\n  const d = DateTime.fromFormat(\"1982/05/25 09:10:11.445 Asia/Tokyo\", \"yyyy/MM/dd HH:mm:ss.SSS z\", {\n    setZone: true,\n  });\n  expect(d.zoneName).toBe(\"Asia/Tokyo\");\n  expect(d.offset).toBe(9 * 60);\n  expect(d.hour).toBe(9);\n  expect(d.minute).toBe(10);\n});\n\ntest(\"DateTime.fromFormat() parses fixed offsets\", () => {\n  const formats = [\n    [\"Z\", \"-4\"],\n    [\"ZZ\", \"-4:00\"],\n    [\"ZZZ\", \"-0400\"],\n  ];\n\n  for (const i in formats) {\n    if (Object.prototype.hasOwnProperty.call(formats, i)) {\n      const [format, example] = formats[i],\n        dt = DateTime.fromFormat(\n          `1982/05/25 09:10:11.445 ${example}`,\n          `yyyy/MM/dd HH:mm:ss.SSS ${format}`\n        );\n      expect(dt.toUTC().hour).toBe(13);\n      expect(dt.toUTC().minute).toBe(10);\n    }\n  }\n});\n\ntest(\"DateTime.fromFormat() with setZone parses fixed offsets and sets it\", () => {\n  const formats = [\n    [\"Z\", \"-4\"],\n    [\"ZZ\", \"-4:00\"],\n    [\"ZZZ\", \"-0400\"],\n  ];\n\n  for (const i in formats) {\n    if (Object.prototype.hasOwnProperty.call(formats, i)) {\n      const [format, example] = formats[i],\n        dt = DateTime.fromFormat(\n          `1982/05/25 09:10:11.445 ${example}`,\n          `yyyy/MM/dd HH:mm:ss.SSS ${format}`,\n          { setZone: true }\n        );\n      expect(dt.offset).toBe(-4 * 60);\n      expect(dt.toUTC().hour).toBe(13);\n      expect(dt.toUTC().minute).toBe(10);\n    }\n  }\n});\n\ntest(\"DateTime.fromFormat() prefers IANA zone id\", () => {\n  const i = DateTime.fromFormat(\n    \"2021-11-12T09:07:13.000+08:00[Australia/Perth]\",\n    \"yyyy-MM-dd'T'HH:mm:ss.SSSZZ[z]\",\n    { setZone: true }\n  );\n  expect(i.isValid).toBe(true);\n  expect(i.year).toBe(2021);\n  expect(i.month).toBe(11);\n  expect(i.day).toBe(12);\n  expect(i.hour).toBe(9);\n  expect(i.minute).toBe(7);\n  expect(i.second).toBe(13);\n  expect(i.millisecond).toBe(0);\n  expect(i.offset).toBe(480); //+08:00\n  expect(i.zoneName).toBe(\"Australia/Perth\");\n});\n\ntest(\"DateTime.fromFormat() ignores numerical offsets when they conflict with the zone\", () => {\n  // +11:00 is not a valid offset for the Australia/Perth time zone\n  const i = DateTime.fromFormat(\n    \"2021-11-12T09:07:13.000+11:00[Australia/Perth]\",\n    \"yyyy-MM-dd'T'HH:mm:ss.SSSZZ[z]\",\n    { setZone: true }\n  );\n  expect(i.isValid).toBe(true);\n  expect(i.year).toBe(2021);\n  expect(i.month).toBe(11);\n  expect(i.day).toBe(12);\n  expect(i.hour).toBe(9);\n  expect(i.minute).toBe(7);\n  expect(i.second).toBe(13);\n  expect(i.millisecond).toBe(0);\n  expect(i.offset).toBe(480); //+08:00\n  expect(i.zoneName).toBe(\"Australia/Perth\");\n});\n\ntest(\"DateTime.fromFormat() ignores numerical offsets when they are are wrong right now\", () => {\n  // DST is not in effect at this timestamp, so +10:00 is the correct offset\n  const i = DateTime.fromFormat(\n    \"2021-10-03T01:30:00.000+11:00[Australia/Sydney]\",\n    \"yyyy-MM-dd'T'HH:mm:ss.SSSZZ[z]\",\n    { setZone: true }\n  );\n  expect(i.isValid).toBe(true);\n  expect(i.year).toBe(2021);\n  expect(i.month).toBe(10);\n  expect(i.day).toBe(3);\n  expect(i.hour).toBe(1);\n  expect(i.minute).toBe(30);\n  expect(i.second).toBe(0);\n  expect(i.millisecond).toBe(0);\n  expect(i.offset).toBe(600); //+10:00\n  expect(i.zoneName).toBe(\"Australia/Sydney\");\n});\n\ntest(\"DateTime.fromFormat() maintains offset that belongs to time zone during overlap\", () => {\n  // On this day, 02:30 exists for both offsets, due to DST ending.\n  let i = DateTime.fromFormat(\n    \"2021-04-04T02:30:00.000+11:00[Australia/Sydney]\",\n    \"yyyy-MM-dd'T'HH:mm:ss.SSSZZ[z]\",\n    { setZone: true }\n  );\n  expect(i.isValid).toBe(true);\n  expect(i.year).toBe(2021);\n  expect(i.month).toBe(4);\n  expect(i.day).toBe(4);\n  expect(i.hour).toBe(2);\n  expect(i.minute).toBe(30);\n  expect(i.second).toBe(0);\n  expect(i.millisecond).toBe(0);\n  expect(i.offset).toBe(660); //+11:00\n  expect(i.zoneName).toBe(\"Australia/Sydney\");\n\n  i = DateTime.fromFormat(\n    \"2021-04-04T02:30:00.000+10:00[Australia/Sydney]\",\n    \"yyyy-MM-dd'T'HH:mm:ss.SSSZZ[z]\",\n    { setZone: true }\n  );\n  expect(i.isValid).toBe(true);\n  expect(i.year).toBe(2021);\n  expect(i.month).toBe(4);\n  expect(i.day).toBe(4);\n  expect(i.hour).toBe(2);\n  expect(i.minute).toBe(30);\n  expect(i.second).toBe(0);\n  expect(i.millisecond).toBe(0);\n  expect(i.offset).toBe(600); //+10:00\n  expect(i.zoneName).toBe(\"Australia/Sydney\");\n});\n\ntest(\"DateTime.format() uses local zone when setZone is false and offset in input\", () => {\n  const i = DateTime.fromFormat(\"2021-11-12T09:07:13.000+08:00\", \"yyyy-MM-dd'T'HH:mm:ss.SSSZZ\", {\n    setZone: false,\n  });\n  expect(i.isValid).toBe(true);\n  expect(i.year).toBe(2021);\n  expect(i.month).toBe(11);\n  expect(i.day).toBe(11);\n  expect(i.hour).toBe(20);\n  expect(i.minute).toBe(7);\n  expect(i.second).toBe(13);\n  expect(i.millisecond).toBe(0);\n  expect(i.offset).toBe(-300);\n  expect(i.zoneName).toBe(\"America/New_York\");\n});\n\ntest(\"DateTime.format() uses local zone when setZone is false and zone id in input\", () => {\n  const i = DateTime.fromFormat(\n    \"2021-11-12T09:07:13.000+08:00[Australia/Perth]\",\n    \"yyyy-MM-dd'T'HH:mm:ss.SSSZZ[z]\",\n    { setZone: false }\n  );\n  expect(i.isValid).toBe(true);\n  expect(i.year).toBe(2021);\n  expect(i.month).toBe(11);\n  expect(i.day).toBe(11);\n  expect(i.hour).toBe(20);\n  expect(i.minute).toBe(7);\n  expect(i.second).toBe(13);\n  expect(i.millisecond).toBe(0);\n  expect(i.offset).toBe(-300);\n  expect(i.zoneName).toBe(\"America/New_York\");\n});\n\ntest(\"DateTime.fromFormat() parses localized macro tokens\", () => {\n  const formatGroups = [\n    {\n      formats: [\"D\", \"DD\", \"DDD\", \"DDDD\"],\n      expectEqual: {\n        year: true,\n        month: true,\n        day: true,\n      },\n    },\n\n    {\n      formats: [\"t\", \"T\"],\n      expectEqual: {\n        hour: true,\n        minute: true,\n      },\n    },\n    {\n      formats: [\"tt\", \"TT\"],\n      expectEqual: {\n        hour: true,\n        minute: true,\n        second: true,\n      },\n    },\n\n    {\n      formats: [\"F\", \"FF\"],\n      expectEqual: {\n        year: true,\n        month: true,\n        day: true,\n        hour: true,\n        minute: true,\n        second: true,\n      },\n    },\n\n    // Parsing time zone names like `EDT` or `Eastern Daylight Time` is not supported\n    {\n      formats: [\"ttt\", \"tttt\", \"TTT\", \"TTTT\", \"FFF\", \"FFFF\"],\n      expectInvalid: true,\n    },\n  ];\n\n  const sampleDateTime = DateTime.fromMillis(1555555555555);\n\n  for (const { formats, expectEqual, expectInvalid } of formatGroups) {\n    for (const locale of [null, \"en-gb\", \"de\"]) {\n      for (const format of formats) {\n        const formatted = sampleDateTime.toFormat(format, { locale });\n        const parsed = DateTime.fromFormat(formatted, format, { locale });\n\n        if (expectInvalid) {\n          expect(parsed.isValid).toBe(false);\n        } else {\n          expect(parsed.isValid).toBe(true);\n\n          for (const key of Object.keys(expectEqual)) {\n            expect(parsed[key]).toBe(sampleDateTime[key]);\n          }\n        }\n      }\n    }\n  }\n});\n\ntest(\"DateTime.fromFormat() allows non-breaking white-space to be substituted inside macro-tokens\", () => {\n  expect(DateTime.fromFormat(\"5:54 PM\", \"t\", { locale: \"en-US\" }).isValid).toBe(true);\n  expect(DateTime.fromFormat(\"5:54 PM\", \"t\", { locale: \"en-US\" }).isValid).toBe(true);\n  expect(DateTime.fromFormat(\"5:54\\nPM\", \"t\", { locale: \"en-US\" }).isValid).toBe(false);\n});\n\ntest(\"DateTime.fromFormat() throws if you don't provide a format\", () => {\n  expect(() => DateTime.fromFormat(\"yo\")).toThrowError();\n});\n\ntest(\"DateTime.fromFormat validates weekdays\", () => {\n  let dt = DateTime.fromFormat(\"Wed 2017-11-29 02:00\", \"EEE yyyy-MM-dd HH:mm\");\n  expect(dt.isValid).toBe(true);\n\n  dt = DateTime.fromFormat(\"Thu 2017-11-29 02:00\", \"EEE yyyy-MM-dd HH:mm\");\n  expect(dt.isValid).toBe(false);\n\n  dt = DateTime.fromFormat(\"Wed 2017-11-29 02:00 +12:00\", \"EEE yyyy-MM-dd HH:mm ZZ\");\n  expect(dt.isValid).toBe(true);\n\n  dt = DateTime.fromFormat(\"Wed 2017-11-29 02:00 +12:00\", \"EEE yyyy-MM-dd HH:mm ZZ\", {\n    setZone: true,\n  });\n\n  expect(dt.isValid).toBe(true);\n\n  dt = DateTime.fromFormat(\"Tue 2017-11-29 02:00 +12:00\", \"EEE yyyy-MM-dd HH:mm ZZ\", {\n    setZone: true,\n  });\n  expect(dt.isValid).toBe(false);\n});\n\ntest(\"DateTime.fromFormat containing special regex token\", () => {\n  const ianaFormat = \"yyyy-MM-dd'T'HH-mm[z]\";\n  const dt = DateTime.fromFormat(\"2019-01-14T11-30[Indian/Maldives]\", ianaFormat, {\n    setZone: true,\n  });\n  expect(dt.isValid).toBe(true);\n  expect(dt.zoneName).toBe(\"Indian/Maldives\");\n\n  expect(\n    DateTime.fromFormat(\"2019-01-14T11-30[[Indian/Maldives]]\", \"yyyy-MM-dd'T'HH-mm[[z]]\").isValid\n  ).toBe(true);\n\n  expect(\n    DateTime.fromFormat(\"2019-01-14T11-30tIndian/Maldivest\", \"yyyy-MM-dd'T'HH-mm't'z't'\").isValid\n  ).toBe(true);\n\n  expect(\n    DateTime.fromFormat(\"2019-01-14T11-30\\tIndian/Maldives\\t\", \"yyyy-MM-dd'T'HH-mm't'z't'\").isValid\n  ).toBe(false);\n});\n\n// #1362\ntest(\"DateTime.fromFormat only an offset\", () => {\n  const dt = DateTime.fromFormat(\"+0100\", \"ZZZ\", { setZone: true });\n  expect(dt.isValid).toBe(true);\n  expect(dt.offset).toBe(60);\n});\n\n//------\n// .fromFormatExplain\n//-------\n\nfunction keyCount(o) {\n  return Object.keys(o).length;\n}\n\ntest(\"DateTime.fromFormatExplain() explains success\", () => {\n  const ex = DateTime.fromFormatExplain(\"May 25, 1982 09:10:12.445\", \"MMMM dd, yyyy HH:mm:ss.SSS\");\n  expect(ex.rawMatches).toBeInstanceOf(Array);\n  expect(ex.matches).toBeInstanceOf(Object);\n  expect(keyCount(ex.matches)).toBe(7);\n  expect(ex.result).toBeInstanceOf(Object);\n  expect(keyCount(ex.result)).toBe(7);\n});\n\ntest(\"DateTime.fromFormatExplain() explains a bad match\", () => {\n  const ex = DateTime.fromFormatExplain(\"May 25, 1982 09:10:12.445\", \"MMMM dd, yyyy mmmm\");\n  expect(ex.rawMatches).toBeNull();\n  expect(ex.matches).toBeInstanceOf(Object);\n  expect(keyCount(ex.matches)).toBe(0);\n  expect(ex.result).toBeInstanceOf(Object);\n  expect(keyCount(ex.result)).toBe(0);\n});\n\ntest(\"DateTime.fromFormatExplain() parses zone correctly\", () => {\n  const ex = DateTime.fromFormatExplain(\n    \"America/New_York 1-April-2019 04:10:48 PM Mon\",\n    \"z d-MMMM-yyyy hh:mm:ss a EEE\"\n  );\n  expect(ex.rawMatches).toBeInstanceOf(Array);\n  expect(ex.matches).toBeInstanceOf(Object);\n  expect(keyCount(ex.matches)).toBe(9);\n  expect(ex.result).toBeInstanceOf(Object);\n  expect(keyCount(ex.result)).toBe(7);\n  expect(ex.matches).toEqual({\n    E: 1,\n    M: 4,\n    a: 1,\n    d: 1,\n    h: 16,\n    m: 10,\n    s: 48,\n    y: 2019,\n    z: \"America/New_York\",\n  });\n});\n\ntest(\"DateTime.fromFormatExplain() parses localized string with numberingSystem correctly\", () => {\n  const cldr = cldrMajorVersion();\n  const ex1 = DateTime.fromFormatExplain(\n    cldr && cldr < 46\n      ? \"೦೩-ಏಪ್ರಿಲ್-೨೦೧೯ ೧೨:೨೬:೦೭ ಅಪರಾಹ್ನ Asia/Calcutta\"\n      : \"೦೩-ಏಪ್ರಿಲ್-೨೦೧೯ ೧೨:೨೬:೦೭ PM Asia/Calcutta\",\n    \"dd-MMMM-yyyy hh:mm:ss a z\",\n    { locale: \"kn\", numberingSystem: \"knda\" }\n  );\n  expect(ex1.rawMatches).toBeInstanceOf(Array);\n  expect(ex1.matches).toBeInstanceOf(Object);\n  expect(keyCount(ex1.matches)).toBe(8);\n  expect(ex1.result).toBeInstanceOf(Object);\n  expect(keyCount(ex1.result)).toBe(6);\n  expect(ex1.matches).toEqual({\n    M: 4,\n    a: 1,\n    d: 3,\n    h: 12,\n    m: 26,\n    s: 7,\n    y: 2019,\n    z: \"Asia/Calcutta\",\n  });\n\n  const ex2 = DateTime.fromFormatExplain(\n    \"〇三-四-二〇一九 一二:三四:四九 下午 Asia/Shanghai\",\n    \"dd-MMMM-yyyy hh:mm:ss a z\",\n    { locale: \"zh\", numberingSystem: \"hanidec\" }\n  );\n  expect(ex2.rawMatches).toBeInstanceOf(Array);\n  expect(ex2.matches).toBeInstanceOf(Object);\n  expect(keyCount(ex2.matches)).toBe(8);\n  expect(ex2.result).toBeInstanceOf(Object);\n  expect(keyCount(ex2.result)).toBe(6);\n  expect(ex2.matches).toEqual({\n    M: 4,\n    a: 1,\n    d: 3,\n    h: 12,\n    m: 34,\n    s: 49,\n    y: 2019,\n    z: \"Asia/Shanghai\",\n  });\n\n  const ex3 = DateTime.fromFormatExplain(\"٠٣-أبريل-٢٠١٩ ٠٣:٤٦:٠١ م\", \"dd-MMMM-yyyy hh:mm:ss a\", {\n    locale: \"ar\",\n    numberingSystem: \"arab\",\n  });\n  expect(ex3.rawMatches).toBeInstanceOf(Array);\n  expect(ex3.matches).toBeInstanceOf(Object);\n  expect(keyCount(ex3.matches)).toBe(7);\n  expect(ex3.result).toBeInstanceOf(Object);\n  expect(keyCount(ex3.result)).toBe(6);\n  expect(ex3.matches).toEqual({\n    M: 4,\n    a: 1,\n    d: 3,\n    h: 15,\n    m: 46,\n    s: 1,\n    y: 2019,\n  });\n\n  const ex4 = DateTime.fromFormatExplain(\"۰۳-أبريل-۲۰۱۹ ۰۳:۴۷:۲۱ م\", \"dd-MMMM-yyyy hh:mm:ss a\", {\n    locale: \"ar\",\n    numberingSystem: \"arabext\",\n  });\n  expect(ex4.rawMatches).toBeInstanceOf(Array);\n  expect(ex4.matches).toBeInstanceOf(Object);\n  expect(keyCount(ex4.matches)).toBe(7);\n  expect(ex4.result).toBeInstanceOf(Object);\n  expect(keyCount(ex4.result)).toBe(6);\n\n  const ex5 = DateTime.fromFormatExplain(\"᭐᭓-April-᭒᭐᭑᭙ ᭐᭒:᭔᭔:᭐᭗ PM\", \"dd-MMMM-yyyy hh:mm:ss a\", {\n    locale: \"id\",\n    numberingSystem: \"bali\",\n  });\n  expect(ex5.rawMatches).toBeInstanceOf(Array);\n  expect(ex5.matches).toBeInstanceOf(Object);\n  expect(keyCount(ex5.matches)).toBe(7);\n  expect(ex5.result).toBeInstanceOf(Object);\n  expect(keyCount(ex5.result)).toBe(6);\n\n  const ex6 = DateTime.fromFormatExplain(\"০৩ এপ্রিল ২০১৯ ১২.৫৭\", \"dd MMMM yyyy hh.mm\", {\n    locale: \"bn\",\n    numberingSystem: \"beng\",\n  });\n  expect(ex6.rawMatches).toBeInstanceOf(Array);\n  expect(ex6.matches).toBeInstanceOf(Object);\n  expect(keyCount(ex6.matches)).toBe(5);\n  expect(ex6.result).toBeInstanceOf(Object);\n  expect(keyCount(ex6.result)).toBe(5);\n  expect(ex6.matches).toEqual({\n    M: 4,\n    d: 3,\n    h: 12,\n    m: 57,\n    y: 2019,\n  });\n\n  const ex7 = DateTime.fromFormatExplain(\n    \"０３-April-２０１９ ０２:４７:０４ PM\",\n    \"dd-MMMM-yyyy hh:mm:ss a\",\n    {\n      locale: \"en-US\",\n      numberingSystem: \"fullwide\",\n    }\n  );\n  expect(ex7.rawMatches).toBeInstanceOf(Array);\n  expect(ex7.matches).toBeInstanceOf(Object);\n  expect(keyCount(ex7.matches)).toBe(7);\n  expect(ex7.result).toBeInstanceOf(Object);\n  expect(keyCount(ex7.result)).toBe(6);\n\n  const ex8 = DateTime.fromFormatExplain(\"०३-April-२०१९ ०२:५३:१९ PM\", \"dd-MMMM-yyyy hh:mm:ss a\", {\n    numberingSystem: \"deva\",\n  });\n  expect(ex8.rawMatches).toBeInstanceOf(Array);\n  expect(ex8.matches).toBeInstanceOf(Object);\n  expect(keyCount(ex8.matches)).toBe(7);\n  expect(ex8.result).toBeInstanceOf(Object);\n  expect(keyCount(ex8.result)).toBe(6);\n\n  const ex9 = DateTime.fromFormatExplain(\"૦૩-એપ્રિલ-૨૦૧૯ ૦૨:૫૫:૨૧ PM\", \"dd-MMMM-yyyy hh:mm:ss a\", {\n    locale: \"gu\",\n    numberingSystem: \"gujr\",\n  });\n  expect(ex9.rawMatches).toBeInstanceOf(Array);\n  expect(ex9.matches).toBeInstanceOf(Object);\n  expect(keyCount(ex9.matches)).toBe(7);\n  expect(ex9.result).toBeInstanceOf(Object);\n  expect(keyCount(ex9.result)).toBe(6);\n\n  const ex10 = DateTime.fromFormatExplain(\"០៣-April-២០១៩ ០៣:៤៩:២០ PM\", \"dd-MMMM-yyyy hh:mm:ss a\", {\n    numberingSystem: \"khmr\",\n  });\n  expect(ex10.rawMatches).toBeInstanceOf(Array);\n  expect(ex10.matches).toBeInstanceOf(Object);\n  expect(keyCount(ex10.matches)).toBe(7);\n  expect(ex10.result).toBeInstanceOf(Object);\n  expect(keyCount(ex10.result)).toBe(6);\n\n  const ex11 = DateTime.fromFormatExplain(\"໐໓-April-໒໐໑໙ ໐໓:໕໒:໑໑ PM\", \"dd-MMMM-yyyy hh:mm:ss a\", {\n    numberingSystem: \"laoo\",\n  });\n  expect(ex11.rawMatches).toBeInstanceOf(Array);\n  expect(ex11.matches).toBeInstanceOf(Object);\n  expect(keyCount(ex11.matches)).toBe(7);\n  expect(ex11.result).toBeInstanceOf(Object);\n  expect(keyCount(ex11.result)).toBe(6);\n\n  const ex12 = DateTime.fromFormatExplain(\"᥆᥉-April-᥈᥆᥇᥏ ᥆᥉:᥋᥉:᥇᥎ PM\", \"dd-MMMM-yyyy hh:mm:ss a\", {\n    numberingSystem: \"limb\",\n  });\n  expect(ex12.rawMatches).toBeInstanceOf(Array);\n  expect(ex12.matches).toBeInstanceOf(Object);\n  expect(keyCount(ex12.matches)).toBe(7);\n  expect(ex12.result).toBeInstanceOf(Object);\n  expect(keyCount(ex12.result)).toBe(6);\n\n  const ex13 = DateTime.fromFormatExplain(\"൦൩-ഏപ്രിൽ-൨൦൧൯ ൦൩:൫൪:൦൮ PM\", \"dd-MMMM-yyyy hh:mm:ss a\", {\n    locale: \"ml\",\n    numberingSystem: \"mlym\",\n  });\n  expect(ex13.rawMatches).toBeInstanceOf(Array);\n  expect(ex13.matches).toBeInstanceOf(Object);\n  expect(keyCount(ex13.matches)).toBe(7);\n  expect(ex13.result).toBeInstanceOf(Object);\n  expect(keyCount(ex13.result)).toBe(6);\n\n  const ex14 = DateTime.fromFormatExplain(\"᠐᠓-April-᠒᠐᠑᠙ ᠐᠓:᠕᠖:᠑᠙ PM\", \"dd-MMMM-yyyy hh:mm:ss a\", {\n    numberingSystem: \"mong\",\n  });\n  expect(ex14.rawMatches).toBeInstanceOf(Array);\n  expect(ex14.matches).toBeInstanceOf(Object);\n  expect(keyCount(ex14.matches)).toBe(7);\n  expect(ex14.result).toBeInstanceOf(Object);\n  expect(keyCount(ex14.result)).toBe(6);\n\n  const ex15 = DateTime.fromFormatExplain(\"୦୩-April-୨୦୧୯ ୦୩:୫୮:୪୩ PM\", \"dd-MMMM-yyyy hh:mm:ss a\", {\n    numberingSystem: \"orya\",\n  });\n  expect(ex15.rawMatches).toBeInstanceOf(Array);\n  expect(ex15.matches).toBeInstanceOf(Object);\n  expect(keyCount(ex15.matches)).toBe(7);\n  expect(ex15.result).toBeInstanceOf(Object);\n  expect(keyCount(ex15.result)).toBe(6);\n\n  const ex16 = DateTime.fromFormatExplain(\n    cldr && cldr < 45 ? \"௦௩-ஏப்ரல்-௨௦௧௯ ௦௪:௦௦:௪௧ பிற்பகல்\" : \"௦௩-ஏப்ரல்-௨௦௧௯ ௦௪:௦௦:௪௧ PM\",\n    \"dd-MMMM-yyyy hh:mm:ss a\",\n    {\n      locale: \"ta\",\n      numberingSystem: \"tamldec\",\n    }\n  );\n  expect(ex16.rawMatches).toBeInstanceOf(Array);\n  expect(ex16.matches).toBeInstanceOf(Object);\n  expect(keyCount(ex16.matches)).toBe(7);\n  expect(ex16.result).toBeInstanceOf(Object);\n  expect(keyCount(ex16.result)).toBe(6);\n\n  const ex17 = DateTime.fromFormatExplain(\n    \"౦౩-ఏప్రిల్-౨౦౧౯ ౦౪:౦౧:౩౩ PM\",\n    \"dd-MMMM-yyyy hh:mm:ss a\",\n    {\n      locale: \"te\",\n      numberingSystem: \"telu\",\n    }\n  );\n  expect(ex17.rawMatches).toBeInstanceOf(Array);\n  expect(ex17.matches).toBeInstanceOf(Object);\n  expect(keyCount(ex17.matches)).toBe(7);\n  expect(ex17.result).toBeInstanceOf(Object);\n  expect(keyCount(ex17.result)).toBe(6);\n\n  const ex18 = DateTime.fromFormatExplain(\n    \"๐๓-เมษายน-๒๐๑๙ ๐๔:๐๒:๒๔ หลังเที่ยง\",\n    \"dd-MMMM-yyyy hh:mm:ss a\",\n    {\n      locale: \"th\",\n      numberingSystem: \"thai\",\n    }\n  );\n  expect(ex18.rawMatches).toBeInstanceOf(Array);\n  expect(ex18.matches).toBeInstanceOf(Object);\n  expect(keyCount(ex18.matches)).toBe(7);\n  expect(ex18.result).toBeInstanceOf(Object);\n  expect(keyCount(ex18.result)).toBe(6);\n\n  const ex19 = DateTime.fromFormatExplain(\"༠༣-April-༢༠༡༩ ༠༤:༠༣:༢༥ PM\", \"dd-MMMM-yyyy hh:mm:ss a\", {\n    numberingSystem: \"tibt\",\n  });\n  expect(ex19.rawMatches).toBeInstanceOf(Array);\n  expect(ex19.matches).toBeInstanceOf(Object);\n  expect(keyCount(ex19.matches)).toBe(7);\n  expect(ex19.result).toBeInstanceOf(Object);\n  expect(keyCount(ex19.result)).toBe(6);\n\n  const ex20 = DateTime.fromFormatExplain(\"၀၃-April-၂၀၁၉ ၀၄:၁၀:၀၁ PM\", \"dd-MMMM-yyyy hh:mm:ss a\", {\n    numberingSystem: \"mymr\",\n  });\n  expect(ex20.rawMatches).toBeInstanceOf(Array);\n  expect(ex20.matches).toBeInstanceOf(Object);\n  expect(keyCount(ex20.matches)).toBe(7);\n  expect(ex20.result).toBeInstanceOf(Object);\n  expect(keyCount(ex20.result)).toBe(6);\n});\n\ntest(\"DateTime.fromFormatExplain() takes the same options as fromFormat\", () => {\n  const ex = DateTime.fromFormatExplain(\"Janv. 25 1982\", \"LLL dd yyyy\", { locale: \"fr\" });\n  expect(keyCount(ex.result)).toBe(3);\n});\n\n//------\n// .fromStringExplain\n//-------\ntest(\"DateTime.fromStringExplain is an alias for DateTime.fromFormatExplain\", () => {\n  const ff = DateTime.fromStringExplain(\"1982/05/25 09:10:11.445\", \"yyyy/MM/dd HH:mm:ss.SSS\"),\n    fs = DateTime.fromFormatExplain(\"1982/05/25 09:10:11.445\", \"yyyy/MM/dd HH:mm:ss.SSS\");\n\n  expect(ff).toEqual(fs);\n});\n\n//------\n// .fromString\n//-------\n\ntest(\"DateTime.fromString is an alias for DateTime.fromFormat\", () => {\n  const ff = DateTime.fromString(\"1982/05/25 09:10:11.445\", \"yyyy/MM/dd HH:mm:ss.SSS\"),\n    fs = DateTime.fromFormat(\"1982/05/25 09:10:11.445\", \"yyyy/MM/dd HH:mm:ss.SSS\");\n\n  expect(ff).toEqual(fs);\n});\n\n//------\n// .parseFormatForOpts\n//-------\n\ntest(\"DateTime.parseFormatForOpts returns a parsing format\", () => {\n  const format = DateTime.parseFormatForOpts(DateTime.DATETIME_FULL);\n  expect(format).toEqual(\"MMMM d, yyyyy at h:m a ZZZ\");\n});\n\ntest(\"DateTime.parseFormatForOpts returns a parsing format\", () => {\n  const format = DateTime.parseFormatForOpts(\"\");\n  expect(format).toBeNull();\n});\ntest(\"DateTime.parseFormatForOpts respects the hour cycle\", () => {\n  const enFormat = DateTime.parseFormatForOpts(DateTime.TIME_SIMPLE, { locale: \"en-US\" });\n  expect(enFormat).toEqual(\"h:m a\");\n\n  const deFormat = DateTime.parseFormatForOpts(DateTime.TIME_SIMPLE, { locale: \"de-DE\" });\n  expect(deFormat).toEqual(\"H:m\");\n});\ntest(\"DateTime.parseFormatForOpts respects the hour cycle when forced by the options\", () => {\n  const format = DateTime.parseFormatForOpts(DateTime.TIME_24_SIMPLE, { locale: \"en-US\" });\n  expect(format).toEqual(\"H:m\");\n});\n\n//------\n// .expandFormat\n//-------\n//\ntest(\"DateTime.expandFormat works with the default locale\", () => {\n  const format = DateTime.expandFormat(\"D\");\n  expect(format).toBe(\"M/d/yyyyy\");\n});\n\ntest(\"DateTime.expandFormat works with other locales\", () => {\n  const format = DateTime.expandFormat(\"D\", { locale: \"en-gb\" });\n  expect(format).toBe(\"d/M/yyyyy\");\n});\n\ntest(\"DateTime.expandFormat respects the hour cycle\", () => {\n  const enFormat = DateTime.expandFormat(\"t\", { locale: \"en-US\" });\n  expect(enFormat).toBe(\"h:m a\");\n\n  const deFormat = DateTime.expandFormat(\"t\", { locale: \"de-DE\" });\n  expect(deFormat).toBe(\"H:m\");\n});\n\ntest(\"DateTime.expandFormat respects the hour cycle when forced by the macro token\", () => {\n  const format = DateTime.expandFormat(\"T\", { locale: \"en-US\" });\n  expect(format).toBe(\"H:m\");\n});\n\n//------\n// .fromFormatParser\n//-------\n\ntest(\"DateTime.fromFormatParser behaves equivalently to DateTime.fromFormat\", () => {\n  const dateTimeStr = \"1982/05/25 09:10:11.445\";\n  const format = \"yyyy/MM/dd HH:mm:ss.SSS\";\n  const formatParser = DateTime.buildFormatParser(format);\n  const ff1 = DateTime.fromFormat(dateTimeStr, format),\n    ffP1 = DateTime.fromFormatParser(dateTimeStr, formatParser);\n\n  expect(ffP1).toEqual(ff1);\n  expect(ffP1.isValid).toBe(true);\n});\n\ntest(\"DateTime.fromFormatParser throws error when used with a different locale than it was created with\", () => {\n  const format = \"yyyy/MM/dd HH:mm:ss.SSS\";\n  const formatParser = DateTime.buildFormatParser(format, { locale: \"es-ES\" });\n  expect(() =>\n    DateTime.fromFormatParser(\"1982/05/25 09:10:11.445\", formatParser, { locale: \"es-MX\" })\n  ).toThrowError(\n    \"fromFormatParser called with a locale of Locale(es-MX, null, null), but the format parser was created for Locale(es-ES, null, null)\"\n  );\n});\n"
  },
  {
    "path": "test/datetime/transform.test.js",
    "content": "/* global test expect */\n\nimport { DateTime } from \"../../src/luxon\";\n\nconst dtMaker = () =>\n    DateTime.fromObject(\n      {\n        year: 1982,\n        month: 5,\n        day: 25,\n        hour: 9,\n        minute: 23,\n        second: 54,\n        millisecond: 123,\n      },\n      {\n        zone: \"utc\",\n      }\n    ),\n  dt = dtMaker();\n\n//------\n// #toMillis()\n//------\ntest(\"DateTime#toMillis() returns milliseconds for valid DateTimes\", () => {\n  const js = dt.toJSDate();\n  expect(dt.toMillis()).toBe(js.getTime());\n});\n\ntest(\"DateTime#toMillis() returns NaN for invalid DateTimes\", () => {\n  const invalid = DateTime.invalid(\"reason\");\n  expect(invalid.toMillis()).toBe(NaN);\n});\n\n//------\n// #toSeconds()\n//------\ntest(\"DateTime#toSeconds() returns seconds for valid DateTimes\", () => {\n  const js = dt.toJSDate();\n  expect(dt.toSeconds()).toBe(js.getTime() / 1000);\n});\n\ntest(\"DateTime#toSeconds() returns NaN for invalid DateTimes\", () => {\n  const invalid = DateTime.invalid(\"reason\");\n  expect(invalid.toSeconds()).toBe(NaN);\n});\n\n//------\n// #valueOf()\n//------\ntest(\"DateTime#valueOf() just does toMillis()\", () => {\n  expect(dt.valueOf()).toBe(dt.toMillis());\n  const invalid = DateTime.invalid(\"reason\");\n  expect(invalid.valueOf()).toBe(invalid.toMillis());\n});\n\n//------\n// #toJSDate()\n//------\ntest(\"DateTime#toJSDate() returns a native Date equivalent\", () => {\n  const js = dt.toJSDate();\n  expect(js).toBeInstanceOf(Date);\n  expect(js.getTime()).toBe(dt.toMillis());\n});\n\n//------\n// #toBSON()\n//------\ntest(\"DateTime#toBSON() return a BSON serializable equivalent\", () => {\n  const js = dt.toBSON();\n  expect(js).toBeInstanceOf(Date);\n  expect(js.getTime()).toBe(dt.toMillis());\n});\n"
  },
  {
    "path": "test/datetime/typecheck.test.js",
    "content": "/* global test expect */\nimport { DateTime } from \"../../src/luxon\";\n\n//------\n// #isDateTime()\n//------\ntest(\"DateTime#isDateTime return true for valid DateTime\", () => {\n  const dt = DateTime.now();\n  expect(DateTime.isDateTime(dt)).toBe(true);\n});\n\ntest(\"DateTime#isDateTime return true for invalid DateTime\", () => {\n  const dt = DateTime.invalid(\"because\");\n  expect(DateTime.isDateTime(dt)).toBe(true);\n});\n\ntest(\"DateTime#isDateTime return false for primitives\", () => {\n  expect(DateTime.isDateTime({})).toBe(false);\n  expect(DateTime.isDateTime({ hours: 60 })).toBe(false);\n  expect(DateTime.isDateTime(1)).toBe(false);\n  expect(DateTime.isDateTime(\"\")).toBe(false);\n  expect(DateTime.isDateTime(null)).toBe(false);\n  expect(DateTime.isDateTime()).toBe(false);\n});\n"
  },
  {
    "path": "test/datetime/zone.test.js",
    "content": "/* global test expect */\n\nimport { DateTime, Settings, IANAZone } from \"../../src/luxon\";\n\nvar Helpers = require(\"../helpers\");\n\nconst millis = 391147200000,\n  // 1982-05-25T04:00:00.000Z\n  dt = () => DateTime.fromMillis(millis);\n\n//------\n// defaults\n//------\ntest(\"setZone defaults to local\", () => {\n  expect(dt().isOffsetFixed).toBe(false);\n});\n\n//------\n// #toUTC()\n//------\ntest(\"DateTime#utc() puts the dt in UTC 'mode'\", () => {\n  const zoned = dt().toUTC();\n  expect(zoned.valueOf()).toBe(millis);\n  expect(zoned.hour).toBe(4);\n  expect(zoned.zoneName).toBe(\"UTC\");\n  expect(zoned.isOffsetFixed).toBe(true);\n  expect(zoned.isInDST).toBe(false);\n});\n\ntest(\"DateTime#utc(offset) sets dt in UTC+offset 'mode'\", () => {\n  const zoned = dt().toUTC(5 * 60);\n  expect(zoned.valueOf()).toBe(millis);\n  expect(zoned.hour).toBe(9);\n  expect(zoned.zoneName).toBe(\"UTC+5\");\n  expect(zoned.isOffsetFixed).toBe(true);\n  expect(zoned.isInDST).toBe(false);\n});\n\ntest(\"DateTime#utc maintains invalidity\", () => {\n  expect(DateTime.invalid(\"because\").toUTC().isValid).toBe(false);\n});\n\n//------\n// #toLocal()\n//------\ntest(\"DateTime#toLocal() sets the calendar back to local\", () => {\n  const relocaled = dt().toUTC().toLocal(),\n    expected = new Date(millis).getHours();\n  expect(relocaled.isOffsetFixed).toBe(false);\n  expect(relocaled.valueOf()).toBe(millis);\n  expect(relocaled.hour).toBe(expected);\n});\n\ntest(\"DateTime#toLocal() accepts the default locale\", () => {\n  Helpers.withDefaultZone(\"Asia/Tokyo\", () => {\n    const tokyoLocal = DateTime.local();\n    Helpers.withDefaultZone(\"UTC\", () => expect(tokyoLocal.toLocal().zoneName).toBe(\"UTC\"));\n  });\n});\n\n//------\n// #setZone()\n//------\ntest(\"DateTime#setZone setZone sets the TZ to the specified zone\", () => {\n  const zoned = dt().setZone(\"America/Los_Angeles\");\n\n  expect(zoned.zoneName).toBe(\"America/Los_Angeles\");\n  expect(zoned.isOffsetFixed).toBe(false);\n  expect(zoned.valueOf()).toBe(millis);\n  expect(zoned.day).toBe(24);\n  expect(zoned.hour).toBe(21);\n  // pacific daylight time\n  expect(zoned.isInDST).toBe(true);\n});\n\ntest('DateTime#setZone accepts \"system\"', () => {\n  const zoned = DateTime.utc().setZone(\"system\");\n  expect(zoned.offset).toBe(DateTime.local().offset);\n});\n\ntest('DateTime#setZone accepts \"local\"', () => {\n  const zoned = DateTime.utc().setZone(\"local\");\n  expect(zoned.offset).toBe(DateTime.local().offset);\n});\n\ntest('DateTime#setZone accepts \"system\" and uses the system zone', () => {\n  const systemZone = Settings.defaultZone.name;\n  expect(DateTime.utc().setZone(\"system\").zoneName).toBe(systemZone);\n});\n\ntest('DateTime#setZone accepts \"default\" and uses the default zone', () => {\n  Helpers.withDefaultZone(\"Europe/Paris\", () => {\n    expect(DateTime.utc().setZone(\"default\").zoneName).toBe(\"Europe/Paris\");\n  });\n});\n\ntest('DateTime#setZone accepts \"utc\"', () => {\n  const zoned = DateTime.local().setZone(\"utc\");\n  expect(zoned.offset).toBe(0);\n  expect(zoned.offsetNameShort).toBe(\"UTC\");\n  expect(zoned.offsetNameLong).toBe(\"UTC\");\n});\n\ntest('DateTime#setZone accepts \"gmt\"', () => {\n  const zoned = DateTime.local().setZone(\"gmt\");\n  expect(zoned.offset).toBe(0);\n  expect(zoned.offsetNameShort).toBe(\"UTC\");\n  expect(zoned.offsetNameLong).toBe(\"UTC\");\n});\n\ntest('DateTime#setZone accepts \"utc+3\"', () => {\n  const zoned = DateTime.local().setZone(\"utc+3\");\n  expect(zoned.zone.name).toBe(\"UTC+3\");\n  expect(zoned.offset).toBe(3 * 60);\n  expect(zoned.offsetNameShort).toBe(\"UTC+3\");\n  expect(zoned.offsetNameLong).toBe(\"UTC+3\");\n});\n\ntest('DateTime#setZone accepts \"utc-3\"', () => {\n  const zoned = DateTime.local().setZone(\"utc-3\");\n  expect(zoned.zone.name).toBe(\"UTC-3\");\n  expect(zoned.offset).toBe(-3 * 60);\n  expect(zoned.offsetNameShort).toBe(\"UTC-3\");\n  expect(zoned.offsetNameLong).toBe(\"UTC-3\");\n});\n\ntest('DateTime#setZone accepts \"utc-3:30\"', () => {\n  const zoned = DateTime.local().setZone(\"utc-3:30\");\n  expect(zoned.zone.name).toBe(\"UTC-3:30\");\n  expect(zoned.offset).toBe(-3 * 60 - 30);\n  expect(zoned.offsetNameShort).toBe(\"UTC-3:30\");\n  expect(zoned.offsetNameLong).toBe(\"UTC-3:30\");\n});\n\ntest(\"DateTime#setZone does not accept dumb things\", () => {\n  Helpers.withDefaultZone(\"system\", () => {\n    const zoned = DateTime.local().setZone(\"utc-yo\");\n    // this is questionable; should this be invalid instead?\n    expect(zoned.zone.type).toBe(\"system\");\n  });\n});\n\ntest(\"DateTime#setZone accepts IANA zone names\", () => {\n  const zoned = dt().setZone(\"Europe/Paris\");\n  expect(zoned.zoneName).toBe(\"Europe/Paris\");\n  // not convinced this is universal. Could also be 'CEDT'\n  expect(zoned.offsetNameShort).toBe(\"GMT+2\");\n  expect(zoned.offsetNameLong).toBe(\"Central European Summer Time\");\n  expect(zoned.valueOf()).toBe(millis);\n  expect(zoned.hour).toBe(6); // cedt is +2\n});\n\ntest.each([\n  [\"PST8PDT\", -7], // my date is in daylight time\n  [\"EST5EDT\", -4],\n  [\"GMT+0\", 0],\n  [\"GMT0\", 0],\n])(\"DateTime#setZone accepts whacky zone %p\", (iana, offset) => {\n  const zoned = dt().setZone(iana);\n  expect(zoned.isValid).toBe(true);\n  expect(zoned.zoneName).toBe(iana);\n  expect(zoned.offset).toBe(offset * 60);\n});\n\ntest(\"DateTime#setZone accepts a keepLocalTime option\", () => {\n  const zoned = dt().toUTC().setZone(\"America/Los_Angeles\", { keepLocalTime: true });\n  expect(zoned.zoneName).toBe(\"America/Los_Angeles\");\n  expect(zoned.year).toBe(1982);\n  expect(zoned.month).toBe(5);\n  expect(zoned.day).toBe(25);\n  expect(zoned.hour).toBe(4);\n  expect(zoned.isOffsetFixed).toBe(false);\n\n  const zonedMore = zoned.setZone(\"America/New_York\", {\n    keepLocalTime: true,\n  });\n  expect(zonedMore.zoneName).toBe(\"America/New_York\");\n  expect(zonedMore.year).toBe(1982);\n  expect(zonedMore.month).toBe(5);\n  expect(zonedMore.day).toBe(25);\n  expect(zonedMore.hour).toBe(4);\n  expect(zonedMore.isOffsetFixed).toBe(false);\n});\n\ntest(\"DateTime#setZone with keepLocalTime can span wacky offsets\", () => {\n  const d = DateTime.fromISO(\"0001-01-01\", { zone: \"UTC\" });\n  const d2 = d.setZone(\"America/Curacao\", { keepLocalTime: true });\n  expect(d2.year).toBe(1);\n  expect(d2.month).toBe(1);\n  expect(d2.day).toBe(1);\n  expect(d2.hour).toBe(0);\n});\n\ntest(\"DateTime#setZone with keepLocalTime handles zones with very different offsets than the current one\", () => {\n  const local = DateTime.local(2016, 10, 30, 2, 59);\n  const zoned = local.setZone(\"Europe/Athens\", { keepLocalTime: true });\n  expect(zoned.hour).toBe(2);\n});\n\ntest(\"DateTime#setZone rejects jibberish\", () => {\n  const zoned = dt().setZone(\"blorp\");\n  expect(zoned.isValid).toBe(false);\n  expect(zoned.invalidReason).toBe(\"unsupported zone\");\n});\n\n// #650\ntest(\"DateTime#setZone works for dates before 1970 with milliseconds\", () => {\n  const offset = DateTime.fromJSDate(new Date(\"1967-01-01T00:00:00.001Z\")).setZone(\n    \"America/New_York\"\n  ).offset;\n  expect(offset).toBe(-300);\n});\n\n// # 1179\ntest(\"DateTime#setZone handles negative years\", () => {\n  const dt = DateTime.fromMillis(-84753824400000).setZone(\"Europe/Rome\");\n  expect(dt.year).toBe(-716);\n  expect(dt.offset < 60).toBe(true);\n});\n\n//------\n// #isInDST()\n//------\ntest(\"DateTime#isInDST() returns false for pre-DST times\", () => {\n  const zoned = dt().setZone(\"America/Los_Angeles\");\n  expect(zoned.set({ month: 1 }).isInDST).toBe(false);\n});\n\ntest(\"DateTime#isInDST() returns true for during-DST times\", () => {\n  const zoned = dt().setZone(\"America/Los_Angeles\");\n  expect(zoned.set({ month: 5 }).isInDST).toBe(true);\n});\n\ntest(\"DateTime#isInDST() returns false for post-DST times\", () => {\n  const zoned = dt().setZone(\"America/Los_Angeles\");\n  expect(zoned.set({ month: 12 }).isInDST).toBe(false);\n});\n\ntest(\"DateTime#isInDST() returns true for 1974 whole year in USA- from January 6th to October 27th\", () => {\n  const zoned = dt().setZone(\"America/Los_Angeles\");\n  expect(zoned.set({ year: 1974, month: 1, day: 6 }).isInDST).toBe(true);\n  expect(zoned.set({ year: 1974, month: 10, day: 27 }).isInDST).toBe(false);\n});\n\n//------\n// #getPossibleOffsets()\n//------\ntest(\"DateTime#getPossibleOffsets() returns the same DateTime for fixed zones\", () => {\n  const fixedZoned = dt().setZone(\"+02:00\");\n  const possibleOffsets = fixedZoned.getPossibleOffsets();\n  expect(possibleOffsets).toHaveLength(1);\n  expect(possibleOffsets[0]).toBe(fixedZoned);\n});\n\ntest(\"DateTime#getPossibleOffsets() returns the same DateTime when not at an ambiguous local time\", () => {\n  const zoned = DateTime.fromISO(\"2023-01-01T15:00\", { zone: \"Europe/Berlin\" });\n  const possibleOffsets = zoned.getPossibleOffsets();\n  expect(possibleOffsets).toHaveLength(1);\n  expect(possibleOffsets[0]).toBe(zoned);\n});\n\ntest(\"DateTime#getPossibleOffsets() returns the possible DateTimes when at an ambiguous local time\", () => {\n  const zoned = DateTime.fromISO(\"2023-10-29T02:30:00+01:00\", { zone: \"Europe/Berlin\" });\n  const possibleOffsets = zoned.getPossibleOffsets();\n  expect(possibleOffsets).toHaveLength(2);\n  expect(possibleOffsets[0].toISO()).toBe(\"2023-10-29T02:30:00.000+02:00\");\n  expect(possibleOffsets[1].toISO()).toBe(\"2023-10-29T02:30:00.000+01:00\");\n});\n\n//------\n// #invalid\n//------\n\n// these functions got tested in the individual zones, but let's do invalid DateTimes\n\ntest(\"DateTime#offset returns NaN for invalid times\", () => {\n  const zoned = DateTime.invalid(\"because\");\n  expect(zoned.isInDST).toBeFalsy();\n});\n\ntest(\"DateTime#offsetNameLong returns null for invalid times\", () => {\n  const zoned = DateTime.invalid(\"because\");\n  expect(zoned.offsetNameLong).toBe(null);\n});\n\ntest(\"DateTime#offsetNameShort returns null for invalid times\", () => {\n  const zoned = DateTime.invalid(\"because\");\n  expect(zoned.offsetNameShort).toBe(null);\n});\n\n//------\n// Etc/GMT zones\n//------\ntest.each([\n  [\"Etc/GMT+8\", -8],\n  [\"Etc/GMT-5\", 5],\n  [\"Etc/GMT\", 0],\n  [\"Etc/GMT-0\", 0],\n  [\"Etc/GMT\", 0],\n])(\"Etc/GMTx zones now work natively\", (zone, expectedOffset) => {\n  let zoned = dt().setZone(zone);\n  expect(zoned.isValid).toBe(true);\n  expect(zoned.offset).toEqual(expectedOffset * 60);\n});\n\n//------\n// local zone\n//------\n\ntest(\"The local zone does local stuff\", () => {\n  expect(DateTime.local(2016, 8, 6).offsetNameLong).toBe(\"Eastern Daylight Time\");\n  expect(DateTime.local(2016, 8, 6).offsetNameShort).toBe(\"EDT\");\n});\n\n//------\n// default zone\n//------\n\ntest(\"Setting the default zone results in a different creation zone\", () => {\n  Helpers.withDefaultZone(\"Asia/Tokyo\", () => {\n    expect(DateTime.local().zoneName).toBe(\"Asia/Tokyo\");\n    expect(DateTime.fromObject({}).zoneName).toBe(\"Asia/Tokyo\");\n  });\n});\n\ntest(\"Setting the default zone to 'system' gives you back the system zone\", () => {\n  const systemZone = Settings.defaultZone.name;\n  Helpers.withDefaultZone(\"Asia/Tokyo\", () => {\n    Settings.defaultZone = \"system\";\n    expect(DateTime.local().zoneName).toBe(systemZone);\n  });\n});\n\n//------\n// invalid\n//------\n\ntest(\"invalid DateTimes have no zone\", () => {\n  expect(DateTime.invalid(\"because\").zoneName).toBe(null);\n});\n\ntest(\"can parse zones with special JS keywords as invalid\", () => {\n  for (const kw of [\"constructor\", \"__proto__\"]) {\n    const dt = DateTime.fromISO(`2020-01-01T11:22:33+01:00[${kw}]`);\n    expect(dt.invalidReason).toBe(\"unsupported zone\");\n    expect(dt.invalidExplanation).toBe(`the zone \"${kw}\" is not supported`);\n  }\n});\n\ntest(\"Special JS keywords produce invalid Zone\", () => {\n  for (const kw of [\"constructor\", \"__proto__\"]) {\n    const zone = IANAZone.create(kw);\n    expect(zone.isValid).toBe(false);\n  }\n});\n\ntest(\"Invalid Zones named after special JS keywords produce NaN offset\", () => {\n  for (const kw of [\"constructor\", \"__proto__\"]) {\n    const zone = IANAZone.create(kw);\n    expect(zone.offset(1742926058000)).toBe(NaN);\n  }\n});\n\ntest(\"Invalid zones produce NaN offset\", () => {\n  expect(IANAZone.create(\"INVALID\").offset(1742926058000)).toBe(NaN);\n});\n"
  },
  {
    "path": "test/duration/accuracy.test.js",
    "content": "/* global test expect */\n\nimport { Duration } from \"../../src/luxon\";\n\nconst convert = (amt, from, to, accuracy) =>\n  Duration.fromObject({ [from]: amt }, { conversionAccuracy: accuracy }).as(to);\n\ntest(\"There are slightly more than 365 days in a year\", () => {\n  expect(convert(1, \"years\", \"days\", \"casual\")).toBeCloseTo(365, 4);\n  expect(convert(1, \"years\", \"days\", \"longterm\")).toBeCloseTo(365.2425, 4);\n\n  expect(convert(365, \"days\", \"years\", \"casual\")).toBeCloseTo(1, 4);\n  expect(convert(365.2425, \"days\", \"years\", \"longterm\")).toBeCloseTo(1, 4);\n});\n\ntest(\"There are slightly more than 30 days in a month\", () => {\n  expect(convert(1, \"month\", \"days\", \"casual\")).toBeCloseTo(30, 4);\n  expect(convert(1, \"month\", \"days\", \"longterm\")).toBeCloseTo(30.4369, 4);\n});\n\ntest(\"There are slightly more than 91 days in a quarter\", () => {\n  expect(convert(1, \"quarter\", \"days\", \"casual\")).toBeCloseTo(91, 4);\n  expect(convert(1, \"quarter\", \"days\", \"longterm\")).toBeCloseTo(91.3106, 4);\n});\n"
  },
  {
    "path": "test/duration/create.test.js",
    "content": "/* global test expect */\n\nimport { Duration } from \"../../src/luxon\";\n\n//------\n// .fromObject()\n//-------\ntest(\"Duration.fromObject sets all the values\", () => {\n  const dur = Duration.fromObject({\n    years: 1,\n    months: 2,\n    days: 3,\n    hours: 4,\n    minutes: 5,\n    seconds: 6,\n    milliseconds: 7,\n  });\n  expect(dur.years).toBe(1);\n  expect(dur.months).toBe(2);\n  expect(dur.days).toBe(3);\n  expect(dur.hours).toBe(4);\n  expect(dur.minutes).toBe(5);\n  expect(dur.seconds).toBe(6);\n  expect(dur.milliseconds).toBe(7);\n});\n\ntest(\"Duration.fromObject sets all the fractional values\", () => {\n  const dur = Duration.fromObject({\n    years: 1,\n    months: 2,\n    days: 3,\n    hours: 4.5,\n  });\n  expect(dur.years).toBe(1);\n  expect(dur.months).toBe(2);\n  expect(dur.days).toBe(3);\n  expect(dur.hours).toBe(4.5);\n  expect(dur.minutes).toBe(0);\n  expect(dur.seconds).toBe(0);\n  expect(dur.milliseconds).toBe(0);\n});\n\ntest(\"Duration.fromObject sets all the values from the object having string type values\", () => {\n  const dur = Duration.fromObject({\n    years: \"1\",\n    months: \"2\",\n    days: \"3\",\n    hours: \"4\",\n    minutes: \"5\",\n    seconds: \"6\",\n    milliseconds: \"7\",\n  });\n  expect(dur.years).toBe(1);\n  expect(dur.months).toBe(2);\n  expect(dur.days).toBe(3);\n  expect(dur.hours).toBe(4);\n  expect(dur.minutes).toBe(5);\n  expect(dur.seconds).toBe(6);\n  expect(dur.milliseconds).toBe(7);\n});\n\ntest(\"Duration.fromObject accepts a conversionAccuracy\", () => {\n  const dur = Duration.fromObject({ days: 1 }, { conversionAccuracy: \"longterm\" });\n  expect(dur.conversionAccuracy).toBe(\"longterm\");\n});\n\ntest(\"Duration.fromObject throws if the argument is not an object\", () => {\n  expect(() => Duration.fromObject()).toThrow();\n  expect(() => Duration.fromObject(null)).toThrow();\n  expect(() => Duration.fromObject(\"foo\")).toThrow();\n});\n\ntest(\"Duration.fromObject({}) constructs zero duration\", () => {\n  const dur = Duration.fromObject({});\n  expect(dur.years).toBe(0);\n  expect(dur.months).toBe(0);\n  expect(dur.days).toBe(0);\n  expect(dur.hours).toBe(0);\n  expect(dur.minutes).toBe(0);\n  expect(dur.seconds).toBe(0);\n  expect(dur.milliseconds).toBe(0);\n});\n\ntest(\"Duration.fromObject throws if the initial object has invalid keys\", () => {\n  expect(() => Duration.fromObject({ foo: 0 })).toThrow();\n  expect(() => Duration.fromObject({ years: 1, foo: 0 })).toThrow();\n});\n\ntest(\"Duration.fromObject throws if the initial object has invalid values\", () => {\n  expect(() => Duration.fromObject({ years: {} })).toThrow();\n  expect(() => Duration.fromObject({ months: \"some\" })).toThrow();\n  expect(() => Duration.fromObject({ days: NaN })).toThrow();\n  expect(() => Duration.fromObject({ hours: true })).toThrow();\n  expect(() => Duration.fromObject({ minutes: false })).toThrow();\n  expect(() => Duration.fromObject({ seconds: \"\" })).toThrow();\n});\n\ntest(\"Duration.fromObject is valid if providing options only\", () => {\n  const dur = Duration.fromObject({}, { conversionAccuracy: \"longterm\" });\n  expect(dur.years).toBe(0);\n  expect(dur.months).toBe(0);\n  expect(dur.days).toBe(0);\n  expect(dur.hours).toBe(0);\n  expect(dur.minutes).toBe(0);\n  expect(dur.seconds).toBe(0);\n  expect(dur.milliseconds).toBe(0);\n  expect(dur.isValid).toBe(true);\n});\n\n//------\n// .fromDurationLike()\n//-------\n\nit(\"Duration.fromDurationLike returns a Duration from millis\", () => {\n  const dur = Duration.fromDurationLike(1000);\n  expect(dur).toBeInstanceOf(Duration);\n  expect(dur).toMatchInlineSnapshot(`\"PT1S\"`);\n});\n\nit(\"Duration.fromDurationLike returns a Duration from object\", () => {\n  const dur = Duration.fromDurationLike({ hours: 1 });\n  expect(dur).toBeInstanceOf(Duration);\n  expect(dur.toObject()).toStrictEqual({ hours: 1 });\n});\n\nit(\"Duration.fromDurationLike returns passed Duration\", () => {\n  const durFromObject = Duration.fromObject({ hours: 1 });\n  const dur = Duration.fromDurationLike(durFromObject);\n  expect(dur).toStrictEqual(durFromObject);\n});\n\nit(\"Duration.fromDurationLike throws for invalid inputs\", () => {\n  expect(() => Duration.fromDurationLike(\"foo\")).toThrow();\n  expect(() => Duration.fromDurationLike(null)).toThrow();\n  expect(() => Duration.fromDurationLike(Infinity)).toThrow();\n  expect(() => Duration.fromDurationLike(NaN)).toThrow();\n});\n"
  },
  {
    "path": "test/duration/customMatrix.test.js",
    "content": "/* global test expect */\n\nimport { Duration } from \"../../src/luxon\";\nimport { casualMatrix } from \"../../src/duration\";\n\nconst businessMatrix = {\n  ...casualMatrix,\n  months: {\n    weeks: 4,\n    days: 22,\n    hours: 22 * 7,\n    minutes: 22 * 7 * 60,\n    seconds: 22 * 7 * 60 * 60,\n    milliseconds: 22 * 7 * 60 * 60 * 1000,\n  },\n  weeks: {\n    days: 5,\n    hours: 5 * 7,\n    minutes: 5 * 7 * 60,\n    seconds: 5 * 7 * 60 * 60,\n    milliseconds: 5 * 7 * 60 * 60 * 1000,\n  },\n  days: {\n    hours: 7,\n    minutes: 7 * 60,\n    seconds: 7 * 60 * 60,\n    milliseconds: 7 * 60 * 60 * 1000,\n  },\n};\n\nconst convert = (amt, from, to) =>\n  Duration.fromObject({ [from]: amt }, { matrix: businessMatrix }).as(to);\n\ntest(\"One day is made of 7 hours\", () => {\n  expect(convert(1, \"days\", \"hours\")).toBeCloseTo(7, 4);\n  expect(convert(7, \"hours\", \"days\")).toBeCloseTo(1, 4);\n});\n\ntest(\"One and a half week is made of 7 days 3 hours and 30 minutes\", () => {\n  const dur = Duration.fromObject({ weeks: 1.5 }, { matrix: businessMatrix }).shiftTo(\n    \"days\",\n    \"hours\",\n    \"minutes\"\n  );\n\n  expect(dur.days).toBeCloseTo(7, 4);\n  expect(dur.hours).toBeCloseTo(3, 4);\n  expect(dur.minutes).toBeCloseTo(30, 4);\n});\n"
  },
  {
    "path": "test/duration/equality.test.js",
    "content": "/* global test expect */\nimport { Duration } from \"../../src/luxon\";\n\ntest(\"equals self\", () => {\n  const l = Duration.fromObject({ years: 5, days: 6 });\n  expect(l.equals(l)).toBe(true);\n});\n\ntest(\"equals identically constructed\", () => {\n  const l1 = Duration.fromObject({ years: 5, days: 6 }),\n    l2 = Duration.fromObject({ years: 5, days: 6 });\n  expect(l1.equals(l2)).toBe(true);\n});\n\ntest(\"equals identically constructed with fractional values\", () => {\n  const l1 = Duration.fromObject({ years: 5.5, days: 6 }),\n    l2 = Duration.fromObject({ years: 5.5, days: 6 });\n  expect(l1.equals(l2)).toBe(true);\n});\n\ntest(\"equals identically constructed but one has string type values\", () => {\n  const l1 = Duration.fromObject({ years: 5, days: 6 }),\n    l2 = Duration.fromObject({ years: \"5\", days: \"6\" });\n  expect(l1.equals(l2)).toBe(true);\n});\n\ntest(\"equals identically constructed but one has fractional string type values\", () => {\n  const l1 = Duration.fromObject({ years: 5.5, days: 6 }),\n    l2 = Duration.fromObject({ years: \"5.5\", days: \"6\" });\n  expect(l1.equals(l2)).toBe(true);\n});\n\n// #809\ntest(\"equals with extra zero units\", () => {\n  const l1 = Duration.fromObject({ years: 5, days: 6 }),\n    l2 = Duration.fromObject({ years: 5, days: 6, minutes: 0, seconds: -0 });\n  expect(l1.equals(l2)).toBe(true);\n  expect(l2.equals(l1)).toBe(true);\n});\n\ntest(\"does not equal an invalid duration\", () => {\n  const l1 = Duration.fromObject({ years: 5, days: 6 }),\n    l2 = Duration.invalid(\"because\");\n  expect(l1.equals(l2)).toBe(false);\n});\n\ntest(\"does not equal a different locale\", () => {\n  const l1 = Duration.fromObject({ years: 5, days: 6 }),\n    l2 = Duration.fromObject({ years: 5, days: 6 }).reconfigure({ locale: \"fr\" });\n  expect(l1.equals(l2)).toBe(false);\n});\n\ntest(\"does not equal a different numbering system\", () => {\n  const l1 = Duration.fromObject({ years: 5, days: 6 }),\n    l2 = Duration.fromObject({ years: 5, days: 6 }).reconfigure({ numberingSystem: \"beng\" });\n  expect(l1.equals(l2)).toBe(false);\n});\n\ntest(\"does not equal a different set of units\", () => {\n  const l1 = Duration.fromObject({ years: 5, days: 6 }),\n    l2 = Duration.fromObject({ years: 5, months: 6 });\n  expect(l1.equals(l2)).toBe(false);\n});\n\ntest(\"does not equal a subset of units\", () => {\n  const l1 = Duration.fromObject({ years: 5, days: 6 }),\n    l2 = Duration.fromObject({ years: 5 });\n  expect(l1.equals(l2)).toBe(false);\n});\n\ntest(\"does not equal a superset of units\", () => {\n  const l1 = Duration.fromObject({ years: 5 }),\n    l2 = Duration.fromObject({ years: 5, days: 6 });\n  expect(l1.equals(l2)).toBe(false);\n});\n\ntest(\"does not equal a different unit values\", () => {\n  const l1 = Duration.fromObject({ years: 5, days: 6 }),\n    l2 = Duration.fromObject({ years: 5, days: 7 });\n  expect(l1.equals(l2)).toBe(false);\n});\n"
  },
  {
    "path": "test/duration/format.test.js",
    "content": "/* global test expect */\nimport { Duration } from \"../../src/luxon\";\n\nconst dur = () =>\n  Duration.fromObject({\n    years: 1,\n    months: 2,\n    weeks: 1,\n    days: 3,\n    hours: 4,\n    minutes: 5,\n    seconds: 6,\n    milliseconds: 7,\n  });\n\n//------\n// #toISO()\n//------\n\ntest(\"Duration#toISO fills out every field\", () => {\n  expect(dur().toISO()).toBe(\"P1Y2M1W3DT4H5M6.007S\");\n});\n\ntest(\"Duration#toISO fills out every field with fractional\", () => {\n  const dur = Duration.fromObject({\n    years: 1.1,\n    months: 2.2,\n    weeks: 1.1,\n    days: 3.3,\n    hours: 4.4,\n    minutes: 5.5,\n    seconds: 6.6,\n    milliseconds: 7,\n  });\n  expect(dur.toISO()).toBe(\"P1.1Y2.2M1.1W3.3DT4.4H5.5M6.607S\");\n});\n\ntest(\"Duration#toISO creates a minimal string\", () => {\n  expect(Duration.fromObject({ years: 3, seconds: 45 }).toISO()).toBe(\"P3YT45S\");\n  expect(Duration.fromObject({ months: 4, seconds: 45 }).toISO()).toBe(\"P4MT45S\");\n  expect(Duration.fromObject({ months: 5 }).toISO()).toBe(\"P5M\");\n  expect(Duration.fromObject({ minutes: 5 }).toISO()).toBe(\"PT5M\");\n});\n\ntest(\"Duration#toISO handles negative durations\", () => {\n  expect(Duration.fromObject({ years: -3, seconds: -45 }).toISO()).toBe(\"P-3YT-45S\");\n});\n\ntest(\"Duration#toISO handles mixed negative/positive durations\", () => {\n  expect(Duration.fromObject({ years: 3, seconds: -45 }).toISO()).toBe(\"P3YT-45S\");\n  expect(Duration.fromObject({ years: 0, seconds: -45 }).toISO()).toBe(\"PT-45S\");\n  expect(Duration.fromObject({ years: -5, seconds: 34 }).toISO()).toBe(\"P-5YT34S\");\n});\n\ntest(\"Duration#toISO handles zero durations\", () => {\n  expect(Duration.fromMillis(0).toISO()).toBe(\"PT0S\");\n});\n\ntest(\"Duration#toISO returns null for invalid durations\", () => {\n  expect(Duration.invalid(\"because\").toISO()).toBe(null);\n});\n\ntest(\"Duration#toISO handles milliseconds duration\", () => {\n  expect(Duration.fromObject({ milliseconds: 7 }).toISO()).toBe(\"PT0.007S\");\n});\n\ntest(\"Duration#toISO handles seconds/milliseconds duration\", () => {\n  expect(Duration.fromObject({ seconds: 17, milliseconds: 548 }).toISO()).toBe(\"PT17.548S\");\n});\n\ntest(\"Duration#toISO handles negative seconds/milliseconds duration\", () => {\n  expect(Duration.fromObject({ seconds: -17, milliseconds: -548 }).toISO()).toBe(\"PT-17.548S\");\n});\n\ntest(\"Duration#toISO handles mixed negative/positive numbers in seconds/milliseconds durations\", () => {\n  expect(Duration.fromObject({ seconds: 17, milliseconds: -548 }).toISO()).toBe(\"PT16.452S\");\n  expect(Duration.fromObject({ seconds: -17, milliseconds: 548 }).toISO()).toBe(\"PT-16.452S\");\n});\n\n//------\n// #toISOTime()\n//------\n\nconst hhmmssSSS = Duration.fromObject({ hours: 11, minutes: 22, seconds: 33, milliseconds: 444 });\nconst hhSSS = Duration.fromObject({ hours: 11, milliseconds: 444 });\nconst hhss = Duration.fromObject({ hours: 11, seconds: 33 });\nconst hh = Duration.fromObject({ hours: 11 });\n\ntest(\"Duration#toISOTime creates a correct extended string\", () => {\n  expect(hhmmssSSS.toISOTime()).toBe(\"11:22:33.444\");\n});\n\ntest(\"Duration#toISOTime suppresses milliseconds correctly\", () => {\n  expect(hhSSS.toISOTime({ suppressMilliseconds: true })).toBe(\"11:00:00.444\");\n  expect(hhss.toISOTime({ suppressMilliseconds: true })).toBe(\"11:00:33\");\n  expect(hh.toISOTime({ suppressMilliseconds: true })).toBe(\"11:00:00\");\n});\n\ntest(\"Duration#toISOTime suppresses seconds correctly\", () => {\n  expect(hhSSS.toISOTime({ suppressSeconds: true })).toBe(\"11:00:00.444\");\n  expect(hhss.toISOTime({ suppressSeconds: true })).toBe(\"11:00:33.000\");\n  expect(hh.toISOTime({ suppressSeconds: true })).toBe(\"11:00\");\n});\n\ntest(\"Duration#toISOTime includes the prefix correctly\", () => {\n  expect(hh.toISOTime({ includePrefix: true })).toBe(\"T11:00:00.000\");\n});\n\ntest(\"Duration#toISOTime creates a correct basic string\", () => {\n  expect(hhmmssSSS.toISOTime({ format: \"basic\" })).toBe(\"112233.444\");\n  expect(hh.toISOTime({ format: \"basic\", suppressMilliseconds: true })).toBe(\"110000\");\n  expect(hh.toISOTime({ format: \"basic\", suppressSeconds: true })).toBe(\"1100\");\n});\n\ntest(\"Duration#toISOTime returns null if the value is outside the range of one day\", () => {\n  expect(Duration.fromObject({ hours: 24 }).toISOTime()).toBe(null);\n  expect(Duration.fromObject({ milliseconds: -1 }).toISOTime()).toBe(null);\n});\n\ntest(\"Duration#toISOTime is not influenced by the locale\", () => {\n  expect(Duration.fromObject({ hours: 3, minutes: 10 }, { locale: \"ar-QA\" }).toISOTime()).toBe(\n    \"03:10:00.000\"\n  );\n});\n\n//------\n// #toMillis()\n//------\n\ntest(\"Duration#toMillis returns the value in milliseconds\", () => {\n  expect(Duration.fromMillis(1000).toMillis()).toBe(1000);\n  expect(dur().valueOf()).toBe(dur().toMillis());\n});\n\n//------\n// #toJSON()\n//------\n\ntest(\"Duration#toJSON returns the ISO representation\", () => {\n  expect(dur().toJSON()).toBe(dur().toISO());\n});\n\n//------\n// #toString()\n//------\n\ntest(\"Duration#toString returns the ISO representation\", () => {\n  expect(dur().toString()).toBe(dur().toISO());\n});\n\n//------\n// #toFormat()\n//------\n\ntest(\"Duration#toFormat('S') returns milliseconds\", () => {\n  expect(dur().toFormat(\"S\")).toBe(\"37598706007\");\n\n  const lil = Duration.fromMillis(5);\n  expect(lil.toFormat(\"S\")).toBe(\"5\");\n  expect(lil.toFormat(\"SS\")).toBe(\"05\");\n  expect(lil.toFormat(\"SSSSS\")).toBe(\"00005\");\n});\n\ntest(\"Duration#toFormat('s') returns seconds\", () => {\n  expect(dur().toFormat(\"s\")).toBe(\"37598706\");\n  expect(dur().toFormat(\"s\", { floor: false })).toBe(\"37598706.007\");\n  expect(dur().toFormat(\"s.SSS\")).toBe(\"37598706.007\");\n\n  const lil = Duration.fromObject({ seconds: 6 });\n  expect(lil.toFormat(\"s\")).toBe(\"6\");\n  expect(lil.toFormat(\"ss\")).toBe(\"06\");\n  expect(lil.toFormat(\"sss\")).toBe(\"006\");\n  expect(lil.toFormat(\"ssss\")).toBe(\"0006\");\n});\n\ntest(\"Duration#toFormat('m') returns minutes\", () => {\n  expect(dur().toFormat(\"m\")).toBe(\"626645\");\n  expect(dur().toFormat(\"m\", { floor: false })).toBe(\"626645.1\");\n  expect(dur().toFormat(\"m:ss\")).toBe(\"626645:06\");\n  expect(dur().toFormat(\"m:ss.SSS\")).toBe(\"626645:06.007\");\n\n  const lil = Duration.fromObject({ minutes: 6 });\n  expect(lil.toFormat(\"m\")).toBe(\"6\");\n  expect(lil.toFormat(\"mm\")).toBe(\"06\");\n  expect(lil.toFormat(\"mmm\")).toBe(\"006\");\n  expect(lil.toFormat(\"mmmm\")).toBe(\"0006\");\n});\n\ntest(\"Duration#toFormat('h') returns hours\", () => {\n  expect(dur().toFormat(\"h\")).toBe(\"10444\");\n  expect(dur().toFormat(\"h\", { floor: false })).toBe(\"10444.085\");\n  expect(dur().toFormat(\"h:ss\")).toBe(\"10444:306\");\n  expect(dur().toFormat(\"h:mm:ss.SSS\")).toBe(\"10444:05:06.007\");\n\n  const lil = Duration.fromObject({ hours: 6 });\n  expect(lil.toFormat(\"h\")).toBe(\"6\");\n  expect(lil.toFormat(\"hh\")).toBe(\"06\");\n  expect(lil.toFormat(\"hhh\")).toBe(\"006\");\n  expect(lil.toFormat(\"hhhh\")).toBe(\"0006\");\n});\n\ntest(\"Duration#toFormat('d') returns days\", () => {\n  expect(dur().toFormat(\"d\")).toBe(\"435\");\n  expect(dur().toFormat(\"d\", { floor: false })).toBe(\"435.17\");\n  expect(dur().toFormat(\"d:h:ss\")).toBe(\"435:4:306\");\n  expect(dur().toFormat(\"d:h:mm:ss.SSS\")).toBe(\"435:4:05:06.007\");\n\n  const lil = Duration.fromObject({ days: 6 });\n  expect(lil.toFormat(\"d\")).toBe(\"6\");\n  expect(lil.toFormat(\"dd\")).toBe(\"06\");\n  expect(lil.toFormat(\"ddd\")).toBe(\"006\");\n  expect(lil.toFormat(\"dddd\")).toBe(\"0006\");\n});\n\ntest(\"Duration#toFormat('w') returns weeks\", () => {\n  expect(dur().toFormat(\"w\")).toBe(\"61\");\n  expect(dur().toFormat(\"w\", { floor: false })).toBe(\"61.453\");\n  expect(dur().toFormat(\"w:s\")).toBe(\"61:273906\");\n  expect(dur().toFormat(\"w:dd:h:mm:ss.SSS\")).toBe(\"61:03:4:05:06.007\");\n\n  const lil = Duration.fromObject({ weeks: 6 });\n  expect(lil.toFormat(\"w\")).toBe(\"6\");\n  expect(lil.toFormat(\"ww\")).toBe(\"06\");\n  expect(lil.toFormat(\"www\")).toBe(\"006\");\n  expect(lil.toFormat(\"wwww\")).toBe(\"0006\");\n});\n\ntest(\"Duration#toFormat('M') returns months\", () => {\n  expect(dur().toFormat(\"M\")).toBe(\"14\");\n  expect(dur().toFormat(\"M\", { floor: false })).toBe(\"14.356\");\n  expect(dur().toFormat(\"M:s\")).toBe(\"14:878706\");\n  expect(dur().toFormat(\"M:dd:h:mm:ss.SSS\")).toBe(\"14:10:4:05:06.007\");\n\n  const lil = Duration.fromObject({ months: 6 });\n  expect(lil.toFormat(\"M\")).toBe(\"6\");\n  expect(lil.toFormat(\"MM\")).toBe(\"06\");\n  expect(lil.toFormat(\"MMM\")).toBe(\"006\");\n  expect(lil.toFormat(\"MMMM\")).toBe(\"0006\");\n});\n\ntest(\"Duration#toFormat('y') returns years\", () => {\n  expect(dur().toFormat(\"y\")).toBe(\"1\");\n  expect(dur().toFormat(\"y\", { floor: false })).toBe(\"1.195\");\n  expect(dur().toFormat(\"y:m\")).toBe(\"1:101045\");\n  expect(dur().toFormat(\"y:M:dd:h:mm:ss.SSS\")).toBe(\"1:2:10:4:05:06.007\");\n\n  const lil = Duration.fromObject({ years: 5 });\n  expect(lil.toFormat(\"y\")).toBe(\"5\");\n  expect(lil.toFormat(\"yy\")).toBe(\"05\");\n  expect(lil.toFormat(\"yyyyy\")).toBe(\"00005\");\n});\n\ntest(\"Duration#toFormat accepts the deprecated 'round' option\", () => {\n  expect(dur().toFormat(\"s\", { round: false })).toBe(\"37598706.007\");\n  expect(dur().toFormat(\"m\", { round: false })).toBe(\"626645.1\");\n  expect(dur().toFormat(\"h\", { round: false })).toBe(\"10444.085\");\n  expect(dur().toFormat(\"d\", { round: false })).toBe(\"435.17\");\n  expect(dur().toFormat(\"M\", { round: false })).toBe(\"14.356\");\n  expect(dur().toFormat(\"y\", { round: false })).toBe(\"1.195\");\n});\n\ntest(\"Duration#toFormat leaves in zeros\", () => {\n  const tiny = Duration.fromObject({ seconds: 5 });\n  expect(tiny.toFormat(\"hh:mm:ss\")).toBe(\"00:00:05\");\n  expect(tiny.toFormat(\"hh:mm:ss.SSS\")).toBe(\"00:00:05.000\");\n});\n\ntest(\"Duration#toFormat rounds down\", () => {\n  const tiny = Duration.fromObject({ seconds: 5.7 });\n  expect(tiny.toFormat(\"s\")).toBe(\"5\");\n\n  const unpromoted = Duration.fromObject({ seconds: 59.7 });\n  expect(unpromoted.toFormat(\"mm:ss\")).toBe(\"00:59\");\n});\n\ntest(\"Duration#toFormat localizes the numbers\", () => {\n  expect(dur().reconfigure({ locale: \"bn\" }).toFormat(\"yy:MM:dd:h:mm:ss.SSS\")).toBe(\n    \"০১:০২:১০:৪:০৫:০৬.০০৭\"\n  );\n});\n\ntest(\"Duration#toFormat returns a lame string for invalid durations\", () => {\n  expect(Duration.invalid(\"because\").toFormat(\"yy\")).toBe(\"Invalid Duration\");\n});\n\n// - signMode negativeLargestOnly\n\ntest(\"Duration#toFormat shows negative sign on the largest unit when using signMode negativeLargestOnly\", () => {\n  expect(\n    Duration.fromObject({ years: -3, seconds: -45 }).toFormat(\"yyss\", {\n      signMode: \"negativeLargestOnly\",\n    })\n  ).toBe(\"-0345\");\n  expect(\n    Duration.fromObject({ years: -3, seconds: -45 }).toFormat(\"'before'yy'between'ss'after'\", {\n      signMode: \"negativeLargestOnly\",\n    })\n  ).toBe(\"before-03between45after\");\n  // Intentionally have the seconds not first to make sure years is still picked as the largest unit\n  expect(\n    Duration.fromObject({ seconds: -45, years: -3 }).toFormat(\"ssyy\", {\n      signMode: \"negativeLargestOnly\",\n    })\n  ).toBe(\"45-03\");\n});\n\ntest(\"Duration#toFormat shows no negative sign on the largest unit when using signMode negativeLargestOnly with positive Duration\", () => {\n  expect(\n    Duration.fromObject({ years: 3, seconds: 45 }).toFormat(\"yyss\", {\n      signMode: \"negativeLargestOnly\",\n    })\n  ).toBe(\"0345\");\n  expect(\n    Duration.fromObject({ years: 3, seconds: 45 }).toFormat(\"'before'yy'between'ss'after'\", {\n      signMode: \"negativeLargestOnly\",\n    })\n  ).toBe(\"before03between45after\");\n  // Intentionally have the seconds not first to make sure years is still picked as the largest unit\n  expect(\n    Duration.fromObject({ years: 3, seconds: 45 }).toFormat(\"ssyy\", {\n      signMode: \"negativeLargestOnly\",\n    })\n  ).toBe(\"4503\");\n});\n\n// - signMode all\n\ntest(\"Duration#toFormat with signMode all shows positive sign on positive durations\", () => {\n  expect(\n    Duration.fromObject({ years: 3, seconds: 45 }).toFormat(\"yyss\", {\n      signMode: \"all\",\n    })\n  ).toBe(\"+03+45\");\n  expect(\n    Duration.fromObject({ years: 3, seconds: 45 }).toFormat(\"'before'yy'between'ss'after'\", {\n      signMode: \"all\",\n    })\n  ).toBe(\"before+03between+45after\");\n  // Intentionally have the seconds not first to make sure years is still picked as the largest unit\n  expect(\n    Duration.fromObject({ years: 3, seconds: 45 }).toFormat(\"ssyy\", {\n      signMode: \"all\",\n    })\n  ).toBe(\"+45+03\");\n});\n\ntest(\"Duration#toFormat with signMode all shows positive sign on negative durations\", () => {\n  expect(\n    Duration.fromObject({ years: -3, seconds: -45 }).toFormat(\"yyss\", {\n      signMode: \"all\",\n    })\n  ).toBe(\"-03-45\");\n  expect(\n    Duration.fromObject({ years: -3, seconds: -45 }).toFormat(\"'before'yy'between'ss'after'\", {\n      signMode: \"all\",\n    })\n  ).toBe(\"before-03between-45after\");\n  // Intentionally have the seconds not first to make sure years is still picked as the largest unit\n  expect(\n    Duration.fromObject({ years: -3, seconds: -45 }).toFormat(\"ssyy\", {\n      signMode: \"all\",\n    })\n  ).toBe(\"-45-03\");\n});\n\n//------\n// #humanize()\n//------\n\ntest(\"Duration#toHuman formats out a list\", () => {\n  expect(dur().toHuman()).toEqual(\n    \"1 year, 2 months, 1 week, 3 days, 4 hours, 5 minutes, 6 seconds, 7 milliseconds\"\n  );\n});\n\ntest(\"Duration#toHuman only shows the units you have\", () => {\n  expect(Duration.fromObject({ years: 3, hours: 4 }).toHuman()).toEqual(\"3 years, 4 hours\");\n});\n\ntest(\"Duration#toHuman accepts a listStyle\", () => {\n  expect(dur().toHuman({ listStyle: \"long\" })).toEqual(\n    \"1 year, 2 months, 1 week, 3 days, 4 hours, 5 minutes, 6 seconds, and 7 milliseconds\"\n  );\n});\n\ntest(\"Duration#toHuman accepts number format opts\", () => {\n  expect(dur().toHuman({ unitDisplay: \"short\" })).toEqual(\n    \"1 yr, 2 mths, 1 wk, 3 days, 4 hr, 5 min, 6 sec, 7 ms\"\n  );\n});\n\ntest(\"Duration#toHuman accepts hiding of zero values\", () => {\n  expect(\n    Duration.fromObject({\n      years: 1,\n      months: 0,\n      weeks: 1,\n      days: 0,\n      hours: 4,\n      minutes: 0,\n      seconds: 6,\n      milliseconds: 0,\n    }).toHuman({ showZeros: false })\n  ).toEqual(\"1 year, 1 week, 4 hours, 6 seconds\");\n});\n\ntest(\"Duration#toHuman handles undefined showZeros\", () => {\n  expect(\n    Duration.fromObject({\n      years: 1,\n      months: 0,\n      weeks: 1,\n      days: 0,\n      hours: 4,\n      minutes: 0,\n      seconds: 6,\n      milliseconds: 0,\n    }).toHuman({ showZeros: undefined })\n  ).toEqual(\"1 year, 0 months, 1 week, 0 days, 4 hours, 0 minutes, 6 seconds, 0 milliseconds\");\n});\n\ntest(\"Duration#toHuman works in differt languages\", () => {\n  expect(dur().reconfigure({ locale: \"fr\" }).toHuman()).toEqual(\n    \"1 an, 2 mois, 1 semaine, 3 jours, 4 heures, 5 minutes, 6 secondes, 7 millisecondes\"\n  );\n});\n\ntest(\"Duration#toHuman handles quarters\", () => {\n  expect(\n    Duration.fromObject({\n      years: 1,\n      quarters: 2,\n      hours: 2,\n    }).toHuman()\n  ).toEqual(\"1 year, 6 months, 2 hours\");\n});\n\ntest(\"Duration#toHuman handles quarters and months together\", () => {\n  expect(\n    Duration.fromObject({\n      years: 1,\n      months: 1,\n      quarters: 2,\n      hours: 2,\n    }).toHuman()\n  ).toEqual(\"1 year, 7 months, 2 hours\");\n});\n\ntest(\"Duration#toHuman handles quarters and months with showZeros false\", () => {\n  expect(\n    Duration.fromObject({\n      years: 1,\n      months: 1,\n      quarters: 0,\n      hours: 2,\n    }).toHuman({ showZeros: false })\n  ).toEqual(\"1 year, 1 month, 2 hours\");\n  expect(\n    Duration.fromObject({\n      years: 1,\n      months: 0,\n      quarters: 1,\n      hours: 2,\n    }).toHuman({ showZeros: false })\n  ).toEqual(\"1 year, 3 months, 2 hours\");\n  expect(\n    Duration.fromObject({\n      years: 1,\n      months: 0,\n      quarters: 0,\n      hours: 2,\n    }).toHuman({ showZeros: false })\n  ).toEqual(\"1 year, 2 hours\");\n  expect(\n    Duration.fromObject({\n      years: 1,\n      quarters: 0,\n      hours: 2,\n    }).toHuman({ showZeros: false })\n  ).toEqual(\"1 year, 2 hours\");\n});\n"
  },
  {
    "path": "test/duration/getters.test.js",
    "content": "/* global test expect */\n\nimport { Duration } from \"../../src/luxon\";\n\nconst dur = Duration.fromObject({\n    years: 1,\n    quarters: 2,\n    months: 2,\n    days: 3,\n    hours: 4,\n    minutes: 5,\n    seconds: 6,\n    milliseconds: 7,\n    weeks: 8,\n  }),\n  inv = Duration.invalid(\"because i say so\");\n\n//------\n// years/months/days/hours/minutes/seconds/milliseconds\n//------\n\ntest(\"Duration#years returns the years\", () => {\n  expect(dur.years).toBe(1);\n  expect(inv.years).toBeFalsy();\n});\n\ntest(\"Duration#quarters returns the quarters\", () => {\n  expect(dur.quarters).toBe(2);\n  expect(inv.quarters).toBeFalsy();\n});\n\ntest(\"Duration#months returns the (1-indexed) months\", () => {\n  expect(dur.months).toBe(2);\n  expect(inv.months).toBeFalsy();\n});\n\ntest(\"Duration#days returns the days\", () => {\n  expect(dur.days).toBe(3);\n  expect(inv.days).toBeFalsy();\n});\n\ntest(\"Duration#hours returns the hours\", () => {\n  expect(dur.hours).toBe(4);\n  expect(inv.hours).toBeFalsy();\n});\n\ntest(\"Duration#hours returns the fractional hours\", () => {\n  const localDur = Duration.fromObject({\n      years: 1,\n      quarters: 2,\n      months: 2,\n      days: 3,\n      hours: 4.5,\n      minutes: 5,\n      seconds: 6,\n      milliseconds: 7,\n      weeks: 8,\n    }),\n    localInv = Duration.invalid(\"because i say so\");\n\n  expect(localDur.hours).toBe(4.5);\n  expect(localInv.hours).toBeFalsy();\n});\n\ntest(\"Duration#minutes returns the minutes\", () => {\n  expect(dur.minutes).toBe(5);\n  expect(inv.minutes).toBeFalsy();\n});\n\ntest(\"Duration#seconds returns the seconds\", () => {\n  expect(dur.seconds).toBe(6);\n  expect(inv.seconds).toBeFalsy();\n});\n\ntest(\"Duration#milliseconds returns the milliseconds\", () => {\n  expect(dur.milliseconds).toBe(7);\n  expect(inv.milliseconds).toBeFalsy();\n});\n\ntest(\"Duration#weeks returns the weeks\", () => {\n  expect(dur.weeks).toBe(8);\n  expect(inv.weeks).toBeFalsy();\n});\n"
  },
  {
    "path": "test/duration/info.test.js",
    "content": "/* global test expect */\n\nimport { Duration } from \"../../src/luxon\";\n\nconst dur = Duration.fromObject({\n  years: 1,\n  months: 2,\n  days: 3.3,\n});\n\n//------\n// #toObject\n//-------\ntest(\"Duration#toObject returns the object\", () => {\n  expect(dur.toObject()).toEqual({\n    years: 1,\n    months: 2,\n    days: 3.3,\n  });\n});\n\ntest(\"Duration#toObject returns an empty object for invalid durations\", () => {\n  expect(Duration.invalid(\"because\").toObject()).toEqual({});\n});\n"
  },
  {
    "path": "test/duration/invalid.test.js",
    "content": "/* global test expect */\n\nimport { Duration, DateTime, Settings } from \"../../src/luxon\";\n\ntest(\"Explicitly invalid durations are invalid\", () => {\n  const dur = Duration.invalid(\"just because\", \"seriously, just because\");\n  expect(dur.isValid).toBe(false);\n  expect(dur.invalidReason).toBe(\"just because\");\n  expect(dur.invalidExplanation).toBe(\"seriously, just because\");\n});\n\ntest(\"throwOnInvalid throws\", () => {\n  try {\n    Settings.throwOnInvalid = true;\n    expect(() => Duration.invalid(\"because\")).toThrow();\n  } finally {\n    Settings.throwOnInvalid = false;\n  }\n});\n\ntest(\"Duration.invalid throws if you don't provide a reason\", () => {\n  expect(() => Duration.invalid()).toThrow();\n});\n\ntest(\"Diffing invalid DateTimes creates invalid Durations\", () => {\n  const invalidDT = DateTime.invalid(\"so?\");\n  expect(invalidDT.diff(DateTime.now()).isValid).toBe(false);\n  expect(DateTime.now().diff(invalidDT).isValid).toBe(false);\n});\n\ntest(\"Duration.invalid produces invalid Intervals\", () => {\n  expect(Duration.invalid(\"because\").isValid).toBe(false);\n});\n\ntest(\"Duration.toMillis produces NaN on invalid Durations\", () => {\n  expect(Duration.invalid(\"because\").toMillis()).toBe(NaN);\n});\n\ntest(\"Duration.as produces NaN on invalid Durations\", () => {\n  expect(Duration.invalid(\"because\").as(\"seconds\")).toBe(NaN);\n});\n\ntest(\"Duration.toHuman produces null on invalid Durations\", () => {\n  expect(Duration.invalid(\"because\").toHuman()).toBe(\"Invalid Duration\");\n});\n\ntest(\"Duration.toISO produces null on invalid Durations\", () => {\n  expect(Duration.invalid(\"because\").toISO()).toBeNull();\n});\n\ntest(\"Duration.toFormat produces Invalid Duration on invalid Durations\", () => {\n  expect(Duration.invalid(\"because\").toFormat(\"s\")).toBe(\"Invalid Duration\");\n});\n"
  },
  {
    "path": "test/duration/math.test.js",
    "content": "/* global test expect */\n\nimport { Duration } from \"../../src/luxon\";\n\n//------\n// #plus()\n//------\ntest(\"Duration#plus add straightforward durations\", () => {\n  const first = Duration.fromObject({ hours: 4, minutes: 12, seconds: 2 }),\n    second = Duration.fromObject({ hours: 1, seconds: 6, milliseconds: 14 }),\n    result = first.plus(second);\n\n  expect(result.hours).toBe(5);\n  expect(result.minutes).toBe(12);\n  expect(result.seconds).toBe(8);\n  expect(result.milliseconds).toBe(14);\n});\n\ntest(\"Duration#plus add fractional durations\", () => {\n  const first = Duration.fromObject({ hours: 4.2, minutes: 12, seconds: 2 }),\n    second = Duration.fromObject({ hours: 1, seconds: 6.8, milliseconds: 14 }),\n    result = first.plus(second);\n\n  expect(result.hours).toBeCloseTo(5.2, 8);\n  expect(result.minutes).toBe(12);\n  expect(result.seconds).toBeCloseTo(8.8, 8);\n  expect(result.milliseconds).toBe(14);\n});\n\ntest(\"Duration#plus noops empty druations\", () => {\n  const first = Duration.fromObject({ hours: 4, minutes: 12, seconds: 2 }),\n    second = Duration.fromObject({}),\n    result = first.plus(second);\n\n  expect(result.hours).toBe(4);\n  expect(result.minutes).toBe(12);\n  expect(result.seconds).toBe(2);\n});\n\ntest(\"Duration#plus adds negatives\", () => {\n  const first = Duration.fromObject({ hours: 4, minutes: -12, seconds: -2 }),\n    second = Duration.fromObject({ hours: -5, seconds: 6, milliseconds: 14 }),\n    result = first.plus(second);\n\n  expect(result.hours).toBe(-1);\n  expect(result.minutes).toBe(-12);\n  expect(result.seconds).toBe(4);\n  expect(result.milliseconds).toBe(14);\n});\n\ntest(\"Duration#plus adds single values\", () => {\n  const first = Duration.fromObject({ hours: 4, minutes: 12, seconds: 2 }),\n    result = first.plus({ minutes: 5 });\n\n  expect(result.hours).toBe(4);\n  expect(result.minutes).toBe(17);\n  expect(result.seconds).toBe(2);\n});\n\ntest(\"Duration#plus adds number as milliseconds\", () => {\n  const first = Duration.fromObject({ minutes: 11, seconds: 22 }),\n    result = first.plus(333);\n\n  expect(result.minutes).toBe(11);\n  expect(result.seconds).toBe(22);\n  expect(result.milliseconds).toBe(333);\n});\n\ntest(\"Duration#plus maintains invalidity\", () => {\n  const dur = Duration.invalid(\"because\").plus({ minutes: 5 });\n  expect(dur.isValid).toBe(false);\n  expect(dur.invalidReason).toBe(\"because\");\n});\n\ntest(\"Duration#plus results in the superset of units\", () => {\n  let dur = Duration.fromObject({ hours: 1, minutes: 0 }).plus({ seconds: 3, milliseconds: 0 });\n  expect(dur.toObject()).toEqual({ hours: 1, minutes: 0, seconds: 3, milliseconds: 0 });\n\n  dur = Duration.fromObject({ hours: 1, minutes: 0 }).plus({});\n  expect(dur.toObject()).toEqual({ hours: 1, minutes: 0 });\n});\n\ntest(\"Duration#plus throws with invalid parameter\", () => {\n  expect(() => Duration.fromObject({}).plus(\"123\")).toThrow();\n});\n\n//------\n// #minus()\n//------\ntest(\"Duration#minus subtracts durations\", () => {\n  const first = Duration.fromObject({ hours: 4, minutes: 12, seconds: 2 }),\n    second = Duration.fromObject({ hours: 1, seconds: 6, milliseconds: 14 }),\n    result = first.minus(second);\n\n  expect(result.hours).toBe(3);\n  expect(result.minutes).toBe(12);\n  expect(result.seconds).toBe(-4);\n  expect(result.milliseconds).toBe(-14);\n});\n\ntest(\"Duration#minus subtracts fractional durations\", () => {\n  const first = Duration.fromObject({ hours: 4.2, minutes: 12, seconds: 2 }),\n    second = Duration.fromObject({ hours: 1, seconds: 6, milliseconds: 14 }),\n    result = first.minus(second);\n\n  expect(result.hours).toBeCloseTo(3.2, 8);\n  expect(result.minutes).toBe(12);\n  expect(result.seconds).toBe(-4);\n  expect(result.milliseconds).toBe(-14);\n});\n\ntest(\"Duration#minus subtracts single values\", () => {\n  const first = Duration.fromObject({ hours: 4, minutes: 12, seconds: 2 }),\n    result = first.minus({ minutes: 5 });\n\n  expect(result.hours).toBe(4);\n  expect(result.minutes).toBe(7);\n  expect(result.seconds).toBe(2);\n});\n\ntest(\"Duration#minus maintains invalidity\", () => {\n  const dur = Duration.invalid(\"because\").minus({ minutes: 5 });\n  expect(dur.isValid).toBe(false);\n  expect(dur.invalidReason).toBe(\"because\");\n});\n\n//------\n// #negate()\n//------\n\ntest(\"Duration#negate flips all the signs\", () => {\n  const dur = Duration.fromObject({ hours: 4, minutes: -12, seconds: 2 }),\n    result = dur.negate();\n  expect(result.hours).toBe(-4);\n  expect(result.minutes).toBe(12);\n  expect(result.seconds).toBe(-2);\n});\n\ntest(\"Duration#negate preserves invalidity\", () => {\n  const dur = Duration.invalid(\"because\"),\n    result = dur.negate();\n  expect(result.isValid).toBe(false);\n  expect(result.invalidReason).toBe(\"because\");\n});\n\ntest(\"Duration#negate doesn't mutate\", () => {\n  const orig = Duration.fromObject({ hours: 8 });\n  orig.negate();\n  expect(orig.hours).toBe(8);\n});\n\ntest(\"Duration#negate preserves conversionAccuracy\", () => {\n  const dur = Duration.fromObject(\n      {\n        hours: 4,\n        minutes: -12,\n        seconds: 2,\n      },\n      {\n        conversionAccuracy: \"longterm\",\n      }\n    ),\n    result = dur.negate();\n  expect(result.conversionAccuracy).toBe(\"longterm\");\n});\n\n//------\n// #mapUnits\n//------\n\ntest(\"Duration#units can multiply durations\", () => {\n  const dur = Duration.fromObject({ hours: 1, minutes: 2, seconds: -3, milliseconds: -4 }),\n    result = dur.mapUnits((x) => x * 5);\n\n  expect(result.hours).toBe(5);\n  expect(result.minutes).toBe(10);\n  expect(result.seconds).toBe(-15);\n  expect(result.milliseconds).toBe(-20);\n});\n\ntest(\"Duration#units can take the unit into account\", () => {\n  const dur = Duration.fromObject({ hours: 1, minutes: 2, seconds: -3, milliseconds: -4 }),\n    result = dur.mapUnits((x, u) => x * (u === \"milliseconds\" ? 2 : 5));\n\n  expect(result.hours).toBe(5);\n  expect(result.minutes).toBe(10);\n  expect(result.seconds).toBe(-15);\n  expect(result.milliseconds).toBe(-8);\n});\n\ntest(\"Duration#mapUnits maintains invalidity\", () => {\n  const dur = Duration.invalid(\"because\").mapUnits((x) => x * 5);\n  expect(dur.isValid).toBe(false);\n  expect(dur.invalidReason).toBe(\"because\");\n});\n\ntest(\"Duration#mapUnits requires that fn return a number\", () => {\n  const dur = Duration.fromObject({ hours: 1, minutes: 2, seconds: -3, milliseconds: -4 });\n  expect(() => dur.mapUnits(() => \"hello?\")).toThrow();\n});\n"
  },
  {
    "path": "test/duration/parse.test.js",
    "content": "/* global test expect */\n\nimport { Duration } from \"../../src/luxon\";\n\n//------\n// #fromISO()\n//------\n\nconst check = (s, ob) => {\n  expect(Duration.fromISO(s).toObject()).toEqual(ob);\n};\n\ntest(\"Duration.fromISO can parse a variety of ISO formats\", () => {\n  check(\"P5Y3M\", { years: 5, months: 3 });\n  check(\"PT54M32S\", { minutes: 54, seconds: 32 });\n  check(\"P3DT54M32S\", { days: 3, minutes: 54, seconds: 32 });\n  check(\"P1YT34000S\", { years: 1, seconds: 34000 });\n  check(\"P1W1DT13H23M34S\", { weeks: 1, days: 1, hours: 13, minutes: 23, seconds: 34 });\n  check(\"P2W\", { weeks: 2 });\n  check(\"PT10000000000000000000.999S\", { seconds: 10000000000000000000, milliseconds: 999 });\n});\n\ntest(\"Duration.fromISO can parse mixed or negative durations\", () => {\n  check(\"P-5Y-3M\", { years: -5, months: -3 });\n  check(\"PT-54M32S\", { minutes: -54, seconds: 32 });\n  check(\"P-3DT54M-32S\", { days: -3, minutes: 54, seconds: -32 });\n  check(\"P1YT-34000S\", { years: 1, seconds: -34000 });\n  check(\"P-1W1DT13H23M34S\", { weeks: -1, days: 1, hours: 13, minutes: 23, seconds: 34 });\n  check(\"P-2W\", { weeks: -2 });\n  check(\"-P1D\", { days: -1 });\n  check(\"-P5Y3M\", { years: -5, months: -3 });\n  check(\"-P-5Y-3M\", { years: 5, months: 3 });\n  check(\"-P-1W1DT13H-23M34S\", { weeks: 1, days: -1, hours: -13, minutes: 23, seconds: -34 });\n  check(\"PT-1.5S\", { seconds: -1, milliseconds: -500 });\n  check(\"PT-0.5S\", { seconds: 0, milliseconds: -500 });\n  check(\"PT1.5S\", { seconds: 1, milliseconds: 500 });\n  check(\"PT0.5S\", { seconds: 0, milliseconds: 500 });\n});\n\ntest(\"Duration.fromISO can parse fractions of seconds\", () => {\n  expect(Duration.fromISO(\"PT54M32.5S\").toObject()).toEqual({\n    minutes: 54,\n    seconds: 32,\n    milliseconds: 500,\n  });\n  expect(Duration.fromISO(\"PT54M32.53S\").toObject()).toEqual({\n    minutes: 54,\n    seconds: 32,\n    milliseconds: 530,\n  });\n  expect(Duration.fromISO(\"PT54M32.534S\").toObject()).toEqual({\n    minutes: 54,\n    seconds: 32,\n    milliseconds: 534,\n  });\n  expect(Duration.fromISO(\"PT54M32.5348S\").toObject()).toEqual({\n    minutes: 54,\n    seconds: 32,\n    milliseconds: 534,\n  });\n  expect(Duration.fromISO(\"PT54M32.034S\").toObject()).toEqual({\n    minutes: 54,\n    seconds: 32,\n    milliseconds: 34,\n  });\n});\n\ntest(\"Duration.fromISO can parse fractions\", () => {\n  expect(Duration.fromISO(\"P1.5Y\").toObject()).toEqual({\n    years: 1.5,\n  });\n  expect(Duration.fromISO(\"P1.5M\").toObject()).toEqual({\n    months: 1.5,\n  });\n  expect(Duration.fromISO(\"P1.5W\").toObject()).toEqual({\n    weeks: 1.5,\n  });\n  expect(Duration.fromISO(\"P1.5D\").toObject()).toEqual({\n    days: 1.5,\n  });\n  expect(Duration.fromISO(\"PT9.5H\").toObject()).toEqual({\n    hours: 9.5,\n  });\n});\n\nconst rejects = (s) => {\n  expect(Duration.fromISO(s).isValid).toBe(false);\n};\n\ntest(\"Duration.fromISO rejects junk\", () => {\n  rejects(\"poop\");\n  rejects(\"PTglorb\");\n  rejects(\"P5Y34S\");\n  rejects(\"5Y\");\n  rejects(\"P34S\");\n  rejects(\"P34K\");\n  rejects(\"P5D2W\");\n});\n\n//------\n// #fromISOTime()\n//------\n\nconst checkTime = (s, ob) => {\n  expect(Duration.fromISOTime(s).toObject()).toEqual(ob);\n};\n\ntest(\"Duration.fromISOTime can parse a variety of extended ISO time formats\", () => {\n  checkTime(\"11:22:33.444\", { hours: 11, minutes: 22, seconds: 33, milliseconds: 444 });\n  checkTime(\"11:22:33\", { hours: 11, minutes: 22, seconds: 33 });\n  checkTime(\"11:22\", { hours: 11, minutes: 22, seconds: 0 });\n  checkTime(\"T11:22\", { hours: 11, minutes: 22, seconds: 0 });\n});\n\ntest(\"Duration.fromISOTime can parse a variety of basic ISO time formats\", () => {\n  checkTime(\"112233.444\", { hours: 11, minutes: 22, seconds: 33, milliseconds: 444 });\n  checkTime(\"112233\", { hours: 11, minutes: 22, seconds: 33 });\n  checkTime(\"1122\", { hours: 11, minutes: 22, seconds: 0 });\n  checkTime(\"11\", { hours: 11, minutes: 0, seconds: 0 });\n  checkTime(\"T1122\", { hours: 11, minutes: 22, seconds: 0 });\n});\n\nconst rejectsTime = (s) => {\n  expect(Duration.fromISOTime(s).isValid).toBe(false);\n};\n\ntest(\"Duration.fromISOTime rejects junk\", () => {\n  rejectsTime(\"poop\");\n  rejectsTime(\"Tglorb\");\n  rejectsTime(\"-00:00\");\n});\n"
  },
  {
    "path": "test/duration/proto.test.js",
    "content": "import { Duration } from \"../../src/luxon\";\n\ntest(\"Duration prototype properties should not throw when addressed\", () => {\n  const d = Duration.fromObject({ hours: 1 });\n  expect(() =>\n    Object.getOwnPropertyNames(Object.getPrototypeOf(d)).forEach(\n      (name) => Object.getPrototypeOf(d)[name]\n    )\n  ).not.toThrow();\n});\n"
  },
  {
    "path": "test/duration/reconfigure.test.js",
    "content": "/* global test expect */\n\nimport { Duration } from \"../../src/luxon\";\n\nconst dur = Duration.fromObject(\n  {\n    years: 1,\n    months: 2,\n    days: 3,\n  },\n  {\n    locale: \"fr\",\n    numberingSystem: \"beng\",\n    conversionAccuracy: \"longterm\",\n  }\n);\n\n//------\n// #reconfigure()\n//------\n\ntest(\"Duration#reconfigure() sets the locale\", () => {\n  const recon = dur.reconfigure({ locale: \"it\" });\n  expect(recon.locale).toBe(\"it\");\n  expect(recon.numberingSystem).toBe(\"beng\");\n  expect(recon.conversionAccuracy).toBe(\"longterm\");\n});\n\ntest(\"Duration#reconfigure() sets the numberingSystem\", () => {\n  const recon = dur.reconfigure({ numberingSystem: \"thai\" });\n  expect(recon.locale).toBe(\"fr\");\n  expect(recon.numberingSystem).toBe(\"thai\");\n  expect(recon.conversionAccuracy).toBe(\"longterm\");\n});\n\ntest(\"Duration#reconfigure() sets the conversion accuracy\", () => {\n  const recon = dur.reconfigure({ conversionAccuracy: \"casual\" });\n  expect(recon.locale).toBe(\"fr\");\n  expect(recon.numberingSystem).toBe(\"beng\");\n  expect(recon.conversionAccuracy).toBe(\"casual\");\n});\n\ntest(\"Duration#reconfigure() with no arguments does nothing\", () => {\n  const recon = dur.reconfigure();\n  expect(recon.locale).toBe(\"fr\");\n  expect(recon.numberingSystem).toBe(\"beng\");\n  expect(recon.conversionAccuracy).toBe(\"longterm\");\n});\n"
  },
  {
    "path": "test/duration/set.test.js",
    "content": "/* global test expect */\nimport { Duration } from \"../../src/luxon\";\n\n//------\n// years/months/days/hours/minutes/seconds/milliseconds\n//-------\nconst dur = () =>\n  Duration.fromObject({\n    years: 1,\n    months: 1,\n    days: 1,\n    hours: 1,\n    minutes: 1,\n    seconds: 1,\n    milliseconds: 1,\n  });\n\ntest(\"Duration#set() sets the values\", () => {\n  expect(dur().set({ years: 2 }).years).toBe(2);\n  expect(dur().set({ months: 2 }).months).toBe(2);\n  expect(dur().set({ days: 2 }).days).toBe(2);\n  expect(dur().set({ hours: 4 }).hours).toBe(4);\n  expect(dur().set({ hours: 4.5 }).hours).toBe(4.5);\n  expect(dur().set({ minutes: 16 }).minutes).toBe(16);\n  expect(dur().set({ seconds: 45 }).seconds).toBe(45);\n  expect(dur().set({ milliseconds: 86 }).milliseconds).toBe(86);\n});\n\ntest(\"Duration#set() throws for metadata\", () => {\n  expect(() => dur.set({ locale: \"be\" })).toThrow();\n  expect(() => dur.set({ numberingSystem: \"thai\" })).toThrow();\n  expect(() => dur.set({ invalid: 42 })).toThrow();\n});\n\ntest(\"Duration#set maintains invalidity\", () => {\n  expect(Duration.invalid(\"because\").set({ hours: 200 }).isValid).toBe(false);\n});\n"
  },
  {
    "path": "test/duration/typecheck.test.js",
    "content": "/* global test expect */\n\nimport { Duration } from \"../../src/luxon\";\n\n//------\n// #isDuration\n//-------\ntest(\"Duration#isDuration return true for valid duration\", () => {\n  const dur = Duration.fromObject({ hours: 1, minutes: 4.5 });\n  expect(Duration.isDuration(dur)).toBe(true);\n});\n\ntest(\"Duration#isDuration return true for invalid duration\", () => {\n  const dur = Duration.invalid(\"because\");\n  expect(Duration.isDuration(dur)).toBe(true);\n});\n\ntest(\"Duration#isDuration return false for primitives\", () => {\n  expect(Duration.isDuration({})).toBe(false);\n  expect(Duration.isDuration({ hours: 60 })).toBe(false);\n  expect(Duration.isDuration(1)).toBe(false);\n  expect(Duration.isDuration(1.1)).toBe(false);\n  expect(Duration.isDuration(\"\")).toBe(false);\n  expect(Duration.isDuration(null)).toBe(false);\n  expect(Duration.isDuration()).toBe(false);\n});\n"
  },
  {
    "path": "test/duration/units.test.js",
    "content": "/* global test expect */\nimport { Duration } from \"../../src/luxon\";\n\n//------\n// #shiftTo()\n//-------\ntest(\"Duration#shiftTo rolls milliseconds up hours and minutes\", () => {\n  const dur = Duration.fromMillis(5760000);\n  expect(dur.shiftTo(\"hours\").hours).toBe(1.6);\n\n  const mod = dur.shiftTo(\"hours\", \"minutes\");\n  expect(mod.toObject()).toEqual({ hours: 1, minutes: 36 });\n});\n\ntest(\"Duration#shiftTo boils hours down milliseconds\", () => {\n  const dur = Duration.fromObject({ hours: 1 }).shiftTo(\"milliseconds\");\n  expect(dur.milliseconds).toBe(3600000);\n});\n\ntest(\"Duration boils hours down shiftTo minutes and milliseconds\", () => {\n  const dur = Duration.fromObject({ hours: 1, seconds: 30 }).shiftTo(\"minutes\", \"milliseconds\");\n  expect(dur.toObject()).toEqual({ minutes: 60, milliseconds: 30000 });\n});\n\ntest(\"Duration#shiftTo boils down and then rolls up\", () => {\n  const dur = Duration.fromObject({ years: 2, hours: 5000 }).shiftTo(\"months\", \"days\", \"minutes\");\n  expect(dur.toObject()).toEqual({ months: 30, days: 28, minutes: 8 * 60 });\n});\n\ntest(\"Duration#shiftTo throws on invalid units\", () => {\n  expect(() => {\n    Duration.fromObject({ years: 2, hours: 5000 }).shiftTo(\"months\", \"glorp\");\n  }).toThrow();\n});\n\ntest(\"Duration#shiftTo tacks decimals onto the end\", () => {\n  const dur = Duration.fromObject({ minutes: 73 }).shiftTo(\"hours\");\n  expect(dur.isValid).toBe(true);\n  expect(dur.hours).toBeCloseTo(1.2167, 4);\n});\n\ntest(\"Duration#shiftTo deconstructs decimal inputs\", () => {\n  const dur = Duration.fromObject({ hours: 2.3 }).shiftTo(\"hours\", \"minutes\");\n  expect(dur.isValid).toBe(true);\n  expect(dur.hours).toBe(2);\n  expect(dur.minutes).toBeCloseTo(18, 8);\n});\n\ntest(\"Duration#shiftTo deconstructs in cascade and tacks decimal onto the end\", () => {\n  const dur = Duration.fromObject({ hours: 1.17 }).shiftTo(\"hours\", \"minutes\", \"seconds\");\n  expect(dur.isValid).toBe(true);\n  expect(dur.hours).toBe(1);\n  expect(dur.minutes).toBe(10);\n  expect(dur.seconds).toBeCloseTo(12, 8);\n});\n\ntest(\"Duration#shiftTo maintains invalidity\", () => {\n  const dur = Duration.invalid(\"because\").shiftTo(\"years\");\n  expect(dur.isValid).toBe(false);\n  expect(dur.invalidReason).toBe(\"because\");\n});\n\ntest(\"Duration#shiftTo without any units no-ops\", () => {\n  const dur = Duration.fromObject({ years: 3 }).shiftTo();\n  expect(dur.isValid).toBe(true);\n  expect(dur.toObject()).toEqual({ years: 3 });\n});\n\ntest(\"Duration#shiftTo accumulates when rolling up\", () => {\n  expect(\n    Duration.fromObject({ minutes: 59, seconds: 183 })\n      .shiftTo(\"hours\", \"minutes\", \"seconds\")\n      .toObject()\n  ).toEqual({ hours: 1, minutes: 2, seconds: 3 });\n});\n\ntest(\"Duration#shiftTo keeps unnecessary higher-order negative units 0\", () => {\n  expect(\n    Duration.fromObject({ milliseconds: -100 }).shiftTo(\"hours\", \"minutes\", \"seconds\").toObject()\n  ).toEqual({ hours: 0, minutes: 0, seconds: -0.1 });\n});\n\ntest(\"Duration#shiftTo does not normalize values\", () => {\n  // Normalizing would convert to { quarters: 4, months: 1, days: 10 }\n  // which would be converted back to 404 days instead\n  expect(\n    Duration.fromObject({ quarters: 0, months: 0, days: 400 }).shiftTo(\"days\").toObject()\n  ).toEqual({ days: 400 });\n});\n\ntest(\"Duration#shiftTo boils hours down to hours and minutes\", () => {\n  const dur = Duration.fromObject({ hour: 2.4 });\n  expect(dur.shiftTo(\"hours\", \"minutes\").toObject()).toEqual({\n    hours: 2,\n    minutes: 24,\n  });\n});\n\ntest(\"Duration#shiftTo handles mixed units\", () => {\n  const dur = Duration.fromObject({ weeks: -1, days: 14 });\n  expect(dur.shiftTo(\"years\", \"months\", \"weeks\").toObject()).toEqual({\n    years: 0,\n    months: 0,\n    weeks: 1,\n  });\n});\n\ntest(\"Duration#shiftTo does not produce unnecessary fractions in higher order units\", () => {\n  const duration = Duration.fromObject(\n    { years: 2.5, weeks: -1 },\n    { conversionAccuracy: \"longterm\" }\n  );\n  const shifted = duration.shiftTo(\"years\", \"weeks\", \"minutes\").toObject();\n  expect(shifted.years).toBe(2);\n  expect(shifted.weeks).toBe(25);\n  expect(shifted.minutes).toBeCloseTo(894.6, 5);\n});\n\n//------\n// #shiftToAll()\n//-------\ntest(\"Duration#shiftToAll shifts to all available units\", () => {\n  const dur = Duration.fromMillis(5760000).shiftToAll();\n  expect(dur.toObject()).toEqual({\n    years: 0,\n    months: 0,\n    weeks: 0,\n    days: 0,\n    hours: 1,\n    minutes: 36,\n    seconds: 0,\n    milliseconds: 0,\n  });\n});\n\ntest(\"Duration#shiftToAll does not produce unnecessary fractions in higher order units\", () => {\n  const duration = Duration.fromObject(\n    { years: 2.5, weeks: -1, seconds: 0 },\n    { conversionAccuracy: \"longterm\" }\n  );\n  const toAll = duration.shiftToAll().toObject();\n  expect(toAll.years).toBe(2);\n  expect(toAll.months).toBe(5);\n  expect(toAll.weeks).toBe(3);\n  expect(toAll.days).toBe(2);\n  expect(toAll.hours).toBe(10);\n  expect(toAll.minutes).toBe(29);\n  expect(toAll.seconds).toBe(6);\n  expect(toAll.milliseconds).toBeCloseTo(0, 5);\n});\n\ntest(\"Duration#shiftToAll maintains invalidity\", () => {\n  const dur = Duration.invalid(\"because\").shiftToAll();\n  expect(dur.isValid).toBe(false);\n  expect(dur.invalidReason).toBe(\"because\");\n});\n\n//------\n// #normalize()\n//-------\ntest(\"Duration#normalize rebalances negative units\", () => {\n  const dur = Duration.fromObject({ years: 2, days: -2 }).normalize();\n  expect(dur.toObject()).toEqual({ years: 1, days: 363 });\n});\n\ntest(\"Duration#normalize de-overflows\", () => {\n  const dur = Duration.fromObject({ years: 2, days: 5000 }).normalize();\n  expect(dur.years).toBe(15);\n  expect(dur.days).toBe(255);\n  expect(dur.toObject()).toEqual({ years: 15, days: 255 });\n});\n\ntest(\"Duration#normalize handles fully negative durations\", () => {\n  const dur = Duration.fromObject({ years: -2, days: -5000 }).normalize();\n  expect(dur.toObject()).toEqual({ years: -15, days: -255 });\n});\n\ntest(\"Duration#normalize handles the full grid partially negative durations\", () => {\n  const sets = [\n    [\n      { months: 1, days: 32 },\n      { months: 2, days: 2 },\n    ],\n    [\n      { months: 1, days: 28 },\n      { months: 1, days: 28 },\n    ],\n    [\n      { months: 1, days: -32 },\n      { months: 0, days: -2 },\n    ],\n    [\n      { months: 1, days: -28 },\n      { months: 0, days: 2 },\n    ],\n    [\n      { months: -1, days: 32 },\n      { months: 0, days: 2 },\n    ],\n    [\n      { months: -1, days: 28 },\n      { months: 0, days: -2 },\n    ],\n    [\n      { months: -1, days: -32 },\n      { months: -2, days: -2 },\n    ],\n    [\n      { months: -1, days: -28 },\n      { months: -1, days: -28 },\n    ],\n    [\n      { months: 0, days: 32 },\n      { months: 1, days: 2 },\n    ],\n    [\n      { months: 0, days: 28 },\n      { months: 0, days: 28 },\n    ],\n    [\n      { months: 0, days: -32 },\n      { months: -1, days: -2 },\n    ],\n    [\n      { months: 0, days: -28 },\n      { months: 0, days: -28 },\n    ],\n    [\n      { hours: 96, minutes: 0, seconds: -10 },\n      { hours: 95, minutes: 59, seconds: 50 },\n    ],\n  ];\n\n  sets.forEach(([from, to]) => {\n    expect(Duration.fromObject(from).normalize().toObject()).toEqual(to);\n  });\n});\n\ntest(\"Duration#normalize maintains invalidity\", () => {\n  const dur = Duration.invalid(\"because\").normalize();\n  expect(dur.isValid).toBe(false);\n  expect(dur.invalidReason).toBe(\"because\");\n});\n\ntest(\"Duration#normalize can convert all unit pairs\", () => {\n  const units = [\n    \"years\",\n    \"quarters\",\n    \"months\",\n    \"weeks\",\n    \"days\",\n    \"hours\",\n    \"minutes\",\n    \"seconds\",\n    \"milliseconds\",\n  ];\n\n  for (let i = 0; i < units.length; i++) {\n    for (let j = i + 1; j < units.length; j++) {\n      const duration = Duration.fromObject({ [units[i]]: 1, [units[j]]: 2 });\n      const normalizedDuration = duration.normalize().toObject();\n      expect(normalizedDuration[units[i]]).not.toBe(NaN);\n      expect(normalizedDuration[units[j]]).not.toBe(NaN);\n\n      const accurateDuration = duration.reconfigure({ conversionAccuracy: \"longterm\" });\n      const normalizedAccurateDuration = accurateDuration.normalize().toObject();\n      expect(normalizedAccurateDuration[units[i]]).not.toBe(NaN);\n      expect(normalizedAccurateDuration[units[j]]).not.toBe(NaN);\n    }\n  }\n});\n\ntest(\"Duration#normalize moves fractions to lower-order units\", () => {\n  expect(Duration.fromObject({ years: 2.5, days: 0, hours: 0 }).normalize().toObject()).toEqual({\n    years: 2,\n    days: 182,\n    hours: 12,\n  });\n  expect(Duration.fromObject({ years: -2.5, days: 0, hours: 0 }).normalize().toObject()).toEqual({\n    years: -2,\n    days: -182,\n    hours: -12,\n  });\n  expect(Duration.fromObject({ years: 2.5, days: 12, hours: 0 }).normalize().toObject()).toEqual({\n    years: 2,\n    days: 194,\n    hours: 12,\n  });\n  expect(Duration.fromObject({ years: 2.5, days: 12.25, hours: 0 }).normalize().toObject()).toEqual(\n    { years: 2, days: 194, hours: 18 }\n  );\n});\n\ntest(\"Duration#normalize does not produce fractions in higher order units when rolling up negative lower order unit values\", () => {\n  const normalized = Duration.fromObject(\n    { years: 100, months: 0, weeks: -1, days: 0 },\n    { conversionAccuracy: \"longterm\" }\n  )\n    .normalize()\n    .toObject();\n  expect(normalized.years).toBe(99);\n  expect(normalized.months).toBe(11);\n  expect(normalized.weeks).toBe(3);\n  expect(normalized.days).toBeCloseTo(2.436875, 7);\n});\n\n//------\n// #rescale()\n//-------\ntest(\"Duration#rescale normalizes, shifts to all units and remove units with a value of 0\", () => {\n  const sets = [\n    [{ milliseconds: 90000 }, { minutes: 1, seconds: 30 }],\n    [\n      { minutes: 70, milliseconds: 12100 },\n      { hours: 1, minutes: 10, seconds: 12, milliseconds: 100 },\n    ],\n    [{ months: 2, days: -30 }, { months: 1 }],\n  ];\n\n  sets.forEach(([from, to]) => {\n    expect(Duration.fromObject(from).rescale().toObject()).toEqual(to);\n  });\n});\n\ntest(\"Duration#rescale maintains invalidity\", () => {\n  const dur = Duration.invalid(\"because\").rescale();\n  expect(dur.isValid).toBe(false);\n  expect(dur.invalidReason).toBe(\"because\");\n});\n\n//------\n// #as()\n//-------\n\ntest(\"Duration#as shifts to one unit and returns it\", () => {\n  const dur = Duration.fromMillis(5760000);\n  expect(dur.as(\"hours\")).toBe(1.6);\n});\n\ntest(\"Duration#as returns null for invalid durations\", () => {\n  expect(Duration.invalid(\"because\").as(\"hours\")).toBeFalsy();\n});\n\n//------\n// #valueOf()\n//-------\n\ntest(\"Duration#valueOf value of zero duration\", () => {\n  const dur = Duration.fromObject({});\n  expect(dur.valueOf()).toBe(0);\n});\n\ntest(\"Duration#valueOf returns as millisecond value (lower order units)\", () => {\n  const dur = Duration.fromObject({ hours: 1, minutes: 36, seconds: 0 });\n  expect(dur.valueOf()).toBe(5760000);\n});\n\ntest(\"Duration#valueOf value of the duration with lower and higher order units\", () => {\n  const dur = Duration.fromObject({ days: 2, seconds: 1 });\n  expect(dur.valueOf()).toBe(172801000);\n});\n\n//------\n// #removeZeroes()\n//-------\n\ntest(\"Duration#removeZeros leaves empty object if everything was zero\", () => {\n  expect(Duration.fromObject({ years: 0, days: 0, hours: 0 }).removeZeros().toObject()).toEqual({});\n});\n\ntest(\"Duration#removeZeros removes units with zero value\", () => {\n  expect(\n    Duration.fromObject({\n      years: 1,\n      months: 0,\n      weeks: 1,\n      days: 0,\n      hours: 4,\n      minutes: 0,\n      seconds: 6,\n      milliseconds: 0,\n    })\n      .removeZeros()\n      .toObject()\n  ).toEqual({\n    years: 1,\n    weeks: 1,\n    hours: 4,\n    seconds: 6,\n  });\n});\n\ntest(\"Duration#removeZeros removes nothing if no value is zero\", () => {\n  const dur = {\n    years: 1,\n    months: 2,\n    weeks: 1,\n    days: 3,\n    hours: 4,\n    minutes: 5,\n    seconds: 6,\n    milliseconds: 7,\n  };\n  expect(Duration.fromObject(dur).removeZeros().toObject()).toEqual(dur);\n});\n\ntest(\"Duration#removeZeros maintains invalidity\", () => {\n  const dur = Duration.invalid(\"because\").removeZeros();\n  expect(dur.isValid).toBe(false);\n  expect(dur.invalidReason).toBe(\"because\");\n});\n"
  },
  {
    "path": "test/helpers.js",
    "content": "/* global test */\nimport { DateTime, Settings } from \"../src/luxon\";\nimport { hasLocaleWeekInfo } from \"../src/impl/util\";\n\nexports.withoutRTF = function (name, f) {\n  const fullName = `With no RelativeTimeFormat support, ${name}`;\n  test(fullName, () => {\n    const rtf = Intl.RelativeTimeFormat;\n    try {\n      Intl.RelativeTimeFormat = undefined;\n      Settings.resetCaches();\n      f();\n    } finally {\n      Intl.RelativeTimeFormat = rtf;\n    }\n  });\n};\n\nexports.withoutLocaleWeekInfo = function (name, f) {\n  const fullName = `With no Intl.Locale.weekInfo support, ${name}`;\n  test(fullName, () => {\n    const l = Intl.Locale;\n    try {\n      Intl.Locale = undefined;\n      Settings.resetCaches();\n      f();\n    } finally {\n      Intl.Locale = l;\n    }\n  });\n};\n\nexports.withNow = function (name, dt, f) {\n  test(name, () => {\n    const oldNow = Settings.now;\n\n    try {\n      Settings.now = () => dt.valueOf();\n      f();\n    } finally {\n      Settings.now = oldNow;\n    }\n  });\n};\n\n// not a tester!\nexports.withDefaultZone = function (zone, f) {\n  try {\n    Settings.defaultZone = zone;\n    f();\n  } finally {\n    Settings.defaultZone = null;\n  }\n};\n\nexports.withDefaultLocale = function (locale, f) {\n  try {\n    Settings.defaultLocale = locale;\n    f();\n  } finally {\n    Settings.defaultLocale = null;\n  }\n};\n\nexports.setUnset = function (prop) {\n  return (value, f) => {\n    const existing = Settings[prop];\n    try {\n      Settings[prop] = value;\n      f();\n    } finally {\n      Settings[prop] = existing;\n    }\n  };\n};\n\nexports.atHour = function (hour) {\n  return DateTime.fromObject({ year: 2017, month: 5, day: 25 }).startOf(\"day\").set({ hour });\n};\n\nexports.cldrMajorVersion = function () {\n  try {\n    const cldr = process?.versions?.cldr;\n    if (cldr) {\n      const match = cldr.match(/^(\\d+)\\./);\n      if (match) {\n        return parseInt(match[1]);\n      }\n    }\n    return null;\n  } catch {\n    return null;\n  }\n};\n\nexports.supportsMinDaysInFirstWeek = function () {\n  if (!hasLocaleWeekInfo()) return false;\n  const locale = new Intl.Locale(\"en-US\");\n  const wi = locale.getWeekInfo?.() ?? locale.weekInfo;\n  return \"minimalDays\" in wi;\n};\n"
  },
  {
    "path": "test/impl/english.test.js",
    "content": "/* global test expect */\nimport * as Formats from \"../../src/impl/formats\";\nimport { formatRelativeTime, formatString, weekdays, eras } from \"../../src/impl/english\";\n\ntest(\"today\", () => {\n  expect(formatRelativeTime(\"days\", 0, \"auto\")).toBe(\"today\");\n  expect(formatRelativeTime(\"days\", 0, \"always\")).toBe(\"in 0 days\");\n});\n\ntest(\"tomorrow\", () => {\n  expect(formatRelativeTime(\"days\", 1, \"auto\")).toBe(\"tomorrow\");\n  expect(formatRelativeTime(\"days\", 1, \"always\")).toBe(\"in 1 day\");\n});\n\ntest(\"yesterday\", () => {\n  expect(formatRelativeTime(\"days\", -1, \"auto\")).toBe(\"yesterday\");\n  expect(formatRelativeTime(\"days\", -1, \"always\")).toBe(\"1 day ago\");\n});\n\ntest(\"in 0.5 days\", () => {\n  expect(formatRelativeTime(\"days\", 1.5, \"auto\")).toBe(\"in 1.5 days\");\n  expect(formatRelativeTime(\"days\", 1.5, \"always\")).toBe(\"in 1.5 days\");\n});\n\ntest(\"0.5 days ago\", () => {\n  expect(formatRelativeTime(\"days\", -1.5, \"auto\")).toBe(\"1.5 days ago\");\n  expect(formatRelativeTime(\"days\", -1.5, \"always\")).toBe(\"1.5 days ago\");\n});\n\ntest(\"2 days ago\", () => {\n  expect(formatRelativeTime(\"days\", -2, \"auto\")).toBe(\"2 days ago\");\n  expect(formatRelativeTime(\"days\", -2, \"always\")).toBe(\"2 days ago\");\n});\n\ntest(\"this month\", () => {\n  expect(formatRelativeTime(\"months\", 0, \"auto\")).toBe(\"this month\");\n  expect(formatRelativeTime(\"months\", 0, \"always\")).toBe(\"in 0 months\");\n  expect(formatRelativeTime(\"months\", -0, \"always\")).toBe(\"0 months ago\");\n  expect(formatRelativeTime(\"months\", 0, \"always\", true)).toBe(\"in 0 mo.\");\n  expect(formatRelativeTime(\"months\", -0, \"always\", true)).toBe(\"0 mo. ago\");\n});\n\ntest(\"next month\", () => {\n  expect(formatRelativeTime(\"months\", 1, \"auto\")).toBe(\"next month\");\n  expect(formatRelativeTime(\"months\", 1, \"auto\", true)).toBe(\"next month\");\n  expect(formatRelativeTime(\"months\", 1, \"always\")).toBe(\"in 1 month\");\n  expect(formatRelativeTime(\"months\", 1, \"always\", true)).toBe(\"in 1 mo.\");\n});\n\ntest(\"last month\", () => {\n  expect(formatRelativeTime(\"months\", -1, \"auto\")).toBe(\"last month\");\n  expect(formatRelativeTime(\"months\", -1, \"auto\", true)).toBe(\"last month\");\n  expect(formatRelativeTime(\"months\", -1, \"always\")).toBe(\"1 month ago\");\n  expect(formatRelativeTime(\"months\", -1, \"always\", true)).toBe(\"1 mo. ago\");\n});\n\ntest(\"in 3 months\", () => {\n  expect(formatRelativeTime(\"months\", 3, \"auto\")).toBe(\"in 3 months\");\n  expect(formatRelativeTime(\"months\", 3, \"auto\", true)).toBe(\"in 3 mo.\");\n  expect(formatRelativeTime(\"months\", 3, \"always\")).toBe(\"in 3 months\");\n  expect(formatRelativeTime(\"months\", 3, \"always\", true)).toBe(\"in 3 mo.\");\n});\n\ntest(\"in 1 hour\", () => {\n  expect(formatRelativeTime(\"hours\", 1, \"auto\")).toBe(\"in 1 hour\");\n  expect(formatRelativeTime(\"hours\", 1, \"always\")).toBe(\"in 1 hour\");\n});\n\ntest(\"in 1 hour\", () => {\n  expect(formatRelativeTime(\"hours\", 1, \"auto\")).toBe(\"in 1 hour\");\n  expect(formatRelativeTime(\"hours\", 1, \"auto\", true)).toBe(\"in 1 hr.\");\n  expect(formatRelativeTime(\"hours\", 1, \"always\")).toBe(\"in 1 hour\");\n  expect(formatRelativeTime(\"hours\", 1, \"always\", true)).toBe(\"in 1 hr.\");\n});\n\ntest(\"1 hour ago\", () => {\n  expect(formatRelativeTime(\"hours\", -1, \"auto\")).toBe(\"1 hour ago\");\n  expect(formatRelativeTime(\"hours\", -1, \"auto\", true)).toBe(\"1 hr. ago\");\n  expect(formatRelativeTime(\"hours\", -1, \"always\")).toBe(\"1 hour ago\");\n  expect(formatRelativeTime(\"hours\", -1, \"always\", true)).toBe(\"1 hr. ago\");\n});\n\ntest(\"formatString\", () => {\n  expect(formatString(Formats.DATE_SHORT)).toBe(\"M/d/yyyy\");\n  expect(formatString(Formats.DATE_MED)).toBe(\"LLL d, yyyy\");\n  expect(formatString(Formats.DATE_MED_WITH_WEEKDAY)).toBe(\"EEE, LLL d, yyyy\");\n  expect(formatString(Formats.DATE_FULL)).toBe(\"LLLL d, yyyy\");\n  expect(formatString(Formats.DATE_HUGE)).toBe(\"EEEE, LLLL d, yyyy\");\n  expect(formatString(Formats.TIME_SIMPLE)).toBe(\"h:mm a\");\n  expect(formatString(Formats.TIME_WITH_SECONDS)).toBe(\"h:mm:ss a\");\n  expect(formatString(Formats.TIME_WITH_SHORT_OFFSET)).toBe(\"h:mm a\");\n  expect(formatString(Formats.TIME_WITH_LONG_OFFSET)).toBe(\"h:mm a\");\n  expect(formatString(Formats.TIME_24_SIMPLE)).toBe(\"HH:mm\");\n  expect(formatString(Formats.TIME_24_WITH_SECONDS)).toBe(\"HH:mm:ss\");\n  expect(formatString(Formats.TIME_24_WITH_SHORT_OFFSET)).toBe(\"HH:mm\");\n  expect(formatString(Formats.TIME_24_WITH_LONG_OFFSET)).toBe(\"HH:mm\");\n  expect(formatString(Formats.DATETIME_SHORT)).toBe(\"M/d/yyyy, h:mm a\");\n  expect(formatString(Formats.DATETIME_MED)).toBe(\"LLL d, yyyy, h:mm a\");\n  expect(formatString(Formats.DATETIME_FULL)).toBe(\"LLLL d, yyyy, h:mm a\");\n  expect(formatString(Formats.DATETIME_HUGE)).toBe(\"EEEE, LLLL d, yyyy, h:mm a\");\n  expect(formatString(Formats.DATETIME_SHORT_WITH_SECONDS)).toBe(\"M/d/yyyy, h:mm:ss a\");\n  expect(formatString(Formats.DATETIME_MED_WITH_SECONDS)).toBe(\"LLL d, yyyy, h:mm:ss a\");\n  expect(formatString(Formats.DATETIME_MED_WITH_WEEKDAY)).toBe(\"EEE, d LLL yyyy, h:mm a\");\n  expect(formatString(Formats.DATETIME_FULL_WITH_SECONDS)).toBe(\"LLLL d, yyyy, h:mm:ss a\");\n  expect(formatString(Formats.DATETIME_HUGE_WITH_SECONDS)).toBe(\"EEEE, LLLL d, yyyy, h:mm:ss a\");\n  expect(formatString(\"Default\")).toBe(\"EEEE, LLLL d, yyyy, h:mm a\");\n});\n\ntest(\"weekdays\", () => {\n  expect(weekdays(\"narrow\")).toStrictEqual([\"M\", \"T\", \"W\", \"T\", \"F\", \"S\", \"S\"]);\n  expect(weekdays(\"short\")).toStrictEqual([\"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\", \"Sun\"]);\n  expect(weekdays(\"long\")).toStrictEqual([\n    \"Monday\",\n    \"Tuesday\",\n    \"Wednesday\",\n    \"Thursday\",\n    \"Friday\",\n    \"Saturday\",\n    \"Sunday\",\n  ]);\n  expect(weekdays(\"numeric\")).toStrictEqual([\"1\", \"2\", \"3\", \"4\", \"5\", \"6\", \"7\"]);\n  expect(weekdays(null)).toStrictEqual(null);\n});\n\ntest(\"eras\", () => {\n  expect(eras(\"narrow\")).toStrictEqual([\"B\", \"A\"]);\n  expect(eras(\"short\")).toStrictEqual([\"BC\", \"AD\"]);\n  expect(eras(\"long\")).toStrictEqual([\"Before Christ\", \"Anno Domini\"]);\n  expect(eras(\"default\")).toStrictEqual(null);\n});\n"
  },
  {
    "path": "test/info/features.test.js",
    "content": "/* global test expect */\nimport { Info } from \"../../src/luxon\";\n\nconst Helpers = require(\"../helpers\");\n\ntest(\"Info.features shows this environment supports all the features\", () => {\n  expect(Info.features().relative).toBe(true);\n  expect(Info.features().localeWeek).toBe(true);\n});\n\nHelpers.withoutRTF(\"Info.features shows no support\", () => {\n  expect(Info.features().relative).toBe(false);\n});\n\nHelpers.withoutLocaleWeekInfo(\"Info.features shows no support\", () => {\n  expect(Info.features().localeWeek).toBe(false);\n});\n"
  },
  {
    "path": "test/info/listers.test.js",
    "content": "/* global test expect */\n\nimport { Info } from \"../../src/luxon\";\n\nimport Helpers from \"../helpers\";\nconst withDefaultLocale = Helpers.withDefaultLocale;\n\n//------\n// .months()\n//------\n\ntest(\"Info.months lists all the months\", () => {\n  expect(Info.months(\"long\")).toEqual([\n    \"January\",\n    \"February\",\n    \"March\",\n    \"April\",\n    \"May\",\n    \"June\",\n    \"July\",\n    \"August\",\n    \"September\",\n    \"October\",\n    \"November\",\n    \"December\",\n  ]);\n\n  expect(Info.months(\"short\", { locale: \"en\" })).toEqual([\n    \"Jan\",\n    \"Feb\",\n    \"Mar\",\n    \"Apr\",\n    \"May\",\n    \"Jun\",\n    \"Jul\",\n    \"Aug\",\n    \"Sep\",\n    \"Oct\",\n    \"Nov\",\n    \"Dec\",\n  ]);\n\n  expect(Info.months(\"narrow\", { locale: \"en\" })).toEqual([\n    \"J\",\n    \"F\",\n    \"M\",\n    \"A\",\n    \"M\",\n    \"J\",\n    \"J\",\n    \"A\",\n    \"S\",\n    \"O\",\n    \"N\",\n    \"D\",\n  ]);\n\n  expect(Info.months(\"numeric\", { locale: \"en\" })).toEqual([\n    \"1\",\n    \"2\",\n    \"3\",\n    \"4\",\n    \"5\",\n    \"6\",\n    \"7\",\n    \"8\",\n    \"9\",\n    \"10\",\n    \"11\",\n    \"12\",\n  ]);\n\n  expect(Info.months(\"2-digit\", { locale: \"en\" })).toEqual([\n    \"01\",\n    \"02\",\n    \"03\",\n    \"04\",\n    \"05\",\n    \"06\",\n    \"07\",\n    \"08\",\n    \"09\",\n    \"10\",\n    \"11\",\n    \"12\",\n  ]);\n});\n\ntest(\"Info.months respects the numbering system\", () => {\n  expect(Info.months(\"numeric\", { locale: \"en\", numberingSystem: \"beng\" })).toEqual([\n    \"১\",\n    \"২\",\n    \"৩\",\n    \"৪\",\n    \"৫\",\n    \"৬\",\n    \"৭\",\n    \"৮\",\n    \"৯\",\n    \"১০\",\n    \"১১\",\n    \"১২\",\n  ]);\n});\n\ntest(\"Info.months respects the calendar\", () => {\n  expect(Info.months(\"long\", { locale: \"en\", outputCalendar: \"islamic\" })).toEqual([\n    \"Muharram\",\n    \"Safar\",\n    \"Rabiʻ I\",\n    \"Rabiʻ II\",\n    \"Jumada I\",\n    \"Jumada II\",\n    \"Rajab\",\n    \"Shaʻban\",\n    \"Ramadan\",\n    \"Shawwal\",\n    \"Dhuʻl-Qiʻdah\",\n    \"Dhuʻl-Hijjah\",\n  ]);\n});\n\ntest(\"Info.months respects the locale\", () => {\n  expect(Info.months(\"numeric\", { locale: \"bn\" })).toEqual([\n    \"১\",\n    \"২\",\n    \"৩\",\n    \"৪\",\n    \"৫\",\n    \"৬\",\n    \"৭\",\n    \"৮\",\n    \"৯\",\n    \"১০\",\n    \"১১\",\n    \"১২\",\n  ]);\n\n  // formatToParts outputs 月 as \"literal\" instead of \"month\", but it arguably is part of the name of the month.\n  // this tests that we correctly work around this\n  // see https://github.com/moment/luxon/issues/549\n  for (const locale of [\"ja\", \"ja-JP\"]) {\n    for (const length of [\"long\", \"short\", \"narrow\"]) {\n      for (const formatter of [Info.months, Info.monthsFormat]) {\n        expect(formatter(length, { locale })).toEqual([\n          \"1月\",\n          \"2月\",\n          \"3月\",\n          \"4月\",\n          \"5月\",\n          \"6月\",\n          \"7月\",\n          \"8月\",\n          \"9月\",\n          \"10月\",\n          \"11月\",\n          \"12月\",\n        ]);\n      }\n    }\n  }\n\n  expect(Info.monthsFormat(\"long\", { locale: \"ru\" })).toEqual([\n    \"января\",\n    \"февраля\",\n    \"марта\",\n    \"апреля\",\n    \"мая\",\n    \"июня\",\n    \"июля\",\n    \"августа\",\n    \"сентября\",\n    \"октября\",\n    \"ноября\",\n    \"декабря\",\n  ]);\n});\n\ntest(\"Info.months defaults to long names\", () => {\n  expect(Info.months()).toEqual([\n    \"January\",\n    \"February\",\n    \"March\",\n    \"April\",\n    \"May\",\n    \"June\",\n    \"July\",\n    \"August\",\n    \"September\",\n    \"October\",\n    \"November\",\n    \"December\",\n  ]);\n});\n\n//------\n// .monthsFormat()\n//------\ntest(\"Info.monthsFormat lists all the months\", () => {\n  expect(Info.monthsFormat(\"long\", { locale: \"en\" })).toEqual([\n    \"January\",\n    \"February\",\n    \"March\",\n    \"April\",\n    \"May\",\n    \"June\",\n    \"July\",\n    \"August\",\n    \"September\",\n    \"October\",\n    \"November\",\n    \"December\",\n  ]);\n\n  // this passes, but is wrong. These are the same as the standalone values\n  expect(Info.monthsFormat(\"long\", { locale: \"ru\" })).toEqual([\n    \"января\",\n    \"февраля\",\n    \"марта\",\n    \"апреля\",\n    \"мая\",\n    \"июня\",\n    \"июля\",\n    \"августа\",\n    \"сентября\",\n    \"октября\",\n    \"ноября\",\n    \"декабря\",\n  ]);\n\n  expect(Info.monthsFormat(\"short\", { locale: \"en\" })).toEqual([\n    \"Jan\",\n    \"Feb\",\n    \"Mar\",\n    \"Apr\",\n    \"May\",\n    \"Jun\",\n    \"Jul\",\n    \"Aug\",\n    \"Sep\",\n    \"Oct\",\n    \"Nov\",\n    \"Dec\",\n  ]);\n\n  expect(Info.monthsFormat(\"numeric\", { locale: \"en\" })).toEqual([\n    \"1\",\n    \"2\",\n    \"3\",\n    \"4\",\n    \"5\",\n    \"6\",\n    \"7\",\n    \"8\",\n    \"9\",\n    \"10\",\n    \"11\",\n    \"12\",\n  ]);\n});\n\ntest(\"Info.monthsFormat defaults to long names\", () => {\n  expect(Info.monthsFormat()).toEqual([\n    \"January\",\n    \"February\",\n    \"March\",\n    \"April\",\n    \"May\",\n    \"June\",\n    \"July\",\n    \"August\",\n    \"September\",\n    \"October\",\n    \"November\",\n    \"December\",\n  ]);\n});\n\n//------\n// .weekdays()\n//------\ntest(\"Info.weekdays lists all the weekdays\", () => {\n  expect(Info.weekdays(\"long\", { locale: \"en\" })).toEqual([\n    \"Monday\",\n    \"Tuesday\",\n    \"Wednesday\",\n    \"Thursday\",\n    \"Friday\",\n    \"Saturday\",\n    \"Sunday\",\n  ]);\n\n  expect(Info.weekdays(\"short\", { locale: \"en\" })).toEqual([\n    \"Mon\",\n    \"Tue\",\n    \"Wed\",\n    \"Thu\",\n    \"Fri\",\n    \"Sat\",\n    \"Sun\",\n  ]);\n\n  expect(Info.weekdays(\"narrow\", { locale: \"en\" })).toEqual([\"M\", \"T\", \"W\", \"T\", \"F\", \"S\", \"S\"]);\n\n  expect(Info.weekdays(\"long\", { locale: \"ru\" })).toEqual([\n    \"понедельник\",\n    \"вторник\",\n    \"среда\",\n    \"четверг\",\n    \"пятница\",\n    \"суббота\",\n    \"воскресенье\",\n  ]);\n});\n\ntest(\"Info.weekdays defaults to long names\", () => {\n  expect(Info.weekdays()).toEqual([\n    \"Monday\",\n    \"Tuesday\",\n    \"Wednesday\",\n    \"Thursday\",\n    \"Friday\",\n    \"Saturday\",\n    \"Sunday\",\n  ]);\n});\n\n//------\n// .weekdaysFormat()\n//------\ntest(\"Info.weekdaysFormat lists all the weekdays\", () => {\n  expect(Info.weekdaysFormat(\"long\", { locale: \"en\" })).toEqual([\n    \"Monday\",\n    \"Tuesday\",\n    \"Wednesday\",\n    \"Thursday\",\n    \"Friday\",\n    \"Saturday\",\n    \"Sunday\",\n  ]);\n\n  expect(Info.weekdaysFormat(\"short\", { locale: \"en\" })).toEqual([\n    \"Mon\",\n    \"Tue\",\n    \"Wed\",\n    \"Thu\",\n    \"Fri\",\n    \"Sat\",\n    \"Sun\",\n  ]);\n});\n\ntest(\"Info.weekdaysFormat defaults to long names\", () => {\n  expect(Info.weekdaysFormat()).toEqual([\n    \"Monday\",\n    \"Tuesday\",\n    \"Wednesday\",\n    \"Thursday\",\n    \"Friday\",\n    \"Saturday\",\n    \"Sunday\",\n  ]);\n});\n\n//------\n// .meridiems()\n//------\ntest(\"Info.meridiems lists the meridiems\", () => {\n  expect(Info.meridiems({ locale: \"en\" })).toEqual([\"AM\", \"PM\"]);\n  expect(Info.meridiems({ locale: \"my\" })).toEqual([\"နံနက်\", \"ညနေ\"]);\n});\n\ntest(\"Info.meridiems defaults to the current locale\", () => {\n  expect(Info.meridiems()).toEqual([\"AM\", \"PM\"]);\n});\n\n//------\n// .eras()\n//------\n\ntest(\"Info.eras lists both eras\", () => {\n  expect(Info.eras()).toEqual([\"BC\", \"AD\"]);\n  expect(Info.eras(\"short\")).toEqual([\"BC\", \"AD\"]);\n  expect(Info.eras(\"long\")).toEqual([\"Before Christ\", \"Anno Domini\"]);\n  expect(Info.eras(\"short\", { locale: \"fr\" })).toEqual([\"av. J.-C.\", \"ap. J.-C.\"]);\n  expect(Info.eras(\"long\", { locale: \"fr\" })).toEqual([\"avant Jésus-Christ\", \"après Jésus-Christ\"]);\n});\n\n//------\n// general\n//------\ntest(\"Info English lists are not mutable\", () => {\n  withDefaultLocale(\"en-US\", () => {\n    const cachingMethods = [\n      [\"weekdays\", \"short\"],\n      [\"weekdays\", \"long\"],\n      [\"weekdays\", \"narrow\"],\n      [\"weekdays\", \"numeric\"],\n      [\"months\", \"short\"],\n      [\"months\", \"long\"],\n      [\"months\", \"narrow\"],\n      [\"months\", \"numeric\"],\n      [\"months\", \"2-digit\"],\n      [\"eras\", \"narrow\"],\n      [\"eras\", \"short\"],\n      [\"eras\", \"long\"],\n    ];\n\n    for (const [method, arg] of cachingMethods) {\n      const fn = Info[method];\n      const original = [...fn(arg)];\n      fn(arg).pop();\n      const expected = fn(arg);\n      expect(expected).toEqual(original);\n    }\n  });\n});\n"
  },
  {
    "path": "test/info/localeWeek.test.js",
    "content": "/* global test expect */\nimport { Info } from \"../../src/luxon\";\nimport { supportsMinDaysInFirstWeek } from \"../helpers\";\n\nconst Helpers = require(\"../helpers\");\n\ntest(\"Info.getStartOfWeek reports the correct start of the week\", () => {\n  expect(Info.getStartOfWeek({ locale: \"en-US\" })).toBe(7);\n  expect(Info.getStartOfWeek({ locale: \"de-DE\" })).toBe(1);\n});\n\nHelpers.withoutLocaleWeekInfo(\"Info.getStartOfWeek reports Monday as the start of the week\", () => {\n  expect(Info.getStartOfWeek({ locale: \"en-US\" })).toBe(1);\n  expect(Info.getStartOfWeek({ locale: \"de-DE\" })).toBe(1);\n});\n\ntest(\"Info.getMinimumDaysInFirstWeek reports the correct value\", () => {\n  expect(Info.getMinimumDaysInFirstWeek({ locale: \"en-US\" })).toBe(\n    supportsMinDaysInFirstWeek() ? 1 : 4\n  );\n  expect(Info.getMinimumDaysInFirstWeek({ locale: \"de-DE\" })).toBe(4);\n});\n\nHelpers.withoutLocaleWeekInfo(\"Info.getMinimumDaysInFirstWeek reports 4\", () => {\n  expect(Info.getMinimumDaysInFirstWeek({ locale: \"en-US\" })).toBe(4);\n  expect(Info.getMinimumDaysInFirstWeek({ locale: \"de-DE\" })).toBe(4);\n});\n\ntest(\"Info.getWeekendWeekdays reports the correct value\", () => {\n  expect(Info.getWeekendWeekdays({ locale: \"en-US\" })).toStrictEqual([6, 7]);\n  expect(Info.getWeekendWeekdays({ locale: \"he\" })).toStrictEqual([5, 6]);\n});\n\nHelpers.withoutLocaleWeekInfo(\"Info.getWeekendWeekdays reports [6, 7]\", () => {\n  expect(Info.getWeekendWeekdays({ locale: \"en-US\" })).toStrictEqual([6, 7]);\n  expect(Info.getWeekendWeekdays({ locale: \"he\" })).toStrictEqual([6, 7]);\n});\n\ntest(\"Info.getStartOfWeek honors the default locale\", () => {\n  Helpers.withDefaultLocale(\"en-US\", () => {\n    expect(Info.getStartOfWeek()).toBe(7);\n    expect(Info.getMinimumDaysInFirstWeek()).toBe(supportsMinDaysInFirstWeek() ? 1 : 4);\n    expect(Info.getWeekendWeekdays()).toStrictEqual([6, 7]);\n  });\n\n  Helpers.withDefaultLocale(\"de-DE\", () => {\n    expect(Info.getStartOfWeek()).toBe(1);\n  });\n\n  Helpers.withDefaultLocale(\"he\", () => {\n    expect(Info.getWeekendWeekdays()).toStrictEqual([5, 6]);\n  });\n\n  Helpers.withDefaultLocale(\"he\", () => {\n    expect(Info.getWeekendWeekdays()).toStrictEqual([5, 6]);\n  });\n});\n"
  },
  {
    "path": "test/info/zones.test.js",
    "content": "/* global test expect */\n\nimport {\n  Info,\n  FixedOffsetZone,\n  IANAZone,\n  InvalidZone,\n  SystemZone,\n  Settings,\n} from \"../../src/luxon\";\n\nconst Helpers = require(\"../helpers\");\n\n//------\n// .hasDST()\n//------\n\ntest(\"Info.hasDST returns true for America/New_York\", () => {\n  expect(Info.hasDST(\"America/New_York\")).toBe(true);\n});\n\ntest(\"Info.hasDST returns false for America/Aruba\", () => {\n  expect(Info.hasDST(\"America/Aruba\")).toBe(false);\n});\n\ntest(\"Info.hasDST returns false for America/Cancun\", () => {\n  expect(Info.hasDST(\"America/Cancun\")).toBe(false);\n});\n\ntest(\"Info.hasDST returns true for Europe/Andora\", () => {\n  expect(Info.hasDST(\"Europe/Andora\")).toBe(true);\n});\n\ntest(\"Info.hasDST defaults to the global zone\", () => {\n  Helpers.withDefaultZone(\"America/Cancun\", () => {\n    expect(Info.hasDST()).toBe(false);\n  });\n});\n\n//------\n// .isValidIANAZone()\n//------\n\ntest(\"Info.isValidIANAZone returns true for valid zones\", () => {\n  expect(Info.isValidIANAZone(\"America/Cancun\")).toBe(true);\n});\n\ntest(\"Info.isValidIANAZone returns true for single-section zones\", () => {\n  expect(Info.isValidIANAZone(\"UTC\")).toBe(true);\n});\n\ntest(\"Info.isValidIANAZone returns false for junk\", () => {\n  expect(Info.isValidIANAZone(\"blorp\")).toBe(false);\n});\n\ntest(\"Info.isValidIANAZone returns false for well-specified but invalid zones\", () => {\n  expect(Info.isValidIANAZone(\"America/Blork\")).toBe(false);\n});\n\ntest(\"Info.isValidIANAZone returns true for valid zones like America/Indiana/Indianapolis\", () => {\n  expect(Info.isValidIANAZone(\"America/Indiana/Indianapolis\")).toBe(true);\n});\n\ntest(\"Info.isValidIANAZone returns false for well-specified but invalid zones like America/Indiana/Blork\", () => {\n  expect(Info.isValidIANAZone(\"America/Indiana/Blork\")).toBe(false);\n});\n\n//------\n// .normalizeZone()\n//------\n\ntest(\"Info.normalizeZone returns Zone objects unchanged\", () => {\n  const fixedOffsetZone = FixedOffsetZone.instance(5);\n  expect(Info.normalizeZone(fixedOffsetZone)).toBe(fixedOffsetZone);\n\n  const ianaZone = new IANAZone(\"Europe/Paris\");\n  expect(Info.normalizeZone(ianaZone)).toBe(ianaZone);\n\n  const invalidZone = new InvalidZone(\"bumblebee\");\n  expect(Info.normalizeZone(invalidZone)).toBe(invalidZone);\n\n  const systemZone = SystemZone.instance;\n  expect(Info.normalizeZone(systemZone)).toBe(systemZone);\n});\n\ntest.each([\n  [\"Local\", SystemZone.instance],\n  [\"System\", SystemZone.instance],\n  [\"UTC\", FixedOffsetZone.utcInstance],\n  [\"GMT\", FixedOffsetZone.utcInstance],\n  [\"Etc/GMT+5\", new IANAZone(\"Etc/GMT+5\")],\n  [\"Etc/GMT-10\", new IANAZone(\"Etc/GMT-10\")],\n  [\"Europe/Paris\", new IANAZone(\"Europe/Paris\")],\n  [0, FixedOffsetZone.utcInstance],\n  [3, FixedOffsetZone.instance(3)],\n  [-11, FixedOffsetZone.instance(-11)],\n])(\"Info.normalizeZone converts valid input %p into valid Zone instance\", (input, expected) => {\n  expect(Info.normalizeZone(input)).toEqual(expected);\n});\n\ntest(\"Info.normalizeZone converts unknown name to invalid Zone\", () => {\n  expect(Info.normalizeZone(\"bumblebee\").isValid).toBe(false);\n});\n\ntest(\"Info.normalizeZone converts null and undefined to default Zone\", () => {\n  expect(Info.normalizeZone(null)).toBe(Settings.defaultZone);\n  expect(Info.normalizeZone(undefined)).toBe(Settings.defaultZone);\n});\n\n// Local zone no longer refers to default one but behaves as system\n// As per Docker Container, zone is America/New_York\ntest(\"Info.normalizeZone converts local to system Zone\", () => {\n  expect(Info.normalizeZone(\"local\")).toBe(Settings.defaultZone);\n  Helpers.withDefaultZone(\"America/New_York\", () => {\n    expect(Info.normalizeZone(\"local\").name).toBe(\"America/New_York\");\n  });\n});\n"
  },
  {
    "path": "test/interval/create.test.js",
    "content": "/* global test expect */\nimport { DateTime, Interval, Duration, Settings } from \"../../src/luxon\";\nimport Helpers from \"../helpers\";\n\nconst withThrowOnInvalid = Helpers.setUnset(\"throwOnInvalid\");\n\n//------\n// .fromObject()\n//-------\ntest(\"Interval.fromDateTimes creates an interval from datetimes\", () => {\n  const start = DateTime.fromObject({ year: 2016, month: 5, day: 25 }),\n    end = DateTime.fromObject({ year: 2016, month: 5, day: 27 }),\n    int = Interval.fromDateTimes(start, end);\n\n  expect(int.start).toBe(start);\n  expect(int.end).toBe(end);\n});\n\ntest(\"Interval.fromDateTimes creates an interval from objects\", () => {\n  const start = { year: 2016, month: 5, day: 25 },\n    end = { year: 2016, month: 5, day: 27 },\n    int = Interval.fromDateTimes(start, end);\n\n  expect(int.start).toEqual(DateTime.fromObject(start));\n  expect(int.end).toEqual(DateTime.fromObject(end));\n});\n\ntest(\"Interval.fromDateTimes creates an interval from Dates\", () => {\n  const start = DateTime.fromObject({\n      year: 2016,\n      month: 5,\n      day: 25,\n    }).toJSDate(),\n    end = DateTime.fromObject({ year: 2016, month: 5, day: 27 }).toJSDate(),\n    int = Interval.fromDateTimes(start, end);\n\n  expect(int.start.toJSDate()).toEqual(start);\n  expect(int.end.toJSDate()).toEqual(end);\n});\n\ntest(\"Interval.fromDateTimes results in an invalid Interval if the endpoints are invalid\", () => {\n  const validDate = DateTime.fromObject({ year: 2016, month: 5, day: 25 }),\n    invalidDate = DateTime.invalid(\"because\");\n\n  expect(Interval.fromDateTimes(validDate, invalidDate).invalidReason).toBe(\n    \"missing or invalid end\"\n  );\n  expect(Interval.fromDateTimes(invalidDate, validDate).invalidReason).toBe(\n    \"missing or invalid start\"\n  );\n\n  expect(Interval.fromDateTimes(validDate.plus({ days: 1 }), validDate).invalidReason).toBe(\n    \"end before start\"\n  );\n});\n\ntest(\"Interval.fromDateTimes throws with invalid input\", () => {\n  expect(() => Interval.fromDateTimes(DateTime.now(), true)).toThrow();\n});\n\ntest(\"Interval.fromDateTimes throws with start date coming after end date\", () => {\n  const start = DateTime.fromObject({\n      year: 2016,\n      month: 5,\n      day: 25,\n    }).toJSDate(),\n    end = DateTime.fromObject({ year: 2016, month: 5, day: 27 }).toJSDate();\n\n  withThrowOnInvalid(true, () => {\n    expect(() => Interval.fromDateTimes(end, start)).toThrow();\n  });\n});\n\n//------\n// .after()\n//-------\ntest(\"Interval.after takes a duration\", () => {\n  const start = DateTime.fromObject({ year: 2016, month: 5, day: 25 }),\n    int = Interval.after(start, Duration.fromObject({ days: 3 }));\n\n  expect(int.start).toBe(start);\n  expect(int.end.day).toBe(28);\n});\n\ntest(\"Interval.after an object\", () => {\n  const start = DateTime.fromObject({ year: 2016, month: 5, day: 25 }),\n    int = Interval.after(start, { days: 3 });\n\n  expect(int.start).toBe(start);\n  expect(int.end.day).toBe(28);\n});\n\n//------\n// .before()\n//-------\ntest(\"Interval.before takes a duration\", () => {\n  const end = DateTime.fromObject({ year: 2016, month: 5, day: 25 }),\n    int = Interval.before(end, Duration.fromObject({ days: 3 }));\n\n  expect(int.start.day).toBe(22);\n  expect(int.end).toBe(end);\n});\n\ntest(\"Interval.before takes a number and unit\", () => {\n  const end = DateTime.fromObject({ year: 2016, month: 5, day: 25 }),\n    int = Interval.before(end, { days: 3 });\n\n  expect(int.start.day).toBe(22);\n  expect(int.end).toBe(end);\n});\n\n//------\n// .invalid()\n//-------\ntest(\"Interval.invalid produces invalid Intervals\", () => {\n  expect(Interval.invalid(\"because\").isValid).toBe(false);\n});\n\ntest(\"Interval.invalid throws if throwOnInvalid is set\", () => {\n  try {\n    Settings.throwOnInvalid = true;\n    expect(() => Interval.invalid(\"because\")).toThrow();\n  } finally {\n    Settings.throwOnInvalid = false;\n  }\n});\n\ntest(\"Interval.invalid throws if no reason is specified\", () => {\n  expect(() => Interval.invalid()).toThrow();\n});\n"
  },
  {
    "path": "test/interval/format.test.js",
    "content": "/* global test expect */\nimport { Interval, DateTime } from \"../../src/luxon\";\n\nconst fromISOs = (s, e) =>\n    DateTime.fromISO(s, { setZone: true }).until(DateTime.fromISO(e, { setZone: true })),\n  interval = fromISOs(\"1982-05-25T09:00Z\", \"1983-10-14T13:30Z\"),\n  invalid = Interval.invalid(\"because\");\n\n//------\n// .toString()\n//------\n\ntest(\"Interval#toString returns a simple range format\", () =>\n  expect(interval.toString()).toBe(\"[1982-05-25T09:00:00.000Z – 1983-10-14T13:30:00.000Z)\"));\n\ntest(\"Interval#toString returns an unfriendly string for invalid intervals\", () =>\n  expect(invalid.toString()).toBe(\"Invalid Interval\"));\n\n//------\n// .toLocaleString()\n//------\n\ntest(\"Interval#toLocaleString defaults to the DATE_SHORT format\", () =>\n  expect(interval.toLocaleString()).toBe(\"5/25/1982 – 10/14/1983\"));\n\ntest(\"Interval#toLocaleString returns an unfriendly string for invalid intervals\", () =>\n  expect(invalid.toLocaleString()).toBe(\"Invalid Interval\"));\n\ntest(\"Interval#toLocaleString lets the locale set the numbering system\", () => {\n  expect(\n    Interval.after(interval.start.reconfigure({ locale: \"ja-JP\" }), { hour: 2 }).toLocaleString({\n      hour: \"numeric\",\n    })\n  ).toBe(\"9時～11時\");\n});\n\ntest(\"Interval#toLocaleString accepts locale settings from the start DateTime\", () => {\n  expect(\n    Interval.fromDateTimes(\n      interval.start.reconfigure({ locale: \"be\" }),\n      interval.end\n    ).toLocaleString()\n  ).toBe(\"25.5.1982 – 14.10.1983\");\n});\n\ntest(\"Interval#toLocaleString accepts numbering system settings from the start DateTime\", () => {\n  expect(\n    Interval.fromDateTimes(\n      interval.start.reconfigure({ numberingSystem: \"beng\" }),\n      interval.end\n    ).toLocaleString()\n  ).toBe(\"৫/২৫/১৯৮২ – ১০/১৪/১৯৮৩\");\n});\n\ntest(\"Interval#toLocaleString accepts ouptput calendar settings from the start DateTime\", () => {\n  expect(\n    Interval.fromDateTimes(\n      interval.start.reconfigure({ outputCalendar: \"islamic\" }),\n      interval.end\n    ).toLocaleString()\n  ).toBe(\"8/2/1402 – 1/8/1404 AH\");\n});\n\ntest(\"Interval#toLocaleString accepts options to the formatter\", () => {\n  expect(interval.toLocaleString({ weekday: \"short\" })).toBe(\"Tue – Fri\");\n});\n\ntest(\"Interval#toLocaleString can override the start DateTime's locale\", () => {\n  expect(\n    Interval.fromDateTimes(\n      interval.start.reconfigure({ locale: \"be\" }),\n      interval.end\n    ).toLocaleString({}, { locale: \"fr\" })\n  ).toBe(\"25/05/1982 – 14/10/1983\");\n});\n\ntest(\"Interval#toLocaleString can override the start DateTime's numbering system\", () => {\n  expect(\n    Interval.fromDateTimes(\n      interval.start.reconfigure({ numberingSystem: \"beng\" }),\n      interval.end\n    ).toLocaleString({ numberingSystem: \"mong\" })\n  ).toBe(\"᠕/᠒᠕/᠑᠙᠘᠒ – ᠑᠐/᠑᠔/᠑᠙᠘᠓\");\n});\n\ntest(\"Interval#toLocaleString can override the start DateTime's output calendar\", () => {\n  expect(\n    Interval.fromDateTimes(\n      interval.start.reconfigure({ outputCalendar: \"islamic\" }),\n      interval.end\n    ).toLocaleString({}, { outputCalendar: \"coptic\" })\n  ).toBe(\"9/17/1698 – 2/3/1700 ERA1\");\n});\n\ntest(\"Interval#toLocaleString shows things in the right IANA zone\", () => {\n  expect(\n    Interval.fromDateTimes(\n      interval.start.setZone(\"Australia/Melbourne\"),\n      interval.end\n    ).toLocaleString(DateTime.DATETIME_SHORT)\n  ).toBe(\"5/25/1982, 7:00 PM – 10/14/1983, 11:30 PM\");\n});\n\ntest(\"Interval#toLocaleString shows things in the right fixed-offset zone\", () => {\n  expect(\n    Interval.fromDateTimes(interval.start.setZone(\"UTC-8\"), interval.end).toLocaleString(\n      DateTime.DATETIME_SHORT\n    )\n  ).toBe(\"5/25/1982, 1:00 AM – 10/14/1983, 5:30 AM\");\n});\n\ntest(\"Interval#toLocaleString shows things in the right fixed-offset zone when showing the zone\", () => {\n  expect(\n    Interval.fromDateTimes(interval.start.setZone(\"UTC-8\"), interval.end).toLocaleString(\n      DateTime.DATETIME_FULL\n    )\n  ).toBe(\"May 25, 1982 at 1:00 AM GMT-8 – October 14, 1983 at 5:30 AM GMT-8\");\n});\n\ntest(\"Interval#toLocaleString shows things with UTC if fixed-offset with 0 offset is used\", () => {\n  expect(\n    Interval.fromDateTimes(interval.start.setZone(\"UTC\"), interval.end).toLocaleString(\n      DateTime.DATETIME_FULL\n    )\n  ).toBe(\"May 25, 1982 at 9:00 AM UTC – October 14, 1983 at 1:30 PM UTC\");\n});\n\ntest(\"Interval#toLocaleString does the best it can with unsupported fixed-offset zone when showing the zone\", () => {\n  expect(\n    Interval.fromDateTimes(interval.start.setZone(\"UTC+4:30\"), interval.end).toLocaleString(\n      DateTime.DATETIME_FULL\n    )\n  ).toBe(\"May 25, 1982 at 9:00 AM UTC – October 14, 1983 at 1:30 PM UTC\");\n});\n\ntest(\"Interval#toLocaleString uses locale-appropriate time formats\", () => {\n  expect(\n    Interval.after(interval.start.reconfigure({ locale: \"en-US\" }), { hour: 2 }).toLocaleString(\n      DateTime.TIME_SIMPLE\n    )\n  ).toBe(\"9:00 – 11:00 AM\");\n  expect(\n    Interval.after(interval.start.reconfigure({ locale: \"en-US\" }), { hour: 2 }).toLocaleString(\n      DateTime.TIME_24_SIMPLE\n    )\n  ).toBe(\"09:00 – 11:00\");\n\n  // France has 24-hour by default\n  expect(\n    Interval.after(interval.start.reconfigure({ locale: \"fr\" }), { hour: 2 }).toLocaleString(\n      DateTime.TIME_SIMPLE\n    )\n  ).toBe(\"09:00 – 11:00\");\n  expect(\n    Interval.after(interval.start.reconfigure({ locale: \"fr\" }), { hour: 2 }).toLocaleString(\n      DateTime.TIME_24_SIMPLE\n    )\n  ).toBe(\"09:00 – 11:00\");\n\n  // Spain does't prefix with \"0\" and doesn't use spaces\n  expect(\n    Interval.after(interval.start.reconfigure({ locale: \"es\" }), { hour: 2 }).toLocaleString(\n      DateTime.TIME_SIMPLE\n    )\n  ).toBe(\"9:00–11:00\");\n  expect(\n    Interval.after(interval.start.reconfigure({ locale: \"es\" }), { hour: 2 }).toLocaleString(\n      DateTime.TIME_24_SIMPLE\n    )\n  ).toBe(\"9:00–11:00\");\n});\n\ntest(\"Interval#toLocaleString sets the separator between days for same-month dates\", () => {\n  expect(Interval.after(interval.start, { day: 2 }).toLocaleString(DateTime.DATE_MED)).toBe(\n    \"May 25 – 27, 1982\"\n  );\n});\n\n//------\n// .toISO()\n//------\n\ntest(\"Interval#toISO returns a simple ISO format\", () =>\n  expect(interval.toISO()).toBe(\"1982-05-25T09:00:00.000Z/1983-10-14T13:30:00.000Z\"));\n\ntest(\"Interval#toISO accepts ISO options\", () =>\n  expect(interval.toISO({ suppressSeconds: true })).toBe(\"1982-05-25T09:00Z/1983-10-14T13:30Z\"));\n\ntest(\"Interval#toISO returns an unfriendly string for invalid intervals\", () =>\n  expect(invalid.toISO()).toBe(\"Invalid Interval\"));\n\n//------\n// .toISODate()\n//------\n\ntest(\"Interval#toISODate returns a simple ISO date interval format\", () =>\n  expect(interval.toISODate()).toBe(\"1982-05-25/1983-10-14\"));\n\ntest(\"Interval#toISODate returns an unfriendly string for invalid intervals\", () =>\n  expect(invalid.toISODate()).toBe(\"Invalid Interval\"));\n\n//------\n// .toISOTime()\n//------\n\ntest(\"Interval#toISOTime returns a simple ISO time interval format\", () =>\n  expect(interval.toISOTime()).toBe(\"09:00:00.000Z/13:30:00.000Z\"));\n\ntest(\"Interval#toISOTime returns an unfriendly string for invalid intervals\", () =>\n  expect(invalid.toISOTime()).toBe(\"Invalid Interval\"));\n\ntest(\"Interval#toISOTime accepts ISO options\", () => {\n  expect(interval.toISOTime({ suppressSeconds: true })).toBe(\"09:00Z/13:30Z\");\n  expect(interval.toISOTime({ suppressMilliseconds: true })).toBe(\"09:00:00Z/13:30:00Z\");\n});\n\n//------\n// .toFormat()\n//------\n\ntest(\"Interval#toFormat accepts date formats\", () => {\n  expect(interval.toFormat(\"EEE, LLL dd, yyyy\")).toBe(\"Tue, May 25, 1982 – Fri, Oct 14, 1983\");\n  expect(interval.toFormat(\"HH:mm\")).toBe(\"09:00 – 13:30\");\n});\n\ntest(\"Interval#toFormat accepts date formats\", () => {\n  expect(interval.toFormat(\"EEE, LLL dd, yyyy\", { separator: \" until \" })).toBe(\n    \"Tue, May 25, 1982 until Fri, Oct 14, 1983\"\n  );\n});\n\ntest(\"Interval#toFormat returns an unfriendly string for invalid intervals\", () => {\n  expect(invalid.toFormat(\"EEE, LLL dd, yyyy\")).toBe(\"Invalid Interval\");\n});\n"
  },
  {
    "path": "test/interval/getters.test.js",
    "content": "/* global test expect */\nimport { Interval } from \"../../src/luxon\";\n\nconst Helpers = require(\"../helpers\");\n\nconst todayFrom = (h1, h2) => Interval.fromDateTimes(Helpers.atHour(h1), Helpers.atHour(h2)),\n  invalid = Interval.invalid(\"because\");\n\ntest(\"Interval.start gets the start\", () => {\n  expect(todayFrom(3, 5).start.hour).toBe(3);\n});\n\ntest(\"Interval.start returns null for invalid intervals\", () => {\n  expect(invalid.start).toBe(null);\n});\n\ntest(\"Interval.end gets the end\", () => {\n  expect(todayFrom(3, 5).end.hour).toBe(5);\n});\n\ntest(\"Interval.end returns null for invalid intervals\", () => {\n  expect(invalid.end).toBe(null);\n});\n"
  },
  {
    "path": "test/interval/info.test.js",
    "content": "/* global test expect */\nimport { DateTime, Interval } from \"../../src/luxon\";\n\nconst Helpers = require(\"../helpers\");\n\nconst fromISOs = (s, e) => DateTime.fromISO(s).until(DateTime.fromISO(e));\n\n//------\n// #length()\n//-------\ntest(\"Interval#length defaults to milliseconds\", () => {\n  const n = DateTime.now(),\n    d = n.until(n.plus({ minutes: 1 }));\n  expect(d.length()).toBe(60 * 1000);\n});\n\ntest(\"Interval#length('days') returns 1 for yesterday\", () => {\n  expect(Helpers.atHour(13).minus({ days: 1 }).until(Helpers.atHour(13)).length(\"days\")).toBe(1);\n});\n\ntest(\"Interval#length('months') returns the right number of months\", () => {\n  expect(Math.floor(fromISOs(\"1996-02-17\", \"2012-08-14\").length(\"months\"))).toBe(197);\n});\n\ntest(\"Interval#length('years') returns the right number of years\", () => {\n  expect(Math.floor(fromISOs(\"1996-02-17\", \"2012-08-14\").length(\"years\"))).toBe(16);\n});\n\ntest(\"Interval#length() returns NaN for invalid intervals\", () => {\n  const i = Interval.invalid(\"because\");\n  expect(i.length(\"years\")).toBeFalsy();\n});\n\n//------\n// #count()\n//-------\ntest(\"Interval#count('days') returns 1 inside a day\", () => {\n  const i = DateTime.fromISO(\"2016-05-25T03:00\").until(DateTime.fromISO(\"2016-05-25T14:00\"));\n  expect(i.count(\"days\")).toBe(1);\n});\n\ntest(\"Interval#count('days') returns 2 if the interval crosses midnight\", () => {\n  const i = DateTime.fromISO(\"2016-05-25T03:00\").until(DateTime.fromISO(\"2016-05-26T14:00\"));\n  expect(i.count(\"days\")).toBe(2);\n});\n\ntest(\"Interval#count('years') returns 1 inside a year\", () => {\n  const i = DateTime.fromISO(\"2016-05-25\").until(DateTime.fromISO(\"2016-05-26\"));\n  expect(i.count(\"years\")).toBe(1);\n});\n\ntest(\"Interval#count('years') returns 2 if the interval crosses the new year\", () => {\n  const i = DateTime.fromISO(\"2016-05-25\").until(DateTime.fromISO(\"2017-05-26\"));\n  expect(i.count(\"years\")).toBe(2);\n});\n\ntest(\"Interval#count() does not count the endpoint of the interval\", () => {\n  const i = DateTime.fromISO(\"2022-10-01\").until(DateTime.fromISO(\"2022-10-03\"));\n  expect(i.count(\"days\")).toBe(2);\n});\n\ntest(\"Interval#count() uses milliseconds by default\", () => {\n  const i = DateTime.fromISO(\"2016-05-25T03:00\").until(DateTime.fromISO(\"2016-05-25T14:00\"));\n  expect(i.count()).toBe(39600000);\n});\n\ntest(\"Interval#count() returns NaN for invalid intervals\", () => {\n  const i = Interval.invalid(\"because\");\n  expect(i.count(\"years\")).toBeFalsy();\n});\n\n//------\n// #toDuration()\n//-------\ntest(\"Interval#toDuration creates a duration in those units\", () => {\n  const int = Interval.fromDateTimes(Helpers.atHour(9), Helpers.atHour(13));\n\n  expect(int.toDuration().toObject()).toEqual({ milliseconds: 4 * 3600 * 1000 });\n  expect(int.toDuration(\"milliseconds\").toObject()).toEqual({ milliseconds: 4 * 3600 * 1000 });\n  expect(int.toDuration(\"seconds\").toObject()).toEqual({ seconds: 4 * 3600 });\n  expect(int.toDuration(\"minutes\").toObject()).toEqual({ minutes: 4 * 60 });\n  expect(int.toDuration(\"hours\").toObject()).toEqual({ hours: 4 });\n  expect(int.toDuration(\"days\").toObject()).toEqual({ days: 1 / 6 });\n  expect(int.toDuration(\"weeks\").toObject()).toEqual({ weeks: 1 / (6 * 7) });\n});\n\ntest(\"Interval#toDuration accepts multiple units\", () => {\n  const int = Interval.fromDateTimes(\n    Helpers.atHour(9).plus({ minutes: 3 }),\n    Helpers.atHour(13).plus({ minutes: 47 })\n  );\n\n  expect(int.toDuration([\"hours\", \"minutes\"]).toObject()).toEqual({ hours: 4, minutes: 44 });\n});\n\ntest(\"Interval#toDuration accepts duration options\", () => {\n  const int = Interval.fromDateTimes(Helpers.atHour(9), Helpers.atHour(13)),\n    dur = int.toDuration([\"hours\"], { conversionAccuracy: \"longterm\" });\n  expect(dur.conversionAccuracy).toBe(\"longterm\");\n});\n\ntest(\"Interval#toDuration returns an invalid duration for invalid intervals\", () => {\n  const int = Interval.invalid(\"because\"),\n    dur = int.toDuration([\"hours\"]);\n  expect(dur.isValid).toBeFalsy();\n});\n\n//------\n// #contains()\n//-------\ntest(\"Interval#contains returns true for DateTimes in the interval\", () => {\n  const i = fromISOs(\"1982-05-25T06:00\", \"1982-05-25T07:00\");\n  expect(i.contains(DateTime.fromISO(\"1982-05-25T06:30\"))).toBe(true);\n});\n\ntest(\"Interval#contains returns false for DateTimes after the interval\", () => {\n  const i = fromISOs(\"1982-05-25T06:00\", \"1982-05-25T07:00\");\n  expect(i.contains(DateTime.fromISO(\"1982-05-25T08:30\"))).toBe(false);\n});\n\ntest(\"Interval#contains returns false for DateTimes before the interval\", () => {\n  const i = fromISOs(\"1982-05-25T06:00\", \"1982-05-25T07:00\");\n  expect(i.contains(DateTime.fromISO(\"1982-05-25T05:30\"))).toBe(false);\n});\n\ntest(\"Interval#contains returns true for the start endpoint\", () => {\n  const i = fromISOs(\"1982-05-25T06:00\", \"1982-05-25T07:00\");\n  expect(i.contains(DateTime.fromISO(\"1982-05-25T06:00\"))).toBe(true);\n});\n\ntest(\"Interval#contains returns false for the end endpoint\", () => {\n  const i = fromISOs(\"1982-05-25T06:00\", \"1982-05-25T07:00\");\n  expect(i.contains(DateTime.fromISO(\"1982-05-25T07:00\"))).toBe(false);\n});\n\ntest(\"Interval#contains returns false for invalid intervals\", () => {\n  const i = Interval.invalid(\"because\");\n  expect(i.contains(DateTime.fromISO(\"1982-05-25T07:00\"))).toBe(false);\n});\n\n//------\n// #isEmpty()\n//-------\ntest(\"Interval#isEmpty returns true for empty intervals\", () => {\n  const i = fromISOs(\"1982-05-25T06:00\", \"1982-05-25T06:00\");\n  expect(i.isEmpty()).toBe(true);\n});\n\ntest(\"Interval#isEmpty returns false for non-empty intervals\", () => {\n  const i = fromISOs(\"1982-05-25T06:00\", \"1982-05-25T08:00\");\n  expect(i.isEmpty()).toBe(false);\n});\n\n//------\n// #isBefore()\n//------\ntest(\"Interval#isBefore returns true for intervals fully before the input\", () => {\n  const n = DateTime.fromISO(\"1982-05-25T06:00\"),\n    i = Interval.fromDateTimes(n.minus({ days: 2 }), n.minus({ days: 1 }));\n  expect(i.isBefore(n)).toBe(true);\n});\n\ntest(\"Interval#isBefore returns false for intervals containing the input\", () => {\n  const n = DateTime.fromISO(\"1982-05-25T06:00\"),\n    i = Interval.fromDateTimes(n.minus({ days: 2 }), n.plus({ days: 2 }));\n  expect(i.isBefore(n)).toBe(false);\n});\n\ntest(\"Interval#isBefore returns false for intervals fully after the input \", () => {\n  const n = DateTime.fromISO(\"1982-05-25T06:00\"),\n    i = Interval.fromDateTimes(n.plus({ days: 2 }), n.plus({ days: 3 }));\n  expect(i.isBefore(n)).toBe(false);\n});\n\ntest(\"Interval#isBefore returns true for intervals ending at the input\", () => {\n  const n = DateTime.fromISO(\"1982-05-25T06:00\"),\n    i = Interval.fromDateTimes(n.minus({ days: 1 }), n);\n  expect(i.isBefore(n)).toBe(true);\n});\n\ntest(\"Interval#isBefore returns false for intervals just inside the input\", () => {\n  const n = DateTime.fromISO(\"1982-05-25T06:00\"),\n    i = Interval.fromDateTimes(n.minus({ days: 1 }), n);\n  expect(i.isBefore(n.minus(1))).toBe(false);\n});\n\ntest(\"Interval#isBefore returns false for invalid intervals\", () => {\n  const n = DateTime.fromISO(\"1982-05-25T06:00\"),\n    i = Interval.invalid(\"because\");\n  expect(i.isBefore(n)).toBe(false);\n});\n\n//------\n// #isAfter()\n//-------\ntest(\"Interval#isAfter returns true for intervals fully after the input\", () => {\n  const n = DateTime.fromISO(\"1982-05-25T06:00\"),\n    i = Interval.fromDateTimes(n.plus({ days: 1 }), n.plus({ days: 2 }));\n  expect(i.isAfter(n)).toBe(true);\n});\n\ntest(\"Interval#isAfter returns false for intervals containing the input\", () => {\n  const n = DateTime.fromISO(\"1982-05-25T06:00\"),\n    i = Interval.fromDateTimes(n.minus({ day: 2 }), n.plus({ days: 2 }));\n  expect(i.isAfter(n)).toBe(false);\n});\n\ntest(\"Interval#isAfter returns false for fully before the input \", () => {\n  const n = DateTime.fromISO(\"1982-05-25T06:00\"),\n    i = Interval.fromDateTimes(n.minus({ day: 2 }), n.minus(1, \"day\"));\n  expect(i.isAfter(n)).toBe(false);\n});\n\ntest(\"Interval#isAfter returns false for intervals beginning at the input\", () => {\n  const n = DateTime.fromISO(\"1982-05-25T06:00\"),\n    i = Interval.fromDateTimes(n, n.plus({ days: 1 }));\n  expect(i.isAfter(n)).toBe(false);\n});\n\ntest(\"Interval#isAfter returns false for invalid intervals\", () => {\n  const n = DateTime.fromISO(\"1982-05-25T06:00\"),\n    i = Interval.invalid(\"because\");\n  expect(i.isAfter(n)).toBe(false);\n});\n\n//------\n// #hasSame()\n//-------\ntest(\"Interval#hasSame('day') returns true for durations on the same day\", () => {\n  const n = DateTime.fromISO(\"1982-05-25T06:00\"),\n    i = Interval.fromDateTimes(n, n.plus({ hours: 5 }));\n  expect(i.hasSame(\"day\")).toBe(true);\n});\n\ntest(\"Interval#hasSame('day') returns true for durations that last until the next day\", () => {\n  const n = DateTime.fromISO(\"1982-05-25T06:00\"),\n    i = Interval.fromDateTimes(n, n.plus({ hours: 20 }));\n  expect(i.hasSame(\"day\")).toBe(false);\n});\n\ntest(\"Interval#hasSame('day') returns true for durations durations ending at midnight\", () => {\n  const n = DateTime.fromISO(\"1982-05-25T06:00\"),\n    i = Interval.fromDateTimes(n, n.plus({ days: 1 }).startOf(\"day\"));\n  expect(i.hasSame(\"day\")).toBe(true);\n});\n\ntest(\"Interval#hasSame returns false for invalid intervals\", () => {\n  const i = Interval.invalid(\"because\");\n  expect(i.hasSame(\"day\")).toBe(false);\n});\n\ntest.each([\n  [\"1982-05-25T00:00:00\"],\n  [\"1982-05-25T00:00:02\"],\n  [\"1982-05-25T12:00:00\"],\n  [\"1982-05-25T23:59:58\"],\n  [\"1982-05-25T23:59:59\"],\n])(\"Interval#hasSame returns true for empty intervals\", (dt) => {\n  const n = DateTime.fromISO(dt),\n    i = Interval.fromDateTimes(n, n);\n  expect(i.hasSame(\"day\")).toBe(true);\n});\n"
  },
  {
    "path": "test/interval/localeWeek.test.js",
    "content": "/* global test expect */\n\nimport { DateTime, Interval } from \"../../src/luxon\";\n\n//------\n// .count() with useLocaleWeeks\n//------\ntest(\"count(weeks) with useLocaleWeeks adheres to the locale\", () => {\n  const start = DateTime.fromISO(\"2023-06-04T13:00:00Z\", { setZone: true, locale: \"en-US\" });\n  const end = DateTime.fromISO(\"2023-06-23T13:00:00Z\", { setZone: true, locale: \"en-US\" });\n  const interval = Interval.fromDateTimes(start, end);\n\n  expect(interval.count(\"weeks\", { useLocaleWeeks: true })).toBe(3);\n});\n\ntest(\"count(weeks) with useLocaleWeeks uses the start locale\", () => {\n  const start = DateTime.fromISO(\"2023-06-04T13:00:00Z\", { setZone: true, locale: \"de-DE\" });\n  const end = DateTime.fromISO(\"2023-06-23T13:00:00Z\", { setZone: true, locale: \"en-US\" });\n  const interval = Interval.fromDateTimes(start, end);\n\n  expect(interval.count(\"weeks\", { useLocaleWeeks: true })).toBe(4);\n});\n"
  },
  {
    "path": "test/interval/many.test.js",
    "content": "/* global test expect */\nimport { DateTime, Interval, Duration } from \"../../src/luxon\";\n\nconst Helpers = require(\"../helpers\");\n\nconst fromISOs = (s, e) => DateTime.fromISO(s).until(DateTime.fromISO(e)),\n  todayFrom = (h1, h2) => Interval.fromDateTimes(Helpers.atHour(h1), Helpers.atHour(h2));\n\n//-------\n// #equals()\n//-------\ntest(\"Interval#equals returns true iff the times are the same\", () => {\n  const s = \"2016-10-14\",\n    e = \"2016-10-15\",\n    s2 = \"2016-10-13\",\n    e2 = \"2016-10-16\",\n    first = fromISOs(s, e);\n\n  expect(first.equals(fromISOs(s, e))).toBeTruthy();\n  expect(first.equals(fromISOs(s2, e))).toBeFalsy();\n  expect(first.equals(fromISOs(s, e2))).toBeFalsy();\n  expect(first.equals(fromISOs(s2, e2))).toBeFalsy();\n});\n\ntest(\"Interval#equals returns false for invalid intervals\", () => {\n  const invalid = Interval.invalid(\"blarg\"),\n    normal = fromISOs(\"2017-01-01\", \"2017-01-02\");\n\n  expect(invalid.equals(invalid)).toBeFalsy();\n  expect(normal.equals(invalid)).toBeFalsy();\n  expect(invalid.equals(normal)).toBeFalsy();\n  expect(normal.equals(normal)).toBeTruthy();\n});\n\n//-------\n// #union()\n//-------\ntest(\"Interval#union returns an interval spanning a later interval\", () => {\n  expect(todayFrom(5, 8).union(todayFrom(9, 11)).equals(todayFrom(5, 11))).toBeTruthy();\n});\n\ntest(\"Interval#union returns an interval spanning a earlier interval\", () => {\n  expect(todayFrom(5, 8).union(todayFrom(3, 4)).equals(todayFrom(3, 8))).toBeTruthy();\n});\n\ntest(\"Interval#union returns an interval spanning a partially later interval\", () => {\n  expect(todayFrom(5, 8).union(todayFrom(7, 10)).equals(todayFrom(5, 10))).toBeTruthy();\n});\n\ntest(\"Interval#union returns an interval spanning a partially earlier interval\", () => {\n  expect(todayFrom(5, 8).union(todayFrom(4, 6)).equals(todayFrom(4, 8))).toBeTruthy();\n});\n\ntest(\"Interval#union returns an interval no-ops when applied to an engulfed interval\", () => {\n  expect(todayFrom(5, 8).union(todayFrom(6, 7)).equals(todayFrom(5, 8))).toBeTruthy();\n});\n\ntest(\"Interval#union expands to an engulfing interval\", () => {\n  expect(todayFrom(5, 8).union(todayFrom(4, 10)).equals(todayFrom(4, 10))).toBeTruthy();\n});\n\ntest(\"Interval#union spans adjacent intervals\", () => {\n  expect(todayFrom(5, 8).union(todayFrom(8, 10)).equals(todayFrom(5, 10))).toBeTruthy();\n});\n\ntest(\"Interval#union returns invalid for invalid intervals\", () => {\n  expect(Interval.invalid(\"any reason\").union(todayFrom(8, 10)).isValid).toBeFalsy();\n});\n\n//-------\n// #intersection()\n//-------\n// todo - is this what should happen here? Seems annoying.\ntest(\"Interval#intersection returns null if there's no intersection\", () => {\n  expect(todayFrom(5, 8).intersection(todayFrom(3, 4))).toBe(null);\n});\n\ntest(\"Interval#intersection returns the intersection for overlapping intervals\", () => {\n  expect(todayFrom(5, 8).intersection(todayFrom(3, 7)).equals(todayFrom(5, 7))).toBeTruthy();\n});\n\ntest(\"Interval#intersection returns null for adjacent intervals\", () => {\n  expect(todayFrom(5, 8).intersection(todayFrom(8, 10))).toBeNull();\n});\n\ntest(\"Interval#intersection returns invalid for invalid intervals\", () => {\n  expect(Interval.invalid(\"any reason\").intersection(todayFrom(8, 10)).isValid).toBeFalsy();\n});\n\n//-------\n// .merge()\n//-------\ntest(\"Interval.merge returns the minimal set of intervals\", () => {\n  const list = [\n      todayFrom(5, 8),\n      todayFrom(4, 7),\n      todayFrom(10, 11),\n      todayFrom(11, 12),\n      todayFrom(13, 15),\n    ],\n    results = Interval.merge(list);\n\n  expect(results.length).toBe(3);\n  expect(results[0] && results[0].equals(todayFrom(4, 8))).toBeTruthy();\n  expect(results[1] && results[1].equals(todayFrom(10, 12))).toBeTruthy();\n  expect(results[2] && results[2].equals(todayFrom(13, 15))).toBeTruthy();\n});\n\ntest(\"Interval.merge returns empty for an empty input\", () => {\n  expect(Interval.merge([])).toEqual([]);\n});\n\n//-------\n// .xor()\n//-------\nfunction xor(items, expected) {\n  const r = Interval.xor(items);\n  expect(r.length).toBe(expected.length);\n  for (const i in expected) {\n    if (Object.prototype.hasOwnProperty.call(expected, i)) {\n      expect(r[i] && r[i].equals(expected[i])).toBeTruthy();\n    }\n  }\n  return r;\n}\n\ntest(\"Interval.xor returns non-overlapping intervals as-is\", () => {\n  const ix = [todayFrom(6, 7), todayFrom(8, 9)];\n  xor(ix, ix);\n});\n\ntest(\"Interval.xor returns empty for an empty input\", () => {\n  xor([], []);\n});\n\ntest(\"Interval.xor returns empty for a fully overlapping set of intervals\", () => {\n  xor([todayFrom(5, 8), todayFrom(5, 8)], []);\n  xor([todayFrom(5, 8), todayFrom(5, 6), todayFrom(6, 8)], []);\n});\n\ntest(\"Interval.xor returns the non-overlapping parts of intervals\", () => {\n  // overlapping\n  xor([todayFrom(5, 8), todayFrom(7, 11)], [todayFrom(5, 7), todayFrom(8, 11)]);\n\n  // engulfing\n  xor([todayFrom(5, 12), todayFrom(9, 10)], [todayFrom(5, 9), todayFrom(10, 12)]);\n\n  // adjacent\n  xor([todayFrom(5, 6), todayFrom(6, 8)], [todayFrom(5, 8)]);\n\n  // three intervals\n  xor(\n    [todayFrom(10, 13), todayFrom(8, 11), todayFrom(12, 14)],\n    [todayFrom(8, 10), todayFrom(11, 12), todayFrom(13, 14)]\n  );\n});\n\ntest(\"Interval.xor handles funny adjacency cases\", () => {\n  xor(\n    [todayFrom(5, 14), todayFrom(7, 11), todayFrom(11, 12)],\n    [todayFrom(5, 7), todayFrom(12, 14)]\n  );\n\n  xor([todayFrom(5, 10), todayFrom(9, 11), todayFrom(9, 12)], [todayFrom(5, 9), todayFrom(11, 12)]);\n\n  xor([todayFrom(5, 9), todayFrom(9, 11), todayFrom(9, 12), todayFrom(5, 9)], [todayFrom(11, 12)]);\n});\n\n//-------\n// #difference()\n//-------\nfunction diff(interval, items, expected) {\n  const r = interval.difference(...items);\n  expect(r.length).toBe(expected.length);\n  for (const i in expected) {\n    if (Object.prototype.hasOwnProperty.call(expected, i)) {\n      expect(r[i] && r[i].equals(expected[i])).toBeTruthy();\n    }\n  }\n  return r;\n}\n\ntest(\"Interval#difference returns self for non-overlapping intervals\", () => {\n  diff(todayFrom(8, 9), [todayFrom, (10, 11)], [todayFrom(8, 9)]);\n  diff(todayFrom(8, 9), [todayFrom, (6, 7)], [todayFrom(8, 9)]);\n});\n\ntest(\"Interval#difference returns the non-overlapping parts of intervals\", () => {\n  diff(todayFrom(8, 10), [todayFrom(9, 11)], [todayFrom(8, 9)]);\n  diff(todayFrom(9, 11), [todayFrom(8, 10)], [todayFrom(10, 11)]);\n  diff(todayFrom(9, 11), [todayFrom(8, 9), todayFrom(9, 10)], [todayFrom(10, 11)]);\n});\n\ntest(\"Interval#difference returns the empty for fully subtracted intervals\", () => {\n  diff(todayFrom(8, 10), [todayFrom(7, 11)], []);\n  diff(todayFrom(8, 10), [todayFrom(8, 9), todayFrom(9, 10)], []);\n  diff(todayFrom(8, 12), [todayFrom(8, 10), todayFrom(9, 11), todayFrom(10, 13)], []);\n});\n\ntest(\"Interval#difference returns the outside parts when engulfing another interval\", () => {\n  diff(todayFrom(8, 12), [todayFrom(9, 11)], [todayFrom(8, 9), todayFrom(11, 12)]);\n  diff(\n    todayFrom(8, 12),\n    [todayFrom(9, 10), todayFrom(10, 11)],\n    [todayFrom(8, 9), todayFrom(11, 12)]\n  );\n});\n\ntest(\"Interval#difference allows holes\", () => {\n  diff(\n    todayFrom(8, 13),\n    [todayFrom(9, 10), todayFrom(11, 12)],\n    [todayFrom(8, 9), todayFrom(10, 11), todayFrom(12, 13)]\n  );\n});\n\n//-------\n// #engulfs()\n//-------\ntest(\"Interval#engulfs\", () => {\n  const i = todayFrom(9, 12);\n\n  expect(i.engulfs(todayFrom(13, 15), \"wholly later\")).toBeFalsy();\n  expect(i.engulfs(todayFrom(11, 15), \"partially later\")).toBeFalsy();\n  expect(i.engulfs(todayFrom(6, 8), \"wholly earlier\")).toBeFalsy();\n  expect(i.engulfs(todayFrom(6, 10), \"partially earlier\")).toBeFalsy();\n  expect(i.engulfs(todayFrom(8, 13), \"engulfed\")).toBeFalsy();\n\n  expect(i.engulfs(todayFrom(10, 11), \"engulfing\")).toBeTruthy();\n  expect(i.engulfs(todayFrom(9, 12), \"equal\")).toBeTruthy();\n});\n\ntest(\"Interval#engulfs returns false for invalid intervals\", () => {\n  expect(Interval.invalid(\"because\").engulfs(todayFrom(9, 12))).toBe(false);\n  expect(todayFrom(9, 12).engulfs(Interval.invalid(\"because\"))).toBe(false);\n});\n\n//-------\n// #abutsStart()\n//-------\ntest(\"Interval#abutsStart\", () => {\n  expect(todayFrom(9, 10).abutsStart(todayFrom(10, 11))).toBeTruthy();\n  expect(todayFrom(9, 10).abutsStart(todayFrom(11, 12))).toBeFalsy();\n  expect(todayFrom(9, 10).abutsStart(todayFrom(8, 11))).toBeFalsy();\n  expect(todayFrom(9, 10).abutsStart(todayFrom(9, 10))).toBeFalsy();\n});\n\ntest(\"Interval#abutsStart returns false for invalid intervals\", () => {\n  expect(Interval.invalid(\"because\").abutsStart(todayFrom(9, 12))).toBe(false);\n  expect(todayFrom(9, 12).abutsStart(Interval.invalid(\"because\"))).toBe(false);\n});\n\n//-------\n// #abutsEnd()\n//-------\ntest(\"Interval#abutsEnd\", () => {\n  expect(todayFrom(9, 11).abutsEnd(todayFrom(8, 9))).toBeTruthy();\n  expect(todayFrom(9, 11).abutsEnd(todayFrom(8, 10))).toBeFalsy();\n  expect(todayFrom(9, 11).abutsEnd(todayFrom(7, 8))).toBeFalsy();\n  expect(todayFrom(9, 11).abutsEnd(todayFrom(9, 11))).toBeFalsy();\n});\n\ntest(\"Interval#abutsEnd returns false for invalid intervals\", () => {\n  expect(Interval.invalid(\"because\").abutsEnd(todayFrom(9, 12))).toBe(false);\n  expect(todayFrom(9, 12).abutsEnd(Interval.invalid(\"because\"))).toBe(false);\n});\n\n//-------\n// #splitAt()\n//-------\ntest(\"Interval#splitAt breaks up the interval\", () => {\n  const split = todayFrom(8, 13).splitAt(Helpers.atHour(9), Helpers.atHour(11));\n  expect(split.length).toBe(3);\n  expect(split[0].equals(todayFrom(8, 9))).toBeTruthy();\n  expect(split[1].equals(todayFrom(9, 11))).toBeTruthy();\n  expect(split[2].equals(todayFrom(11, 13))).toBeTruthy();\n});\n\ntest(\"Interval#splitAt returns [] for invalid intervals\", () => {\n  const split = Interval.invalid(\"because\").splitAt(Helpers.atHour(9), Helpers.atHour(11));\n  expect(split).toEqual([]);\n});\n\ntest(\"Interval#splitAt ignores times outside the interval\", () => {\n  const allBefore = todayFrom(8, 13).splitAt(Helpers.atHour(7));\n  expect(allBefore.length).toBe(1);\n  expect(allBefore[0]).toEqual(todayFrom(8, 13));\n\n  const allAfter = todayFrom(8, 13).splitAt(Helpers.atHour(14));\n  expect(allAfter.length).toBe(1);\n  expect(allAfter[0]).toEqual(todayFrom(8, 13));\n\n  const oneBeforeOneDuring = todayFrom(8, 13).splitAt(Helpers.atHour(7), Helpers.atHour(11));\n  expect(oneBeforeOneDuring.length).toBe(2);\n  expect(oneBeforeOneDuring[0]).toEqual(todayFrom(8, 11));\n  expect(oneBeforeOneDuring[1]).toEqual(todayFrom(11, 13));\n\n  const oneAfterOneDuring = todayFrom(8, 13).splitAt(Helpers.atHour(11), Helpers.atHour(15));\n  expect(oneAfterOneDuring.length).toBe(2);\n  expect(oneAfterOneDuring[0]).toEqual(todayFrom(8, 11));\n  expect(oneAfterOneDuring[1]).toEqual(todayFrom(11, 13));\n});\n\ntest(\"Interval#splitAt handles DST shifts\", () => {\n  const zone = \"Europe/Berlin\";\n  const dayStart = DateTime.fromISO(\"2023-10-29T00:00:00+02:00\", { zone });\n  const dayEnd = DateTime.fromISO(\"2023-10-30T00:00:00+01:00\", { zone });\n  const dstShiftStart = DateTime.fromISO(\"2023-10-29T02:00:00+02:00\", { zone });\n  const dstShiftEnd = DateTime.fromISO(\"2023-10-29T02:00:00+01:00\", { zone });\n\n  const splitByDSTStartAndEnd = Interval.fromDateTimes(dayStart, dayEnd)\n    .splitAt(dstShiftStart, dstShiftEnd)\n    .map((i) => i.toISO());\n\n  expect(splitByDSTStartAndEnd).toEqual([\n    \"2023-10-29T00:00:00.000+02:00/2023-10-29T02:00:00.000+02:00\",\n    \"2023-10-29T02:00:00.000+02:00/2023-10-29T02:00:00.000+01:00\",\n    \"2023-10-29T02:00:00.000+01:00/2023-10-30T00:00:00.000+01:00\",\n  ]);\n});\n\n//-------\n// #splitBy()\n//-------\ntest(\"Interval#splitBy accepts an object\", () => {\n  const split = todayFrom(8, 13).splitBy({ hours: 2 });\n  expect(split.length).toBe(3);\n  expect(split[0].equals(todayFrom(8, 10))).toBeTruthy();\n  expect(split[1].equals(todayFrom(10, 12))).toBeTruthy();\n  expect(split[2].equals(todayFrom(12, 13))).toBeTruthy();\n});\n\ntest(\"Interval#splitBy accepts a duration\", () => {\n  const split = todayFrom(8, 13).splitBy(Duration.fromObject({ hours: 2 }));\n  expect(split.length).toBe(3);\n  expect(split[0].equals(todayFrom(8, 10))).toBeTruthy();\n  expect(split[1].equals(todayFrom(10, 12))).toBeTruthy();\n  expect(split[2].equals(todayFrom(12, 13))).toBeTruthy();\n});\n\ntest(\"Interval#splitBy returns [] for invalid intervals\", () => {\n  const split = Interval.invalid(\"because\").splitBy({ hours: 2 });\n  expect(split).toEqual([]);\n});\n\ntest(\"Interval#split by returns [] for invalid durations\", () => {\n  const split = todayFrom(8, 3).splitBy(Duration.invalid(\"because\"));\n  expect(split).toEqual([]);\n});\n\ntest(\"Interval#split by returns [] for durations of length 0\", () => {\n  const split = todayFrom(8, 3).splitBy(Duration.fromObject({}));\n  expect(split).toEqual([]);\n});\n\ntest(\"Interval#split by works across varying length months\", () => {\n  Helpers.withDefaultZone(\"Europe/London\", () => {\n    const start = DateTime.fromISO(\"2019-12-30T00:00:00.000+00:00\");\n    const end = DateTime.fromISO(\"2020-05-02T23:30:00.000+00:00\");\n    const interval = Interval.fromDateTimes(start, end);\n\n    const months = interval.splitBy(Duration.fromISO(\"P1M\"));\n    expect(months.length).toEqual(5);\n\n    for (let i = 0; i < months.length; i++) {\n      const month = months[i];\n      const expectedStart = start.plus({ month: i });\n      const expectedEnd = start.plus({ month: i + 1 });\n\n      expect(month.start).toEqual(expectedStart);\n\n      if (expectedEnd > end) {\n        expect(month.end).toEqual(end);\n      } else {\n        expect(month.end).toEqual(expectedEnd);\n      }\n    }\n  });\n});\n\n//-------\n// #divideEqually()\n//-------\ntest(\"Interval#divideEqually should split a 4 hour period into 4 contiguous 1-hour parts\", () => {\n  const split = todayFrom(5, 9).divideEqually(4);\n  expect(split.length).toBe(4);\n  expect(split[0].equals(todayFrom(5, 6))).toBeTruthy();\n  expect(split[3].equals(todayFrom(8, 9))).toBeTruthy();\n});\n\ntest(\"Interval#divideEqually should split a 1m30s into 3 30-second parts\", () => {\n  const after = (i, m, s) => Interval.after(i, Duration.fromObject({ minutes: m, seconds: s })),\n    split = after(Helpers.atHour(9), 1, 30).divideEqually(3);\n  expect(split.length).toBe(3);\n  expect(split[0].equals(after(Helpers.atHour(9), 0, 30))).toBeTruthy();\n  expect(split[2].equals(after(Helpers.atHour(9).plus({ minutes: 1 }), 0, 30))).toBeTruthy();\n});\n\ntest(\"Interval#divideEqually always gives you the right number of parts\", () => {\n  const int = Interval.after(Helpers.atHour(9), { minutes: 7 }),\n    split = int.divideEqually(17);\n  expect(split.length).toBe(17);\n});\n\ntest(\"Interval#divideEqually returns [] for invalid intervals\", () => {\n  const split = Interval.invalid(\"because\").divideEqually(3);\n  expect(split).toEqual([]);\n});\n\ntest(\"Interval#mapEndpoints returns a new Interval with the mapped endpoints\", () => {\n  const zone = \"America/Los_Angeles\";\n  const original = Interval.fromDateTimes(\n    DateTime.fromObject({}, { zone }),\n    DateTime.fromObject({}, { zone }).plus({ hours: 1 })\n  );\n\n  const mapped = original.mapEndpoints((d) => d.toUTC());\n\n  expect(mapped.s.zoneName).toEqual(\"UTC\");\n  expect(mapped.e.zoneName).toEqual(\"UTC\");\n});\n"
  },
  {
    "path": "test/interval/parse.test.js",
    "content": "/* global test expect */\nimport { Interval } from \"../../src/luxon\";\nimport Helpers from \"../helpers\";\n\nconst withThrowOnInvalid = Helpers.setUnset(\"throwOnInvalid\");\n\n//------\n// .fromISO()\n//------\n\ntest(\"Interval.fromISO can parse a variety of ISO formats\", () => {\n  const check = (s, ob1, ob2) => {\n    const i = Interval.fromISO(s);\n    expect(i.start.toObject()).toEqual(ob1);\n    expect(i.end.toObject()).toEqual(ob2);\n  };\n\n  // keeping these brief because I don't want to rehash the existing DT ISO tests\n\n  check(\n    \"2007-03-01T13:00:00/2008-05-11T15:30:00\",\n    {\n      year: 2007,\n      month: 3,\n      day: 1,\n      hour: 13,\n      minute: 0,\n      second: 0,\n      millisecond: 0,\n    },\n    {\n      year: 2008,\n      month: 5,\n      day: 11,\n      hour: 15,\n      minute: 30,\n      second: 0,\n      millisecond: 0,\n    }\n  );\n\n  check(\n    \"2007-03-01T13:00:00/2016-W21-3\",\n    {\n      year: 2007,\n      month: 3,\n      day: 1,\n      hour: 13,\n      minute: 0,\n      second: 0,\n      millisecond: 0,\n    },\n    {\n      year: 2016,\n      month: 5,\n      day: 25,\n      hour: 0,\n      minute: 0,\n      second: 0,\n      millisecond: 0,\n    }\n  );\n\n  check(\n    \"2007-03-01T13:00:00/P1Y2M10DT2H30M\",\n    {\n      year: 2007,\n      month: 3,\n      day: 1,\n      hour: 13,\n      minute: 0,\n      second: 0,\n      millisecond: 0,\n    },\n    {\n      year: 2008,\n      month: 5,\n      day: 11,\n      hour: 15,\n      minute: 30,\n      second: 0,\n      millisecond: 0,\n    }\n  );\n\n  check(\n    \"P1Y2M10DT2H30M/2008-05-11T15:30:00\",\n    {\n      year: 2007,\n      month: 3,\n      day: 1,\n      hour: 13,\n      minute: 0,\n      second: 0,\n      millisecond: 0,\n    },\n    {\n      year: 2008,\n      month: 5,\n      day: 11,\n      hour: 15,\n      minute: 30,\n      second: 0,\n      millisecond: 0,\n    }\n  );\n});\n\ntest(\"Interval.fromISO accepts a zone argument\", () => {\n  const dateDate = Interval.fromISO(\"2016-01-01/2016-12-31\", { zone: \"Europe/Paris\" });\n  expect(dateDate.isValid).toBe(true);\n  expect(dateDate.start.zoneName).toBe(\"Europe/Paris\");\n\n  const dateDur = Interval.fromISO(\"2016-01-01/P1Y\", { zone: \"Europe/Paris\" });\n  expect(dateDur.isValid).toBe(true);\n  expect(dateDur.start.zoneName).toBe(\"Europe/Paris\");\n\n  const durDate = Interval.fromISO(\"P1Y/2016-01-01\", { zone: \"Europe/Paris\" });\n  expect(durDate.isValid).toBe(true);\n  expect(durDate.start.zoneName).toBe(\"Europe/Paris\");\n});\n\n// #728\ntest(\"Interval.fromISO works with Settings.throwOnInvalid\", () => {\n  withThrowOnInvalid(true, () => {\n    const dateDur = Interval.fromISO(\"2020-06-22T17:30:00.000+02:00/PT5H30M\");\n    expect(dateDur.isValid).toBe(true);\n\n    const durDate = Interval.fromISO(\"PT5H30M/2020-06-22T17:30:00.000+02:00\");\n    expect(durDate.isValid).toBe(true);\n  });\n});\n\nconst badInputs = [\n  null,\n  \"\",\n  \"hello\",\n  \"foo/bar\",\n  \"R5/2008-03-01T13:00:00Z/P1Y2M10DT2H30M\", // valid ISO 8601 interval with a repeat, but not supported here\n];\n\ntest.each(badInputs)(\"Interval.fromISO will return invalid for [%s]\", (s) => {\n  const i = Interval.fromISO(s);\n  expect(i.isValid).toBe(false);\n  expect(i.invalidReason).toBe(\"unparsable\");\n});\n\ndescribe(\"Interval.fromISO defaults missing values in end to start\", () => {\n  test(\"Gregorian, end just time\", () => {\n    const i = Interval.fromISO(\"1988-04-15T09/15:30\");\n    expect(i.start.toISO()).toBe(\"1988-04-15T09:00:00.000-04:00\");\n    expect(i.end.toISO()).toBe(\"1988-04-15T15:30:00.000-04:00\");\n  });\n  test(\"Gregorian, end just time and zone\", () => {\n    const i = Interval.fromISO(\"1988-04-15T09/15:30-07:00\");\n    expect(i.start.toISO()).toBe(\"1988-04-15T09:00:00.000-04:00\");\n    expect(i.end.toISO()).toBe(\"1988-04-15T18:30:00.000-04:00\");\n  });\n  test(\"Gregorian, end just day\", () => {\n    const i = Interval.fromISO(\"1988-04-15T09/17\");\n    expect(i.start.toISO()).toBe(\"1988-04-15T09:00:00.000-04:00\");\n    expect(i.end.toISO()).toBe(\"1988-04-17T00:00:00.000-04:00\");\n  });\n  test(\"Gregorian, end day and time\", () => {\n    const i = Interval.fromISO(\"1988-04-15T09/17T15:30\");\n    expect(i.start.toISO()).toBe(\"1988-04-15T09:00:00.000-04:00\");\n    expect(i.end.toISO()).toBe(\"1988-04-17T15:30:00.000-04:00\");\n  });\n  test(\"Gregorian, end month and day\", () => {\n    const i = Interval.fromISO(\"1988-04-15T09/05-17\");\n    expect(i.start.toISO()).toBe(\"1988-04-15T09:00:00.000-04:00\");\n    expect(i.end.toISO()).toBe(\"1988-05-17T00:00:00.000-04:00\");\n  });\n  test(\"Gregorian, end month, day and time\", () => {\n    const i = Interval.fromISO(\"1988-04-15T09/05-17T15:30\");\n    expect(i.start.toISO()).toBe(\"1988-04-15T09:00:00.000-04:00\");\n    expect(i.end.toISO()).toBe(\"1988-05-17T15:30:00.000-04:00\");\n  });\n  test(\"Gregorian with zone in options and partial date\", () => {\n    const i = Interval.fromISO(\"1988-04-15T09/19\", { zone: \"UTC-06:00\" });\n    expect(i.start.toISO()).toBe(\"1988-04-15T09:00:00.000-06:00\");\n    expect(i.end.toISO()).toBe(\"1988-04-19T00:00:00.000-06:00\");\n  });\n  test(\"Gregorian with zone in options and partial date and time\", () => {\n    const i = Interval.fromISO(\"1988-04-15T09/19T13:00\", { zone: \"UTC-06:00\" });\n    expect(i.start.toISO()).toBe(\"1988-04-15T09:00:00.000-06:00\");\n    expect(i.end.toISO()).toBe(\"1988-04-19T13:00:00.000-06:00\");\n  });\n  test(\"Gregorian with zone in options and full date and time\", () => {\n    const i = Interval.fromISO(\"1988-04-15T09/1989-03-01T13:00\", { zone: \"UTC-06:00\" });\n    expect(i.start.toISO()).toBe(\"1988-04-15T09:00:00.000-06:00\");\n    expect(i.end.toISO()).toBe(\"1989-03-01T13:00:00.000-06:00\");\n  });\n  test(\"Gregorian with zone in options and end zone\", () => {\n    const i = Interval.fromISO(\"1988-04-15T09/16T15:00-07:00\", { zone: \"UTC-06:00\" });\n    expect(i.start.toISO()).toBe(\"1988-04-15T09:00:00.000-06:00\");\n    expect(i.end.toISO()).toBe(\"1988-04-16T16:00:00.000-06:00\");\n  });\n  test(\"Gregorian with zone in options, setZone and end zone\", () => {\n    const i = Interval.fromISO(\"1988-04-15T09/16T15:00-07:00\", {\n      zone: \"UTC-06:00\",\n      setZone: true,\n    });\n    expect(i.start.toISO()).toBe(\"1988-04-15T09:00:00.000-06:00\");\n    expect(i.end.toISO()).toBe(\"1988-04-16T15:00:00.000-07:00\");\n  });\n  test(\"Gregorian with start zone\", () => {\n    const i = Interval.fromISO(\"1988-04-15T09:00:00+01:00/17T15:30\");\n    expect(i.start.toISO()).toBe(\"1988-04-15T04:00:00.000-04:00\");\n    expect(i.end.toISO()).toBe(\"1988-04-17T10:30:00.000-04:00\");\n  });\n  test(\"Gregorian with start zone and zone in options\", () => {\n    const i = Interval.fromISO(\"1988-04-15T09:00:00+01:00/15T15:00\", { zone: \"UTC-06:00\" });\n    expect(i.start.toISO()).toBe(\"1988-04-15T02:00:00.000-06:00\");\n    expect(i.end.toISO()).toBe(\"1988-04-15T08:00:00.000-06:00\");\n  });\n  test(\"Gregorian with start zone and setZone\", () => {\n    const i = Interval.fromISO(\"1988-04-15T09:00:00+01:00/15T15:00\", { setZone: true });\n    expect(i.start.toISO()).toBe(\"1988-04-15T09:00:00.000+01:00\");\n    expect(i.end.toISO()).toBe(\"1988-04-15T15:00:00.000+01:00\");\n  });\n  test(\"Gregorian with two zones\", () => {\n    const i = Interval.fromISO(\"1988-04-15T09:00:00+01:00/15T16:00+02:00\");\n    expect(i.start.toISO()).toBe(\"1988-04-15T04:00:00.000-04:00\");\n    expect(i.end.toISO()).toBe(\"1988-04-15T10:00:00.000-04:00\");\n  });\n  test(\"Gregorian with two zones and setZone\", () => {\n    const i = Interval.fromISO(\"1988-04-15T09:00:00+01:00/15T16:00+02:00\", { setZone: true });\n    expect(i.start.toISO()).toBe(\"1988-04-15T09:00:00.000+01:00\");\n    expect(i.end.toISO()).toBe(\"1988-04-15T16:00:00.000+02:00\");\n  });\n\n  // Week dates\n  test(\"Week, end just time\", () => {\n    const i = Interval.fromISO(\"2025-W20-1T09/15:30\");\n    expect(i.start.toISO()).toBe(\"2025-05-12T09:00:00.000-04:00\");\n    expect(i.end.toISO()).toBe(\"2025-05-12T15:30:00.000-04:00\");\n  });\n  test(\"Week, end just time and zone\", () => {\n    const i = Interval.fromISO(\"2025-W20-1T09/15:30-07:00\");\n    expect(i.start.toISO()).toBe(\"2025-05-12T09:00:00.000-04:00\");\n    expect(i.end.toISO()).toBe(\"2025-05-12T18:30:00.000-04:00\");\n  });\n  test(\"Week, end week day\", () => {\n    const i = Interval.fromISO(\"2025-W20-1T09/3T15:30\");\n    expect(i.start.toISO()).toBe(\"2025-05-12T09:00:00.000-04:00\");\n    expect(i.end.toISO()).toBe(\"2025-05-14T15:30:00.000-04:00\");\n  });\n  test(\"Week, end week number and week day\", () => {\n    const i = Interval.fromISO(\"2025-W20-1T09/W21-3T15:30\");\n    expect(i.start.toISO()).toBe(\"2025-05-12T09:00:00.000-04:00\");\n    expect(i.end.toISO()).toBe(\"2025-05-21T15:30:00.000-04:00\");\n  });\n\n  // Ordinal dates\n  test(\"Ordinal, end just time\", () => {\n    const i = Interval.fromISO(\"2025-132T09/15:30\");\n    expect(i.start.toISO()).toBe(\"2025-05-12T09:00:00.000-04:00\");\n    expect(i.end.toISO()).toBe(\"2025-05-12T15:30:00.000-04:00\");\n  });\n  test(\"Ordinal, end just time and zone\", () => {\n    const i = Interval.fromISO(\"2025-132T09/15:30-07:00\");\n    expect(i.start.toISO()).toBe(\"2025-05-12T09:00:00.000-04:00\");\n    expect(i.end.toISO()).toBe(\"2025-05-12T18:30:00.000-04:00\");\n  });\n  test(\"Ordinal, end with ordinal\", () => {\n    const i = Interval.fromISO(\"2025-132T09/135T15:30\");\n    expect(i.start.toISO()).toBe(\"2025-05-12T09:00:00.000-04:00\");\n    expect(i.end.toISO()).toBe(\"2025-05-15T15:30:00.000-04:00\");\n  });\n\n  // Mixed\n  test(\"Gregorian, end just weekday\", () => {\n    const i = Interval.fromISO(\"2025-05-12T09/4T15:30\");\n    expect(i.start.toISO()).toBe(\"2025-05-12T09:00:00.000-04:00\");\n    expect(i.end.toISO()).toBe(\"2025-05-15T15:30:00.000-04:00\");\n  });\n  test(\"Gregorian, end weekNumber and weekday\", () => {\n    const i = Interval.fromISO(\"2025-05-12T09/W21-1T15:30\");\n    expect(i.start.toISO()).toBe(\"2025-05-12T09:00:00.000-04:00\");\n    expect(i.end.toISO()).toBe(\"2025-05-19T15:30:00.000-04:00\");\n  });\n  test(\"Gregorian, end just ordinal\", () => {\n    const i = Interval.fromISO(\"2025-05-12T09/135T15:30\");\n    expect(i.start.toISO()).toBe(\"2025-05-12T09:00:00.000-04:00\");\n    expect(i.end.toISO()).toBe(\"2025-05-15T15:30:00.000-04:00\");\n  });\n\n  test(\"Week date, end just day\", () => {\n    const i = Interval.fromISO(\"2025-W20-1T09/15T15:30\");\n    expect(i.start.toISO()).toBe(\"2025-05-12T09:00:00.000-04:00\");\n    expect(i.end.toISO()).toBe(\"2025-05-15T15:30:00.000-04:00\");\n  });\n  test(\"Week date, end month and day\", () => {\n    const i = Interval.fromISO(\"2025-W20-1T09/06-15T15:30\");\n    expect(i.start.toISO()).toBe(\"2025-05-12T09:00:00.000-04:00\");\n    expect(i.end.toISO()).toBe(\"2025-06-15T15:30:00.000-04:00\");\n  });\n  test(\"Week date, end just ordinal\", () => {\n    const i = Interval.fromISO(\"2025-W20-1T09/135T15:30\");\n    expect(i.start.toISO()).toBe(\"2025-05-12T09:00:00.000-04:00\");\n    expect(i.end.toISO()).toBe(\"2025-05-15T15:30:00.000-04:00\");\n  });\n\n  test(\"Ordinal, end just day\", () => {\n    const i = Interval.fromISO(\"2025-132T09/15T15:30\");\n    expect(i.start.toISO()).toBe(\"2025-05-12T09:00:00.000-04:00\");\n    expect(i.end.toISO()).toBe(\"2025-05-15T15:30:00.000-04:00\");\n  });\n  test(\"Ordinal, end month and day\", () => {\n    const i = Interval.fromISO(\"2025-132T09/06-15T15:30\");\n    expect(i.start.toISO()).toBe(\"2025-05-12T09:00:00.000-04:00\");\n    expect(i.end.toISO()).toBe(\"2025-06-15T15:30:00.000-04:00\");\n  });\n  test(\"Ordinal, end just weekday\", () => {\n    const i = Interval.fromISO(\"2025-132T09/4T15:30\");\n    expect(i.start.toISO()).toBe(\"2025-05-12T09:00:00.000-04:00\");\n    expect(i.end.toISO()).toBe(\"2025-05-15T15:30:00.000-04:00\");\n  });\n  test(\"Ordinal, end weekNumber and weekday\", () => {\n    const i = Interval.fromISO(\"2025-132T09/W21-1T15:30\");\n    expect(i.start.toISO()).toBe(\"2025-05-12T09:00:00.000-04:00\");\n    expect(i.end.toISO()).toBe(\"2025-05-19T15:30:00.000-04:00\");\n  });\n});\n"
  },
  {
    "path": "test/interval/proto.test.js",
    "content": "/* global test expect */\nimport { DateTime } from \"../../src/luxon\";\n\ntest(\"Interval prototype properties should not throw when addressed\", () => {\n  const i = DateTime.fromISO(\"2018-01-01\").until(DateTime.fromISO(\"2018-01-02\"));\n  expect(() =>\n    Object.getOwnPropertyNames(Object.getPrototypeOf(i)).forEach(\n      (name) => Object.getPrototypeOf(i)[name]\n    )\n  ).not.toThrow();\n});\n"
  },
  {
    "path": "test/interval/setter.test.js",
    "content": "/* global test expect */\nimport { Interval } from \"../../src/luxon\";\n\nconst Helpers = require(\"../helpers\");\n\nconst todayFrom = (h1, h2) => Interval.fromDateTimes(Helpers.atHour(h1), Helpers.atHour(h2));\n\ntest(\"Interval.set can set the start\", () => {\n  expect(todayFrom(3, 5).set({ start: Helpers.atHour(4) }).start.hour).toBe(4);\n});\n\ntest(\"Interval.set can set the end\", () => {\n  expect(todayFrom(3, 5).set({ end: Helpers.atHour(6) }).end.hour).toBe(6);\n});\n\ntest(\"Interval.set preserves invalidity\", () => {\n  const invalid = Interval.invalid(\"because\");\n  expect(invalid.set({ start: Helpers.atHour(4) }).isValid).toBe(false);\n});\n"
  },
  {
    "path": "test/interval/typecheck.test.js",
    "content": "/* global test expect */\n\nimport { Interval, DateTime } from \"../../src/luxon\";\n\n//------\n// #isInterval\n//-------\ntest(\"Interval.isInterval return true for valid duration\", () => {\n  const int = Interval.fromDateTimes(DateTime.now(), DateTime.now());\n  expect(Interval.isInterval(int)).toBe(true);\n});\n\ntest(\"Interval.isInterval return true for invalid duration\", () => {\n  const int = Interval.invalid(\"because\");\n  expect(Interval.isInterval(int)).toBe(true);\n});\n\ntest(\"Interval.isInterval return false for primitives\", () => {\n  expect(Interval.isInterval({})).toBe(false);\n  expect(Interval.isInterval(1)).toBe(false);\n  expect(Interval.isInterval(\"\")).toBe(false);\n  expect(Interval.isInterval(null)).toBe(false);\n  expect(Interval.isInterval()).toBe(false);\n});\n"
  },
  {
    "path": "test/zones/IANA.test.js",
    "content": "/* global test expect */\nimport { FixedOffsetZone, IANAZone } from \"../../src/luxon\";\n\ntest(\"IANAZone.create returns a singleton per zone name\", () => {\n  expect(IANAZone.create(\"UTC\")).toBe(IANAZone.create(\"UTC\"));\n  expect(IANAZone.create(\"America/New_York\")).toBe(IANAZone.create(\"America/New_York\"));\n\n  expect(IANAZone.create(\"UTC\")).not.toBe(IANAZone.create(\"America/New_York\"));\n\n  // hold true even for invalid zone names\n  expect(IANAZone.create(\"blorp\")).toBe(IANAZone.create(\"blorp\"));\n});\n\ntest(\"IANAZone.create should return IANAZone instance\", () => {\n  const result = IANAZone.create(\"America/Cancun\");\n  expect(result).toBeInstanceOf(IANAZone);\n});\n\ntest(\"IANAZone.isValidSpecifier\", () => {\n  expect(IANAZone.isValidSpecifier(\"America/New_York\")).toBe(true);\n  // this used to return true but now returns false, because we just defer to isValidZone\n  expect(IANAZone.isValidSpecifier(\"Fantasia/Castle\")).toBe(false);\n  expect(IANAZone.isValidSpecifier(\"Sport~~blorp\")).toBe(false);\n  expect(IANAZone.isValidSpecifier(\"Etc/GMT+8\")).toBe(true);\n  expect(IANAZone.isValidSpecifier(\"Etc/GMT-8\")).toBe(true);\n  expect(IANAZone.isValidSpecifier(\"Etc/GMT-0\")).toBe(true);\n  expect(IANAZone.isValidSpecifier(\"Etc/GMT-1\")).toBe(true);\n  expect(IANAZone.isValidSpecifier(null)).toBe(false);\n});\n\ntest(\"IANAZone.isValidZone\", () => {\n  expect(IANAZone.isValidZone(\"America/New_York\")).toBe(true);\n  expect(IANAZone.isValidZone(\"Fantasia/Castle\")).toBe(false);\n  expect(IANAZone.isValidZone(\"Sport~~blorp\")).toBe(false);\n  expect(IANAZone.isValidZone(\"\")).toBe(false);\n  expect(IANAZone.isValidZone(undefined)).toBe(false);\n  expect(IANAZone.isValidZone(null)).toBe(false);\n  expect(IANAZone.isValidZone(4)).toBe(false);\n});\n\ntest(\"IANAZone.type returns a static string\", () => {\n  expect(new IANAZone(\"America/Santiago\").type).toBe(\"iana\");\n  expect(new IANAZone(\"America/Blorp\").type).toBe(\"iana\");\n});\n\ntest(\"IANAZone.name returns the zone name passed to the constructor\", () => {\n  expect(new IANAZone(\"America/Santiago\").name).toBe(\"America/Santiago\");\n  expect(new IANAZone(\"America/Blorp\").name).toBe(\"America/Blorp\");\n  expect(new IANAZone(\"foo\").name).toBe(\"foo\");\n});\n\ntest(\"IANAZone is not universal\", () => {\n  expect(new IANAZone(\"America/Santiago\").isUniversal).toBe(false);\n});\n\ntest(\"IANAZone.offsetName with a long format\", () => {\n  const zone = new IANAZone(\"America/Santiago\");\n  const offsetName = zone.offsetName(1552089600, { format: \"long\", locale: \"en-US\" });\n  expect(offsetName).toBe(\"Chile Summer Time\");\n});\n\ntest(\"IANAZone.offsetName with a short format\", () => {\n  const zone = new IANAZone(\"America/Santiago\");\n  const offsetName = zone.offsetName(1552089600, { format: \"short\", locale: \"en-US\" });\n  expect(offsetName).toBe(\"GMT-3\");\n});\n\ntest(\"IANAZone.formatOffset with a short format\", () => {\n  const zone = new IANAZone(\"America/Santiago\");\n  const offsetName = zone.formatOffset(1552089600, \"short\");\n  expect(offsetName).toBe(\"-03:00\");\n});\n\ntest(\"IANAZone.formatOffset with a narrow format\", () => {\n  const zone = new IANAZone(\"America/Santiago\");\n  const offsetName = zone.formatOffset(1552089600, \"narrow\");\n  expect(offsetName).toBe(\"-3\");\n});\n\ntest(\"IANAZone.formatOffset with a techie format\", () => {\n  const zone = new IANAZone(\"America/Santiago\");\n  const offsetName = zone.formatOffset(1552089600, \"techie\");\n  expect(offsetName).toBe(\"-0300\");\n});\n\ntest(\"IANAZone.formatOffset throws for an invalid format\", () => {\n  const zone = new IANAZone(\"America/Santiago\");\n  expect(() => zone.formatOffset(1552089600, \"blorp\")).toThrow();\n});\n\ntest(\"IANAZone.equals requires both zones to be iana\", () => {\n  expect(IANAZone.create(\"UTC\").equals(FixedOffsetZone.utcInstance)).toBe(false);\n});\n\ntest(\"IANAZone.equals returns false even if the two share offsets\", () => {\n  const luxembourg = IANAZone.create(\"Europe/Luxembourg\");\n  const rome = IANAZone.create(\"Europe/Rome\");\n  expect(luxembourg.equals(rome)).toBe(false);\n});\n\ntest(\"IANAZone.isValid returns true for valid zone names\", () => {\n  expect(new IANAZone(\"UTC\").isValid).toBe(true);\n  expect(new IANAZone(\"America/Santiago\").isValid).toBe(true);\n  expect(new IANAZone(\"Europe/Paris\").isValid).toBe(true);\n});\n\ntest(\"IANAZone.isValid returns false for invalid zone names\", () => {\n  expect(new IANAZone(\"\").isValid).toBe(false);\n  expect(new IANAZone(\"foo\").isValid).toBe(false);\n  expect(new IANAZone(\"CEDT\").isValid).toBe(false);\n  expect(new IANAZone(\"GMT+2\").isValid).toBe(false);\n  expect(new IANAZone(\"America/Blorp\").isValid).toBe(false);\n  expect(new IANAZone(null).isValid).toBe(false);\n});\n\ntest(\"IANAZone.normalize normalizes the zone name\", () => {\n  expect(IANAZone.normalizeZone(\"america/nEw_york\")).toBe(\"America/New_York\");\n  expect(IANAZone.normalizeZone(\"AMERICA/NEW_YORK\")).toBe(\"America/New_York\");\n  expect(IANAZone.normalizeZone(\"America/New_York\")).toBe(\"America/New_York\");\n  expect(IANAZone.normalizeZone(\"europe/paris\")).toBe(\"Europe/Paris\");\n  expect(IANAZone.normalizeZone(\"EUROPE/PARIS\")).toBe(\"Europe/Paris\");\n  expect(IANAZone.normalizeZone(\"Asia/Tokyo\")).toBe(\"Asia/Tokyo\");\n  expect(IANAZone.normalizeZone(\"Etc/GMT\")).toBe(\"UTC\");\n});\n\ntest(\"IANAZone returns canonical zone name regardless of input casing\", () => {\n  expect(new IANAZone(\"america/nEw_york\").name).toBe(\"America/New_York\");\n  expect(new IANAZone(\"AMERICA/NEW_YORK\").name).toBe(\"America/New_York\");\n  expect(new IANAZone(\"America/New_York\").name).toBe(\"America/New_York\");\n  expect(new IANAZone(\"europe/paris\").name).toBe(\"Europe/Paris\");\n  expect(new IANAZone(\"EUROPE/PARIS\").name).toBe(\"Europe/Paris\");\n  expect(new IANAZone(\"Asia/Tokyo\").name).toBe(\"Asia/Tokyo\");\n});\n"
  },
  {
    "path": "test/zones/fixedOffset.test.js",
    "content": "/* global test expect */\nimport { FixedOffsetZone, IANAZone } from \"../../src/luxon\";\n\ntest(\"FixedOffsetZone.utcInstance returns a singleton\", () => {\n  expect(FixedOffsetZone.utcInstance).toBe(FixedOffsetZone.utcInstance);\n});\n\ntest(\"FixedOffsetZone.utcInstance provides valid UTC data\", () => {\n  expect(FixedOffsetZone.utcInstance.type).toBe(\"fixed\");\n  expect(FixedOffsetZone.utcInstance.name).toBe(\"UTC\");\n  expect(FixedOffsetZone.utcInstance.offsetName()).toBe(\"UTC\");\n  expect(FixedOffsetZone.utcInstance.formatOffset(0, \"short\")).toBe(\"+00:00\");\n  expect(FixedOffsetZone.utcInstance.isUniversal).toBe(true);\n  expect(FixedOffsetZone.utcInstance.offset()).toBe(0);\n  expect(FixedOffsetZone.utcInstance.isValid).toBe(true);\n});\n\ntest(\"FixedOffsetZone.parseSpecifier returns a valid instance from a UTC offset string\", () => {\n  let zone = FixedOffsetZone.parseSpecifier(\"UTC+6\");\n  expect(zone.isValid).toBe(true);\n  expect(zone.offset()).toBe(360);\n  expect(zone.name).toBe(\"UTC+6\");\n\n  zone = FixedOffsetZone.parseSpecifier(\"UTC+06\");\n  expect(zone.isValid).toBe(true);\n  expect(zone.offset()).toBe(360);\n  expect(zone.name).toBe(\"UTC+6\");\n\n  zone = FixedOffsetZone.parseSpecifier(\"UTC-6:00\");\n  expect(zone.isValid).toBe(true);\n  expect(zone.offset()).toBe(-360);\n  expect(zone.name).toBe(\"UTC-6\");\n});\n\ntest(\"FixedOffsetZone.parseSpecifier returns null for invalid data\", () => {\n  expect(FixedOffsetZone.parseSpecifier()).toBe(null);\n  expect(FixedOffsetZone.parseSpecifier(null)).toBe(null);\n  expect(FixedOffsetZone.parseSpecifier(\"\")).toBe(null);\n  expect(FixedOffsetZone.parseSpecifier(\"foo\")).toBe(null);\n  expect(FixedOffsetZone.parseSpecifier(\"UTC+blorp\")).toBe(null);\n});\n\ntest(\"FixedOffsetZone.formatOffset is consistent despite the provided timestamp\", () => {\n  // formatOffset accepts a timestamp to maintain the call signature of the abstract Zone class,\n  // but because of the nature of a fixed offset zone instance, the TS is ignored.\n  const zone = FixedOffsetZone.instance(-300);\n  expect(zone.formatOffset(0, \"techie\")).toBe(\"-0500\");\n\n  // March 9th 2019. A day before DST started\n  expect(zone.formatOffset(1552089600, \"techie\")).toBe(\"-0500\");\n\n  // March 11th 2019. A day after DST started\n  expect(zone.formatOffset(1552280400, \"techie\")).toBe(\"-0500\");\n});\n\ntest(\"FixedOffsetZone.formatOffset prints the correct sign before the offset\", () => {\n  expect(FixedOffsetZone.instance(-300).formatOffset(0, \"short\")).toBe(\"-05:00\");\n  expect(FixedOffsetZone.instance(-30).formatOffset(0, \"short\")).toBe(\"-00:30\");\n  // Note the negative zero results in a + sign\n  expect(FixedOffsetZone.instance(-0).formatOffset(0, \"short\")).toBe(\"+00:00\");\n  expect(FixedOffsetZone.instance(0).formatOffset(0, \"short\")).toBe(\"+00:00\");\n  expect(FixedOffsetZone.instance(30).formatOffset(0, \"short\")).toBe(\"+00:30\");\n  expect(FixedOffsetZone.instance(300).formatOffset(0, \"short\")).toBe(\"+05:00\");\n});\n\ntest(\"FixedOffsetZone.equals requires both zones to be fixed\", () => {\n  expect(FixedOffsetZone.utcInstance.equals(IANAZone.create(\"UTC\"))).toBe(false);\n});\n\ntest(\"FixedOffsetZone.equals compares fixed offset values\", () => {\n  expect(FixedOffsetZone.utcInstance.equals(FixedOffsetZone.instance(0))).toBe(true);\n  expect(FixedOffsetZone.instance(60).equals(FixedOffsetZone.instance(-60))).toBe(false);\n});\n"
  },
  {
    "path": "test/zones/invalid.test.js",
    "content": "/* global test expect */\nimport { InvalidZone } from \"../../src/luxon\";\n\ntest(\"InvalidZone\", () => {\n  const zone = new InvalidZone(\"foo\");\n\n  expect(zone.type).toBe(\"invalid\");\n  expect(zone.name).toBe(\"foo\");\n  expect(zone.offsetName()).toBe(null); // the abstract class states this returns a string, yet InvalidZones return null :(\n  expect(zone.formatOffset(0, \"short\")).toBe(\"\");\n  expect(zone.isUniversal).toBe(false);\n  expect(zone.offset()).toBe(NaN);\n  expect(zone.isValid).toBe(false);\n  expect(zone.equals(zone)).toBe(false); // always false even if it has the same name\n});\n"
  },
  {
    "path": "test/zones/local.test.js",
    "content": "/* global test expect */\nimport { SystemZone } from \"../../src/luxon\";\n\ntest(\"SystemZone.instance returns a singleton\", () => {\n  expect(SystemZone.instance).toBe(SystemZone.instance);\n});\n\ntest(\"SystemZone.instance provides valid ...\", () => {\n  expect(SystemZone.instance.type).toBe(\"system\");\n  expect(SystemZone.instance.isUniversal).toBe(false);\n  expect(SystemZone.instance.isValid).toBe(true);\n  expect(SystemZone.instance).toBe(SystemZone.instance);\n\n  // todo: figure out how to test these without inadvertently testing IANAZone\n  expect(SystemZone.instance.name).toBe(\"America/New_York\"); // this is true for the provided Docker container, what's the right way to test it?\n  // expect(SystemZone.instance.offsetName()).toBe(\"UTC\");\n  // expect(SystemZone.instance.formatOffset(0, \"short\")).toBe(\"+00:00\");\n  // expect(SystemZone.instance.offset()).toBe(0);\n});\n"
  },
  {
    "path": "test/zones/zoneInterface.test.js",
    "content": "/* global test expect */\nimport { Zone } from \"../../src/luxon\";\n\ntest(\"You can instantiate Zone directly\", () => {\n  expect(() => new Zone().isValid).toThrow();\n});\n"
  }
]