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

## Basic browser setup

- Download full
- Download minified

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

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

You may wish to alias the classes you use:

    var DateTime = luxon.DateTime;

## Node

Install via NPM:

    npm install --save luxon

    var luxon = require('luxon');

## AMD (System.js, RequireJS, etc)

- Download full
- Download minified

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

## ES6

- Download full
- Download minified

    import { DateTime } from 'luxon';

## Webpack

    npm install --save luxon

    import { DateTime } from 'luxon';

## Meteor

Help wanted.

# A quick tour

Luxon is a library that makes it easier to work with dates and times in Javascript. If you want, add and subtract them, format and parse them, ask them hard questions, and so on, Luxon provides a much easier and comprehensive interface than the native types it wraps. We're going to talk about the most immediately useful subset of that interface.

This is going to be a bit brisk, but keep in mind that the API docs are comprehensive, so if you want to know more, feel free to dive into them.

## Your first DateTime

The most important class in Luxon is DateTime. A DateTime represents a specific millisecond in time, along with a time zone and a locale. Here's one that represents May 15, 2017 at 8:30 in the morning:

    var dt = DateTime.local(2017, 5, 15, 8, 30);

To get the current time, just do this:

    var now = DateTime.local();

DateTime.local takes any number of arguments, all the way out to milliseconds. Underneath, this is just a Javascript Date object. But we've decorated it with lots of useful methods.

## Creating a DateTime

There are lots of ways to create a DateTime by parsing strings or constructing them out of parts. You've already seen one, `DateTime.local()`, but let's talk about two more.

### Create from an object

The most powerful way to create a DateTime instance is to provide an object containing all the information:

    dt = DateTime.fromObject({day: 22, hour: 12, zone: 'America/Los_Angeles', numberingSystem: 'beng'})

Don't worry too much about the properties you don't understand yet; the point is that you can set every attribute of a DateTime when you create it. One thing to notice from the example is that we just set the day and hour; the year and month get defaulted to the current one and the minutes, seconds, and milliseconds get defaulted to 0. So DateTime.fromObject is sort of the power user interface.

### Parse from ISO 8601

Luxon has lots of parsing capabilities, but the most important one is parsing ISO 8601 strings, because they're more-or-less the standard wire format for dates and times. Use DateTime.fromISO.

    DateTime.fromISO("2017-05-15")          //=> May 15, 2017 at midnight
    DateTime.fromISO("2017-05-15T08:30:00") //=> May 15, 2017 at 8:30

You can parse a bunch of other formats, including your own custom ones.

## Getting to know your DateTime instance

Now that we've made some DateTimes, let's see what we can ask of it.

### toString

The first thing we want to see is the DateTime as a string. Luxon returns ISO 8601 strings:

    DateTime.local().toString() //=> '2017-09-14T03:20:34.091-04:00'

### Getting at components

We can get at the components of the time individually through getters. For example:

    dt = DateTime.local()
    dt.year     //=> 2017
    dt.month    //=> 9
    dt.day      //=> 14
    dt.second   //=> 47
    dt.weekday  //=> 4

### Other fun accessors

    dt.zoneName     //=> 'America/New_York'
    dt.offset       //=> -240
    dt.daysInMonth  //=> 30

There are lots more!

## Formatting your DateTime

You may want to output your DateTime to a string for a machine or a human to read. Luxon has lots of tools for this, but two of them are most important. If you want to format a human-readable string, use `toLocaleString`:

    dt.toLocaleString()      //=> '9/14/2017'
    dt.toLocaleString({
      month: 'long',
      day: 'numeric',
      hour: 'numeric',
      minute: 'numeric'})    //=> 'September 14, 3:21 AM'

This works well across different locales (languages) by letting the browser figure out what order the different parts go in and how to punctuate them.

If you want the string read by another program, you almost certainly want to use `toISO`:

    dt.toISO() //=> '2017-09-14T03:21:47.070-04:00'

Custom formats are also supported. See formatting.

## Transforming your DateTime

### Immutability

Luxon objects are immutable. That means that you can't alter them in place, just create altered copies. Throughout the documentation, we use terms like "alter", "change", and "set" loosely, but rest assured we mean "create a new instance with different properties".

### Math

This is easier to show than to tell. All of these calls return new DateTime instances:

    var dt = DateTime.local();
    dt.plus({hours: 3, minutes: 2});
    dt.minus({days: 7});
    dt.startOf('day');
    dt.endOf('hour');

### Set

You can create new instances by overriding specific properties:

    var dt = DateTime.local();
    dt.set({hour: 3}).hour   //=> 3

## Intl

Luxon provides several different Intl capabilities, but the most important one is in formatting:

    var dt = DateTime.local();
    var f = {month: 'long', day: 'numeric'};
    dt.setLocale('fr').toLocaleString(f)      //=> '14 septembre'
    dt.setLocale('en-GB).toLocaleString(f)   //=> '14 September'
    dt.setLocale('en-US).toLocaleString(f)  //=> 'September 14'

Luxon's Info class can also list months or weekdays for different locales:

    Info.months('long', {locale: 'fr'}) //=> [ 'janvier', 'février', 'mars', 'avril', ... ]

## Time zones

Luxon supports time zones. There's a whole big section about it. But briefly, you can create DateTimes in specific zones and change their zones:

    DateTime.fromObject({zone: 'America/Los_Angeles'}) // now, but expressed in LA's local time
    DateTime.local().setZone('America/Los_Angeles') // same

Luxon also supports UTC directly:

    DateTime.utc(2017, 5, 15);
    DateTime.utc();
    DateTime.local().toUTC();
    DateTime.utc().toLocal();

## Durations

The Duration class represents a quantity of time such as "2 hours and 7 minutes". You create them like this:

    var dur = Duration.fromObject({hours: 2, minutes: 7});

They can be add or subtracted from DateTimes like this:

    dt.plus(dur);

They have getters just like DateTime:

    dur.hours   //=> 2
    dur.minutes //=> 7
    dur.seconds //=> 0

And some other useful stuff:

    dur.as('seconds') //=> 7620
    dur.toObject()    //=> { hours: 2, minutes: 7 }
    dur.toISO()       //=> 'PT2H7M'

You can also format, negate, and normalize them. See it all in the Duration API docs.

## Intervals

Intervals are a specific period of time, such as "between now and midnight". They're really a wrapper for two DateTimes that form its endpoints. Here's what you can do with them:

    now = DateTime.local();
    later = DateTime.local(2020, 10, 12);
    i = Interval.fromDateTimes(now, later);

    i.length()                             //=> 97098768468
    i.length('years', true)                //=> 3.0762420239726027
    i.contains(DateTime.local(2019))       //=> true

    i.toISO()       //=> '2017-09-14T04:07:11.532-04:00/2020-10-12T00:00:00.000-04:00'
    i.toString()    //=> '[2017-09-14T04:07:11.532-04:00 – 2020-10-12T00:00:00.000-04:00)

Intervals can be split up into smaller intervals, perform set-like operations with other intervals, and few other handy features. See the Interval API docs.

# Intl

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

    DateTime.local().setLocale('el').toLocaleString(DateTime.DATE_FULL); //=>  '24 Σεπτεμβρίου 2017'

## 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. 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 (i.e. Luxon does the string munging for you).

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

## Setting locale

`locale` is a property of Luxon object, and it defaults to 'en-US'. 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:

    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 `fromString`, 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`:

    DateTime.local().setLocale('fr').locale //=> 'fr'

`setLocale` is just a convenience for `reconfigure`:

    DateTime.local().reconfigure({ locale: 'fr' }).locale; //=> 'fr'

## 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`:

    DateTime.fromObject({locale: 'fr-co'}).resolvedLocaleOpts(); //=> { 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.

    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:

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

Ad-hoc formatting also respects the locale:

    dt.setLocale('fr').toFormat('MMMM dd, yyyy GG'); //=> 'septembre 25, 2017 après Jésus-Christ'

### Parsing

You can parse localized strings:

    DateTime.fromString('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:

    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:

    var dt = DateTime.local().setLocale('ar')

    dt.resolvedLocaleOpts() //=> { 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:

    var dt  = DateTime.local().reconfigure({ locale: 'it', numberingSystem: 'beng' })
    dt.toLocaleString(DateTime.DATE_FULL) //=> '২৪ settembre ২০১৭'

# Time zones and offsets

Luxon has support for time zones. This page explains how to use them.

## Don't worry!

You usually don't need to worry about time zones. Your code runs on a computer with a particular time zone and everything will work consistently in that zone without you doing anything. It's when you want to do complicated stuff across zones that you have to think about it. Even then, here are some pointers to help you avoid situations where you have to think carefully about time zones:

1. Don't make servers think about local times. Configure them to use UTC and write your server's code to work in UTC. Times can often be thought of as a simple count of epoch milliseconds; what you would call that time (e.g. 9:30) in what zone doesn't (again, often) matter.
2. Communicate times between systems in ISO 8601, like "2017-05-15T13:30:34Z" where possible (it doesn't matter if you use Z or some local offset; the point is that it precisely identifies the millisecond on the global timeline).
3. Where possible, only think of time zones as a formatting concern; your application ideally never knows that the time it's working with is called "9:00" until it's being rendered to the user.
4. Barring 3, do as much manipulation of the time (say, adding an hour to the current time) in the client code that's already running in the time zone where the results will matter.

All those things will make it less likely you ever need to work explicitly with time zones and may also save you plenty of other headaches. But those aren't possible for some applications; you might need to work with times in zones other than the one the program is running in, for any number of reasons. And that's where Luxon's time zone support comes in.

## Terminology

Bear with me here. Time zones are pain in the ass. Luxon has lots of tools to deal with them, but there's no getting around the fact that they're complicated. The terminology for time zones and offsets isn't well-established. But let's try to impose some order:

1. An offset is a difference between the local time and the UTC time, such as +5 (hours) or -12:30. They may be expressed directly in minutes, or in hours, or in a combination of minutes and hours. Here we'll use hours.
2. A time zone is a set of rules, associated with a geographical location, that determines the local offset from UTC at any given time. The best way to identify a zone is by its IANA string, such as "America/New_York". That zone says something to the effect of "The offset is -4, except between March and November, when it's -5".
3. A fixed-offset time zone is any time zone that never changes offsets, such as UTC. Luxon supports fixed-offset zones directly; they're specified like UTC+7, which you can interpret as "always with an offset of +7".
4. A named offset is a time zone-specific name for an offset, such as Eastern Daylight Time. It expresses both the zone (America's EST roughly implies America/New_York) and the current offset (EST means +4). They are also confusing in that they overspecify the offset (e.g. for any given time it is unnecessary to specify EST vs EDT; it's always whichever one is right). They are also ambiguous (BST is both British Summer Time and Bangladesh Standard Time), unstandardized, and internationalized (what would a Frenchman call the US's EST?). For all these reasons, you should avoid them when specifying times programmatically. Luxon only supports their use in formatting.

Some subtleties:

1. Multiple zones can have the same offset (think about the US's zones and their Canadian equivalents), though they might not have the same offset all the time, depending on when their DSTs are. Thus zones and offsets have a many-to-many relationship.
2. Just because a time zone doesn't have a DST now doesn't mean it's fixed. Perhaps it had one in the past. Regardless, Luxon does not have first-class access to the list of rules, so it assumes any IANA-specified zone is not fixed and checks for its current offset programmatically.

If all this seems too terse, check out these articles. The terminology in them is subtly different but the concepts are the same:

- Time Zones Aren’t Offsets – Offsets Aren’t Time Zones
- Stack Overflows timezone wiki page

## Luxon works with time zones

Luxon's DateTime class supports zones directly. By default, a date created in Luxon is "in" the local time zone of the machine it's running on. By "in" we mean that the DateTime has, as one of its properties, an associated zone.

It's important to remember that a DateTime represents a specific instant in time and that instant has an unambiguous meaning independent of what time zone you're in; the zone is really piece of social metadata that affects how humans interact with the time, rather than a fact about the passing of time itself. Of course, Luxon is a library for humans, so that social metadata affects Luxon's behavior too. It just doesn't change what time it is.

Specifically, a DateTime's zone affects its behavior in these ways:

1. Times will be formatted as they would be in that zone.
2. Transformations to the DateTime (such as `plus` or `startOf`) will obey any DSTs in that zone that affect the calculation (see "Math across DSTs" below)

Generally speaking, Luxon does not support changing a DateTime's offset, just its zone. That allows it to enforce the behaviors in the list above. The offset for that DateTime is just whatever the zone says it is. If you are unconcerned with the effects above, then you can always give your DateTime a fixed-offset zone.

## Specifying a zone

Luxon's API methods that take a zone as an argument all let you specify the zone in a few ways.

Type | Example | Description
--- | --- | ---
IANA | 'America/New_York' | that zone
local | 'local' | the system's local zone
UTC | 'utc' | Universal Coordinated Time
fixed offset | 'UTC+7' | a fixed offset zone
Zone | new YourZone() | A custom implementation of Luxon's Zone interface (advanced only)

### IANA support

IANA-specified zones are string identifiers like "America/New_York" or "Asia/Tokyo". Luxon gains direct support for them by abusing built-in Intl APIs. However, your environment may not support them, in which case, you can't fiddle with the zones directly. You can always use the local zone your system is in, UTC, and any fixed-offset zone like UTC+7. You can check if your runtime environment supports IANA zones with our handy utility:

    Info.features().zones; //=> true

If you're unsure if all your target environments (browser versions and Node versions) support this, check out the Support Matrix. You can generally count on modern browsers to have this feature, except IE (it is supported in Edge). You may also polyfill your environment.

If you specify a zone and your environment doesn't support that zone, you'll get an invalid DateTime. That could be because the environment doesn't support zones at all, because for whatever reason doesn't support that particular zone, or because the zone is just bogus. Like this:

    bogus = DateTime.local().setZone('America/Bogus')

    bogus.isValid;         //=> false
    bogus.invalidReason;   //=> 'unsupported zone'

## Creating DateTimes

### Local by default

By default, DateTime instances are created in the system's local zone and parsed strings are interpreted as specifying times in the system's local zone. For example, my computer is configured to use `America/New_York`, which has an offset of -4 in May:

    var local = DateTime.local(2017, 05, 15, 09, 10, 23);

    local.zoneName;                //=> 'America/New_York'
    local.toString();              //=> '2017-05-15T09:10:23.000-04:00'

    var iso = DateTime.fromISO("2017-05-15T09:10:23");

    iso.zoneName;                  //=> 'America/New_York'
    iso.toString();                //=> '2017-05-15T09:10:23.000-04:00'

### Creating DateTimes in a zone

Many of Luxon's factory methods allow you to tell it specifically what zone to create the DateTime in:

    var overrideZone = DateTime.fromISO("2017-05-15T09:10:23", { zone: 'Europe/Paris' });

    overrideZone.zoneName;         //=> 'Europe/Paris'
    overrideZone.toString();       //=> '2017-05-15T09:10:23.000+02:00'

Note two things:

1. The date and time specified in the string was interpreted as specifying a Parisian local time (i.e. it's the time that corresponds to what would be called 9:10 there).
2. The resulting DateTime object is in Europe/Paris.

Those are conceptually independent (i.e. Luxon could have converted the time to the local zone), but it practice it's more convenient for the same option to govern both.

In addition, one static method, `utc()`, specifically interprets the input as being specified in UTC. It also creates a DateTime in UTC:

    var utc = DateTime.utc(2017, 05, 15, 09, 10, 23);

    utc.zoneName;                  //=> 'UTC'
    utc.toString();                //=> '2017-05-15T09:10:23.000Z'

### Strings that specify an offset

Some input strings may specify an offset as part of the string itself. In these case, Luxon interprets the time as being specified with that offset, but converts the resulting DateTime into the system's local zone:

    var specifyOffset = DateTime.fromISO("2017-05-15T09:10:23-09:00");

    specifyOffset.zoneName;         //=> 'America/New_York'
    specifyOffset.toString();       //=> '2017-05-15T14:10:23.000-04:00'

    var specifyZone = DateTime.fromString("2017-05-15T09:10:23 Europe/Paris", "yyyy-MM-dd'T'HH:mm:ss z");

    specifyZone.zoneName           //=> 'America/New_York'
    specifyZone.toString()         //=> '2017-05-15T03:10:23.000-04:00'

...unless a zone is specified as an option (see previous section), in which case the DateTime gets converted to that zone:

    var specifyOffsetAndOverrideZone = DateTime.fromISO("2017-05-15T09:10:23-09:00", { zone: 'Europe/Paris' });

    specifyOffsetAndOverrideZone.zoneName;                 //=> 'Europe/Paris'
    specifyOffsetAndOverrideZone.toString();               //=> '2017-05-15T20:10:23.000+02:00'

### setZone

Finally, some parsing functions allow you to "keep" the zone in the string as the DateTime's zone. Note that if only an offset is provided by the string, the zone will be a fixed-offset one, since Luxon doesn't know which zone is meant, even if you do.

    var keepOffset = DateTime.fromISO("2017-05-15T09:10:23-09:00", { setZone: true });

    keepOffset.zoneName;           //=> 'UTC-9'
    keepOffset.toString();         //=> '2017-05-15T09:10:23.000-09:00'

    var keepZone = DateTime.fromString("2017-05-15T09:10:23 Europe/Paris", "yyyy-MM-dd'T'HH:mm:ss z", { setZone: true });

    keepZone.zoneName;             //=> 'Europe/Paris'
    keepZone.toString()            //=> '2017-05-15T09:10:23.000+02:00'

## Changing zones

### setZone

Luxon objects are immutable, so when we say "changing zones" we really mean "creating a new instance with a different zone". Changing zone generally means "change the zone in which this DateTime is expressed (and according to which rules it is manipulated), but don't change the underlying timestamp." For example:

    var local = DateTime.local();
    var rezoned = local.setZone('America/Los_Angeles');

    // different local times with different offsets
    local.toString()     //=> '2017-09-13T18:30:51.141-04:00'
    rezoned.toString()   //=> '2017-09-13T15:30:51.141-07:00'

    // but actually the same time
    local.valueOf() === rezoned.valueOf(); //=> true

### keepCalendarTime

Generally, it's best to think of the zone as a sort of metadata that you slide around independent of the underlying count of milliseconds. However, sometimes that's not what you want. Sometimes you want to change zones while keeping the local time fixed and instead altering the timestamp. Luxon supports this:

    var local = DateTime.local();
    var rezoned = local.setZone('America/Los_Angeles', { keepCalendarTime: true });

    local.toString();      //=> '2017-09-13T18:36:23.187-04:00'
    rezoned.toString();    //=> '2017-09-13T18:36:23.187-07:00'

    local.valueOf() === rezoned.valueOf()  //=> false

If you find that confusing, I recommend just not using it.

## Accessors

Luxon DateTimes have a few different accessors that let you find out about the zone and offset:

    var dt = DateTime.local();

    dt.zoneName          //=> 'America/New_York'
    dt.offset            //=> -240
    dt.offsetNameShort   //=> 'EDT'
    dt.offsetNameLong    //=> 'Eastern Daylight Time'
    dt.isOffsetFixed     //=> false
    dt.isInDST           //=> true

Those are all documented in the DateTime API docs.

DateTime also has a `zone` property that holds an Luxon Zone object. You don't normally need to interact with it, but don't get it confused with the `zoneName`.

    dt.zone   //=> LocalZone {}

## DST weirdness

Because our ancestors were morons, they opted for a system wherein many governments shift around the local time twice a year for no good reason. And it's not like they do it in a neat, coordinated fashion. No, they do it whimsically, varying the shifts' timing from country to country (or region to region!) and from year to year. And of course, they do it the opposite way south of the Equator. This all a tremendous waste of everyone's energy and, er, time, but it is how the world works and a date and a time library has to deal with it.

Most of the time, DST shifts will happen without you having to do anything about it and everything will just work. Luxon goes to some pains to make DSTs as unweird as possible. But there are exceptions. This section covers them.

### Invalid times

Some local times simply don't exist. In the Northern Hemisphere, Spring Forward involves shifting the local time forward by (usually) one hour. In my zone, `America/New_York`, on March 12, 2017 the millisecond after 1:59:59.999 became 3:00:00.000. Thus the times between 2:00:00.000 and 2:59:59.000, inclusive, don't exist in that zone. But of course, nothing stops a user from constructing a DateTime out of that local time.

If you create such a DateTime from scratch, the missing time will be advanced by an hour:

    DateTime.local(2017, 3, 12, 2, 30).toString(); //=> '2017-03-12T03:30:00.000-04:00'

You can also do date math that lands you in the middle of the shift. These also push forward:

    DateTime.local(2017, 3, 11, 2, 30).plus({days: 1}).toString()         //=> '2017-03-12T03:30:00.000-04:00'
    DateTime.local(2017, 3, 13, 2, 30).minus({days: 1}).toString()        //=> '2017-03-12T03:30:00.000-04:00'

### Ambiguous times

Harder to handle are ambiguous times. In the Northern Hemisphere, some local times happen twice. In my zone, `America/New_York`, on November 5, 2017 the millisecond after 1:59:59.000 became 1:00:00.000. But of course there was already a 1:00 that day an hour before. So if you create a DateTime with a local time of 1:30, which time do you mean? It's an important question, because those correspond to different moments in time.

However, Luxon's behavior here is undefined. It makes no promises about which of the two possible timestamps the instance will represent. Currently, its specific behavior is like this:

    DateTime.local(2017, 11, 5, 1, 30).offset / 60                   //=> -4
    DateTime.local(2017, 11, 4, 1, 30).plus({days: 1}).offset / 60   //=> -4
    DateTime.local(2017, 11, 6, 1, 30).minus({days: 1}).offset / 60  //=> -5

In other words, sometimes it picks one and sometimes the other. Luxon doesn't guarantee the specific behavior above. That's just what it happens to do.

If you're curious, this lack of definition is because Luxon doesn't actually know that any particular DateTime is an ambiguous time. It doesn't know the time zones rules at all. It just knows the local time does not contradict the offset and leaves it at that. To find out the time is ambiguous and define exact rules for how to resolve it, Luxon would have to test nearby times to see if it can find duplicate local time, and it would have to do that on every creation of a DateTime, regardless of whether it was anywhere near a real DST shift. Because that's onerous, Luxon doesn't bother.

### Math across DSTs

There's a whole section about date and time math, but it's worth highlighting one thing here: when Luxon does math across DSTs, it adjusts for them when working with higher-order, variable-length units like days, weeks, months, and years. When working with lower-order, exact units like hours, minutes, and seconds, it does not. For example, DSTs mean that days are not always the same length: one day a year is (usually) 23 hours long and another is 25 hours long. Luxon makes sure that adding days takes that into account. On the other hand, an hour is always 3,600,000 milliseconds.

An easy way to think of it is that if you add a day to a DateTime, you should always get the same time the next day, regardless of any intervening DSTs. On the other hand, adding 24 hours will result in DateTime that is 24 hours later, which may or may not be the same time the next day. In this example, my zone is `America/New_York`, which had a Spring Forward DST in the early hours of March 12.

    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

## Changing the default zone

By default, Luxon creates DateTimes in the system's local zone. However, you can override this behavior globally:

    Settings.defaultZoneName = 'Asia/Tokyo'
    DateTime.local().zoneName                 //=> 'Asia/Tokyo'

    Settings.defaultZoneName = 'utc'
    DateTime.local().zoneName                 //=> 'UTC'

    // you can reset by setting to 'local'

    Settings.defaultZoneName = 'local'
    DateTime.local().zoneName                 //=> 'America/New_York'

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

    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:

    var dtHebrew = DateTime.local().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.

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

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

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
islamicc | Muharram 3, 1439 AH
japanese | September 24, 29 Heisei
persian | Mehr 2, 1396 AP
roc | September 24, 106 Minguo

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

    dt.toISO();         //=> '2017-04-20T11:32:00.000-04:00'
    dt.toISODate();     //=> '2017-W17-7'
    dt.toISOWeekDate(); //=> '2017-04-20'
    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.

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

## toLocaleString (strings for humans)

### The basics

Modern browsers (and other JS environments) provide brought 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:

    dt.toLocaleString();                                       //=> '4/20/2017'
    dt.toLocaleString(DateTime.DATETIME_FULL);                 //=> 'April 20, 2017, 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:

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

And that's all the preset is:

    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:

    dt.toLocaleString(DateTime.DATE_SHORT); //=>  '4/20/2017'
    var newFormat = Object.assign({ weekday: 'long' }, DateTime.DATE_SHORT);
    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_FULL | full date | October 14, 1983 | 14 octobre 1983
DATE_HUGE | full date with weekday | Tuesday, 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_FULL | full date and time with abbreviated named offset | 14 octobre 1983 à 13:30 UTC−4 | 14 octobre 1983 à 13:30 UTC−4
DATETIME_HUGE | full date and time with weekday and full named offset | Friday, October 14, 1983, 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, 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, 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:

    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 they exact strings you get are implementation-specific.

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

### Escaping

You may escape strings using single quotes:

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

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

    var d = DateTime.fromISO('2014-08-06T13:07:04.054').setLocale('ru');
    d.toFormat("LLLL") //=> 'август' (format)
    d.toFormat("MMMM"); //=> 'августа' (standalone)

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

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

Standlone token | Format token | Description | Example
--- | --- | --- | ---
S |  | millisecond, no padding | 54
SSS |  | millisecond, padded to 3 | 054
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, padded to 2 | 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 an 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-digit year | 2014
G |  | abbreviated localized era | AD
GG |  | unabbreviated localized era | Anno Domini
GGGGG |  | one-letter localized era | A
kk |  | ISO week year, unpadded | 17
kkkk |  | ISO week year, padded to 4 | 2014
W |  | ISO week number, unpadded | 32
WW |  | ISO week number, padded to 2 | 32
o |  | ordinal (day of year), unpadded | 218
ooo |  | ordinal (day of year), padded to 3 | 218
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

# Parsing

Luxon is not an NLP tool and isn't suitable for all date parsing jobs. But it can do some parsing:

1. Direct support for several well-known formats, including most valid ISO 8601 formats
2. An ad-hoc parser for parsing specific formats

## Parsing technical formats

### ISO 8601

Luxon supports a wide range of valid ISO 8601 formats through the fromISO method.

    DateTime.fromISO('2016-05-25');

All of these are parsable by `fromISO`:

    2016-05-25
    20160525
    2016-05-25T09
    2016-05-25T09:24
    2016-05-25T09:24:15
    2016-05-25T09:24:15.123
    2016-05-25T0924
    2016-05-25T092415
    2016-05-25T092415.123
    2016-05-25T09:24:15,123
    2016-W21-3
    2016W213
    2016-W21-3T09:24:15.123
    2016W213T09:24:15.123
    2016-200
    2016200
    2016-200T09:24:15.123

Dates without times are parsed as that day's midnight.

### HTTP and RFC2822

Luxon also provides parsing for strings formatted according to RFC 2822 and the HTTP header specs (RFC 850 and 1123):

    DateTime.fromRFC2822('Tue, 01 Nov 2016 13:23:12 +0630');
    DateTime.fromHTTP('Sunday, 06-Nov-94 08:49:37 GMT');
    DateTime.fromHTTP('Sun, 06 Nov 1994 08:49:37 GMT');

## Ad-hoc parsing

### Consider alternatives

You generally shouldn't use Luxon to parse arbitrarily formatted date strings:

1. If the string was generated by a computer for programmatic access, use a standard format like ISO 8601. Then you can parse it using DateTime.fromISO.
2. If the string is typed out by a human, it may not conform to the format you specify when asking Luxon to parse it. Luxon is quite strict about the format matching the string exactly.

Sometimes, though, you get a string from some legacy system in some terrible ad-hoc format and you need to parse it.

### fromString

See DateTime.fromString for the method signature. A brief example:

    DateTime.fromString('May 25 1982', 'LLLL dd yyyy');

### Intl

Luxon supports parsing internationalized strings:

    DateTime.fromString('mai 25 1982', 'LLLL dd yyyy', { locale: 'fr' });

Note, however, that Luxon derives the list of strings that can match, say, "LLLL" (and their meaning) by introspecting the environment's Intl implementation. Thus the exact strings may in some cases be environment-specific. You also need the Intl API available on the target platform (see the support matrix).

### Limitations

Not every token supported by `DateTime#toFormat` is supported in the parser. For example, there's no `ZZZZ` or `ZZZZZ` tokens. This is for a few reasons:

- Luxon relies on natively-available functionality that only provides the mapping in one way. We can ask what the named offset is and get "Eastern Standard Time" but not ask what "Eastern Standard Time" is most likely to mean.
- Some things are ambiguous. There are several Eastern Standard Times in different countries and Luxon has no way to know which one you mean without additional information (such as that the zone is America/New_York) that would make EST superfluous anyway. Similarly, the single-letter month and weekday formats (EEEEE) that are useful in displaying calendars graphically can't be parsed because of their ambiguity.
- Luxon doesn't yet support parsing the macro tokens it provides for formatting. This may eventually be addressed.

### Debugging

There are two kinds of things that can go wrong when parsing a string: a) you make a mistake with the tokens or b) the information parsed from the string does not correspond to a valid date. To help you sort that out, Luxon provides a method called fromStringExplain. It takes the same arguments as `fromString` but returns a map of information about the parse that can be useful in debugging.

For example, here the code is using "MMMM" where "MMM" was needed. You can see the regex Luxon uses and see that it didn't match anything:

    > DateTime.fromStringExplain("Aug 6 1982", "MMMM d yyyy")

    { input: 'Aug 6 1982',
      tokens:
       [ { literal: false, val: 'MMMM' },
         { literal: false, val: ' ' },
         { literal: false, val: 'd' },
         { literal: false, val: ' ' },
         { literal: false, val: 'yyyy' } ],
      regex: '(January|February|March|April|May|June|July|August|September|October|November|December)( )(\\d\\d?)( )(\\d{4})',
      matches: {},
      result: {},
      zone: null }

If you parse something and get an invalid date, the debugging steps are slightly different. Here, we're attempting to parse August 32nd, which doesn't exist:

    var d = DateTime.fromString("August 32 1982", "MMMM d yyyy")
    d.isValid //=> false
    d.invalidReason //=> 'day out of range'

For more on validity and how to debug it, see validity. You may find more comprehensive tips there. But as it applies specifically to `fromString`, again try `fromStringExplain`:

    > DateTime.fromStringExplain("August 32 1982", "MMMM d yyyy")

    { input: 'August 32 1982',
      tokens:
       [ { literal: false, val: 'MMMM' },
         { literal: false, val: ' ' },
         { literal: false, val: 'd' },
         { literal: false, val: ' ' },
         { literal: false, val: 'yyyy' } ],
      regex: '(January|February|March|April|May|June|July|August|September|October|November|December)( )(\\d\\d?)( )(\\d{4})',
      matches: { M: 8, d: 32, y: 1982 },
      result: { month: 8, day: 32, year: 1982 },
      zone: null }

Because Luxon was able to parse the string without difficulty, the output is a lot richer. And you can see that the "day" field is set to 32. Combined with the "out of range" explanation above, that should clear up the situation.

### Table of tokens

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

Standlone token | Format token | Description | Example
--- | --- | --- | ---
S |  | millisecond, no padding | 54
SSS |  | millisecond, padded to 3 | 054
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, padded to 2 | 9
HH |  | hour in 24-hour time, padded to 2 | 13
Z |  | narrow offset | +5
ZZ |  | short offset | +05:00
ZZZ |  | techie offset | +0500
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
E | c | day of the week, as number from 1-7 (Monday is 1, Sunday is 7) | 3
EEE | ccc | day of the week, as an abbreviate localized string | Wed
EEEE | cccc | day of the week, as an unabbreviated localized string | Wednesday
M | L | month as an unpadded number | 8
MM | LL | month as an padded number | 08
MMM | LLL | month as an abbreviated localized string | Aug
MMMM | LLLL | month as an unabbreviated localized string | August
y |  | year, unpadded | 2014
yy |  | two-digit year | 14
yyyy |  | four-digit year | 2014
G |  | abbreviated localized era | AD
GG |  | unabbreviated localized era | Anno Domini
GGGGG |  | one-letter localized era | A
kk |  | ISO week year, unpadded | 17
kkkk |  | ISO week year, padded to 4 | 2014
W |  | ISO week number, unpadded | 32
WW |  | ISO week number, padded to 2 | 32
o |  | ordinal (day of year), unpadded | 218
ooo |  | ordinal (day of year), padded to 3 | 218
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

# Math

This covers some oddball topics with date and time math, which has some quirky corner cases. This page tries document those corners.

## 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 than 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:

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

Don't worry about leap seconds. Javascript and most other programming environments don't account for them; they just happen as abrupt, invisible changes to the underlying system's time.

### How to think about calendar math

It's best not to think of calendar math as requiring arcane checks for what variability is required. 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:

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

    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

Of course, maybe you want to do math with multiple units:

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

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

    DateTime.fromISO('2017-04-30').plus({months: 1, days: 1}).toISODate() //=> '2017-05-31'

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 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 reflect what people mean. Of course, Luxon can't enforce this rule if you do the math in separate operations:

    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.

## Duration math

### Basics

Durations are quantities of time, like "3 days and 6 hours". They are completely unmoored from the timeline, it has no idea which 3 days and 6 hours they represent; it's just how Luxon represents those quantities in abstract. 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 anyway:

    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 diff method does this and it returns a Duration. For example:

    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:

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

Finally, you can diff using multiple units:

    var end = DateTime.fromISO('2017-03-13');
    var start = DateTime.fromISO('2017-02-15');
    end.diff(start, ['months', 'days']) //=> { 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

    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 | 365 | 365
Month |  | 30 | 4

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

    dur.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:

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

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

| Month | Week | Day
--- | --- | --- | ---
Year | 12 | 52.1775 | 365.2425
Month |  | 30.436875 | 4.348125

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.

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

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

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

    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:

    var end = DateTime.fromISO('2017-03-13');
    var start = DateTime.fromISO('2017-02-13');
    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 15 and March 15!

    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 that got spat out by our computation. 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:

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

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

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

You can even pick multiple units:

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

# Validity

## Invalid DateTimes

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.

Unless if 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`:

    > var dt = DateTime.fromObject({ month: 6, day: 400 });
    dt.isValid //=> false

All of the methods or getters that return primitives return degenerate ones:

    dt.year; //=>  NaN
    dt.toString(); //=> 'Invalid DateTime'
    dt.toObject(); //=> {}

Methods that return other Luxon objects will return invalid ones:

    dt.plus({ days: 4 }).isValid; //=> false

## Reasons a DateTimes can be invalid

The most common way to do that is to over or underflow some unit:

- February 40th
- 28:00
- -4 pm
- etc

But there are other ways to do it:

    // specify a time zone that doesn't exist
    DateTime.local().setZone('America/Blorp').isValid //=> false

    // provide contradictory information (here, this date is not a Wedensday)
    DateTime.fromObject({ year: 2017, month: 5, day: 25, weekday: 3}).isValid //=> false

Note that some other kinds of mistakes throw, based on our judgment that they are more likely programmer errors than data issues:

    DateTime.local().set({ blorp: 7 }); //=> kerplosion

## Debugging invalid DateTimes

Because DateTimes fail silently, they can be a pain to debug. There are two features that can help.

### invalidReason

Invalid DateTime objects are happy to tell you why they're invalid. Like this:

    DateTime.local().setZone('America/Blorp').invalidReason; //=>  'unsupported zone'

### throwOnInvalid

You can make Luxon throw whenever it creates an invalid DateTime.

    Settings.throwOnInvalid = true
    DateTime.local().setZone('America/Blorp'); // Error: Invalid DateTime: unsupported zone

You can of course leave this on in production too, but be sure to try/catch it appropriately.

## Invalid Durations

Durations can be invalid too. The easiest way to get one is to diff an invalid DateTime.

    DateTime.local(2017, 28).diffNow().isValid //=> false

## Invalid Intervals

Intervals can be invalid. This can happen a few different ways:

- The end time is before the start time
- It was created from invalid DateTime or Duration

# API reference

# References

## Class Summary

Static Public Class Summary
public | DateTime A DateTime is an immutable data structure representing a specific date and time and accompanying methods. |
public | Duration A Duration object represents a period of time, like "2 months" or "1 day, 1 hour". |
public | Info The Info class contains static methods for retrieving general time and date related data. |
public | Interval An Interval object represents a half-open interval of time, where each endpoint is a DateTime. |
public | Settings Settings contains static getters and setters that control Luxon's overall behavior. Luxon is a simple library with few options, but the ones it does have live here. |

## Interface Summary

Static Public Interface Summary
public | Zone |

# Support matrix

Luxon uses a slew of new browser Intl capabilities to tackle some of the tricker parts of dates and times. This means that not everything works in every environment.

## What works everywhere

Fortunately, most of Luxon works in anything remotely recent. A non-exhaustive list:

- Create DateTime instances in the local or UTC time zones
- Parse and output known formats
- Parse and output using ad-hoc English formats
- All the transformations like `plus` and `minus`
- All of `Duration` and `Interval`

## New capabilities and how they're used

Here are the areas that need help from newish browser capabilities:

- Basic internationalization. Luxon doesn't have internationalized strings in its code; instead it relies on the hosts implementation of the Intl API. This includes the very handy toLocaleString.
- Internationalized tokens. Listing the months or weekdays of a locale and outputting or parsing ad-hoc formats in non-English locales requires that Luxon be able to programmatically introspect the results of an Intl call. It does this using Intl's formatToParts method, which is relatively recent. So you could have the Intl API without having that.
- Zones. Luxon's support of IANA zones works by abusing the Intl API. That means you have to have that API and that the API must support a reasonable list of time zones. Zones are in some platforms a recent addition

You can check whether your environment supports these capabilities using Luxon's `Info` class:

    Info.features() //=> { intl: true, intlTokens: true, zones: true }

## The matrix

Here's the level of support for these features in different environments:

Area | Chrome | FF | IE | Edge | Safari | Node
--- | --- | --- | --- | --- | --- | ---
Intl | 24+ | 29+ | 11+ | 12+ | 10+ | 0.11_ w/ICU†
Intl tokens | 56+ | 51+ | None | 15+‡ | 11+ | 8+ w/ICU
Zones | 24+† | 52+ | None | 15+‡ | 10+ | 6+

† This is an educated guess. I haven't tested this or found a definitive reference.

‡ Earlier versions may also support this, but I haven't been able to test them.

Notes:

- "w/ICU" refers to providing Node with ICU data. See here for more info
- Safari 11 is still in tech preview as of Sept 2017
- IE is terrible and it's weird that anyone uses it

## What happens if a feature isn't supported?

You shouldn't use features of Luxon on projects that might be run on environments that don't support those features. Luxon tries to degrade gracefully if you don't, though.

[todo: outline the behavior here, with reference to https://github.com/icambron/luxon/issues/23]

## Polyfills

There are a couple of different polyfills available.

### Intl

To backfill the Intl and Intl tokens, there's the Intl polyfill. Use it if your environment doesn't have Intl support or if it has Intl but not `formatToParts`. Note that this fill comes with its own strings; there's no way to, say, just add the `formatToParts` piece. Also note that the data isn't as complete as some of the browsers' and some more obscure parsing/formatting features in Luxon don't work very well with it. Finally, note that it does not add zone capabilities.

### Zones

If you have an Intl API (either natively or through the Intl polyfill above) but no zone support, you can add it via the very nice DateTime format pollyfill.

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

    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:

    var d1 = DateTime.local();
    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.

## 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 Moment instances. E.g. `m.diff('2017-04-01')` would be `dt.diff(DateTime.fromISO('2017-04-01'))`.

## 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

## 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.local()` |
From ISO | `moment(String)` | `DateTime.fromISO(String)` |
From RFC 2822 | `moment(String)` | `DateTime.fromRFC2822(String)` |
From custom format | `moment(String, String)` | `DateTime.fromString(String, String)` |
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...)` | Luxon 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 `keepZone` 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 4

#### 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)` | `zone(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 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(...)` |
RFC 2822 |  | `toRFC2822()` |
HTTP date string |  | `toHTTP()` |
JS Date | `toDate()` | `toJSDate()` |
Epoch time | `valueOf()` | `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 doesn't support these, and won't until the Relative Time Format proposal lands in browsers.

Operation | Moment | Luxon
--- | --- | ---
Time from now | `fromNow()` | None
Time from other time | `from(Moment)` | None
Time to now | `toNow()` | None
Time to other time | `to(Moment) | None
"Calendar time" | `calendar()` | None

## 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 `humanize` method
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.

# Changelog

## 0.4

Experimental, but now broadly useful.