Moment Timezone Documentation

To use moment-timezone, you will need moment@2.6.0+, moment-timezone.js, and the moment-timezone data.

For convenience, there are builds available on momentjs.com/timezone/ with all the zone data or a subset of the data with support for only 2010-2020.

You can also use the data builder at momentjs.com/timezone/data/ to construct a data set with only the zones and years needed.

Node.js

edit
npm install moment-timezone

In Node.js, all the data is preloaded. No additional code is needed for loading data.

var moment = require('moment-timezone');
moment().tz("America/Los_Angeles").format();

Browser

edit
<script src="moment.js"></script>
<script src="moment-timezone-with-data.js"></script>

When using Moment Timezone in the browser, you will need to load the data as well as the library.

You can either use the prebuilt library and data files linked on the homepage or build a subset of the data yourself using the data builder.

moment().tz("America/Los_Angeles").format();

Require.js

edit
require.config({
    paths: {
        "moment": "path/to/moment"
    }
});
define(["path/to/moment-timezone-with-data"], function (moment) {
    moment().tz("America/Los_Angeles").format();
});

There are two interfaces for using timezones with Moment.js.

moment.tz(..., String) is used to create a moment with a timezone, and moment().tz(String) is used to change the timezone on an existing moment.

Parsing in Zone

edit
moment.tz(..., String);

The moment.tz constructor takes all the same arguments as the moment constructor, but uses the last argument as a time zone identifier.

var a = moment.tz("2013-11-18 11:55", "America/Toronto");
var b = moment.tz("May 12th 2014 8PM", "MMM Do YYYY hA", "America/Toronto");
var c = moment.tz(1403454068850, "America/Toronto");
a.format(); // 2013-11-18T11:55:00-05:00
b.format(); // 2014-05-12T20:00:00-04:00
c.format(); // 2014-06-22T12:21:08-04:00

This constructor is DST aware, and will use the correct offset when parsing.

moment.tz("2013-12-01", "America/Los_Angeles").format(); // 2013-12-01T00:00:00-08:00
moment.tz("2013-06-01", "America/Los_Angeles").format(); // 2013-06-01T00:00:00-07:00

The offset is only taken into consideration when constructing with an array, string without offset, or object.

var arr = [2013, 5, 1],
    str = "2013-12-01"
    obj = { year : 2013, month : 5, day : 1 };

moment.tz(arr, "America/Los_Angeles").format(); // 2013-06-01T00:00:00-07:00
moment.tz(str, "America/Los_Angeles").format(); // 2013-06-01T00:00:00-07:00
moment.tz(obj, "America/Los_Angeles").format(); // 2013-06-01T00:00:00-07:00

moment.tz(arr, "America/New_York").format();    // 2013-06-01T00:00:00-04:00
moment.tz(str, "America/New_York").format();    // 2013-06-01T00:00:00-04:00
moment.tz(obj, "America/New_York").format();    // 2013-06-01T00:00:00-04:00

If the input string contains an offset, it is used instead for parsing. The parsed moment is then converted to the target zone.

var zone = "America/Los_Angeles";
moment.tz('2013-06-01T00:00:00',       zone).format(); // 2013-06-01T00:00:00-07:00
moment.tz('2013-06-01T00:00:00-04:00', zone).format(); // 2013-05-31T21:00:00-07:00
moment.tz('2013-06-01T00:00:00+00:00', zone).format(); // 2013-05-31T17:00:00-07:00

Unix timestamps and Date objects refer to specific points in time, thus it doesn't make sense to use the timezone offset when constructing. Using moment.tz(Number|Date, zone) is functionally equivalent to moment(Number|Date).tz(zone).

var timestamp = 1403454068850,
    date = new Date(timestamp);

moment.tz(timestamp, "America/Los_Angeles").format(); // 2014-06-22T09:21:08-07:00
moment(timestamp).tz("America/Los_Angeles").format(); // 2014-06-22T09:21:08-07:00

moment.tz(date, "America/Los_Angeles").format();      // 2014-06-22T09:21:08-07:00
moment(date).tz("America/Los_Angeles").format();      // 2014-06-22T09:21:08-07:00

Parsing Ambiguities

edit

Due to Daylight Saving Time, there is a possibility that a time either does not exist, or has existed twice.

In the spring, at the start of DST, clocks move forward an hour. In reality though, it is not time that is moving, it is the offset moving.

Moving the offset forward gives the illusion that an hour has disappeared. As the clock ticks, you can see it move from 1:58 to 1:59 to 3:00. It is easier to see what is actually happening when you include the offset.

1:58 -8
1:59 -8
3:00 -7
3:01 -7

The result is that any time between 1:59:59 and 3:00:00 never actually happened. Moment Timezone accounts for this. If you try to parse a time that never existed, it will round down an hour.

moment.tz("2012-03-11 01:59:59", "America/New_York"); // 2012-03-11T01:59:59-05:00
moment.tz("2012-03-11 02:00:00", "America/New_York"); // 2012-03-11T01:00:00-05:00
moment.tz("2012-03-11 02:59:59", "America/New_York"); // 2012-03-11T01:59:59-05:00
moment.tz("2012-03-11 03:00:00", "America/New_York"); // 2012-03-11T03:00:00-04:00

In the fall, at the end of DST, clocks move backward an hour. Again, time is not moving backwards, only the offset is. In this case, the illusion is that an hour repeats itself.

Again, it is easier to see what is actually happening when you include the offset.

1:58 -7
1:59 -7
1:00 -8
1:01 -8

Moment Timezone handles this by always using the earlier instance of a duplicated hour.

moment.tz("2012-11-04 00:59:59", "America/New_York"); // 2012-11-04T00:59:59-04:00
moment.tz("2012-11-04 01:00:00", "America/New_York"); // 2012-11-04T01:00:00-04:00
moment.tz("2012-11-04 01:59:59", "America/New_York"); // 2012-11-04T01:59:59-04:00
moment.tz("2012-11-04 02:00:00", "America/New_York"); // 2012-11-04T02:00:00-05:00

You won't be able to create a moment with the later instance of the duplicated hour unless you include the offset when parsing.

moment.tz("2012-11-04 01:00:00-04:00", "America/New_York"); // 2012-11-04T01:00:00-04:00
moment.tz("2012-11-04 01:00:00-05:00", "America/New_York"); // 2012-11-04T01:00:00-05:00

Converting to Zone

edit
moment().tz(String);

The moment#tz mutator will change the time zone and update the offset.

moment("2013-11-18").tz("America/Toronto").format('Z'); // -05:00
moment("2013-11-18").tz("Europe/Berlin").format('Z');   // +01:00

This information is used consistently in other operations, like calculating the start of the day.

var m = moment.tz("2013-11-18 11:55", "America/Toronto");
m.format();                     // 2013-11-18T11:55:00-05:00
m.startOf("day").format();      // 2013-11-18T00:00:00-05:00
m.tz("Europe/Berlin").format(); // 2013-11-18T06:00:00+01:00
m.startOf("day").format();      // 2013-11-18T00:00:00+01:00

Formatting Additions

edit
moment.tz(String).format("Z z"); // -08:00 PST
moment.tz(String).zoneAbbr();    // PST
moment.tz(String).zoneName();    // PST

In addition to including the +00:00 formatting information, Moment Timezone also includes information for the abbreviated timezone name.

moment.tz([2012, 0], 'America/New_York').format('z');    // EST
moment.tz([2012, 5], 'America/New_York').format('z');    // EDT
moment.tz([2012, 0], 'America/Los_Angeles').format('z'); // PST
moment.tz([2012, 5], 'America/Los_Angeles').format('z'); // PDT

Note that these abbreviations may change depending on the timezone offset. This helps to distinguish offsets between places that may or may not use DST.

// Denver observes DST
moment.tz([2012, 0], 'America/Denver').format('Z z');  // -07:00 MST
moment.tz([2012, 5], 'America/Denver').format('Z z');  // -06:00 MDT
// Phoenix does not observe DST
moment.tz([2012, 0], 'America/Phoenix').format('Z z'); // -07:00 MST
moment.tz([2012, 5], 'America/Phoenix').format('Z z'); // -07:00 MST

However, these abbreviations might not change depending on the timezone offset. Australia uses Eastern Standard Time for non DST and Eastern Saving Time for DST. Because Standard and Saving both start with S, the abbreviations are the same.

moment.tz([2012, 0], 'Australia/Melbourne').format('Z z');  // +11:00 EST
moment.tz([2012, 5], 'Australia/Melbourne').format('Z z');  // +10:00 EST

Note also that these abbreviations are not globally unique. The case below is an example of two locations using Eastern and Central, as both North America and Australia have their own east and west coasts.

moment.tz([2012, 0], 'America/New_York').format('z');      // EST
moment.tz([2012, 0], 'Australia/Melbourne').format('z');   // EST
moment.tz([2012, 0], 'America/Chicago').format('z');       // CST
moment.tz([2012, 0], 'Australia/Broken_Hill').format('z'); // CST

You can also use moment#zoneAbbr to get the zone abbreviation. This is what moment.js uses when formatting the z token.

moment.tz([2012, 0], 'America/New_York').zoneAbbr(); // EST
moment.tz([2012, 5], 'America/New_York').zoneAbbr(); // EDT

Moment.js also provides a hook for the long form timezone name. Because these strings are generally localized, Moment Timezone does not provide any long names for zones.

To provide long form names, you can override moment.fn.zoneName and use the zz token.

var abbrs = {
    EST : 'Eastern Standard Time',
    EDT : 'Eastern Daylight Time',
    CST : 'Central Standard Time',
    CDT : 'Central Daylight Time',
    MST : 'Mountain Standard Time',
    MDT : 'Mountain Daylight Time',
    PST : 'Pacific Standard Time',
    PDT : 'Pacific Daylight Time',
};

moment.fn.zoneName = function () {
    var abbr = this.zoneAbbr();
    return abbrs[abbr] || abbr;
};

moment.tz([2012, 0], 'America/New_York').format('zz');    // Eastern Standard Time
moment.tz([2012, 5], 'America/New_York').format('zz');    // Eastern Daylight Time
moment.tz([2012, 0], 'America/Los_Angeles').format('zz'); // Pacific Standard Time
moment.tz([2012, 5], 'America/Los_Angeles').format('zz'); // Pacific Daylight Time

In order to match a timestamp to an offset, Moment Timezone uses a Zone object.

Though you shouldn't even need to use it, this object's constructor is available on the moment.tz.Zone namespace.

This object has 4 properties.

{
    name    : 'America/Los_Angeles',          // the unique identifier
    abbrs   : ['PDT', 'PST'],                 // the abbreviations
    untils  : [1414918800000, 1425808800000], // the timestamps in milliseconds
    offsets : [420, 480]                      // the offsets in minutes
}

Name

edit
zone.name; // America/Los_Angeles

The uniquely identifying name of the timezone.

Abbreviation

edit
zone.abbr(timestamp); // PST

Get the abbreviation for a given timestamp from a Zone.

moment.tz.zone('America/Los_Angeles').abbr(1403465838805); // PDT
moment.tz.zone('America/Los_Angeles').abbr(1388563200000); // PST

Offset

edit
zone.offset(timestamp); // 480

Get the offset for a given timestamp from a Zone.

moment.tz.zone('America/Los_Angeles').offset(1403465838805); // 420
moment.tz.zone('America/Los_Angeles').offset(1388563200000); // 480

Parse Offset

edit
zone.parse(timestamp); // 480

Parse an offset for a timestamp constructed from Date.UTC in that zone.

This is what Moment Timezone uses to parse input into a timezone. The process is conceptually similar to the following.

Assume we want to find the exact moment of March 19 2014 8:30 am in New York. Because the offset varies between -04:00 and -05:00 in New York, we don't know what the offset was on March 19th.

Instead, we create a timestamp in UTC and pass that to zone.parse, which will return the offset at that time.

var zone = moment.tz.zone('America/New_York');
zone.parse(Date.UTC(2012, 2, 19, 8, 30)); // 240

This is the code that handles the cases referenced in the Parsing Ambiguities section above.

var zone = moment.tz.zone('America/New_York');
zone.parse(Date.UTC(2012, 2, 11, 1, 59)); // 300
zone.parse(Date.UTC(2012, 2, 11, 2, 00)); // 240

Moment Timezone uses two data formats. An unpacked version for calculations and a packed version for minified transport.

Unpacked Format

edit

The unpacked format looks exactly like the zone object.

The data below is for Los Angeles between 2014 and 2018.

{
    name    : 'America/Los_Angeles',
    abbrs   : ['PST', 'PDT','PST', 'PDT', 'PST', 'PDT', 'PST', 'PDT', 'PST', 'PDT', 'PST'],
    untils  : [1394359200000, 1414918800000, 1425808800000, 1446368400000, 1457863200000, 1478422800000, 1489312800000, 1509872400000, 1520762400000, 1541322000000, null],
    offsets : [480, 420, 480, 420, 480, 420, 480, 420, 480, 420, 480]
}

The lengths of abbrs, untils, offsets are all the same. The offset and abbr at any index are only active while the timestamp is less than the until at that index.

An easy way to read this aloud is "between untils[n-1] and untils[n], the abbr should be abbrs[n] and the offset should be offsets[n]".

Note that untils are measured in milliseconds and offsets are measured in minutes.

Packed Format

edit

The packed format represents an unpacked zone in a single string.

The data below is for Los Angeles between 2014 and 2018.

'America/Los_Angeles|PST PDT|80 70|01010101010|1Lzm0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0'

In order to save as many bytes as possible, we used a very compact format to store the data.

The data is split into 5 sections separated by pipes.

# Type Example
0 Name America/Los_Angeles
1 Abbr Map PST PDT
2 Offset Map 80 70
3 Abbr/Offset Index 01010101010
4 Timestamp Diff 1Lzm0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0

Name: The canonical name of the timezone.

Abbr Map: A space separated list of all the abbreviations ever used in this timezone.

Offset Map: A space separated list of all the offsets ever used in this timezone in minutes in base 60.

Abbr/Offset Index: A tightly packed array of indices into the offset and abbr maps. These are also in base 60.

Timestamp Diffs: This is where the timestamps are stored.

Because we are dealing with a sorted list of timestamps, we just store the diff from the last timestamps rather than storing the full timestamps.

The first item in the array is a unix timestamp in minutes. All items after the first item are numbers of minutes to be added to the previous value during unpacking. All items are stored in base 60.

As you may have seen from the example above, the timestamp diffs tend to duplicate the same values from year to year. These duplications allow gzip to compress the data even further than if we used full timestamps.

Base 60?

You may be wondering why base 60 is used. Base 62 is a fairly common tool for ascii data compression, using a-z to represent 10-35 and A-Z to represent 36-61.

While it may have saved a few bytes to use base 62, much of the data in Moment Timezone maps nicely to multiples of 60.

There are 60 minutes in a second and 60 seconds in a minute. 3 hours is 30 minutes in base 60 and 300 seconds in base 60 instead of 180 and 10800 in base 10 or 2U and 2Oc in base 62.

Link Format

edit

In order to reduce duplication, the Moment Timezone data packer will create links out of two zones that share data that is exactly the same.

This data is the two zone names separated by a pipe.

moment.tz.add('America/Los_Angeles|PST PDT|80 70|01010101010|1Lzm0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0');
moment.tz.link('America/Los_Angeles|US/Pacific');
moment.tz("2013-12-01", "America/Los_Angeles").format(); // 2013-12-01T00:00:00-08:00
moment.tz("2013-12-01", "US/Pacific").format();          // 2013-12-01T00:00:00-08:00

Once the data has been packed and transported to the client, it must be added to Moment Timezone.

Adding a Zone

edit
moment.tz.add(PackedZoneString)
moment.tz.add(PackedZoneString[])

To add zone data to Moment Timezone, use moment.tz.add.

moment.tz.add('America/Los_Angeles|PST PDT|80 70|0101|1Lzm0 1zb0 Op0');

To add more than one zone, pass an array of packed data.

moment.tz.add([
    'America/Los_Angeles|PST PDT|80 70|0101|1Lzm0 1zb0 Op0',
    'America/New_York|EST EDT|50 40|0101|1Lz50 1zb0 Op0'
]);

Adding a Link

edit
moment.tz.link(PackedLinkString)
moment.tz.link(PackedLinkString[])

To link two zone names to the same data, use moment.tz.link.

The strings passed in should be in the link format: the two zone names separated by a pipe.

moment.tz.link('America/Los_Angeles|US/Pacific');

To add more than one link at a time, pass an array of link strings.

moment.tz.link([
    'America/Los_Angeles|US/Pacific',
    'America/New_York|US/Eastern'
]);

Loading a Data Bundle

edit
moment.tz.load({
    zones : [],
    links : [],
    version : '2014e'
});

The data for Moment Timezone comes from the IANA Time Zone Database. New versions are released periodically as timezone laws change in various countries.

The versions are named after the year and an incrementing letter. 2014a 2014b 2014c...

In order to keep versions together, Moment Timezone has a bundled object format as well.

{
    version : '2014e',
    zones : [
        'America/Los_Angeles|PST PDT|80 70|0101|1Lzm0 1zb0 Op0',
        'America/New_York|EST EDT|50 40|0101|1Lz50 1zb0 Op0'
    ],
    links : [
        'America/Los_Angeles|US/Pacific',
        'America/New_York|US/Eastern'
    ]
}

To load a bundle into Moment Timezone, use moment.tz.load.

moment.tz.load({
    version : '2014e',
    zones : [...],
    links : [...]
})

Checking Zone Existence

edit
moment.tz.zone(name); // Zone or null

To check if a zone exists, use moment.tz.zone. It will return the Zone if it was loaded and null if it was not loaded.

moment.tz.zone("UnloadedZone"); // null
moment.tz.add("UnloadedZone|UZ|0|0|");
moment.tz.zone("UnloadedZone"); // Zone { name : "UnloadedZone", ...}

Getting Zone Names

edit
moment.tz.names(); // String[]

To get a list of all the loaded timezone names, use moment.tz.names.

moment.tz.names(); // ["Africa/Abidjan", "Africa/Accra", "Africa/Addis_Ababa", ...]

Because of the complexity of the packed and unpacked data formats, Moment Timezone has some heavily tested utility functions for working with the data.

Methods for unpacking data are included with the core library, as they are needed in order to use the library.

Methods for packing and subsetting the data are included in an additional moment-timezone-utils.js file. This file adds some more methods to the moment.tz namespace.

// in moment-timezone.js
moment.tz.unpack
moment.tz.unpackBase60
// in moment-timezone-utils.js
moment.tz.pack
moment.tz.packBase60
moment.tz.createLinks
moment.tz.filterYears
moment.tz.filterLinkPack

Pack

edit
moment.tz.pack(UnpackedObject); // PackedString

This converts data in the unpacked format to the packed format.

var unpacked = {
    name    : 'Indian/Mauritius',
    abbrs   : ['LMT', 'MUT', 'MUST', 'MUT', 'MUST', 'MUT'],
    offsets : [-230, -240, -300, -240, -300, -240],
    untils  : [-1988164200000, 403041600000, 417034800000, 1224972000000, 1238274000000, null]
};
moment.tz.pack(unpacked); // "Indian/Mauritius|LMT MUT MUST|-3O -40 -50|012121|-2xorO 34unO 14L0 12kr0 11z0"

Unpack

edit
moment.tz.unpack(PackedString); // UnpackedObject

This converts data in the packed format to the unpacked format.

var packed = "Indian/Mauritius|LMT MUT MUST|-3O -40 -50|012121|-2xorO 34unO 14L0 12kr0 11z0";

moment.tz.unpack(packed);
// {
//     name    : 'Indian/Mauritius',
//     abbrs   : ['LMT', 'MUT', 'MUST', 'MUT', 'MUST', 'MUT'],
//     offsets : [-230, -240, -300, -240, -300, -240],
//     untils  : [-1988164200000, 403041600000, 417034800000, 1224972000000, 1238274000000, null]
// };

Pack Base 60

edit
moment.tz.packBase60(Number); // Base60String

Convert a base 10 number to a base 60 string.

moment.tz.packBase60(9);    // 9
moment.tz.packBase60(10);   // a
moment.tz.packBase60(59);   // X
moment.tz.packBase60(1337); // mh

Much like Number.prototype.toFixed, moment.tz.packBase60 accepts a second argument for the number of digits of precision.

moment.tz.packBase60(1.1667,   1); // 1.a
moment.tz.packBase60(20.12345, 3); // k.7op
moment.tz.packBase60(59,       1); // X

A solitary 0 before the decimal point is dropped.

moment.tz.packBase60(1.1667, 1); // 1.a
moment.tz.packBase60(0.1667, 1); // .a

Trailing zeroes after the decimal point are dropped.

moment.tz.packBase60(1/6, 1); // .a
moment.tz.packBase60(1/6, 5); // .a
moment.tz.packBase60(59, 5);  // X

Unpack Base 60

edit
moment.tz.unpackBase60(Base60String); // Number

Convert a base 60 string to a base 10 number.

moment.tz.unpackBase60('9');     // 9
moment.tz.unpackBase60('a');     // 10
moment.tz.unpackBase60('X');     // 59
moment.tz.unpackBase60('mh');    // 1337
moment.tz.unpackBase60('1.9');   // 1.15
moment.tz.unpackBase60('k.7op'); // 20.123449074074074

Create Links

edit
moment.tz.createLinks(UnpackedBundle); // UnpackedBundle

In order to reduce duplication, we can create links out of two zones that share data.

var unlinked = {
    zones : [
        {name:"Zone/One",abbrs:["OST","ODT"],offsets:[60,120],untils:[403041600000,417034800000]},
        {name:"Zone/Two",abbrs:["OST","ODT"],offsets:[60,120],untils:[403041600000,417034800000]}
    ],
    links : [],
    version : "2014x-doc-example"
};

moment.tz.createLinks(unlinked);

{
    zones : [
        {name:"Zone/One",abbrs:["OST","ODT"],offsets:[60,120],untils:[403041600000,417034800000]}
    ],
    links : ["Zone/One|Zone/Two"],
    version : "2014x-doc-example"
}

This is especially useful when combined with moment.tz.filterYears, as older rules that would have differentiated two Zones may not be in the filtered year range, allowing them to be linked to save space.

Filter Years

edit
moment.tz.filterYears(UnpackedZone, Number, Number); // UnpackedZone

By default, Moment Timezone includes all the data from the IANA Time Zone Database. This includes data from 1900 to 2038. The data for all these years may not be necessary for your use case.

moment.tz.filterYears can be used to filter out data for years outside a certain range.

var all    = { name : "America/Los_Angeles", abbrs : [...], offsets : [...] untils : [...]};
var subset = moment.tz.filterYears(all, 2012, 2016);
all.untils.length;    // 186
subset.untils.length; // 11

If only one year is passed, it will be used for the start and end year.

var all    = { name : "America/Los_Angeles", abbrs : [...], offsets : [...] untils : [...]};
var subset = moment.tz.filterYears(all, 2012);
all.untils.length;    // 186
subset.untils.length; // 3

Filter Years, Create Links, and Pack

edit
moment.tz.filterLinkPack(UnpackedBundle, Number, Number); // PackedBundle

The packing, link creation, and subsetting of years are all tools for compressing data to be transported to the client.

The moment.tz.filterLinkPack method combines all these into one simple interface. Pass in an unpacked bundle, start year, and end year and get a filtered, linked, packed bundle back.

This is what is being used to compress the output for the bundled data + library files on the homepage and on the data builder.