Say you have a time you’d like to display on your site. You’ve entered it in your time zone. You can always be specific and say something like: 3:00 PM Eastern Standard Time. Then leave it up to whoever is reading to convert it to their local time. Every Time Zone is a great site for that.
But it can be nice to localize the time for the reader. Time zone conversion is notoriously confusing. And it’s the type of thing computers are good at. JavaScript know what time zone the reader is in, after all.
I’m sure there are a number of ways to do this. I had to do it recently on a new project, and I followed Dave Rupert’s lead, who implemented it on ShopTalk’s website to display the localized time for our next live show.
Libraries
Moment.js and Moment Timezone are a pair that can get this done. And if you need to do any other heavier date and time manipulation or relative calculations (e.g. “32 minutes ago”, “12 de Agosto de 2015”), you’ll be in good hands with these libraries.
We’ll also need to detect the time zone itself, and thankfully there is a library for that.
So we’ll be using:
- jstz.js
- moment.js
- moment-timezone.js
Package or link those up however you will.
Step 1: Get the Timezone
Might as well stash it in localStorage after you’ve gotten it. That requires less effort on the browser’s part to retrieve next time.
if (!sessionStorage.getItem('timezone')) {
var tz = jstz.determine() || 'UTC';
sessionStorage.setItem('timezone', tz.name());
}
var currTz = sessionStorage.getItem('timezone');
Step 2: Get a Time Value
Moment expects a date/time value (in UTC) like:
2015-08-12T14:30Z
If we’re only interested in the time, we can create the date portion of that string from Moment itself of today’s date (just makes the formatting way easier). The date actually kinda matters since UGHGKGJHGH daylight savings.
var date = moment().format("YYYY-MM-DD");
Then we’ll create the final string, assuming we have a variable with the time we want:
var stamp = date + "T" + theTime + "Z";
And turn it into a Moment object:
var momentTime = moment(stamp);
Step 3: Localize the time
Now we can adjust/localize that time with Moment Timezone:
var tzTime = momentTime.tz(currTz);
And properly format it for display:
var formattedTime = tzTime.format('h:mm A');
Step 4: Use it
Now we can drop it on the page anywhere we want. If you output the time zone as well, you could even replace the time currently output on the page, since even if detecting the time zone fails it will fall back to UTC and output that as the time zone.
output.textContent = "Time in " + currTz + ": " + formattedTime;
Adjusting Times
Sometimes it makes sense (perhaps from a CMS perspective) to ask people to input time in a local time zone. But in order to do the conversion correctly, it makes more sense to have that time in UTC.
Moment itself can help with adjusting times, like:
moment().subtract(4, 'hours');
moment().add(3, 'hours');
Server side languages can help too. For instance PHP can do math on times:
<?php var $sixHoursAgo = strtotime("-6 hours", time()); ?>
It probably makes the most sense to do that with Moment Timezone, because it’s not always an exact formula (again UGHADKGHK daylight savings).
Demo
In this demo we use an HTML5 time input and adjust it on the fly to a localized time.
See the Pen Adjusting Times to Local Time Zones by Chris Coyier (@chriscoyier) on CodePen.
Other Ways
Do you have any experience with this? How did you handle it?
The dependencies we used here are not insignificant! It appears there are native JavaScript methods (e.g. getTimezoneOffset()) that have pretty good support that could be used to similar effect. Any experience relying on those?
This is pretty good though, I never thought to do this client side. At least now I can fetch some post via AJAX and convert their time for their locals.
Moment++
It has been a huge help on my current project, the majority of which is a reservation system that requires loads of date and time logic.
jstz tries to determine timezone from the offset, but it cannot do it reliably – there can be several timezones matching one offset.
so user interaction required – user must validate that the auto-selected timezone is the right one.
You should wrap your save function in a try catch block to avoid issues with safaris private browsing.
You mention “localStorage” in the article, but use “sessionStorage” in the example; the latter being correct as it expires. The reason being is that the timezone can of course change, such as by changing physical location, or automatically, such as with BST/GMT in the UK.
You don’t need a library to do this!
JavaScript dates were originally designed to work in local time and later expanded to support UTC. This means you can convert times easily with no library involved as long as you store in UTC in your code (and you really should have all date stuff in UTC everywhere and only have them localized when displaying).
If you have more real use for moment so that more of it’s features are used then I think it is quite ok. But it certainly is overkill for this particular case.
I was thinking about mentioning the Date object’s local time methods myself already.
Only thing your example leaves “missing”, is the actual name of the time zone; whereas Chris’ example told me it was the time “in Europe/Berlin” – but do I actually need that information? Usually I am aware what timezone I am in, right?
So three (extra) libraries for so little effect? That seems awfully bloated and a waste of resources to me.
Shows my time is 16:30 – it’s 07:30
Bizarrely I can’t seem to find my StackOverflow post existing anymore. I do remember dealing with displaying dates that were almost 2 decades ago when the DST rules were different here. I was having trouble understanding why the time seemed wrong for display. Turns out that the hour didn’t exist due to DST rules that were in effect at the time of development….JS figured it was a great idea to accept the current DST rules on your system regardless of the year. If I recall right this is part of the spec so browsers do not fix the issue. Interestingly though Node.js(0.10-0.11 at the time I think) rounded that non existent hour differently than the browsers do, hooray for consistency :P
I believe this was my fiddle that I linked for the browser in the missing StackOverflow question:
Native Date(): http://jsfiddle.net/ybnhfmer/1/
With MomentJS: http://jsfiddle.net/ybnhfmer/
The timezone is New Zealand. You will probably have to adjust your timezone to see the error. You can see the DST dates here: http://www.timeanddate.com/time/change/new-zealand/auckland?year=1998
It outputs to console:
DATE :Sun Sep 27 1998 03:55:19 GMT+1300 (New Zealand Daylight Time), OFFSET: -780, TIMESTAMP: 906821719000
But clearly the time is off by an hour of what it should be. It just so happens that very day is when DST starts now in 2015: http://www.timeanddate.com/time/change/new-zealand/auckland?year=2015
I had to do similar things on a project related to the Olympics, where the user’s timezone, the timezone of the Olympic events and the timezones of the servers this site ran on all interacted in annoying ways. We found the simplest solution was to have dates/times in the server-generated HTML delivered in the timezone local to the servers and then re-format them on the client side in the timezone of the user’s device.
But what made this easier to manage was using data attributes to store a JS-compatible date string. So our servers in New York would deliver
<span data-datetime="2015-08-18T18:30:00.000Z">Aug 18, 2:30pm EDT</span>
, but a user in California would seeAug 18, 11:30am
. The attr let us grab all the dates that needed formatting with a simple CSS query and the value was parsed without having to do any string manipulation.We didn’t have Moment.js back when we did this project, but that would have made life much easier. Having a browser-native
Intl.DateTimeFormat
would have been even better, and happily that’s almost a reality now.I did this for a chat box embedded into a website via a TamperMonkey userscript that obtained a timestamp (ala
new Date()
) from a Node.js websocket server, and I used this function to convert the time to the user’s timezone on the client:I’m currently working with moment.js, moment-timezone.js & jstz.js to localize analytics data to a specific timezone. You can simplify your updateTime function like this:
By default, moment parse the date set the date to now and override it with the date it receives. You can see it in action here : http://codepen.io/gbergeron/pen/RWbJaX