249

I'm using Moment.js to parse and format dates in my web app. As part of a JSON object, my backend server sends dates as a number of milliseconds from the UTC epoch (Unix offset).

Parsing dates in a specific timezone is easy -- just append the RFC 822 timezone identifier to the end of the string before parsing:

// response varies according to your timezone
const m1 = moment('3/11/2012 13:00').utc().format("MM/DD HH:mm")

// problem solved, always "03/11 17:00"
const m2 = moment('3/11/2012 13:00 -0400').utc().format("MM/DD HH:mm")

console.log({ m1, m2 })
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js"></script>

But how do I format a date in a specifc timezone?

I want consistent results regardless of the browser's current time, but I don't want to display dates in UTC.

Artyom Ionash
  • 405
  • 7
  • 17
quietmint
  • 13,885
  • 6
  • 48
  • 73
  • 2
    Moment doesn't support this yet, but they're working on it. https://github.com/timrwood/moment/pull/671 – Ben Mar 11 '13 at 20:11
  • 6
    This should just work. If you pass moment a time string that includes the desired offset, then it should retain that offset and display the local time as given, rather than automatically adjusting to browser-local time. If I wanted it to adjust to browser-local time, then I'd give it a UTC time instead of explicitly giving it an offset to use. I mean... I give it an explicit offset, why is it basically eating it up and doing it's own conversion to the browsers offset. Terrible design. – Triynko Aug 11 '15 at 15:26
  • @Triynko offsets are subjected to daylight savings so, it doesn't always work as expected. – pinkpanther Aug 16 '16 at 15:25
  • 1
    Something related to this, I want to print time in a specific format, e.g **15 March, 2018** but cant find the format string to do this. The generic `DD MMM, YYYY` does not work, when I use it like `moment.tz("2018-02-15T14:20:00.000+0530", "Asia/Bangkok").format("DD MMM, YYYY")`. Can someone point me in the documentation where I can find all the keys for formatting time when using this api. – pRmdk Feb 14 '18 at 09:56
  • @PramodKumar What's wrong? That gives `"15 Feb, 2018"`. Did you mean to use format string `DD MMMM, YYYY` to get `"15 February, 2018"`? – quietmint Feb 16 '18 at 00:21

9 Answers9

335

As pointed out in Manto's answer, .utcOffset() is the preferred method as of Moment 2.9.0. This function uses the real offset from UTC, not the reverse offset (e.g., -240 for New York during DST). Offset strings like "+0400" work the same as before:

// always "2013-05-23 00:55"
moment(1369266934311).utcOffset(60).format('YYYY-MM-DD HH:mm')
moment(1369266934311).utcOffset('+0100').format('YYYY-MM-DD HH:mm')

The older .zone() as a setter was deprecated in Moment.js 2.9.0. It accepted a string containing a timezone identifier (e.g., "-0400" or "-04:00" for -4 hours) or a number representing minutes behind UTC (e.g., 240 for New York during DST).

// always "2013-05-23 00:55"
moment(1369266934311).zone(-60).format('YYYY-MM-DD HH:mm')
moment(1369266934311).zone('+0100').format('YYYY-MM-DD HH:mm')

To work with named timezones instead of numeric offsets, include Moment Timezone and use .tz() instead:

// determines the correct offset for America/Phoenix at the given moment
// always "2013-05-22 16:55"
moment(1369266934311).tz('America/Phoenix').format('YYYY-MM-DD HH:mm')
Community
  • 1
  • 1
quietmint
  • 13,885
  • 6
  • 48
  • 73
  • I was having trouble with this via a "TypeError: Object 240 has no method 'format'" error. I realized zone is only available as a setter on version 2.1.0 and up of Moment.js (and I was on an older version). Thanks! – Melinda Weathers Oct 10 '13 at 23:05
  • You can also pass an "America/Phoenix" timezone as well. – Eric Cope Nov 05 '13 at 02:03
  • Does moment have a way to use the user's browser time zone? – ebi Nov 25 '13 at 07:23
  • 5
    @ebi It already uses the browser's timezone by default. To *access* the browser's timezone offset, use `.zone()` as a getter, which returns minutes from UTC (e.g., returns 300 for New York during standard time). – quietmint Nov 25 '13 at 13:24
  • 1
    @EricCope No, not quite. You *can* use `.tz("America/Phoenix")` if you include http://momentjs.com/timezone/ as well, however. I've updated the answer with an example. – quietmint Nov 25 '13 at 13:27
  • @user113215 I see how to get the tz offset, but how do you format it to the timezone then? Do I have to include momentjs-timezone? – ebi Nov 25 '13 at 20:26
  • @eji I'm not sure what you're asking since `.format('YYYY-MM-DD HH:mm')` will already format your moment using the browser's timezone. To use the browser's timezone, don't use `.zone()` or `.tz()`. – quietmint Nov 25 '13 at 22:06
  • As of the time of this comment, .zone() has been deprecated in moment. Use utcOffset() instead. – Manto Aug 19 '15 at 23:42
  • 1
    Note that using offset will not work as expected if the timezone have different offsets due to daylight savings. – pinkpanther Aug 16 '16 at 15:27
  • 1
    Will .utcOffset() automaticly manage daylight saving times? like when i set UTC +1 CET in Winter will it bet UTC +2 CEST during Daylight saving time? Cause this makes it usable or not! – haemse Aug 02 '17 at 11:23
  • 1
    @haemse `utcOffset()` has *nothing to do* with DST or time zone rules. It just looks at the moment you've already constructed and gets/sets the number of minutes from UTC. Use the Moment Timezone library with a named timezone ("America/Phoenix") if you want to handle DST and other rules for known time zones. – quietmint Aug 04 '17 at 01:17
  • careful with the minute part of the formatting, it is `mm` not `MM` . lost precious 2 hours of my life :( . – Rakibul Haq Apr 02 '20 at 13:21
  • @quietmint could ouy please help me https://stackoverflow.com/questions/66330037/timezone-of-momentjs-does-not-change – Catalina Feb 23 '21 at 09:27
103

A couple of answers already mention that moment-timezone is the way to go with named timezone. I just want to clarify something about this library that was pretty confusing to me. There is a difference between these two statements:

moment.tz(date, format, timezone)

moment(date, format).tz(timezone)

Assuming that a timezone is not specified in the date passed in:

The first code takes in the date and assumes the timezone is the one passed in. The second one will take date, assume the timezone from the browser and then change the time and timezone according to the timezone passed in.

Example:

moment.tz('2018-07-17 19:00:00', 'YYYY-MM-DD HH:mm:ss', 'UTC').format() // "2018-07-17T19:00:00Z"

moment('2018-07-17 19:00:00', 'YYYY-MM-DD HH:mm:ss').tz('UTC').format() // "2018-07-18T00:00:00Z"

My timezone is +5 from utc. So in the first case it does not change and it sets the date and time to have utc timezone.

In the second case, it assumes the date passed in is in -5, then turns it into UTC, and that's why it spits out the date "2018-07-18T00:00:00Z"

NOTE: The format parameter is really important. If omitted moment might fall back to the Date class which can unpredictable behaviors


Assuming the timezone is specified in the date passed in:

In this case they both behave equally


Even though now I understand why it works that way, I thought this was a pretty confusing feature and worth explaining.

Nicola Pedretti
  • 4,831
  • 3
  • 36
  • 42
73

Use moment-timezone

moment(date).tz('Europe/Berlin').format(format)

Before being able to access a particular timezone, you will need to load it like so (or using alternative methods described here)

moment.tz.add('Europe/Berlin|CET CEST CEMT|-10 -20 -30')
Philip Nuzhnyy
  • 4,630
  • 1
  • 25
  • 17
  • I don't think moment tz was available when the question was asked, but I do think this might be the way to go. I'm currently working on a similiar problem with all timestamps stored as UTC in MySQL, but to be viewed in a specific zone dependent on user config and not the timezone of the client. – nickdnk Aug 18 '15 at 11:32
24

.zone() has been deprecated, and you should use utcOffset instead:

// for a timezone that is +7 UTC hours
moment(1369266934311).utcOffset(420).format('YYYY-MM-DD HH:mm')
Manto
  • 1,642
  • 1
  • 13
  • 28
12

I was having the same issue with Moment.js. I've installed moment-timezone, but the issue wasn't resolved. Then, I did just what here it's exposed, set the timezone and it works like a charm:

moment(new Date({your_date})).zone("+08:00")

Thanks a lot!

facundofarias
  • 2,973
  • 28
  • 27
  • 20
    You shouldn't use the `new Date()` constructor. Moment provides everything you'd ever need to parse dates. See [Parsing](http://momentjs.com/docs/#/parsing/) docs. – quietmint Sep 17 '14 at 21:03
  • Yes, but if I don't do that, I am receiving a warning from Moment, that it is deprecated of something like that – facundofarias Sep 18 '14 at 09:46
  • 7
    The warning means Moment is internally falling back to exactly what you're doing (using `new Date()` internally), but this is [inconsistent across browsers](http://dygraphs.com/date-formats.html). Instead, you should use provide the expected format as the second argument. Example: `moment("12-25-1995", "MM-DD-YYYY")`. – quietmint Sep 18 '14 at 13:52
6

Just came acreoss this, and since I had the same issue, I'd just post the results I came up with

when parsing, you could update the offset (ie I am parsing a data (1.1.2014) and I only want the date, 1st Jan 2014. On GMT+1 I'd get 31.12.2013. So I offset the value first.

moment(moment.utc('1.1.2014').format());

Well, came in handy for me to support across timezones

B

Bard Rotzer
  • 83
  • 1
  • 5
-1

If you have a timestamp that is divided by "T", then you must use utc method first. Here's how I converted the timestamp from UTC to EST/EST:

let createdDate = moment.utc("2023-06-11T00:45:48.42") 
moment(createdDate, "MM/DD/yy").tz("America/New_York").format("MM/DD/yy")}

The result is: 06/10/2023

Reference: https://momentjs.com/timezone/docs/#/using-timezones/

  • Without a zone on the end, your input string is ISO format for local date time. If it was really in UTC, it should end with "Z". As is, you have no idea about the timezone of your input because it simply isn't specified. The input could be anything so blindly assuming UTC is probably a mistake. – quietmint Jul 04 '23 at 12:55
-4

If you pass the timestamp as the parameter to moment() (e.g if the timezone is Asia/Hong_kong which is +08:00), what I do is:

const localDateTime = moment((item.createdAt.seconds + 8 * 3600) * 1000).format('YYYY-MM-DD HH:mm:ss');
Cosmin Staicu
  • 1,809
  • 2
  • 20
  • 27
Cody Ng
  • 309
  • 4
  • 12
  • 1
    This is very dangerous, because named timezones like Asia/Hong Kong are NOT the same as a fixed offset 8 * 3600! Most timezones have daylight saving, so the offset changes throughout the year. – quietmint Mar 09 '21 at 13:43
-13

You can Try this ,

Here you can get the date based on the Client Timezone (Browser).

moment(new Date().getTime()).zone(new Date().toString().match(/([-\+][0-9]+)\s/)[1]).format('YYYY-MM-DD HH:mm:ss')

The regex basically gets you the offset value.

Cheers!!

K.Karthik
  • 7
  • 1
  • 4