luxon Documentation

A library for working with dates and times in JS

16,433 stars JavaScript JavaScript 1 file ยท ~19,883 tokens #dates#datetime#intl#times#timezones
RAW Doc

Docker & Self-Hosted Deployment Verified OCI

Deploy luxon on any server, homelab, or cloud container platform.

1-Click Terminal Command
bash (docker run)
docker run -d --name luxon --restart always moment/luxon:latest

File: README.md

Luxon

MIT License Build Status NPM version Coverage Status PRs welcome

Luxon is a library for working with dates and times in JavaScript.

js
DateTime.now().setZone("America/New_York").minus({ weeks: 1 }).endOf("day").toISO();

๐Ÿš€ The road to Luxon 4.0

Please read about and share your feedback on the plans for the next major version of Luxon!

Upgrading to 3.0

Guide

Features

  • DateTime, Duration, and Interval types.
  • Immutable, chainable, unambiguous API.
  • Parsing and formatting for common and custom formats.
  • Native time zone and Intl support (no locale or tz files).

Download/install

Download/install instructions

Documentation

Development

See contributing.

Phasers to stun


File: docker/readme.md

Luxon provides a Docker container and some wrapping scripts to make it easier to run the tests.

  1. The Dockerfile is really just here as an FYI. You shouldn't need to interact with it
  2. npm is a bash script that runs npm run [arg] inside the Docker container.

File: docs/calendars.md

Calendars

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.

Fully supported calendars

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:

js
DateTime.fromISO('2017-W23-3').plus({ weeks: 1, days: 2 }).toISOWeekDate(); //=>  '2017-W24-5'

The main reason I bring all this is up is to contrast it with the capabilities for other calendars described below.

Output calendars

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".

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".

The output calendar is a property of the DateTime itself. For example:

js
var dtHebrew = DateTime.now().reconfigure({ outputCalendar: "hebrew" });
dtHebrew.outputCalendar; //=> 'hebrew'
dtHebrew.toLocaleString() //=> '4 Tishri 5778'

You can modulate the structure of that string with arguments to toLocaleString (see the docs on that), but the point here is just that you got the alternative calendar.

Generally supported calendars

Here's a table of the different calendars with examples generated formatting the same date generated like this:

js
DateTime.fromObject({ outputCalendar: c }).toLocaleString(DateTime.DATE_FULL);

Since Luxon uses the browser's Intl API, you can use all the supported calendars.
(See Intl.Locale.prototype.getCalendars() for a full list)

Calendar Example
buddhist September 24, 2560 BE
chinese Eighth Month 5, 2017
coptic Tout 14, 1734 ERA1
ethioaa Meskerem 14, 7510 ERA0
ethiopic Meskerem 14, 2010 ERA1
hebrew 4 Tishri 5778
indian Asvina 2, 1939 Saka
islamic Muharram 4, 1439 AH
islamic-civil Muharram 3, 1439 AH
islamic-umalqura Muharram 3, 1439 AH
iso8601 September 24, 2017
japanese September 24, 29 Heisei
persian Mehr 2, 1396 AP
roc September 24, 106 Minguo

Default output calendar

You can set the default output calendar for new DateTime instances like this:

js
Settings.defaultOutputCalendar = 'persian';

File: docs/formatting.md

Formatting

This section covers creating strings to represent a DateTime. There are three types of formatting capabilities:

  1. Technical formats like ISO 8601 and RFC 2822
  2. Internationalizable human-readable formats
  3. Token-based formatting

Technical formats (strings for computers)

ISO 8601

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:

js
dt.toISO(); //=> '2017-04-20T11:32:00.000-04:00'
dt.toISODate(); //=> '2017-04-20'
dt.toISOWeekDate(); //=> '2017-W17-7'
dt.toISOTime(); //=> '11:32:00.000-04:00'

Generally, you'll want the first one. Use it by default when building or interacting with APIs, communicating times over a wire, etc.

HTTP and RFC 2822

There 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.

js
dt.toRFC2822(); //=> 'Thu, 20 Apr 2017 11:32:00 -0400'
dt.toHTTP(); //=> 'Thu, 20 Apr 2017 03:32:00 GMT'

Unix timestamps

DateTime objects can also be converted to numerical Unix timestamps:

js
dt.toMillis(); //=> 1492702320000
dt.toSeconds(); //=> 1492702320.000
dt.toUnixInteger(); // => 1492702320
dt.valueOf(); //=> 1492702320000, same as .toMillis()

toLocaleString (strings for humans)

The basics

Modern 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:

js
dt.toLocaleString(); //=> '4/20/2017'
dt.toLocaleString(DateTime.DATETIME_FULL); //=> 'April 20, 2017 at 11:32 AM EDT'
dt.setLocale('fr').toLocaleString(DateTime.DATETIME_FULL); //=> '20 avril 2017 ร  11:32 UTCโˆ’4'

Intl.DateTimeFormat

In 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. For example:

js
dt.toLocaleString({ month: 'long', day: 'numeric' }); //=> 'April 20'

And that's all the preset is:

text
DateTime.DATETIME_FULL;  //=> {
                         //     year: 'numeric',
                         //     month: 'long',
                         //     day: 'numeric',
                         //     hour: 'numeric',
                         //     minute: '2-digit',
                         //     timeZoneName: 'short'
                         //   }

This also means you can modify the presets as you choose:

js
dt.toLocaleString(DateTime.DATE_SHORT); //=>  '4/20/2017'
var newFormat = {...DateTime.DATE_SHORT, weekday: 'long' };
dt.toLocaleString(newFormat); //=>  'Thursday, 4/20/2017'

Presets

Here's the full set of provided presets using the October 14, 1983 at 13:30:23 as an example.

Name Description Example in en_US Example in fr
DATE_SHORT short date 10/14/1983 14/10/1983
DATE_MED abbreviated date Oct 14, 1983 14 oct. 1983
DATE_MED_WITH_WEEKDAY abbreviated date with abbreviated weekday Fri, Oct 14, 1983 ven. 14 oct. 1983
DATE_FULL full date October 14, 1983 14 octobre 1983
DATE_HUGE full date with weekday Friday, October 14, 1983 vendredi 14 octobre 1983
TIME_SIMPLE time 1:30 PM 13:30
TIME_WITH_SECONDS time with seconds 1:30:23 PM 13:30:23
TIME_WITH_SHORT_OFFSET time with seconds and abbreviated named offset 1:30:23 PM EDT 13:30:23 UTCโˆ’4
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
TIME_24_SIMPLE 24-hour time 13:30 13:30
TIME_24_WITH_SECONDS 24-hour time with seconds 13:30:23 13:30:23
TIME_24_WITH_SHORT_OFFSET 24-hour time with seconds and abbreviated named offset 13:30:23 EDT 13:30:23 UTCโˆ’4
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
DATETIME_SHORT short date & time 10/14/1983, 1:30 PM 14/10/1983 ร  13:30
DATETIME_MED abbreviated date & time Oct 14, 1983, 1:30 PM 14 oct. 1983 ร  13:30
DATETIME_MED_WITH_WEEKDAY abbreviated date & time with abbreviated weekday Fri, Oct 14, 1983, 1:30 PM ven. 14 oct. 1983 ร  13:30
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
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
DATETIME_SHORT_WITH_SECONDS short date & time with seconds 10/14/1983, 1:30:23 PM 14/10/1983 ร  13:30:23
DATETIME_MED_WITH_SECONDS abbreviated date & time with seconds Oct 14, 1983, 1:30:23 PM 14 oct. 1983 ร  13:30:23
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
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

Intl

toLocaleString's behavior is affected by the DateTime's locale, numberingSystem, and outputCalendar properties. See the Intl section for more.

Formatting with tokens (strings for Cthulhu)

This section covers generating strings from DateTimes with programmer-specified formats.

Consider alternatives

You 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.

toFormat

See DateTime#toFormat for the API signature. As a brief motivating example:

js
DateTime.fromISO('2014-08-06T13:07:04.054').toFormat('yyyy LLL dd'); //=> '2014 Aug 06'

The supported tokens are described in the table below.

Intl

All 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.

js
DateTime.fromISO('2014-08-06T13:07:04.054')
  .setLocale('fr')
  .toFormat('yyyy LLL dd'); //=> '2014 aoรปt 06'

Note 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).

Escaping

You may escape strings using single quotes:

js
DateTime.now().toFormat("HH 'hours and' mm 'minutes'"); //=> '20 hours and 55 minutes'

Single quotes themselves can be escaped by doubling them:

js
DateTime.now().toFormat("MMM d ''yy"); //=> Apr 7 '25

Standalone vs format tokens

Some 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.

js
var d = DateTime.fromISO('2014-08-06T13:07:04.054').setLocale('ru');
d.toFormat('LLLL'); //=> 'ะฐะฒะณัƒัั‚' (standalone)
d.toFormat('MMMM'); //=> 'ะฐะฒะณัƒัั‚ะฐ' (format)

Macro tokens

Some 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.

js
DateTime.fromISO('2014-08-06T13:07:04.054').toFormat('ff'); //=> 'Aug 6, 2014, 1:07 PM'

The macro options available correspond one-to-one with the preset formats defined for toLocaleString.

Table of tokens

(Examples below given for 2014-08-06T13:07:04.054 considered as a local time in America/New_York).

Standalone token Format token Description Example
S millisecond, no padding 54
SSS millisecond, padded to 3 054
u fractional seconds, functionally identical to SSS 054
uu fractional seconds, between 0 and 99, padded to 2 05
uuu fractional seconds, between 0 and 9 0
s second, no padding 4
ss second, padded to 2 padding 04
m minute, no padding 7
mm minute, padded to 2 07
h hour in 12-hour time, no padding 1
hh hour in 12-hour time, padded to 2 01
H hour in 24-hour time, no padding 9
HH hour in 24-hour time, padded to 2 13
Z narrow offset +5
ZZ short offset +05:00
ZZZ techie offset +0500
ZZZZ abbreviated named offset EST
ZZZZZ unabbreviated named offset Eastern Standard Time
z IANA zone America/New_York
a meridiem AM
d day of the month, no padding 6
dd day of the month, padded to 2 06
c E day of the week, as number from 1-7 (Monday is 1, Sunday is 7) 3
ccc EEE day of the week, as an abbreviate localized string Wed
cccc EEEE day of the week, as an unabbreviated localized string Wednesday
ccccc EEEEE day of the week, as a single localized letter W
L M month as an unpadded number 8
LL MM month as a padded number 08
LLL MMM month as an abbreviated localized string Aug
LLLL MMMM month as an unabbreviated localized string August
LLLLL MMMMM month as a single localized letter A
y year, unpadded 2014
yy two-digit year 14
yyyy four- to six- digit year, pads to 4 2014
G abbreviated localized era AD
GG unabbreviated localized era Anno Domini
GGGGG one-letter localized era A
kk ISO week year, unpadded 14
kkkk ISO week year, padded to 4 2014
W ISO week number, unpadded 32
WW ISO week number, padded to 2 32
ii Local week year, unpadded 14
iiii Local week year, padded to 4 2014
n Local week number, unpadded 32
nn Local week number, padded to 2 32
o ordinal (day of year), unpadded 218
ooo ordinal (day of year), padded to 3 218
q quarter, no padding 3
qq quarter, padded to 2 03
D localized numeric date 9/4/2017
DD localized date with abbreviated month Aug 6, 2014
DDD localized date with full month August 6, 2014
DDDD localized date with full month and weekday Wednesday, August 6, 2014
t localized time 9:07 AM
tt localized time with seconds 1:07:04 PM
ttt localized time with seconds and abbreviated offset 1:07:04 PM EDT
tttt localized time with seconds and full offset 1:07:04 PM Eastern Daylight Time
T localized 24-hour time 13:07
TT localized 24-hour time with seconds 13:07:04
TTT localized 24-hour time with seconds and abbreviated offset 13:07:04 EDT
TTTT localized 24-hour time with seconds and full offset 13:07:04 Eastern Daylight Time
f short localized date and time 8/6/2014, 1:07 PM
ff less short localized date and time Aug 6, 2014, 1:07 PM
fff verbose localized date and time August 6, 2014, 1:07 PM EDT
ffff extra verbose localized date and time Wednesday, August 6, 2014, 1:07 PM Eastern Daylight Time
F short localized date and time with seconds 8/6/2014, 1:07:04 PM
FF less short localized date and time with seconds Aug 6, 2014, 1:07:04 PM
FFF verbose localized date and time with seconds August 6, 2014, 1:07:04 PM EDT
FFFF extra verbose localized date and time with seconds Wednesday, August 6, 2014, 1:07:04 PM Eastern Daylight Time
X unix timestamp in seconds 1407287224
x unix timestamp in milliseconds 1407287224054

File: docs/home.md

Luxon

Luxon is a library for dealing with dates and times in JavaScript.

js
DateTime.now().setZone('America/New_York').minus({weeks:1}).endOf('day').toISO();

Features

  • A nice API for working with datetimes
  • Interval support (from time x to time y)
  • Duration support (14 days, 5 minutes, 33 seconds)
  • Parsing and Formatting datetimes, intervals, and durations
  • Internationalization of strings using the Intl API
  • Detailed and unambiguous math operations
  • Built-in handling of time zones
  • Partial support for multiple calendar systems

For more, see the docs on the left, including the api docs

Getting started

Logo by John Dalziel


File: docs/install.md

Install guide

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 for additional details.

Basic browser setup

You can also load the files from a CDN.

Just include Luxon in a script tag. You can access its various classes through the luxon global.

html
<script src="luxon.js"></script>

You may wish to alias the classes you use:

js
var DateTime = luxon.DateTime;

Node.js

Supports Node.js 6+. Install via NPM:

text
npm install --save luxon
js
const { DateTime } = require("luxon");

If you want to work with locales, you need ICU support:

  1. For Node.js 13+, it comes built-in, no action necessary
  2. For older versions of Node.js (only 12 is supported), you need to install it yourself:
    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
    2. Install the ICU data externally and point Node.js to it. The instructions on how to do that are below.

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:

text
npm install full-icu
node --icu-data-dir=./node_modules/full-icu

You can also point to the data with an environment var, like this:

text
NODE_ICU_DATA="$(pwd)/node_modules/full-icu" node

AMD (System.js, RequireJS, etc)

js
requirejs(["luxon"], function(luxon) {
  //...
});

ES6

js
import { DateTime } from "luxon";

Webpack

text
npm install --save luxon
js
import { DateTime } from "luxon";

Types

There are third-party typing files for Flow (via flow-typed) and TypeScript (via DefinitelyTyped).

For Flow, use:

text
flow-typed install luxon

For TypeScript, use:

text
npm install --save-dev @types/luxon

React Native

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.

For React Native >=0.60, you should configure the build flavor of jsc in android/app/build.gradle:

diff
-def jscFlavor = 'org.webkit:android-jsc:+'
+def jscFlavor = 'org.webkit:android-jsc-intl:+'

For even older versions of React Native you can use jsc-android-buildscripts to fix it.


File: docs/intl.md

Intl

Luxon uses the native Intl API to provide easy-to-use internationalization. A quick example:

js
DateTime.now()
  .setLocale("el")
  .toLocaleString(DateTime.DATE_FULL); //=>  '24 ฮฃฮตฯ€ฯ„ฮตฮผฮฒฯฮฏฮฟฯ… 2017'

Making sure you have access to other locales

Please see the install guide 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.

How locales work

Luxon DateTimes can be configured using BCP 47 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 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.

The rest of this document will concentrate on what Luxon does when provided with locale information.

Setting the locale

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.

You can generally set it at construction time:

js
var dt = DateTime.fromISO("2017-09-24", { locale: "fr" });
dt.locale; //=> 'fr'

In 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.

You can change the locale of a DateTime instance (meaning, create a clone DateTime with a different locale) using setLocale:

js
DateTime.now().setLocale("fr").locale; //=> 'fr'

setLocale is just a convenience for reconfigure:

js
DateTime.now().reconfigure({ locale: "fr" }).locale; //=> 'fr'

Default locale

Out-of-the-box behavior

By 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.

As 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.

However, 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.

Setting the default

You can set a default locale so that new instances will always be created with the specified locale:

js
Settings.defaultLocale = "fr";
DateTime.now().locale; //=> 'fr'

Note that this also alters the behavior of DateTime#toFormat and DateTime#fromFormat.

Using the system locale in string parsing

You 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:

js
Settings.defaultLocale = DateTime.now().resolvedLocaleOptions().locale;

Checking what you got

The 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:

js
DateTime.local({ locale: "fr-co" }).resolvedLocaleOptions(); //=> { locale: 'fr',
//     numberingSystem: 'latn',
//     outputCalendar: 'gregory' }

Methods affected by the locale

Formatting

The most important method affected by the locale setting is toLocaleString, which allows you to produce internationalized, human-readable strings.

js
dt.setLocale("fr").toLocaleString(DateTime.DATE_FULL); //=> '25 septembre 2017'

That'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:

js
dt.toLocaleString({ locale: "es" , ...DateTime.DATE_FULL }); //=> '25 de septiembre de 2017'

Ad-hoc formatting also respects the locale:

js
dt.setLocale("fr").toFormat("MMMM dd, yyyy GG"); //=> 'septembre 25, 2017 aprรจs Jรฉsus-Christ'

Parsing

You can parse localized strings:

js
DateTime.fromFormat("septembre 25, 2017 aprรจs Jรฉsus-Christ", "MMMM dd, yyyy GG", { locale: "fr" });

Listing

Some of the methods in the Info class let you list strings like months, weekdays, and eras, and they can be localized:

js
Info.months("long", { locale: "fr" }); //=> [ 'janvier', 'fรฉvrier', ...
Info.weekdays("long", { locale: "fr" }); //=> [ 'lundi', 'mardi', ...
Info.eras("long", { locale: "fr" }); //=> [ 'avant Jรฉsus-Christ', 'aprรจs Jรฉsus-Christ' ]

numberingSystem

DateTimes 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:

js
var dt = DateTime.now().setLocale("ar");

dt.resolvedLocaleOptions(); //=> { locale: 'ar',
//     numberingSystem: 'arab',
//     outputCalendar: 'gregory' }

dt.toLocaleString(); //=> 'ูขูคโ€/ูฉโ€/ูขู ูกูง'

For 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:

js
const dt = DateTime.local().reconfigure({ locale: "it", numberingSystem: "beng" });
dt.toLocaleString(DateTime.DATE_FULL); //=> 'เงจเงช settembre เงจเงฆเงงเงญ'

Similar to locale, you can set the default numbering system for new instances:

js
Settings.defaultNumberingSystem = "beng";

Locale-based weeks

Most of Luxon uses the ISO week date system when working with week-related data.
This 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.

This definition works for most use-cases, however locales can define different rules. For example, in many English-speaking countries
the week is said to start on Sunday and the 1 January always defines the first week of the year. This information is
available through the Luxon as well.

Note that your runtime needs to support Intl.Locale#getWeekInfo for this to have an effect. If unsupported, Luxon will fall back
to using the ISO week dates.

Accessing locale-based week info

The Info class exposes methods getStartOfWeek, getMinimumDaysInFirstWeek and getWeekendWeekdays for informational
purposes.

Accessing locale-based week data

js
const dt = DateTime.local(2022, 1, 1, { locale: "en-US" });
dt.localWeekday // 7, because Saturday is the 7th day of the week in en-US
dt.localWeekNumber // 1, because 1 January is always in the first week in en-US
dt.localWeekYear // 2022, because 1 January is always in the first week in en-US
dt.weeksInLocalWeekYear // 53, because 2022 has 53 weeks in en-US

Using locale-based week data when creating DateTimes

When creating a DateTime instance using fromObject, you can use the localWeekday, localWeekNumber and localWeekYear
properties. They will use the same locale that the newly created DateTime will use, either explicitly provided or falling back
to the system default.

js
const dt = DateTime.fromObject({localWeekYear: 2022, localWeekNumber: 53, localWeekday: 7});
dt.toISODate(); // 2022-12-31

Setting locale-based week data

When modifying an existing DateTime instance using set, you can use the localWeekday, localWeekNumber and localWeekYear
properties. They will use the locale of the existing DateTime as reference.

js
const dt = DateTime.local(2022, 1, 2, { locale: "en-US" });
const modified = dt.set({localWeekNumber: 2});
modified.toISODate(); // 2022-01-08

Locale-based weeks with math

The methods startOf, endOf, hasSame of DateTime as well as count of Interval accept an option useLocaleWeeks.
If enabled, the methods will treat the week unit according to the locale, respecting the correct start of the week.

js
const dt = DateTime.local(2022, 1, 6, { locale: "en-US" });
const startOfWeek = dt.startOf('week', {useLocaleWeeks: true});
startOfWeek.toISODate(); // 2022-01-02, a Sunday

Overriding defaults

You can override the runtime-provided defaults for the week settings using Settings.defaultWeekSettings:

js
Settings.defaultWeekSettings = { firstDay: 7, minimalDays: 1, weekend: [6, 7] }

File: docs/math.md

Math

This page covers some oddball topics related to date and time math, which has some quirky corner cases.

Calendar math vs time math

The basics

Math 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:

js
DateTime.local(2017, 2, 13).plus({ months: 1 }).toISODate() //=> '2017-03-13'

DateTime.local(2017, 2, 13).plus({ days: 30 }).toISODate() //=> '2017-03-15'

More generally we can differentiate two modes of math:

  • Calendar math works with higher-order, variable-length units like years and months
  • Time math works with lower-order, constant-length units such as hours, minutes, and seconds.

Which units use which math?

These units use calendar math:

  • Years vary because of leap years.
  • Months vary because they're just different lengths.
  • Days vary because DST transitions mean some days are 23 or 25 hours long.
  • Quarters are always three months, but months vary in length so quarters do too.
  • Weeks are always the same number of days, but days vary so weeks do too.

These units use time math:

  • Hours are always 60 minutes
  • Minutes are always 60 seconds
  • Seconds are always 1000 milliseconds

Leap seconds

Luxon (as JavaScript in general) doesn't account for leap seconds; 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.

The practical effect of leaps seconds are quite limited:

  1. You can't represent the leap second itself (i.e., DateTime.utc(2016, 12, 31, 23, 59, 60).isValid returns false).
  2. 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.

How to think about calendar math

It'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:

js
var d = new Date('2017-02-13')
d.setMonth(d.getMonth() + 1)
d.toLocaleString() //=> '3/13/2017, 12:00:00 AM'

And 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.

DSTs

There's a whole section about this in the time zones documentation. But here's a quick example (Spring Forward is early on March 12 in my time zone):

js
var start = DateTime.local(2017, 3, 11, 10);
start.hour                          //=> 10, just for comparison
start.plus({days: 1}).hour          //=> 10, stayed the same
start.plus({hours: 24}).hour        //=> 11, DST pushed forward an hour

So in adding a day, we kept the hour at 10, even though that's only 23 hours later.

Time math

Time 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.

Math with multiple units

It's possible to do math with multiple units:

js
DateTime.fromISO('2017-05-15').plus({months: 2, days: 6}).toISODate(); //=> '2017-07-21'

This isn't as simple as it looks. For example, what should you expect this to do?

js
DateTime.fromISO('2017-04-30').plus({months: 1, days: 1}).toISODate();

If 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.

Luxon 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:

js
DateTime.fromISO('2017-04-30').plus({days: 1}).plus({months: 1}).toISODate() //=> '2017-06-01'

It's not a coincidence that Luxon's interface makes it awkward to do this wrong.

Comparing DateTimes

DateTime 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.

js
d1 < d2 // is d1 before d2?

However, 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:

js
d1.toMillis() === d2.toMillis() // are d1 and d2 the same instant in time?
+d1 === +d2 // same test, using object coercion

You may also use #hasSame to make more subtle comparisons:

js
d1.hasSame(d2, 'year');   // both DateTimes have the same calendar year
d1.hasSame(d2, 'day');    // both DateTimes have the same calendar day (which implies they also have the same calendar year and month)

Note 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).

If you'd like to compare using a specific unit, you can achieve this by combining #startOf and the #valueOf comparisons above.

js
var d1 = DateTime.fromISO('2017-04-30');
var d2 = DateTime.fromISO('2017-04-01');

d2 < d1                                   //=> true
d2.startOf('year') < d1.startOf('year')   //=> false
d2.startOf('month') < d1.startOf('month') //=> false
d2.startOf('day') < d1.startOf('day')     //=> true

Duration math

Basics

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.

Here's some very basic stuff to get us going:

js
var dur = Duration.fromObject({ days: 3, hours: 6})

// examine it
dur.toObject()          //=> { days: 3, hours: 6 }

// express in minutes
dur.as('minutes')       //=> 4680

// convert to minutes
dur.shiftTo('minutes').toObject() //=> { minutes: 4680 }

// add to a DateTime
DateTime.fromISO("2017-05-15").plus(dur).toISO() //=> '2017-05-18T06:00:00.000-04:00'

Diffs

You 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:

js
var end = DateTime.fromISO('2017-03-13');
var start = DateTime.fromISO('2017-02-13');

var diffInMonths = end.diff(start, 'months');
diffInMonths.toObject(); //=> { months: 1 }

Notice we had to pick the unit to keep track of the diff in. The default is milliseconds:

js
var diff = end.diff(start);
diff.toObject() //=> { milliseconds: 2415600000 }

Finally, you can diff using multiple units:

js
var end = DateTime.fromISO('2017-03-13');
var start = DateTime.fromISO('2017-02-11');
end.diff(start, ['months', 'days']).toObject() //=> { months: 1, days: 2 }

Casual vs longterm conversion accuracy

Durations represent bundles of time with specific units, but Luxon allows you to convert between them:

  • shiftTo returns a new Duration denominated in the specified units.
  • as converts the duration to just that unit and returns its value
js
var dur = Duration.fromObject({ months: 4, weeks: 2, days: 6 })

dur.as('days')                            //=> 140
dur.shiftTo('days').toObject()            //=> { days: 140 }
dur.shiftTo('weeks', 'hours').toObject()  //=> { weeks: 18, hours: 144 }

But how do those conversions actually work? First, uncontroversially:

  • 1 week = 7 days
  • 1 day = 24 hours
  • 1 hour = 60 minutes
  • 1 minute = 60 seconds
  • 1 second = 1000 milliseconds

These 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:

Month Week Day
Year 12 52 365
Quarter 3 13 91
Month 4 30

These should match your intuition and for most purposes they work well. But they're not just wrong; they're not even self-consistent:

js
Duration.fromObject({ years:1 }).shiftTo('months').shiftTo('days').as('years') //=> 0.9863013698630136

This is because 12 * 30 != 365. These errors can be annoying, but they can also cause significant issues if the errors accumulate:

js
var dur = Duration.fromObject({ years: 50000 });
DateTime.now().plus(dur.shiftTo('milliseconds')).year //=> 51984
DateTime.now().plus(dur).year                         //=> 52017

Those are 33 years apart! So Luxon offers an alternative conversion scheme called "longterm", based on the 400-year calendar cycle:

Month Week Day
Year 12 52.1775 365.2425
Quarter 3 13.04435 91.310625
Month 4.348125 30.436875

You can see why these are irritating to work with, which is why they're not the default.

Luxon 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.

js
Duration.fromObject({ years: 23 }, { conversionAccuracy: 'longterm' });
Duration.fromISO('PY23', { conversionAccuracy: 'longterm' });

end.diff(start, 'days', { conversionAccuracy: 'longterm' })

You can also create an accurate Duration out of an existing one:

js
var pedanticDuration = casualDuration.reconfigure({ conversionAccuracy: 'longterm' });

These Durations will do their conversions differently.

Losing information

Be careful of converting between units. It's easy to lose information. Let's say we converted a diff into days:

js
var end = DateTime.fromISO('2017-03-13');
var start = DateTime.fromISO('2017-02-13');

var diffInMonths = end.diff(start, 'months');
diffInMonths.as('days'); //=> 30

That'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!

js
var diffInDays = end.diff(start, 'days');
diffInDays.toObject(); //=> { days: 28 }

It'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:

js
var diff = end.diff(start); // default unit is milliseconds

// wtf, that's not a month!
diff.as('months'); //=> 0.9319444 

// it's not even the right number of days! (hint: my time zone has a DST)
diff.shiftTo('hours').as('days'); //=> 27.958333333333332

Normally 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.

js
var monthsDiff = end.diff(start, "months");
var daysDiff = end.diff(start, "days");

But 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:

js
var end = DateTime.fromISO('2017-03-13');
var start = DateTime.fromISO('2017-02-13');
var i = Interval.fromDateTimes(start, end);

i.length('days');       //=> 28
i.length('months')      //=> 1

Because 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:

js
i.toDuration('months').toObject(); //=> { months: 1 }
i.toDuration('days').toObject(); //=> { days: 28 }

You can even pick multiple units:

js
end = DateTime.fromISO('2018-05-25');
i = start.until(end);
i.toDuration(['years', 'months', 'days']).toObject(); //=> { years: 1, months: 3, days: 12 }

Of 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.


File: docs/matrix.md

Support matrix

This page covers what platforms are supported by Luxon and what caveats apply to them.

Official support

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.

Browser Versions Intl relative time formatting
Chrome >= 73 โœ“
Firefox >= 65 โœ“
Edge >= 79 โœ“
18 โœ—
Safari >= 14 โœ“
13 โœ—
iOS Safari (iOS version numbers) >= 14 โœ“
Node >= 12 โœ“
  • Those capabilities are explained in the next sections, along with possible polyfill options
  • "w/ICU" refers to providing Node with ICU data. See the install for instructions

Effects of missing features

If the platforms you're targeting has all its boxes above check off, ignore this section.

In the support table above, you can see that some environments are missing capabilities. In the current version of
Luxon, there's only one partially-supported feature, so this is currently pretty simple. (Older versions of Luxon supported
older browsers, so there were nuanced feature caveats. Newer versions will add more caveats as new browser capabilities
become available and Luxon takes advantage of them if they're present.)

  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.

If the browser lacks these capabilities, Luxon tries its best:

Feature Full support No relative time format
Most things OK OK
DateTime#toRelative in en-US OK OK
DateTime#toRelative in other locales Uses English Uses English

Older platforms

  • 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.
  • Older versions of IE probably won't work at all.
  • 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.

Other platforms

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:

js
Info.features(); //=> { relative: false }

Specific notes on other platforms:

  • **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:
diff
-def jscFlavor = 'org.webkit:android-jsc:+'
+def jscFlavor = 'org.webkit:android-jsc-intl:+'

For even older versions of React Native you can use jsc-android-buildscripts to fix it.


File: docs/moment.md

For Moment users

Luxon borrows lots of ideas from Moment.js, but there are a lot of differences too. This document clarifies what they are.

Immutability

Luxon's objects are immutable, whereas Moment's are mutable. For example, in Moment:

js
var m1 = moment();
var m2 = m1.add(1, 'hours');
m1.valueOf() === m2.valueOf(); //=> true

This happens because m1 and m2 are really the same object; add() mutated the object to be an hour later. Compare that to Luxon:

js
var d1 = DateTime.now();
var d2 = d1.plus({ hours: 1 });
d1.valueOf() === d2.valueOf(); //=> false

This 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.

Major functional differences

  1. Months in Luxon are 1-indexed instead of 0-indexed like in Moment and the native Date type.
  2. Localizations and time zones are implemented by the native Intl API (or a polyfill of it), instead of by the library itself.
  3. Luxon has both a Duration type and an Interval type. The Interval type is like Twix.

Other API style differences

  1. Luxon methods often take option objects as their last parameter
  2. Luxon has different static methods for object creation (e.g. fromISO), as opposed to Moment's one function that dispatches based on the input
  3. Luxon parsers are very strict, whereas Moment's are more lenient.
  4. Luxon uses getters instead of accessor methods, so dateTime.year instead of dateTime.year()
  5. Luxon centralizes its "setters", like dateTime.set({year: 2016, month: 4}) instead of dateTime.year(2016).month(4) like in Moment.
  6. Luxon's Durations are a separate top-level class.
  7. 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')).

DateTime method equivalence

Here'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.

Creation

Operation Moment Luxon Notes
Now moment() DateTime.now()
From ISO moment(String) DateTime.fromISO(String)
From RFC 2822 moment(String) DateTime.fromRFC2822(String)
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.
From object moment(Object) DateTime.fromObject(Object)
From timestamp moment(Number) DateTime.fromMillis(Number)
From JS Date moment(Date) DateTime.fromJSDate(Date)
From civil time moment(Array) DateTime.local(Number...) Like DateTime.local(2016, 12, 25, 10, 30)
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
Clone moment(Moment) N/A Immutability makes this pointless; just reuse the object
Use the string's offset parseZone See note Methods taking strings that can specify offset or zone take a setZone argument

Getters and setters

Basic information getters

Property Moment Luxon Notes
Validity isValid() isValid See also invalidReason
Locale locale() locale
Zone tz() zone Moment requires a plugin for this, but not Luxon

Unit getters

Property Moment Luxon Notes
Year year() year
Month month() month
Day of month date() day
Day of week day(), weekday(), isoWeekday() weekday 1-7, Monday is 1, Sunday is 7, per ISO
Day of year dayOfYear() ordinal
Hour of day hour() hour
Minute of hour minute() minute
Second of minute second() second
Millisecond of seconds millisecond() millisecond
Week of ISO week year weekYear, isoWeekYear weekYear
Quarter quarter None Just divide the months by 3

Programmatic get and set

For programmatic getting and setting, Luxon and Moment are very similar here:

Operation Moment Luxon Notes
get value get(String) get(String)
set value set(String, Number) None
set values set(Object) set(Object) Like dt.set({ year: 2016, month: 3 })

Transformation

Operation Moment Luxon Notes
Addition add(Number, String) plus(Object) Like dt.plus({ months: 3, days: 2 })
Subtraction subtract(Number, String) minus(Object) Like dt.minus({ months: 3, days: 2 })
Start of unit startOf(String) startOf(String)
End of unit endOf(String) endOf(String)
Change unit values set(Object) set(Object) Like dt.set({ year: 2016, month: 3 })
Change time zone tz(String) setZone(string) Luxon doesn't require a plugin
Change zone to utc utc() toUTC()
Change local zone local() toLocal()
Change offset utcOffset(Number) None Set the zone instead
Change locale locale(String) setLocale(String)

Query

Question Moment Luxon Notes
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.
Is this time after that time? m1.isAfter(m2) dt1 > dt2
Is this time the same or before that time? m1.isSameOrBefore(m2) dt1 <= dt2
Is this time the same or after that time? m1.isSameOrAfter(m2) dt1 >= dt2
Do these two times have the same [unit]? m1.isSame(m2, unit) dt1.hasSame(dt2, unit)
Is this time's [unit] before that time's? m1.isBefore(m2, unit) dt1.startOf(unit) < dt2.startOf(unit)
Is this time's [unit] after that time's? m1.isAfter(m2, unit) dt1.startOf(unit) > dt2.startOf(unit)
Is this time between these two times? m1.isBetween(m2, m3) Interval.fromDateTimes(dt2, dt3).contains(dt1)
Is this time inside a DST isDST() isInDST
Is this time's year a leap year? isInLeapYear() isInLeapYear
How many days are in this time's month? daysInMonth() daysInMonth
How many days are in this time's year? None daysInYear

Output

Basics

See the formatting guide for more about the string-outputting methods.

Output Moment Luxon Notes
simple string toString() toString() Luxon just uses ISO 8601 for this. See Luxon's toLocaleString()
full ISO 8601 iso() toISO()
ISO date only None toISODate()
ISO time only None toISOTime()
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.
RFC 2822 toRFC2822()
HTTP date string toHTTP()
JS Date toDate() toJSDate()
Epoch time valueOf() toMillis() or valueOf()
Object toObject() toObject()
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.

Humanization

Luxon has toRelative and toRelativeCalendar. For internationalization, they use Intl.RelativeTimeFormat (or fall back to English when it is not supported by the browser).

Operation Moment Luxon
Time from now fromNow() toRelative()
Time from other time from(Moment) toRelative({ base: DateTime })
Time to now toNow() DateTime.local().toRelative({ base: this })
Time to other time to(Moment) otherTime.toRelative({ base: this })
"Calendar time" calendar() toRelativeCalendar()

Durations

Moment Durations and Luxon Durations are broadly similar in purpose and capabilities. The main differences are:

  1. 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 for more.
  2. Luxon does not (yet) have an equivalent of Moment's Duration humanize method. Luxon will add that when Intl.UnitFormat is supported by browsers.
  3. Like DateTimes, Luxon Durations have separate methods for creating objects from different sources.

See the Duration API docs for more.

Intervals

Moment 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.