20

What I'm looking for is a way to detect the browser's timezone ID (as defined in the Olson tables) but I don't care for the exact ID, I just need the ID of a timezone that works the same as the user's one (for example "Europe/Rome" is fine if the user is in Paris).

I'm not interested in the current offset, I really need the timezone so that I can send it to the server to do computations for other dates (the server has the Olson tables too).

Theoretically, as I already use Moment.js timezone library and have included the Olson tables, I don't need anything else, but I don't find any API to do the detection. I don't know if it's hidden somewhere or if somebody has it already written. One of the problems is that the current timezone plugin seems to keep its data private.

I dont' want a solution based on the integration of yet another copy or extract of the Olson tables (which I would have to maintain), I know there are a few libraries duplicating them, I want to use the ones I already have in Moment.js.

Denys Séguret
  • 372,613
  • 87
  • 782
  • 758
  • https://bitbucket.org/pellepim/jstimezonedetect – Matt Johnson-Pint Oct 17 '13 at 13:16
  • @MattJohnson This plugin contains an extract of the timezone data. That's what I specifically wanted to avoid. – Denys Séguret Oct 17 '13 at 13:21
  • It's not really an extract, those are well researched test points. There are a lot of edge cases to consider. I do understand your desire to stick with maintainable data, but I'm not sure that it is possible to take a direct approach. – Matt Johnson-Pint Oct 17 '13 at 13:41
  • 1
    Also, make sure you realize the [effects of ECMAScript 15.9.1.8](https://bitbucket.org/pellepim/jstimezonedetect/issue/68/effects-of-ecmascript-15918). Moment.js is also affected by this [in many ways](https://github.com/moment/moment/issues/831). – Matt Johnson-Pint Oct 17 '13 at 13:42
  • It's ES5 that is doing it wrong. ES6 is better, but almost all browsers today are ES5. See also [my blog post on this subject](http://codeofmatt.com/2013/06/07/javascript-date-type-is-horribly-broken/) – Matt Johnson-Pint Oct 17 '13 at 15:14
  • I **had** misunderstood : I thought your citation was from an ES6 draft... I always supposed the browsers already implemented/used a correct calendar library as it's not exactly a totally new field. – Denys Séguret Oct 17 '13 at 15:17
  • One would think so, but apparently the designers of ECMAScript thought time zones were too complicated or something like that. It's been in the spec since 1.0. – Matt Johnson-Pint Oct 17 '13 at 15:24

4 Answers4

17

I made a small script to do that detection. It starts by registering the ids of the available timezones, then, on a call to the matches function tests all timezone ids for the current time and the times 4 and 8 months later (to filter out the timezones with different daylight rules) and five years before.

Here it is :

<script src="moment-with-langs.min.js"></script>
<script src="moment-timezone.min.js"></script>
<script src="moment-timezone-data.js"></script>
<script>
var tzdetect = {
    names: moment.tz.names(),
    matches: function(base){
        var results = [], now = Date.now(), makekey = function(id){
            return [0, 4, 8, -5*12, 4-5*12, 8-5*12, 4-2*12, 8-2*12].map(function(months){
                var m = moment(now + months*30*24*60*60*1000);
                if (id) m.tz(id);
                return m.format("DDHHmm");
            }).join(' ');
        }, lockey = makekey(base);
        tzdetect.names.forEach(function(id){
            if (makekey(id)===lockey) results.push(id);
        });
        return results;
    }
};
</script>

If you just want one timezone id, simply use

var tzid = tzdetect.matches()[0];

Demonstration

GitHub Repository : https://github.com/Canop/tzdetect.js

Update : The code here shown isn't compatible with the most recent versions of moment.js. Please refer to the GitHub repository for a maintained (free to use) code.

2017 Update: There's now an API in moment.js to guess the timezone. That's probably the best solution right now.

Denys Séguret
  • 372,613
  • 87
  • 782
  • 758
  • 2
    This is a great start, but it needs a way to provide tests for edge cases. The tests you make now are reasonable, but don't cover everything. It would make an excellent GitHub project, and moment plugin. :) – Matt Johnson-Pint Oct 17 '13 at 13:44
  • I'll consider contributing to moment.js but as you know it (probably much better than me), I'll have to do more work on the topic before I do such a move. Your warnings here are very welcome and might be useful for other users. – Denys Séguret Oct 17 '13 at 13:46
  • 1
    @MattJohnson For now, I made [a github repository](https://github.com/Canop/tzdetect.js). – Denys Séguret Oct 17 '13 at 14:15
  • 1
    Cool. If I get some time, I'll test it out and send a pull request with proposed changes. May be a while before I can get to it. Cheers! – Matt Johnson-Pint Oct 17 '13 at 15:16
  • Matching time format should include minutes, ie 'DDHHmm' so that times which differ by < 60 minutes are excluded. – Martyn Davis Sep 05 '14 at 09:07
  • 1
    @MartynDavis I merged your change. – Denys Séguret Sep 05 '14 at 09:26
  • @dystroy : how to get user pc timezone id ? it seems that this code tzdetect.matches()[0]; will not return user pc setting wise timezone id. please help if possible. thanks – Thomas Oct 28 '14 at 12:09
  • @Thomas It should return a timezone which works the same than the user's timezone, which is enough as long as you don't need to display the name of the timezone. – Denys Séguret Oct 28 '14 at 14:01
  • the code seems it will return something like first element from array not user pc timezone. – Thomas Oct 28 '14 at 14:07
  • Doesn't seem to be working for me (in Cancún which is EST) – cdmckay Jul 03 '15 at 03:19
12

If you want to use the standard JavaScript API, you can use Intl.DateTimeFormat.resolvedOptions where there is browser support:

Intl.DateTimeFormat().resolvedOptions().timeZone; // "America/Los_Angeles"

resolvedOptions is currently (Jan 2016) available in all browsers except Safari on iOS and desktop: http://caniuse.com/#feat=internationalization

However, the timeZone property is currently only available on Chrome.

Denys Séguret
  • 372,613
  • 87
  • 782
  • 758
  • 1
    Technically, that should be `Intl.DateTimeFormat().resolvedOptions().timeZone`, but either will work. Except the current versions [have issues still](https://code.google.com/p/v8-i18n/issues/detail?id=33). – Matt Johnson-Pint Oct 17 '13 at 13:34
  • @MattJohnson Thanks for this. I'm still unsure, though, and I don't know to what extend the specification can be considered mature today. I have no document describing it with enough detail. Do you have such a documentation ? – Denys Séguret Oct 17 '13 at 13:41
  • It's part of the [ECMAScript Internationalization API](http://norbertlindenberg.com/ecmascript/intl.html#sec-12.3.3), which is not yet finalized. I agree that this is not mature enough to use today. – Matt Johnson-Pint Oct 17 '13 at 13:47
  • As poster says this works in Chrome and some other browsers, but support is patchy enough to make this risky e.g. this is NOT in Safari https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Intl#Browser_compatibility – Darren Shewry Dec 23 '15 at 11:39
7

moment now has the guess() API as described here

Anthony Chuinard
  • 1,028
  • 10
  • 17
-1

There's a javascript tool that does just that :

https://github.com/entraigas/jstz

It seems to deal with timezones ambiguity also.

Combined with momentJS timezone, you can get the timezone and show formatted date :

var tzObj = jstz.determine();
var timezoneCode = tzObj.name();
console.log(moment.tz(timeZoneCode).format());
Alex
  • 9
  • 1
    Thanks for that but my requirement was precisely *"I dont' want a solution based on the integration of yet another copy or extract of the Olson tables"*. – Denys Séguret Oct 07 '14 at 07:15