My single-page javascript app retrieves data in JSON format via REST calls. Dates come formatted using the UTC timezone in standard ISO8601 format, such as 2011-02-04T19:31:09Z
.
When signing up for the service, users select their timezone from a drop down list. This timezone could be different than the user's browser's timezone. The javascript app knows what the user's selected timezone is at all times.
I know how to convert the UTC string into a date. I understand that Javascript only represents dates in the local timezone.
But I'm having troubles figuring out how to display a date formatted for a timezone OTHER than the user's local timezone. It must account for DST on all dates. Internally, I want to deal with all dates as UTC and only convert to string representation of a date in another timezone at display time. I need to display the dates in the timezone selected in the user's profile, not their browser's timezone.
I've experimented with the server sending the timezone offset difference in milliseconds between the user's browser's timezone and the user's profile timezone. But I've found I can't just send one offset value, but need to send an offset for every date to account for variations in DST.
Any suggestions or sample code on how to display dates formatted in various timezones? The options I've found so far:
- Server sends dates as strings already formatted in the right timezone and no date parsing or manipulation is done on the client. This makes doing certain things on the client difficult if not impossible.
- Use a library such as https://github.com/mde/timezone-js, which includes the entire Olson TZ database into Javascript. This means longer load times more memory usage, etc.
- Send a timezoneOffsetMillis value with every date sent to the client. This results in messy JSON data and non-optimal REST interfaces.
Are there any simpler or better solutions?
2 is a bad idea since, as you pointed out, it increases load times. If I were you I would do a combination of 1 and 3. I don't agree that this makes the JSON data messy or the REST interface non-optimal.
This is a classic tradeoff where to accept a bit more complexity in the protocol in order to simplify the client-side code.
Fast-forward to 2015, when it comes to formatting a date from other timezone, using specific locale, there are two options.
In the example, I use French locale and will use a (UTC+5) date of 27 March 2016, 2h15 which is not observed in western Europe (because of DST change, clock moves from 2h00 to 3h00) which is a common source of bugs.
For maximum portability, use moment.js library. Moment comes in with bundled 80+ locales.
Using timestamp and UTC offset:
moment(1459026900000).utcOffset(300).locale('fr').format("LLLL")
// "dimanche 27 mars 2016 02:15"
Using array of integers (note that in moment, month is 0-based, just like in native JS Date object)
moment.utc([2016,3-1,27,2,15]).locale('fr').format("LLLL")
// "dimanche 27 mars 2016 02:15"
You can test those methods by running code in browser's dev tools on http://momentjs.com/
The tricky part here is to use moment.utc
which creates a moment date object with UTC flag, meaning that when that date is formatted, it will not be converted to user's timezone, but just displayed as-is (and since UTC does not observe DST, it is not prone to DST bug).
Future native solution: using Intl
object
(note as of late 2015, this solution is supported by Chrome, Firefox, IE11 but still not supported in Safari 9)
Using array of integers:
new Intl.DateTimeFormat('fr-FR', { timeZone: 'UTC', weekday: 'long',
month: 'long', year: 'numeric', day: 'numeric', hour: 'numeric',
minute: 'numeric' })
.format(Date.UTC(2016,3-1,27,2,15))
// "dimanche 27 mars 2016 02:15"
The key here is once again to use Date.UTC
and timeZone: 'UTC'
to make sure the provided date will not be converted to user's local timezone.
Note that in both cases we are using UTC methods, just to make sure the date will be used as-is without conversions. It's important though to realize those dates are fake UTC dates (they are representing time in a given arbitrary timezone, not UTC time), and should be only used for date formatting and displaying - should not be passed around.
have same problem. #1 is solution i guess. You should send all components of date (year, month, day, hours) at special container from client to server