258

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.

5
  • 2
    Moment doesn't support this yet, but they're working on it. github.com/timrwood/moment/pull/671 Commented Mar 11, 2013 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. Commented Aug 11, 2015 at 15:26
  • @Triynko offsets are subjected to daylight savings so, it doesn't always work as expected. Commented Aug 16, 2016 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. Commented Feb 14, 2018 at 9: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"? Commented Feb 16, 2018 at 0:21

9 Answers 9

342

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') 
Sign up to request clarification or add additional context in comments.

14 Comments

@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).
@EricCope No, not quite. You can use .tz("America/Phoenix") if you include momentjs.com/timezone as well, however. I've updated the answer with an example.
Note that using offset will not work as expected if the timezone have different offsets due to daylight savings.
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 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.
|
114

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.

5 Comments

moment(...).tz is not a function
Did you install moment-timezone?
is there a way to get the parsed and converted moment back without formatting? For example, if I want to format different ways without parsing it all over again.
(I hope you still read this) why would it assume that the date passed in is -5 when your timezone is +5? It feels like entering 19:00 in a +5 zone would result in a 14:00 UTC time being created, not 00:00
Ah, i think what i meant to say is that my timezone is -5 from UTC, not +5 -- I am on EST. Good catch, modifying the answer
74

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

1 Comment

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

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

Comments

13

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!

3 Comments

You shouldn't use the new Date() constructor. Moment provides everything you'd ever need to parse dates. See Parsing docs.
Yes, but if I don't do that, I am receiving a warning from Moment, that it is deprecated of something like that
The warning means Moment is internally falling back to exactly what you're doing (using new Date() internally), but this is inconsistent across browsers. Instead, you should use provide the expected format as the second argument. Example: moment("12-25-1995", "MM-DD-YYYY").
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

Comments

0

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/

1 Comment

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

1 Comment

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

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

1 Comment

Usually, moment gives you everything you need. You never need to match anything custom.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.