Auto detect a time zone with JavaScript

This blog post will attempt to explain how to automatically detect your user’s time zone using JavaScript. If you’re in a hurry, you can skip directly to the demo or just grab the files

Previous attempts to solve this problem:

Server side:

Time is not included in an HTTP request. This means that there is no way to get your user’s time zone using a server side scripting language like PHP.

IP address geocoding:

Another method that people have used to address this problem is to geocode your visitors IP address. IP geocoding is what is used when you go to a website and are shown an ad to “meet other singles in Boulder”. Unfortunately, for simply detecting a timezone, IP geo-coding is an expensive way to go. Just check out the prices for Maxmind and ip2location. There’s no way I’m paying for that. I did find a free provider called hostip, but it is worthless as it couldn’t decide whether I live in CA or NC.

With JavaScript:

The common JavaScript that is used to detect a visitor’s timezone is:

var myDate = new Date();
document.write(myDate.getTimezoneOffset());

As I started reading up on the getTimezoneOffset code I realized it was too buggy to be used in any critical application. The function returned inconsistent results in different browsers and it never seemed to account for daylight savings time correctly. It quickly became clear that I was going to have to write my own script if I wanted this to work.

How I ended up doing it:

There are basically two things needed to figure out a visitors time zone. First, we need to determine the time offset from Greenwich Mean Time (GMT). This can easily be done by creating two dates (one local, and one in GMT) and comparing the time difference between them:

var rightNow = new Date();
var jan1 = new Date(rightNow.getFullYear(), 0, 1, 0, 0, 0, 0);
var temp = jan1.toGMTString();
var jan2 = new Date(temp.substring(0, temp.lastIndexOf(" ")-1));
var std_time_offset = (jan1 - jan2) / (1000 * 60 * 60);

The second thing that you need to know is whether the location observes daylight savings time (DST) or not. Since DST is always observed during the summer, we can compare the time offset between two dates in January, to the time offset between two dates in June. If the offsets are different, then we know that the location observes DST. If the offsets are the same, then we know that the location DOES NOT observe DST.

var june1 = new Date(rightNow.getFullYear(), 6, 1, 0, 0, 0, 0);
temp = june1.toGMTString();
var june2 = new Date(temp.substring(0, temp.lastIndexOf(" ")-1));
var daylight_time_offset = (june1 - june2) / (1000 * 60 * 60);
var dst;
if (std_time_offset == daylight_time_offset) {
    dst = "0"; // daylight savings time is NOT observed
} else {
    dst = "1"; // daylight savings time is observed
}

Once, I had this code written, the next step was to compile a list of the various time zones around the world along with their opinions on DST. I actually ended up using the list of time zones from Microsoft Windows. It was rather time consuming to compile this list, so I hope you can make use of my work to save yourself some time.

Please let me know if you have any comments, questions or problems with this code. As with anything that I post on this blog, feel free to use this code however you want. Just don’t blame me if it breaks.

Update (06/27/07):

My code wasn’t correctly detecting timezones in the lower hemisphere. I have added hemisphere detection for all our Aussie friends out there. I also fixed a bug in the convert() function that was leaving off the + sign at certain offsets. Thanks Val for pointing this out and helping me with the fix.

Update (10/24/08):

Fixed the bug that Rama and Will pointed out in the comments.

Update (12/22/10):

Jon Nylander has taken my original code and written a a more robust solution. Use his version instead.
  • jana

    Thanks lot. Nice script.

  • venkat

    Thanks… awesome script.. saved a lot of my time

    • http://www.onlineaspect.com Josh Fraser

      Awesome. Glad to hear it.

  • guest

    When I tested for a timezone that was offset with a half an hour, like Newfoundland, the script just showed the first entry. Try it with -3:30. It breaks.

  • http://www.hvinvestigations.com Santa Monica PI

    I just stumbled here. Very cool idea. I like the way there is follow up, too.

  • http://twitter.com/yuricake @yuricake

    I don't understand why you need to create two dates and subtract them – wouldn't the internal Date engine just use the timezoneOffset to figure out the difference? In other words, what you are doing is the equivalent of taking jan1.getTimezoneOffset() and jun1.getTimezoneOffset(), no? can you give an example where the results of those two function calls is different from your code?

  • Zhibin

    I found that you are actually using the date of July 1st to compare with January 1st, instead of using June 1st as you claimed. Although this has no impact on the end result, I would just like to point it out so that you may update your blog to correct it.

    Here is the javascript code of yours:
    var june1 = new Date(rightNow.getFullYear(), 6, 1, 0, 0, 0, 0); // june 1st

    "6" actually means July here, because the parameter for the month starts with 0 for January and ends with 11 for December.

    Zhibin

  • sydney

    How do you go about solving the issue where you have multiple time zones with same offset and dst. I want to be able to uniquely identify the timezones.

  • guest

    I have also verified that the script does break if you set your computer timezone to Newfoundaland.

  • http://citadel.org dothebart

    Hey Josh,
    this looks like a very useable piece of js.
    Since you posted it here, your intention probably is to share this and let others make use of this, right?
    As you put just your name over it, gerenal copyright applies and one wouldn't legaly be able to use or redistribute it.
    Most probably something like the BSD license would be appropriate for this work?
    http://opensource.org/licenses/bsd-license.php

    • http://www.onlineaspect.com Josh Fraser

      Notice the comment I put at the bottom "As with anything that I post on this blog, feel free to use this code however you want. Just don’t blame me if it breaks."

      Since then I have started releasing everything under the Apache 2.0 license.

  • hkl

    Hi Josh,

    really great work! I found 2 little bugs: in the convert function you have to take the absolute value of minutes to find the correct index. Then you will get the correct timezone for Newfoundland. And I think the entry for Tehran must be set to dst, right?

  • Surya

    Hi excelent code,

    I have some doubts in DST and timezones.

    as per my requirment I have to check the server time and client time and they both should be in sync.

    as of now I am going like this

    checking the DST is enable or not in client machine and converting the server time and client time to GMT format and checking it.

    Kindly let me know if my logic is wrong or any where I am missing anything.

  • Stakshi

    is there way of chnaging time zone

  • Mhano

    Surely Sydney and Vladivostok are not the same time zone?!?!

    • http://www.onlineaspect.com Josh Fraser

      You're right. They just both have the same GMT offset and both support DST — BUT at opposite times of the year. There really needs to be a follow up post to this because this method has so many flaws in it. It's ideal for getting a true GMT offset and the DST flag, but that's about it.

  • http://www.stompcreative.com Jared

    Thanks for the code, it was very useful!

  • http://twitter.com/thepoetweets @thepoetweets

    wow, that is so amazing! Thank You! :)

  • Chris Pickett

    For those looking for a php map to the select form here you go:

    $timezones = array (

    '-12:00,0' => 'Etc/GMT-12',
    '-11:00,0' => 'Etc/GMT-11',
    '-10:00,0' => 'US/Hawaii',
    '-09:00,1' => 'US/Alaska',
    '-08:00,1' => 'US/Pacific',
    '-07:00,0' => 'US/Arizona',
    '-07:00,1' => 'US/Mountain',
    '-06:00,0' => 'Canada/Saskatchewan',
    '-06:00,1' => 'US/Central',
    '-05:00,0' => 'US/Indiana-Starke',
    '-05:00,1' => 'US/Eastern',
    '-04:00,1' => 'Canada/Atlantic',
    '-04:00,0' => 'America/Caracas',
    '-03:30,1' => 'Canada/Newfoundland',
    '-03:00,1' => 'America/Godthab',
    '-03:00,0' => 'America/Argentina/Buenos_Aires',
    '-02:00,1' => 'Atlantic/South_Georgia',
    '-01:00,1' => 'Atlantic/Azores',
    '-01:00,0' => 'Atlantic/Cape_Verde',
    '00:00,0' => 'Africa/Casablanca',
    '00:00,1' => 'Europe/Dublin',
    '+01:00,1' => 'Europe/Berlin',
    '+01:00,0' => 'Africa/Algiers',
    '+02:00,1' => 'Europe/Athens',
    '+02:00,0' => 'Africa/Harare',
    '+03:00,1' => 'Europe/Moscow',
    '+03:00,0' => 'Asia/Kuwait',
    '+03:30,0' => 'Asia/Tehran',
    '+04:00,0' => 'Asia/Muscat',
    '+04:00,1' => 'Asia/Baku',
    '+04:30,0' => 'Asia/Kabul',
    '+05:00,1' => 'Asia/Yekaterinburg',
    '+05:00,0' => 'Asia/Karachi',
    '+05:30,0' => 'Asia/Kolkata',
    '+05:45,0' => 'Asia/Katmandu',
    '+06:00,0' => 'Asia/Dhaka',
    '+06:00,1' => 'Asia/Almaty',
    '+06:30,0' => 'Asia/Rangoon',
    '+07:00,1' => 'Asia/Krasnoyarsk',
    '+07:00,0' => 'Asia/Bangkok',
    '+08:00,0' => 'Asia/Hong_Kong',
    '+08:00,1' => 'Australia/Perth',
    '+09:00,1' => 'Asia/Yakutsk',
    '+09:00,0' => 'Asia/Seoul',
    '+09:30,0' => 'Australia/Darwin',
    '+09:30,1' => 'Australia/Adelaide',
    '+10:00,0' => 'Australia/Brisbane',
    '+10:00,1' => 'Australia/Melbourne',
    '+11:00,0' => 'Asia/Magadan',
    '+12:00,1' => 'Pacific/Auckland'
    '+12:00,0' => 'Pacific/Fiji',
    '+13:00,0' => 'Pacific/Tongatapu');

    Chris

  • Gustavo

    how i can grab the value from combobox option and put in a php session?

  • http://www.onlineaspect.com Josh Fraser

    you need to submit the form and store it server side or pass it through with an AJAX request

  • http://www.worldpics.fr Nicolas

    I updated the list that chris gave to include missing timezones:

    $timezones = array (
    '-12:00,0' => 'Etc/GMT-12',
    '-11:00,0' => 'Pacific/Niue',
    '-10:00,0' => 'US/Hawaii',
    '-10:00,1' => 'America/Adak',
    '-09:00,0' => 'Pacific/Gambier',
    '-09:00,1' => 'US/Alaska',
    '-08:00,0' => 'Pacific/Pitcairn',
    '-08:00,1' => 'US/Pacific',
    '-07:00,0' => 'US/Arizona',
    '-07:00,1' => 'US/Mountain',
    '-06:00,0' => 'Canada/Saskatchewan',
    '-06:00,1' => 'US/Central',
    '-05:00,0' => 'US/Indiana-Starke',
    '-05:00,1' => 'US/Eastern',
    '-04:00,1' => 'Canada/Atlantic',
    '-04:00,0' => 'America/Caracas',
    '-03:30,1' => 'Canada/Newfoundland',
    '-03:00,1' => 'America/Godthab',
    '-03:00,0' => 'America/Argentina/Buenos_Aires',
    '-02:00,0' => 'America/Noronha',
    '-02:00,1' => 'Atlantic/South_Georgia',
    '-01:00,1' => 'Atlantic/Azores',
    '-01:00,0' => 'Atlantic/Cape_Verde',
    '00:00,0' => 'Africa/Casablanca',
    '00:00,1' => 'Europe/Dublin',
    '+01:00,1' => 'Europe/Paris',
    '+01:00,0' => 'Africa/Algiers',
    '+02:00,1' => 'Europe/Athens',
    '+02:00,0' => 'Africa/Harare',
    '+03:00,1' => 'Europe/Moscow',
    '+03:00,0' => 'Asia/Kuwait',
    '+03:30,0' => 'Asia/Tehran',
    '+04:00,0' => 'Asia/Muscat',
    '+04:00,1' => 'Asia/Baku',
    '+04:30,0' => 'Asia/Kabul',
    '+05:00,1' => 'Asia/Yekaterinburg',
    '+05:00,0' => 'Asia/Karachi',
    '+05:30,0' => 'Asia/Kolkata',
    '+05:45,0' => 'Asia/Katmandu',
    '+06:00,0' => 'Asia/Dhaka',
    '+06:00,1' => 'Asia/Almaty',
    '+06:30,0' => 'Asia/Rangoon',
    '+07:00,1' => 'Asia/Krasnoyarsk',
    '+07:00,0' => 'Asia/Bangkok',
    '+08:00,0' => 'Asia/Hong_Kong',
    '+08:00,1' => 'Australia/Perth',
    '+08:45,0' => 'Australia/Eucla',
    '+09:00,1' => 'Asia/Yakutsk',
    '+09:00,0' => 'Asia/Seoul',
    '+09:30,0' => 'Australia/Darwin',
    '+09:30,1' => 'Australia/Adelaide',
    '+10:00,0' => 'Australia/Brisbane',
    '+10:00,1' => 'Australia/Melbourne',
    '+10:30,1' => 'Australia/Lord_Howe',
    '+11:00,0' => 'Pacific/Noumea',
    '+11:00,1' => 'Asia/Magadan',
    '+11:30,0' => 'Pacific/Norfolk',
    '+12:00,1' => 'Pacific/Auckland',
    '+12:00,0' => 'Pacific/Fiji',
    '+12:45,1' => 'Pacific/Chatham',
    '+13:00,0' => 'Pacific/Tongatapu',
    '+14:00,0' => 'Pacific/Kiritimati',
    );

  • http://ipfunctions.com Brad

    <script type="text/javascript">

    var d = new Date()
    var gmtHours = -d.getTimezoneOffset()/60;
    document.write("The local time zone is: GMT " + gmtHours);

    </script>

  • http://www.astrosage.com Punit Pandey

    Thanks Josh. Your code is helpful in my new astrology software.

  • http://www.laridersforum.com rEID

    thought i'd spruce this up a bit.. YOu definitely saved me on this one bro. I mean A LOT!! awesome job.

  • http://www.laridersforum.com rEID

    This script was awesome.. my last comment was deleted.. not sure if you did it or not.

  • Bob

    This is a very useful script, however I wonder if anyone knows how to get the date range between which DST is in effect? It has been mentioned in previous comments as a deficiency here, and indeed this script would be complete if we were able to also determine:

    1) The begin and end date when DST is in effect (it differs across the globe) and
    2) The DST offset amount (as mentioned, some places are 30 minutes instead of 1 hour, etc).

    Any ideas? I'm thinking that I should iterate through every day of the year and just check to see when it comes into and leaves effect, but I haven't tried yet…

    • http://www.onlineaspect.com Josh Fraser

      All good points, and no, I don't know how to get that easily. I'd be curious to hear how it works to iterate through each day. Hopefully someone else here will chime in with an easier solution, but either way please report back on what you find.

    • Ed Eaglehouse

      You would need to get the official daylight saving time tables. If you wanted historical dates, you'd need the tables that go back through the times you are storing. Josh's code is used for converting between current local times and current UTC times. For historical times, you have to determine which table to use before applying the timezone offset or you may end up using a DST offset when standard time was in effect.

      Note that in .NET, the historical tables are not available, only current ones, unless you call for the time zone info structures a special way. Sorry if that's vague, but I don't recall exactly which time zone methods and classes do what.

  • http://www.gmtslider.com GMTSlider.com

    Yes, I also notices the mistake. Thanks anyway.

  • http://urlsave.net sarav

    Hi Josh,

    Good script. In your code you have mentioned june but it actually selects the july :

    var june1 = new Date(rightNow.getFullYear(), 6, 1, 0, 0, 0, 0); // this is july not june.

    • http://www.onlineaspect.com Josh Fraser

      did you test that or are you just saying that?

  • Vencedor

    There's one small error:

    var june1 = new Date(rightNow.getFullYear(), 6, 1, 0, 0, 0, 0);

    June = 5, not 6. Because it's zero based.

    Thanks for the script.

    • http://www.onlineaspect.com Josh Fraser

      that's actually not correct. try it and see.Sent from my iPhone(803) 981-2446 http://www.joshfraser.com

  • asymetrix
  • http://www.tommymedia.ca tom

    Thank you for sharing this, this scrip is excellent.

  • Marcelo

    thanks from Argentina

  • pellepim

    I utterly and completely love you for doing this! This is of course the smartest way (currently) of accurately determining a user's timezone in a web browser.

    You've forgotten one important thing though. There are many cases of ambiguity, say for example that your script determines that your timezone is '-07:00,1'. I.e UTC-7 and uses daylight savings. That's cool, but there's ambiguity there, using the Olsen database this can either map to 'America/Denver' or 'America/Chihuahua'. These two timezones are roughly equal but their daylight savings starts on different dates, with a difference of almost three weeks.

    The hardest region by far is the +02:00 northern hemisphere where there are six timezones that each start their DST on different dates or times.

    But with your code as a superb starting point I've now taken your code and made it map to Olsen database timezone keys. And I've made it so that if it comes across a case where there might be ambiguity it tries to look up specific dates to determine when daylight savings has started, thus making the determination much more granular.

    I've made a public repository on Bitbucket of my modified version of your script. You can find it here: https://bitbucket.org/pellepim/jstimezonedetect. I read in your comments that you would not mind and of course you are fully credited both in the code and in the repository wiki.

    Again, thank you soo much for this!

    ps. The Olsen tz database: (http://en.wikipedia.org/wiki/List_of_tz_database_time_zones)

    • http://www.onlineaspect.com Josh Fraser

      Wow, this is awesome. I'll update my post to give you a more prominent link. Thanks for running with this.

  • Aries

    when I click on the check box "Automatically adjust daylight saving changes" check box in Adjust Date/Time window in XP, your script is not working and also Olsen database… Check it out

    • pellepim

      There is no way to solve this. The whole determination of the specific timezone is based on finding quirks in daylight savings. If you turn off daylight savings in your operating system, there is absolutely no way for javascript to know that.

      On the other hand, if you turn off daylight savings, then as a user you are perhaps happy with another timezone with the same UTC offset?

      Nice find though. This really highlights the issue that it is hard to remove the need for user interaction when determining timezone.

      • http://www.onlineaspect.com Josh Fraser

        I wouldn't remove the user interaction if it's important for the data to be correct although you can still take them to your best guess.

        In some cases you don't need the data to be perfect in which case you're probably close enough to hide it. Adding a select dropdown with that many options in it is an awful amount of friction to add into any sign-up process and should be avoided as much as possible.

        • pellepim

          Exactly.

  • CapnCrunch

    pellepim: Nice piece of work, albeit useless with regards to Windows Standard (TimeZone) Format. In that aspect Josh's script is much more useul. Now, what would be impressive if there were a mashup using Josh's "detect_timezone.js", pellepim's "jsTimezoneDetect" and Tim Davis' "Olson Time Zone Database to Standard Windows Time Zone" ( http://www.timdavis.com.au/data/olson-time-zone-d… )

    • pellepim

      Its not useless. What is useless is Windows Standard TimeZones. Even if you are running your software on a Windows machine – every language and framework I have ever come across has libraries that work with the Olson db on Windows thus bypassing the need to even care about Windows Non-Standard stuff :)

  • ajoo

    i am adding offeset value and able to see updated time, but how can we sure that this is working as per daily light saving perspective.

  • http://twitter.com/Abitha6 @Abitha6

    Hi,
    This is the exact script am looking for.
    I also found information about time zone and other GMT related information here.
    http://timezoneguide.com/time-gmt+5:30.html

  • sanjana

    hii
    i have used it, the time is changed..but i want the time to be updated on my machine based on the changed time..how im aable to do it???

  • http://www.boatparadise.com/ Go Sailing

    Used this script on boatparadise.com and it didn't work because of the "exception" errors. Any help, pls, anyone?

  • simple

    simple javascript code; var localdate=new Date(); localdate will then contain local date & timezone (as set in user's local PC, hence will be 99.99% accurate)…….for ex- Wed Jul 20 2011 22:05:42 GMT+0530 (India Standard Time) …….this can be easily parsed in standard way and used for exact timezone info…….good luck

  • Nithra

    Hello Josh,

    I have one question for you.I created one hidden asp control and use Javascript code to get the user timezone.I need to pass the javascript return variable value to hidden control and access in code behind cs file on page load of Infopath form.
    Is there a way how it works?

  • samson99

    The accurate and often undesirable. We need to introduce a user option to allow the user to choose their language.Thanks for sharing informative post.Graham and Spencer

  • http://www.trickymak.com MAK

    Thanx ton…i'm from india, my time zone +5:50, but it shows me -5.50

  • Thiru

    Have you came across something like this ?

    User Machine's timezone is GMT (London) and for current date(Sep 06 2012) the offset should have been -60 (BST), which is GMT+1. But it returns 0 UTC offset, and date is also reflecting in UTC which is kind of weird.

    I tried the following simple alert statement to determine what was returned,

    window.alert("Current Local Desktop Date "" + new Date() + "" whose offset difference is " + new Date().getTimezoneOffset() + " mins from UTC");

    new Date() retuns in UTC instead of GMT+1 but the local time is in GMT.

    Ruledout possiblities:
    1. Daylight saving settings in Date&Time service. Because if this setting isnt checked then the taskbar time should have been different. But the task bar time reflects in BST.
    2. Browser issue –> Tried in both IE & Chrome. Both returns the same.

  • madeinindia

    very helpful for auto detection the gmt time

  • Nishanth

    this gives the timeoffset difference, is there any way to get timeZone ID like IST , EST, EDT in JS or flash . and also i need to know about the getting the system's region through flash or Java Script

  • Summved

    Hey Josh,

    Thanks for this.
    I have one more query on this. Please check the below scenario.
    I am using it on my mobile application : –
    -Timezone on mobile is setup GMT +1. It works fine.
    -But without exiting the app if the user changes the device timezone to GMT +2. it still picks up the same
    timezone i.e. GMT +1. It has to show GMT +2 but it did not.

    Please suggest something if you have any idea on this.

    Kind Regards,
    Summved

  • http://myrentals-tenerife.com Martin

    Josh – Well done for tackling a consistent problem with such class! I have avoided this area up to now and have no option but to include a solution in my current project. Thank you for saving me some considerable time and avoiding other methods that are substandard. :)

  • https://astrodevam.com/ Tarun Kumar

    Excellant code and very useful step i read it in Books.

  • http://www.onlineaspect.com Josh Fraser

    Awesome! Glad you found it useful. Be sure to subscribe. I've got some more good stuff coming. :)