Format date in a specific timezone
Asked Answered
H

9

253

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.

Hendrika answered 11/3, 2013 at 20:5 Comment(5)
Moment doesn't support this yet, but they're working on it. github.com/timrwood/moment/pull/671Epithet
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.Kickapoo
@Kickapoo offsets are subjected to daylight savings so, it doesn't always work as expected.Twannatwattle
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.Toshiatoshiko
@PramodKumar What's wrong? That gives "15 Feb, 2018". Did you mean to use format string DD MMMM, YYYY to get "15 February, 2018"?Hendrika
H
339

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')
Hendrika answered 23/5, 2013 at 0:3 Comment(13)
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!Testaceous
You can also pass an "America/Phoenix" timezone as well.Lantana
Does moment have a way to use the user's browser time zone?Hepplewhite
@Hepplewhite 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).Hendrika
@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.Hendrika
@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?Hepplewhite
@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().Hendrika
As of the time of this comment, .zone() has been deprecated in moment. Use utcOffset() instead.Seise
Note that using offset will not work as expected if the timezone have different offsets due to daylight savings.Twannatwattle
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!Supple
@Supple 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.Hendrika
careful with the minute part of the formatting, it is mm not MM . lost precious 2 hours of my life :( .Noreennorene
@Hendrika could ouy please help me #66330537Rations
A
109

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.

Augmenter answered 13/7, 2018 at 22:3 Comment(5)
moment(...).tz is not a functionPepsinate
Did you install moment-timezone?Augmenter
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.Vidrine
(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:00Quirinus
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 answerAugmenter
S
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')
Shrew answered 18/4, 2015 at 18:52 Comment(1)
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.Holston
S
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')
Seise answered 19/8, 2015 at 23:45 Comment(0)
F
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!

Fuld answered 17/9, 2014 at 16:18 Comment(3)
You shouldn't use the new Date() constructor. Moment provides everything you'd ever need to parse dates. See Parsing docs.Hendrika
Yes, but if I don't do that, I am receiving a warning from Moment, that it is deprecated of something like thatFuld
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").Hendrika
E
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

Esophagus answered 27/5, 2015 at 10:26 Comment(0)
P
-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/

Peter answered 3/7, 2023 at 16:47 Comment(1)
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.Hendrika
B
-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');
Butterfingers answered 8/3, 2021 at 10:32 Comment(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.Hendrika
P
-14

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

Prisage answered 21/8, 2018 at 9:52 Comment(1)
Usually, moment gives you everything you need. You never need to match anything custom.Nave

© 2022 - 2024 — McMap. All rights reserved.