├── .agignore
├── .babelrc
├── .editorconfig
├── .gitattributes
├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
└── workflows
│ └── test.yml
├── .gitignore
├── .husky
├── .gitignore
└── pre-commit
├── .prettierrc
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE.md
├── README.md
├── babel.config.js
├── benchmarks
├── datetime.js
├── index.js
├── info.js
└── package.json
├── codecov.yml
├── docker
├── Dockerfile
├── build
├── npm
├── push
└── readme.md
├── docs
├── calendars.md
├── formatting.md
├── home.md
├── install.md
├── intl.md
├── math.md
├── matrix.md
├── moment.md
├── parsing.md
├── tour.md
├── upgrading.md
├── validity.md
├── why.md
└── zones.md
├── jest.config.js
├── package-lock.json
├── package.json
├── scripts
├── bootstrap.js
├── deploy-site
├── jest
├── readme.md
├── release
├── repl
├── tag
├── test
└── version
├── site
├── .nojekyll
├── demo
│ ├── demo.css
│ ├── demo.js
│ ├── global.html
│ └── requirejs.html
├── docs
│ ├── _coverpage.md
│ ├── _media
│ │ ├── Luxon_icon.svg
│ │ ├── Luxon_icon_180x180.png
│ │ ├── Luxon_icon_180x180@2x.png
│ │ ├── Luxon_icon_32x32.png
│ │ └── Luxon_icon_64x64.png
│ └── _sidebar.md
└── index.html
├── src
├── datetime.js
├── duration.js
├── errors.js
├── impl
│ ├── conversions.js
│ ├── diff.js
│ ├── digits.js
│ ├── english.js
│ ├── formats.js
│ ├── formatter.js
│ ├── invalid.js
│ ├── locale.js
│ ├── regexParser.js
│ ├── tokenParser.js
│ ├── util.js
│ └── zoneUtil.js
├── info.js
├── interval.js
├── luxon.js
├── package.json
├── settings.js
├── zone.js
└── zones
│ ├── IANAZone.js
│ ├── fixedOffsetZone.js
│ ├── invalidZone.js
│ └── systemZone.js
├── tasks
├── build.js
├── buildAll.js
├── buildGlobal.js
└── buildNode.js
└── test
├── datetime
├── create.test.js
├── degrade.test.js
├── diff.test.js
├── dst.test.js
├── equality.test.js
├── format.test.js
├── getters.test.js
├── info.test.js
├── invalid.test.js
├── localeWeek.test.js
├── many.test.js
├── math.test.js
├── misc.test.js
├── proto.test.js
├── reconfigure.test.js
├── regexParse.test.js
├── relative.test.js
├── set.test.js
├── toFormat.test.js
├── tokenParse.test.js
├── transform.test.js
├── typecheck.test.js
└── zone.test.js
├── duration
├── accuracy.test.js
├── create.test.js
├── customMatrix.test.js
├── equality.test.js
├── format.test.js
├── getters.test.js
├── info.test.js
├── invalid.test.js
├── math.test.js
├── parse.test.js
├── proto.test.js
├── reconfigure.test.js
├── set.test.js
├── typecheck.test.js
└── units.test.js
├── helpers.js
├── impl
└── english.test.js
├── info
├── features.test.js
├── listers.test.js
├── localeWeek.test.js
└── zones.test.js
├── interval
├── create.test.js
├── format.test.js
├── getters.test.js
├── info.test.js
├── localeWeek.test.js
├── many.test.js
├── parse.test.js
├── proto.test.js
├── setter.test.js
└── typecheck.test.js
└── zones
├── IANA.test.js
├── fixedOffset.test.js
├── invalid.test.js
├── local.test.js
└── zoneInterface.test.js
/.agignore:
--------------------------------------------------------------------------------
1 | /build
2 | package-lock.json
3 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "@babel/preset-env"
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_size = 2
6 | end_of_line = lf
7 | indent_style = space
8 | insert_final_newline = false
9 | trim_trailing_whitespace = true
10 |
11 | [*.md]
12 | trim_trailing_whitespace = false
13 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | scripts/* linguist-vendored
2 | docker/* linguist-vendored
3 | site/** linguist-vendored
4 | .husky/* linguist-vendored
5 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ""
5 | labels: bug
6 | ---
7 |
8 | **Describe the bug**
9 | A clear and concise description of what the bug is.
10 |
11 | **To Reproduce**
12 | Please share a minimal code example that triggers the problem:
13 |
14 | **Actual vs Expected behavior**
15 | A clear and concise description of what you expected to happen.
16 |
17 | **Desktop (please complete the following information):**
18 |
19 | - OS: [e.g. iOS]
20 | - Browser [e.g. Chrome 84, safari 14.0]
21 | - Luxon version [e.g. 1.25.0]
22 | - Your timezone [e.g. "America/New_York"]
23 |
24 | **Additional context**
25 | Add any other context about the problem here.
26 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ""
5 | labels: enhancement
6 | ---
7 |
8 | **Is your feature request related to a problem? Please describe.**
9 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
10 |
11 | **Describe the solution you'd like**
12 | A clear and concise description of what you want to happen.
13 |
14 | **Describe alternatives you've considered**
15 | A clear and concise description of any alternative solutions or features you've considered.
16 |
17 | **Additional context**
18 | Add any other context or screenshots about the feature request here.
19 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: Test
2 |
3 | on:
4 | push:
5 | branches: [master]
6 | pull_request:
7 | branches: [master]
8 |
9 | jobs:
10 | build-and-test:
11 | runs-on: ubuntu-latest
12 |
13 | env:
14 | LANG: en_US.utf8
15 | LIMIT_JEST: yes
16 | TZ: America/New_York
17 |
18 | strategy:
19 | matrix:
20 | node-version:
21 | - 18.20.6 # latest 18.x
22 | - 20.18.3 # latest 20.x
23 | - 22.14.0 # latest 22.x
24 |
25 | steps:
26 | - uses: actions/checkout@v3
27 | - name: Use Node.js ${{ matrix.node-version }}
28 | uses: actions/setup-node@v3
29 | with:
30 | node-version: ${{ matrix.node-version }}
31 | cache: "npm"
32 | - run: npm ci
33 | - run: npm run build
34 | - run: npm run format-check
35 | - run: npm run test
36 | - run: npm run site
37 | - run: bash <(curl -s https://codecov.io/bash)
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .tern-port
3 | /build
4 | #*
5 | .#*
6 | coverage/
7 | .DS_Store
8 | .external-ecmascript.js
9 | .idea
10 | .vscode
11 | test/scratch.test.js
12 |
--------------------------------------------------------------------------------
/.husky/.gitignore:
--------------------------------------------------------------------------------
1 | _
2 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | . "$(dirname "$0")/_/husky.sh"
3 |
4 | npx lint-staged
5 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | { "printWidth": 100 }
2 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to Luxon
2 |
3 | ## General guidelines
4 |
5 | Patches 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.
6 |
7 | Here are some vague notes on Luxon's design philosophy:
8 |
9 | 1. 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.
10 | 1. 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.
11 | 1. 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.
12 | 1. 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.
13 | 1. 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.
14 | 1. You'll need to sign a CLA as part of your first pull request to Luxon.
15 |
16 | ## Building and testing
17 |
18 | Building 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.
19 |
20 | ### OSX
21 |
22 | Mac is easy:
23 | Open the terminal.
24 |
25 | ```
26 | brew install node --with-full-icu
27 | npm install
28 | ./scripts/test
29 | ```
30 |
31 | If that's for whatever reason a pain, the Linux instructions should also work, as well as the Docker ones.
32 |
33 | ### Linux
34 |
35 | There 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:
36 |
37 | ```
38 | npm install
39 | npm install full-icu
40 | ./scripts/test
41 | ```
42 |
43 | Where `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.
44 |
45 | ### Windows
46 |
47 | If 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.
48 |
49 | I would love to add instructions for a non-WSL install of the dev env!
50 |
51 | ### Docker
52 |
53 | In 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:
54 |
55 | ```
56 | ./docker/npm install --ignore-scripts
57 | ./docker/npm test
58 | ```
59 |
60 | ## Patch basics
61 |
62 | Once you're sure your bugfix or feature makes sense for Luxon, make sure you take these steps:
63 |
64 | 1. Be sure to add tests and run them with `scripts/test`
65 | 1. Be sure you run `npm run format` before you commit. Note this will modify your source files to line up with the style guidelines.
66 | 1. 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.
67 | 1. 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`.
68 | 1. To test in Node, run `npm run build` and then run something like `var DateTime = require('./build/cjs-browser/luxon').DateTime`.
69 |
70 | Luxon 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.
71 |
72 | ## npm script reference
73 |
74 | | Command | Function |
75 | | ---------------------------- | --------------------------------------- |
76 | | `npm run build` | Build all the distributable files |
77 | | `npm run build-node` | Build just for Node |
78 | | `npm run test` | Run the test suite, but see notes above |
79 | | `npm run format` | Run the Prettier formatter |
80 | | `npm run site` | Build the Luxon website, including docs |
81 | | `npm run check-doc-coverage` | Check whether there's full doc coverage |
82 | | `npm run benchmark` | Run performance benchmarks |
83 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Copyright 2019 JS Foundation and other contributors
2 |
3 | Permission 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:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | THE 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.
8 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Luxon
2 |
3 | [![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]
4 |
5 | Luxon is a library for working with dates and times in JavaScript.
6 |
7 | ```js
8 | DateTime.now().setZone("America/New_York").minus({ weeks: 1 }).endOf("day").toISO();
9 | ```
10 |
11 | ## Upgrading to 3.0
12 |
13 | [Guide](https://moment.github.io/luxon/#upgrading)
14 |
15 | ## Features
16 | * DateTime, Duration, and Interval types.
17 | * Immutable, chainable, unambiguous API.
18 | * Parsing and formatting for common and custom formats.
19 | * Native time zone and Intl support (no locale or tz files).
20 |
21 | ## Download/install
22 |
23 | [Download/install instructions](https://moment.github.io/luxon/#/install)
24 |
25 | ## Documentation
26 |
27 | * [General documentation](https://moment.github.io/luxon/#/?id=luxon)
28 | * [API docs](https://moment.github.io/luxon/api-docs/index.html)
29 | * [Quick tour](https://moment.github.io/luxon/#/tour)
30 | * [For Moment users](https://moment.github.io/luxon/#/moment)
31 | * [Why does Luxon exist?](https://moment.github.io/luxon/#/why)
32 | * [A quick demo](https://moment.github.io/luxon/demo/global.html)
33 |
34 | ## Development
35 |
36 | See [contributing](CONTRIBUTING.md).
37 |
38 | ![Phasers to stun][phasers-image]
39 |
40 | [license-image]: https://img.shields.io/badge/license-MIT-blue.svg
41 | [license]: LICENSE.md
42 |
43 | [github-action-image]: https://github.com/moment/luxon/actions/workflows/test.yml/badge.svg
44 | [github-action-url]: https://github.com/moment/luxon/actions/workflows/test.yml
45 |
46 | [npm-url]: https://npmjs.org/package/luxon
47 | [npm-version-image]: https://badge.fury.io/js/luxon.svg
48 |
49 | [test-coverage-url]: https://codecov.io/gh/moment/luxon
50 | [test-coverage-image]: https://codecov.io/gh/moment/luxon/branch/master/graph/badge.svg
51 |
52 | [contributing-url]: https://github.com/moment/luxon/blob/master/CONTRIBUTING.md
53 | [contributing-image]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg
54 |
55 | [phasers-image]: https://img.shields.io/badge/phasers-stun-brightgreen.svg
56 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = { presets: ["@babel/preset-env"] };
2 |
--------------------------------------------------------------------------------
/benchmarks/datetime.js:
--------------------------------------------------------------------------------
1 | import Benchmark from "benchmark";
2 | import DateTime from "../src/datetime.js";
3 | import Settings from "../src/settings.js";
4 |
5 | function runDateTimeSuite() {
6 | return new Promise((resolve, reject) => {
7 | const suite = new Benchmark.Suite();
8 |
9 | const dt = DateTime.now();
10 |
11 | const formatParser = DateTime.buildFormatParser("yyyy/MM/dd HH:mm:ss.SSS");
12 |
13 | suite
14 | .add("DateTime.now", () => {
15 | DateTime.now();
16 | })
17 | .add("DateTime.fromObject with locale", () => {
18 | DateTime.fromObject({}, { locale: "fr" });
19 | })
20 | .add("DateTime.local with numbers", () => {
21 | DateTime.local(2017, 5, 15);
22 | })
23 | .add("DateTime.local with numbers and zone", () => {
24 | DateTime.local(2017, 5, 15, 11, 7, 35, { zone: "America/New_York" });
25 | })
26 | .add("DateTime.fromISO", () => {
27 | DateTime.fromISO("1982-05-25T09:10:11.445Z");
28 | })
29 | .add("DateTime.fromSQL", () => {
30 | DateTime.fromSQL("2016-05-14 10:23:54.2346");
31 | })
32 | .add("DateTime.fromFormat", () => {
33 | DateTime.fromFormat("1982/05/25 09:10:11.445", "yyyy/MM/dd HH:mm:ss.SSS");
34 | })
35 | .add("DateTime.fromFormat with zone", () => {
36 | DateTime.fromFormat("1982/05/25 09:10:11.445", "yyyy/MM/dd HH:mm:ss.SSS", {
37 | zone: "America/Los_Angeles",
38 | });
39 | })
40 | .add("DateTime.fromFormatParser", () => {
41 | DateTime.fromFormatParser("1982/05/25 09:10:11.445", formatParser);
42 | })
43 | .add("DateTime.fromFormatParser with zone", () => {
44 | DateTime.fromFormatParser("1982/05/25 09:10:11.445", formatParser, {
45 | zone: "America/Los_Angeles",
46 | });
47 | })
48 | .add("DateTime#setZone", () => {
49 | dt.setZone("America/Los_Angeles");
50 | })
51 | .add("DateTime#toFormat", () => {
52 | dt.toFormat("yyyy-MM-dd");
53 | })
54 | .add("DateTime#toFormat with macro", () => {
55 | dt.toFormat("T");
56 | })
57 | .add("DateTime#toFormat with macro no cache", () => {
58 | dt.toFormat("T");
59 | Settings.resetCaches();
60 | })
61 | .add("DateTime#format in german", () => {
62 | dt.setLocale("de-De").toFormat("d. LLL. HH:mm");
63 | })
64 | .add("DateTime#format in german and no-cache", () => {
65 | dt.setLocale("de-De").toFormat("d. LLL. HH:mm");
66 | Settings.resetCaches();
67 | })
68 | .add("DateTime#add", () => {
69 | dt.plus({ milliseconds: 3434 });
70 | })
71 | .add("DateTime#toISO", () => {
72 | dt.toISO();
73 | })
74 | .add("DateTime#toLocaleString", () => {
75 | dt.toLocaleString();
76 | })
77 | .add("DateTime#toLocaleString in utc", () => {
78 | dt.toUTC().toLocaleString();
79 | })
80 | .add("DateTime#toRelativeCalendar", () => {
81 | dt.toRelativeCalendar({ base: DateTime.now(), locale: "fi" });
82 | })
83 | .on("cycle", (event) => {
84 | console.log(String(event.target));
85 | })
86 | .on("complete", function () {
87 | console.log("Fastest is " + this.filter("fastest").map("name"));
88 | resolve();
89 | })
90 | .on("error", function () {
91 | reject(this.error);
92 | })
93 | .run();
94 | });
95 | }
96 |
97 | const allSuites = [runDateTimeSuite];
98 |
99 | export default allSuites;
100 |
--------------------------------------------------------------------------------
/benchmarks/index.js:
--------------------------------------------------------------------------------
1 | import dateTimeSuites from "./datetime.js";
2 | import infoSuites from "./info.js";
3 |
4 | const allSuites = [...dateTimeSuites, ...infoSuites];
5 |
6 | async function runAllSuites() {
7 | for (const runSuite of allSuites) {
8 | await runSuite();
9 | }
10 | }
11 |
12 | runAllSuites();
13 |
--------------------------------------------------------------------------------
/benchmarks/info.js:
--------------------------------------------------------------------------------
1 | import Benchmark from "benchmark";
2 | import Info from "../src/info.js";
3 | import Locale from "../src/impl/locale.js";
4 |
5 | function runWeekdaysSuite() {
6 | return new Promise((resolve, reject) => {
7 | const locale = Locale.create(null, null, null);
8 |
9 | new Benchmark.Suite()
10 | .add("Info.weekdays with existing locale", () => {
11 | Info.weekdays("long", { locObj: locale });
12 | })
13 | .add("Info.weekdays", () => {
14 | Info.weekdays("long");
15 | })
16 | .on("cycle", (event) => {
17 | console.log(String(event.target));
18 | })
19 | .on("complete", function () {
20 | console.log("Fastest is " + this.filter("fastest").map("name"));
21 | resolve();
22 | })
23 | .on("error", function () {
24 | reject(this.error);
25 | })
26 | .run();
27 | });
28 | }
29 |
30 | function runWeekdaysFormatSuite() {
31 | return new Promise((resolve, reject) => {
32 | const locale = Locale.create(null, null, null);
33 |
34 | new Benchmark.Suite()
35 | .add("Info.weekdaysFormat with existing locale", () => {
36 | Info.weekdaysFormat("long", { locObj: locale });
37 | })
38 | .add("Info.weekdaysFormat", () => {
39 | Info.weekdaysFormat("long");
40 | })
41 | .on("cycle", (event) => {
42 | console.log(String(event.target));
43 | })
44 | .on("complete", function () {
45 | console.log("Fastest is " + this.filter("fastest").map("name"));
46 | resolve();
47 | })
48 | .on("error", function () {
49 | reject(this.error);
50 | })
51 | .run();
52 | });
53 | }
54 |
55 | function runMonthsSuite() {
56 | return new Promise((resolve, reject) => {
57 | const locale = Locale.create(null, null, null);
58 | new Benchmark.Suite()
59 | .add("Info.months with existing locale", () => {
60 | Info.months("long", { locObj: locale });
61 | })
62 | .add("Info.months", () => {
63 | Info.months("long");
64 | })
65 | .on("cycle", (event) => {
66 | console.log(String(event.target));
67 | })
68 | .on("complete", function () {
69 | console.log("Fastest is " + this.filter("fastest").map("name"));
70 | resolve();
71 | })
72 | .on("error", function () {
73 | reject(this.error);
74 | })
75 | .run();
76 | });
77 | }
78 |
79 | function runMonthsFormatSuite() {
80 | return new Promise((resolve, reject) => {
81 | const locale = Locale.create(null, null, null);
82 |
83 | new Benchmark.Suite()
84 | .add("Info.monthsFormat with existing locale", () => {
85 | Info.monthsFormat("long", { locObj: locale });
86 | })
87 | .add("Info.monthsFormat", () => {
88 | Info.monthsFormat("long");
89 | })
90 | .on("cycle", (event) => {
91 | console.log(String(event.target));
92 | })
93 | .on("complete", function () {
94 | console.log("Fastest is " + this.filter("fastest").map("name"));
95 | resolve();
96 | })
97 | .on("error", function () {
98 | reject(this.error);
99 | })
100 | .run();
101 | });
102 | }
103 |
104 | const allSuites = [runMonthsSuite, runMonthsFormatSuite, runWeekdaysSuite, runWeekdaysFormatSuite];
105 |
106 | export default allSuites;
107 |
--------------------------------------------------------------------------------
/benchmarks/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "module"
3 | }
4 |
--------------------------------------------------------------------------------
/codecov.yml:
--------------------------------------------------------------------------------
1 | comment: false
2 | coverage:
3 | status:
4 | project:
5 | default:
6 | target: auto
7 | threshold: null
8 | base: auto
9 |
--------------------------------------------------------------------------------
/docker/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM library/node:15-slim
2 |
3 | ENV HUSKY_SKIP_INSTALL=1
4 |
5 | ENV LANG=en_US.utf8
6 | ENV LIMIT_JEST=yes
7 | ENV CI=yes
8 | ENV TZ=America/New_York
9 |
--------------------------------------------------------------------------------
/docker/build:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | docker build docker -t icambron/luxon
3 |
--------------------------------------------------------------------------------
/docker/npm:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | docker run -it --rm -v $(pwd):/luxon -w /luxon icambron/luxon npm $@
3 |
--------------------------------------------------------------------------------
/docker/push:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | docker push icambron/luxon
3 |
--------------------------------------------------------------------------------
/docker/readme.md:
--------------------------------------------------------------------------------
1 | Luxon provides a Docker container and some wrapping scripts to make it easier to run the tests.
2 |
3 | 1. The Dockerfile is really just here as an FYI. You shouldn't need to interact with it
4 | 1. `npm` is a bash script that runs `npm run [arg]` inside the Docker container.
5 |
--------------------------------------------------------------------------------
/docs/calendars.md:
--------------------------------------------------------------------------------
1 | # Calendars
2 |
3 | This 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.
4 |
5 | ## Fully supported calendars
6 |
7 | Luxon 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:
8 |
9 | ```js
10 | DateTime.fromISO('2017-W23-3').plus({ weeks: 1, days: 2 }).toISOWeekDate(); //=> '2017-W24-5'
11 | ```
12 |
13 | The main reason I bring all this is up is to contrast it with the capabilities for other calendars described below.
14 |
15 | ## Output calendars
16 |
17 | Luxon 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".
18 |
19 | In 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".
20 |
21 | The output calendar is a property of the DateTime itself. For example:
22 |
23 | ```js
24 | var dtHebrew = DateTime.now().reconfigure({ outputCalendar: "hebrew" });
25 | dtHebrew.outputCalendar; //=> 'hebrew'
26 | dtHebrew.toLocaleString() //=> '4 Tishri 5778'
27 | ```
28 |
29 | You 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.
30 |
31 | ### Generally supported calendars
32 |
33 | Here's a table of the different calendars with examples generated formatting the same date generated like this:
34 |
35 | ```js
36 | DateTime.fromObject({ outputCalendar: c }).toLocaleString(DateTime.DATE_FULL);
37 | ```
38 |
39 | Since Luxon uses the browser's **Intl API**, you can use all the supported calendars.
40 | (See [Intl.Locale.prototype.getCalendars()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/getCalendars) for a full list)
41 |
42 | | Calendar | Example |
43 | | ------------------ | ------------------------ |
44 | | buddhist | September 24, 2560 BE |
45 | | chinese | Eighth Month 5, 2017 |
46 | | coptic | Tout 14, 1734 ERA1 |
47 | | ethioaa | Meskerem 14, 7510 ERA0 |
48 | | ethiopic | Meskerem 14, 2010 ERA1 |
49 | | hebrew | 4 Tishri 5778 |
50 | | indian | Asvina 2, 1939 Saka |
51 | | islamic | Muharram 4, 1439 AH |
52 | | islamic-civil | Muharram 3, 1439 AH |
53 | | islamic-umalqura | Muharram 3, 1439 AH |
54 | | iso8601 | September 24, 2017 |
55 | | japanese | September 24, 29 Heisei |
56 | | persian | Mehr 2, 1396 AP |
57 | | roc | September 24, 106 Minguo |
58 |
59 |
60 |
61 | ### Default output calendar
62 |
63 | You can set the default output calendar for new DateTime instances like this:
64 |
65 | ```js
66 | Settings.defaultOutputCalendar = 'persian';
67 | ```
68 |
--------------------------------------------------------------------------------
/docs/home.md:
--------------------------------------------------------------------------------
1 | ## Luxon
2 |
3 | Luxon is a library for dealing with dates and times in JavaScript.
4 |
5 | ```js
6 | DateTime.now().setZone('America/New_York').minus({weeks:1}).endOf('day').toISO();
7 | ```
8 |
9 | ### Features
10 |
11 | * A nice API for working with datetimes
12 | * Interval support (from time x to time y)
13 | * Duration support (14 days, 5 minutes, 33 seconds)
14 | * [Parsing](parsing.md) and [Formatting](formatting.md) datetimes, intervals, and durations
15 | * [Internationalization](intl.md) of strings using the Intl API
16 | * Detailed and unambiguous [math](math.md) operations
17 | * Built-in handling of [time zones](zones.md)
18 | * Partial support for multiple [calendar systems](calendars.md)
19 |
20 | For more, see the docs on the left, including the [api docs](api-docs/index.html ':ignore')
21 |
22 | ### Getting started
23 |
24 | * [Demo](https://moment.github.io/luxon/demo/global.html ':ignore')
25 | * Read the [quick tour](tour.md)
26 | * Browse the topic docs on the left
27 | * Read the [api docs](api-docs/index.html ':ignore')
28 |
29 | Logo by [John Dalziel](https://github.com/crashposition)
30 |
--------------------------------------------------------------------------------
/docs/install.md:
--------------------------------------------------------------------------------
1 | # Install guide
2 |
3 | Luxon 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.
4 |
5 | ## Basic browser setup
6 |
7 | - [Download full](https://moment.github.io/luxon/global/luxon.js)
8 | - [Download minified](https://moment.github.io/luxon/global/luxon.min.js)
9 |
10 | You can also load the files from a [CDN](https://www.jsdelivr.com/package/npm/luxon).
11 |
12 | Just include Luxon in a script tag. You can access its various classes through the `luxon` global.
13 |
14 | ```html
15 |
16 | ```
17 |
18 | You may wish to alias the classes you use:
19 |
20 | ```js
21 | var DateTime = luxon.DateTime;
22 | ```
23 |
24 | ## Node.js
25 |
26 | Supports Node.js 6+. Install via NPM:
27 |
28 | ```
29 | npm install --save luxon
30 | ```
31 |
32 | ```js
33 | const { DateTime } = require("luxon");
34 | ```
35 |
36 | If you want to work with locales, you need ICU support:
37 |
38 | 1. **For Node.js 13+, it comes built-in, no action necessary**
39 | 2. For older versions of Node.js (only 12 is supported), you need to install it yourself:
40 | 1. Install a build of Node.js with full ICU baked in, such as via nvm: nvm install -s --with-intl=full-icu --download=all or brew: brew install node --with-full-icu
41 | 2. Install the ICU data externally and point Node.js to it. The instructions on how to do that are below.
42 |
43 | The 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:
44 |
45 | ```
46 | npm install full-icu
47 | node --icu-data-dir=./node_modules/full-icu
48 | ```
49 |
50 | You can also point to the data with an environment var, like this:
51 |
52 | ```
53 | NODE_ICU_DATA="$(pwd)/node_modules/full-icu" node
54 | ```
55 |
56 | ## AMD (System.js, RequireJS, etc)
57 |
58 | - [Download full](https://moment.github.io/luxon/amd/luxon.js)
59 | - [Download minified](https://moment.github.io/luxon/amd/luxon.min.js)
60 |
61 | ```js
62 | requirejs(["luxon"], function(luxon) {
63 | //...
64 | });
65 | ```
66 |
67 | ## ES6
68 |
69 | - [Download full](https://moment.github.io/luxon/es6/luxon.js)
70 | - [Download minified](https://moment.github.io/luxon/es6/luxon.min.js)
71 |
72 | ```js
73 | import { DateTime } from "luxon";
74 | ```
75 |
76 | ## Webpack
77 |
78 | ```
79 | npm install --save luxon
80 | ```
81 |
82 | ```js
83 | import { DateTime } from "luxon";
84 | ```
85 |
86 | ## Types
87 |
88 | There 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)).
89 |
90 | For Flow, use:
91 |
92 | ```
93 | flow-typed install luxon
94 | ```
95 |
96 | For TypeScript, use:
97 |
98 | ```
99 | npm install --save-dev @types/luxon
100 | ```
101 |
102 | ## React Native
103 |
104 | React 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).
105 |
106 | For React Native >=0.60, you should configure the build flavor of jsc in `android/app/build.gradle`:
107 |
108 | ```diff
109 | -def jscFlavor = 'org.webkit:android-jsc:+'
110 | +def jscFlavor = 'org.webkit:android-jsc-intl:+'
111 | ```
112 |
113 | For even older versions of React Native you can use [jsc-android-buildscripts](https://github.com/SoftwareMansion/jsc-android-buildscripts) to fix it.
114 |
--------------------------------------------------------------------------------
/docs/matrix.md:
--------------------------------------------------------------------------------
1 | # Support matrix
2 |
3 | This page covers what platforms are supported by Luxon and what caveats apply to them.
4 |
5 | ## Official support
6 |
7 | Luxon 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.
8 |
9 | | Browser | Versions | Intl relative time formatting |
10 | | -------------------------------- | -------- | ----------------------------- |
11 | | Chrome | >= 73 | ✓ |
12 | | Firefox | >= 65 | ✓ |
13 | | Edge | >= 79 | ✓ |
14 | | | 18 | ✗ |
15 | | Safari | >= 14 | ✓ |
16 | | | 13 | ✗ |
17 | | iOS Safari (iOS version numbers) | >= 14 | ✓ |
18 | | Node | >= 12 | ✓ |
19 |
20 | - Those capabilities are explained in the next sections, along with possible polyfill options
21 | - "w/ICU" refers to providing Node with ICU data. See the [install](install.md?id=node) for instructions
22 |
23 | ## Effects of missing features
24 |
25 | **If the platforms you're targeting has all its boxes above check off, ignore this section**.
26 |
27 | In the support table above, you can see that some environments are missing capabilities. In the current version of
28 | Luxon, there's only one partially-supported feature, so this is currently pretty simple. (Older versions of Luxon supported
29 | older browsers, so there were nuanced feature caveats. Newer versions will add more caveats as new browser capabilities
30 | become available and Luxon takes advantage of them if they're present.)
31 |
32 | 1. **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.
33 |
34 | If the browser lacks these capabilities, Luxon tries its best:
35 |
36 | | Feature | Full support | No relative time format |
37 | | -------------------------------------- | ------------ | ----------------------- |
38 | | Most things | OK | OK |
39 | | `DateTime#toRelative` in en-US | OK | OK |
40 | | `DateTime#toRelative` in other locales | Uses English | Uses English |
41 |
42 |
43 | ## Older platforms
44 |
45 | - **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.
46 | - **Older versions of IE** probably won't work at all.
47 | - **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.
48 |
49 | ## Other platforms
50 |
51 | If the platform you're targeting isn't on the list and you're unsure what caveats apply, you can check which pieces are supported:
52 |
53 | ```js
54 | Info.features(); //=> { relative: false }
55 | ```
56 |
57 | Specific notes on other platforms:
58 |
59 | - **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`:
60 |
61 | ```diff
62 | -def jscFlavor = 'org.webkit:android-jsc:+'
63 | +def jscFlavor = 'org.webkit:android-jsc-intl:+'
64 | ```
65 |
66 | For even older versions of React Native you can use [jsc-android-buildscripts](https://github.com/SoftwareMansion/jsc-android-buildscripts) to fix it.
67 |
--------------------------------------------------------------------------------
/docs/upgrading.md:
--------------------------------------------------------------------------------
1 | # Upgrading Luxon
2 |
3 | ## 2.x to 3.0
4 |
5 | Version 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":
6 |
7 | ```js
8 | Settings.defaultZone = "America/Chicago";
9 |
10 | DateTime.now().setZone("default") // results in Chicago time
11 | DateTime.now().setZone("system") // uses the user's system time
12 | ```
13 |
14 | If this seems obvious, just be aware that it didn't work like that before!
15 |
16 | ## 1.x to 2.0
17 |
18 | Version 2.0 of Luxon has a number of breaking changes.
19 |
20 | ### Environment support
21 |
22 | Luxon 2.0 does not support Node < 12, or any version of IE. It also only supports newer versions of major browsers. This change
23 | allows Luxon to make more assumptions about what's supported in the environment and will allow Luxon's code to simplify. See
24 | the [Support Matrix](matrix.md) for more.
25 |
26 | For this same reason, a polyfilled build is no longer provided; everything Luxon needs comes standard on browsers.
27 |
28 | ### Breaking signature changes
29 |
30 | There are many more specific breaking changes. Most are aimed and making Luxon's handling of option parameters more consistent.
31 |
32 | #### fromObject
33 | `DateTime.fromObject()` and `Duration.fromObject()` now accept two parameters: one for the object and one for the options.
34 |
35 | For example:
36 |
37 | ```js
38 | // Luxon 1.x
39 | DateTime.fromObject({ hour: 3, minute: 2, zone: "America/New_York", locale: "ru" });
40 | Duration.fromObject({ hours: 3, minutes: 2, conversionAccuracy: "casual", locale: "ru" });
41 |
42 | // vs Luxon 2.x
43 | DateTime.fromObject({ hour: 3, minute: 2 }, { zone: "America/New_York", locale: "ru" });
44 | Duration.fromObject({ hours: 3, minutes: 2 }, { conversionAccuracy: "casual", locale: "ru" });
45 | ```
46 |
47 | #### toLocaleString
48 |
49 | In Luxon 1.x, you can mix Intl options with overrides of the DateTime configuration into the same options parameter. These are now
50 | two separate parameters:
51 |
52 | ```js
53 |
54 | // Luxon 1.x
55 | DateTime.now().toLocaleString({ hour: "2-digit", locale: "ru" })
56 |
57 | // vs Luxon 2.x
58 |
59 | DateTime.now().toLocaleString({ hour: "2-digit" }, { locale: "ru" })
60 | ```
61 |
62 | #### System zone
63 |
64 | The zone of the executing environment (e.g. the time set on the computer running the browser running Luxon), is now called
65 | "system" instead of "local" to reduce confusion.
66 |
67 | ```js
68 | DateTime.fromObject({}, { zone: "local" }) // still works
69 | DateTime.fromObject({}, { zone: "system" }) // preferred
70 |
71 | DateTime.fromObject({}, { zone: "system" }).zone // => type is SystemZone
72 | DateTime.fromObject({}, { zone: "system" }).zone.type // => "system"
73 | ```
74 |
75 | #### Default zone
76 |
77 | Luxon 2.x cleans up the handling of `Settings.defaultZone`:
78 |
79 | ```js
80 |
81 | // setting
82 | Settings.defaultZone = "America/New_York"; // can take a string
83 | Settings.defaultZone = IANAZone.create("America/New_York"); // or a Zone instance
84 |
85 | // getting
86 | Settings.defaultZone //=> a Zone instance
87 | ```
88 |
89 | The most significant breaking change here is that `Settings.defaultZoneName` no longer exists.
90 |
91 | #### Other breaking changes
92 |
93 | * `DateTime#toObject` no longer accepts an `includeConfig` option
94 | * `resolvedLocaleOpts` is now `resolvedLocaleOptions`
95 | * `Zone#universal` is now `Zone#isUniversal`
96 |
97 | ### Non-breaking changes
98 |
99 | * `DateTime.local()` and `DateTime.utc()` now take an options parameter for setting zone and locale, same as `fromObject()`.
100 |
101 | ### A note
102 |
103 | We originally had more ambitious plans for Luxon 2.0: a port to Typescript, an overhaul of error handling, and lots of other changes.
104 | The problem is that we're very busy, and in the meantime browsers have evolved quickly, the mistakes in our API bothered a lot
105 | of developers, and our need to support old environments made Luxon more difficult to change. So we made a basic set of changes
106 | to give us some operating room. And hopefully someday we'll get back to those more ambitious plans.
107 |
--------------------------------------------------------------------------------
/docs/validity.md:
--------------------------------------------------------------------------------
1 | # Validity
2 |
3 | ## Invalid DateTimes
4 |
5 | One 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.
6 |
7 | Unless 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`:
8 |
9 | ```js
10 | > var dt = DateTime.fromObject({ month: 6, day: 400 });
11 | dt.isValid //=> false
12 | ```
13 |
14 | All of the methods or getters that return primitives return degenerate ones:
15 |
16 | ```js
17 | dt.year; //=> NaN
18 | dt.toString(); //=> 'Invalid DateTime'
19 | dt.toObject(); //=> {}
20 | ```
21 |
22 | Methods that return other Luxon objects will return invalid ones:
23 |
24 | ```js
25 | dt.plus({ days: 4 }).isValid; //=> false
26 | ```
27 |
28 | ## Reasons a DateTimes can be invalid
29 |
30 | The most common way to do that is to over- or underflow some unit:
31 |
32 | - February 40th
33 | - 28:00
34 | - -4 pm
35 | - etc
36 |
37 | But there are other ways to do it:
38 |
39 | ```js
40 | // specify a time zone that doesn't exist
41 | DateTime.now().setZone("America/Blorp").isValid; //=> false
42 |
43 | // provide contradictory information (here, this date is not a Wednesday)
44 | DateTime.fromObject({ year: 2017, month: 5, day: 25, weekday: 3 }).isValid; //=> false
45 | ```
46 |
47 | Note that some other kinds of mistakes throw, based on our judgment that they are more likely programmer errors than data issues:
48 |
49 | ```js
50 | DateTime.now().set({ blorp: 7 }); //=> kerplosion
51 | ```
52 |
53 | ## Debugging invalid DateTimes
54 |
55 | Because DateTimes fail silently, they can be a pain to debug. Luxon has some features that can help.
56 |
57 | ### invalidReason and invalidExplanation
58 |
59 | Invalid 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
60 |
61 | ```js
62 | var dt = DateTime.now().setZone("America/Blorp");
63 | dt.invalidReason; //=> 'unsupported zone'
64 | dt.invalidExplanation; //=> 'the zone "America/Blorp" is not supported'
65 | ```
66 |
67 | ### throwOnInvalid
68 |
69 | You can make Luxon throw whenever it creates an invalid DateTime. The message will combine `invalidReason` and `invalidExplanation`:
70 |
71 | ```js
72 | Settings.throwOnInvalid = true;
73 | DateTime.now().setZone("America/Blorp"); //=> Error: Invalid DateTime: unsupported zone: the zone "America/Blorp" is not supported
74 | ```
75 |
76 | You can of course leave this on in production too, but be sure to try/catch it appropriately.
77 |
78 | ## Invalid Durations
79 |
80 | Durations can be invalid too. The easiest way to get one is to diff an invalid DateTime.
81 |
82 | ```js
83 | DateTime.local(2017, 28).diffNow().isValid; //=> false
84 | ```
85 |
86 | ## Invalid Intervals
87 |
88 | Intervals can be invalid. This can happen a few different ways:
89 |
90 | - The end time is before the start time
91 | - It was created from invalid DateTime or Duration
92 |
--------------------------------------------------------------------------------
/docs/why.md:
--------------------------------------------------------------------------------
1 | # Why does Luxon exist?
2 |
3 | What'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.
4 |
5 | ## A disclaimer
6 |
7 | I 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.
8 |
9 | ## Origin
10 |
11 | Luxon 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:
12 |
13 | - 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.
14 | - 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.
15 | - I wanted to completely rethink how internationalization worked by using the Intl API that comes packaged in browsers.
16 | - I wanted to use a modern JS toolchain, which would require a major retrofit to Moment.
17 |
18 | So 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.
19 |
20 | ## Ideas in Luxon
21 |
22 | Luxon is built around a few core ideas:
23 |
24 | 1. Keep the basic chainable date wrapper idea from Moment.
25 | 1. Make all the types immutable.
26 | 1. Make the API explicit; different methods do different things and have well-defined options.
27 | 1. Use the Intl API to provide internationalization, including token parsing. Fall back to English if the browser doesn't support those APIs.
28 | 1. Abuse the Intl API horribly to provide time zone support. Only possible for modern browsers.
29 | 1. Provide more comprehensive duration support.
30 | 1. Directly provide interval support.
31 | 1. Write inline docs for everything.
32 |
33 | These ideas have some big advantages:
34 |
35 | 1. It's much easier to understand and debug code that uses Luxon.
36 | 1. Using native browser capabilities for internationalization leads to a much better behavior and is dramatically easier to maintain.
37 | 1. Luxon has the best time zone support of any JS date library.
38 | 1. Luxon's durations are both flexible and easy to use.
39 | 1. The documentation is very good.
40 |
41 | They also have some disadvantages:
42 |
43 | 1. Using modern browser capabilities means that the fallback behavior introduces complexity for the programmer.
44 | 1. Never keeping internationalized strings in the code base means that some capabilities have to wait until the browsers provide it.
45 | 1. Some aspects of the Intl API are browser-dependent, which means Luxon's behavior is too.
46 |
47 | ## Place in the Moment project
48 |
49 | Luxon lives in the Moment project because, basically, we all really like it, and it represents a huge improvement.
50 |
51 | But 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.
52 |
53 | Luxon 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.
54 |
55 | So 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.
56 |
57 | There, now you know as much as I do.
58 |
59 | ## Future plans
60 |
61 | Luxon 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.
62 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | testEnvironment: "node",
3 | roots: ["test"],
4 | coverageDirectory: "build/coverage",
5 | collectCoverageFrom: ["src/**/*.js", "!src/zone.js"],
6 | transform: {
7 | "^.+\\.js$": "babel-jest",
8 | },
9 | };
10 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "luxon",
3 | "version": "3.6.1",
4 | "description": "Immutable date wrapper",
5 | "author": "Isaac Cambron",
6 | "keywords": [
7 | "date",
8 | "immutable"
9 | ],
10 | "repository": "https://github.com/moment/luxon",
11 | "exports": {
12 | ".": {
13 | "import": "./src/luxon.js",
14 | "require": "./build/node/luxon.js"
15 | },
16 | "./package.json": "./package.json"
17 | },
18 | "scripts": {
19 | "build": "babel-node tasks/buildAll.js",
20 | "build-node": "babel-node tasks/buildNode.js",
21 | "build-global": "babel-node tasks/buildGlobal.js",
22 | "jest": "jest",
23 | "test": "jest --coverage",
24 | "api-docs": "mkdir -p build && documentation build src/luxon.js -f html -o build/api-docs && sed -i.bak 's/<\\/body>/
6 |
7 |
8 |
9 |