Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Guess current timezone #220

Closed
wants to merge 7 commits into from
Closed

Conversation

timrwood
Copy link
Member

This adds the ability to guess the browser's timezone. See #138.

Demo

http://jsfiddle.net/bvjxt7jr/11/

Implementation

The implementation is fairly similar to jstimezonedetect. We look through a whitelist of zones and check if the offset matches in both January and June.

There are about 500 different timezone identifiers, and about 60 unique January/June offset pairs in use this year. The limitations of jstimezonedetect also apply here. We don't differentiate between Europe/Berlin and Europe/Stockholm as they are equivalent in recent years.

Whitelisted names

These are the whitelisted timezone names for each January/June offset pair.

Jan Jul Timezone Name
-11:00 -11:00 Pacific/Pago_Pago
-10:00 -10:00 Pacific/Honolulu
-10:00 -09:00 America/Adak
-09:30 -09:30 Pacific/Marquesas
-09:00 -09:00 Pacific/Gambier
-09:00 -08:00 America/Anchorage
-08:00 -08:00 Pacific/Pitcairn
-08:00 -07:00 America/Los_Angeles
-07:00 -07:00 America/Phoenix
-07:00 -06:00 America/Denver
-06:00 -06:00 America/Guatemala
-06:00 -05:00 America/Chicago
-05:00 -05:00 Pacific/Easter
-05:00 -04:00 America/New_York
-04:30 -04:30 America/Caracas
-04:00 -04:00 America/Santo_Domingo
-04:00 -03:00 America/Halifax
-03:30 -02:30 America/St_Johns
-03:00 -04:00 America/Campo_Grande
-03:00 -03:00 America/Santiago
-03:00 -02:00 America/Godthab
-02:00 -03:00 America/Montevideo
-02:00 -02:00 America/Noronha
-01:00 -01:00 Atlantic/Cape_Verde
-01:00 +00:00 Atlantic/Azores
+00:00 +00:00 Etc/UTC
+00:00 +01:00 Europe/London
+00:00 +02:00 Antarctica/Troll
+01:00 +01:00 Africa/Lagos
+01:00 +02:00 Europe/Berlin
+02:00 +01:00 Africa/Windhoek
+02:00 +02:00 Africa/Johannesburg
+02:00 +03:00 Asia/Beirut
+03:00 +03:00 Europe/Moscow
+03:30 +04:30 Asia/Tehran
+04:00 +04:00 Asia/Dubai
+04:00 +05:00 Asia/Baku
+04:30 +04:30 Asia/Kabul
+05:00 +05:00 Asia/Yekaterinburg
+05:30 +05:30 Asia/Kolkata
+05:45 +05:45 Asia/Kathmandu
+06:00 +06:00 Asia/Omsk
+06:30 +06:30 Asia/Rangoon
+07:00 +07:00 Asia/Krasnoyarsk
+07:00 +08:00 Asia/Hovd
+08:00 +08:00 Asia/Shanghai
+08:00 +09:00 Asia/Ulaanbaatar
+08:45 +08:45 Australia/Eucla
+09:00 +09:00 Asia/Yakutsk
+09:30 +09:30 Australia/Darwin
+10:00 +10:00 Australia/Brisbane
+10:30 +09:30 Australia/Adelaide
+11:00 +10:00 Australia/Sydney
+11:00 +10:30 Australia/Lord_Howe
+11:00 +11:00 Pacific/Noumea
+11:30 +11:30 Pacific/Norfolk
+12:00 +12:00 Pacific/Tarawa
+13:00 +12:00 Pacific/Auckland
+13:00 +13:00 Pacific/Tongatapu
+13:45 +12:45 Pacific/Chatham
+14:00 +13:00 Pacific/Apia
+14:00 +14:00 Pacific/Kiritimati

Keeping whitelist up to date

It is likely that this list will change based on updates to the tzdb, so we will probably need a way to tie this to data updates.

One problem we will need to solve is how to determine which timezone to use in the whitelist for a given offset pair. For example, there are many timezones that match -08:00/-07:00 this year. America/Dawson, America/Ensenada, America/Los_Angeles, America/Santa_Isabel, America/Tijuana, America/Vancouver, America/Whitehorse, Canada/Pacific, Canada/Yukon, Mexico/BajaNorte, PST8PDT, US/Pacific, US/Pacific-New.

The current whitelist is mostly based on the list from jstimezonedetect with a couple additions for new tzdb data. Ideally, we could get data for the population in each timezone and use that to choose the best candidate, but I'm not sure where we could find that data.

User added whitelisting

Because we are just looking through an array of names, we can expose that list to the user to add their own names.

moment.tz.currentZone(); // America/Chicago
moment.tz.currentZoneWhitelist.unshift('US/Central');
moment.tz.currentZone(); // US/Central

We should probably make a dedicated api for this rather than having to know the internals of whether to push or unshift onto the array.

Bikeshedding

Now the fun part.

There are a few different names we could go with here.

moment.tz.identify();
moment.tz.detect();
moment.tz.guess();
moment.tz.currentZone();
moment.tz.guessZone();
moment.tz.userZone();

To add user defined whitelisted names, we also have a couple options.

moment.tz.detect('US/Pacific'); // used as a getter/setter
moment.tz.identify('US/Pacific');
moment.tz.canDetect('US/Pacific');
moment.tz.canGuess('US/Pacific');
moment.tz.currentZoneWhitelist('US/Pacific');
moment.tz.userZoneWhitelist('US/Pacific');

@timrwood
Copy link
Member Author

For the api names, I'm leaning towards this.

moment.tz.detect(); // get the users timezone
moment.tz.detect('US/Pacific'); // prefer US/Pacific over America/Los_Angeles
moment.tz.detect(['US/Central', 'US/Eastern']); // add multiple preferred zones
moment.tz.ignore('US/Central'); // remove US/Central from the preferred list

@mattjohnsonpint
Copy link
Contributor

Still looking over the implementation, but I like the direction this is heading.

Thoughts:

  • I'm ok with the detect naming, but it sounds very affirmative. IMHO calling it guess would be better aligned with what it does.. Either way, we should call out in the docs that it is an educated guess, not an absolute positive match. A primary use case would be to pre-select a time zone from a list of time zones - still allowing the user to deviate by choosing a different item in the list.
  • Should we prefer the ECMAScript Internationalization API when available? I made a similar PR to jsTimeZoneDetect awhile back.
  • The Jan/Jul test is a good start, but that doesn't help distinguish between zones with differing DST rules. The +02:00 offset zones are particularly problematic. For example, we have whitelisted Asia/Beirut for zones with +2/+3, but that zone's DST transition dates are not aligned with Asia/Jerusalem, Africa/Cairo, or EU countries like Europe/Athens and Europe/Helsinki. jsTimeZoneDetect attempts to differentiate between these zones, but ES5 15.9.1.8 (our Effects of ECMAScript 15.9.1.8 moment#831) makes it difficult. To do it right is difficult, as it ends up changing from year to year, and we don't have any guarantees about what tzdb updates are on the end-user's computer. Anyway - do you have any thoughts on this? Or do we just avoid it and say we limit ourselves to whitelisted zones?

@timrwood
Copy link
Member Author

Should we prefer the ECMAScript Internationalization API when available?

Looks like it is pretty well supported across all but safari, mobile, and ie10 and lower. http://caniuse.com/#search=intl

I'll add a try/catch somewhere that checks if Intl.DateTimeFormat().resolvedOptions().timeZone is one of the available zones and returns early.

To do it right is difficult, as it ends up changing from year to year, and we don't have any guarantees about what tzdb updates are on the end-user's computer.

Yeah, I think this is a pretty slippery slope. I think between framing it as a guess in the documentation and adding the more accurate Intl lookup we will have covered a great majority of use cases.

If we did want to try to disambiguate cases like +02:00, we could maybe do a binary search on the current year to find the start and end of DST for all matched timezones and filter them from there. It does become difficult to test and maintain those edge cases, especially with data updates, and I'm not sure the cost outweighs the benefits.

@blake-nouribekian
Copy link

Is this functionality built into the current moment timezone (0.4.0-2015d) yet?

@Calyhre
Copy link

Calyhre commented Jun 18, 2015

:shipit:

done();
},

// These tests were built with 2015b data.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Were they built manually or with a script? If a script was written, could you also share it to have the ability to rebuild when moment updates with the new data?

@sompylasar
Copy link

@timrwood Thanks for the great work! Looking forward for this to be merged!

As for the APIs, browserZone could be a candidate as this is what actually being guessed.

@joshstrange
Copy link

Really looking forward to this getting merged! @blake-nouribekian: No, it's not in yet, I wish it was :) I could really use it right about now.

@sompylasar
Copy link

I haven't got this confirmed for sure yet as it comes in auto-reports from several users of a large online service, and I'm not yet sure if the timezones recorded are set by the web app, but if it is so, then the Intl.DateTimeFormat().resolvedOptions().timeZone can return an invalid timezone string, e.g. it returns "PHT" instead of Asia/Manila in some environments. Would be great if someone could either confirm this or prove it wrong. We've already got "PHT", "COT", "ORAT", "VOLT", "MSK", "TJT", "CST", "IST", "KGT", "IRKT", "YEKT", "GST", "BOT".

We use jsTimezoneDetect as a fallback if the Intl API is missing, and for now we are going to introduce a simple validation around the Intl API to throw away non-existent timezones and fall back to jsTimezoneDetect in these cases, too.

@JTallis
Copy link

JTallis commented Jun 27, 2015

Is this going to be added to momentjs.com soon?

@markterrill
Copy link

+1

3 similar comments
@ernestopye
Copy link

+1

@jeffkole
Copy link

jeffkole commented Jul 2, 2015

+1

@Cellule
Copy link

Cellule commented Jul 11, 2015

+1

@mathieumg
Copy link

+1 👍

@ansonphong
Copy link

+1 Yes please merge this, it would be great to not have to load an additional third party library to detect user's timezone.

@mattjohnsonpint
Copy link
Contributor

For those +1-ing. We all understand that client time zone detection is an important use case, but I'm not 100% convinced that this is ready for merging. There is more analysis to be done.

@JTallis
Copy link

JTallis commented Jul 12, 2015

I've been using this feature. I've had no reports of it reporting an incorrect timezone from various users across the US, UK, Finland, Sweden and Netherlands however that is only a small percentage of all the possible timezones. Just thought I'd say my experience thus far. :)

@mattjohnsonpint
Copy link
Contributor

@JTallis - Thanks, but I wonder...

  1. Have you tested near DST boundaries?
  2. How prevalent is the actual time zone id in your app? Would your users notice?
  3. Do you have users in Israel, Russia, Africa, South America, etc. that may be getting assigned to EU or US zones and not realizing it?

@JTallis
Copy link

JTallis commented Jul 12, 2015

No, I only have a small user base and it's not really used in anywhere other than one place, which is fairly noticeable. I do also have three other users from Pakistan, Iran and India. I'll ask users to confirm that the timezone chosen for them is correct and if it's not correct, I can report back.

Is there any information you'd need other than the timezone chosen and their actual timezone?

@mattjohnsonpint
Copy link
Contributor

Iran and India are easy to detect due to their fractional offsets. Pakistan could get detected as a handful of other countries, but I'm not sure it would matter since they don't have DST.

Thanks, but I think the best thing to do is to run the code through testing on different time zones. We need some grunt scripts that iterate through all time zones, change the system time zone (or env var), run the detection code, and output a list of all matches and mismatches.

Probably best to start with a Linux or Mac environment with the same tzdata update as the one being used in moment-timezone. Eventually we'd want to expand it to support Windows zones through CLDR mappings.

@marcalj
Copy link

marcalj commented Oct 22, 2015

Hey, when will be able to use this feature? :) thanks!

@prusswan
Copy link

prusswan commented Nov 3, 2015

can't wait!

@jstoeffler
Copy link

+1 For those who can't wait, there's http://pellepim.bitbucket.org/jstz/
But I can't wait for a moment version :)

@mattjohnsonpint
Copy link
Contributor

Yes, jstz is good, but since that already exists, we don't want to rush moment's version of this out the door without it being significantly better. We made some progress a few weeks back, but I've been tied up since then (I assume @timrwood has been also). Hopefully I can get some time to focus on this soon and we can get it in the next release. Hang in there! 😄

@sompylasar
Copy link

Already using jstz, yet looking forward for the moment's version!

@frekele
Copy link

frekele commented Nov 12, 2015

My timezone is America/Sao_Paulo but i see America/Montevideo in the result detected.
I will try to use jstz, because I need detect zone name now.
I waiting for next release the Moment, with zone name.

+1

@prusswan
Copy link

+1 I hope jstz and moment-timezone can eventually be merged. Timezone work is incredibly messy so it is best to concentrate community efforts.

@marcalj
Copy link

marcalj commented Nov 16, 2015

I don't think it's important to correctly detect the region or zone in the world/map. The timezone is important for the TIME setting. If I'm in Madrid but the browser detects "Europe/London" instead of "Europe/Madrid" is ok because it's the same hour right now.
If I need more accurate location tracking I would use a GPS service or something, but you can never rely on this guesses for tracking.
Thanks!

@TWiStErRob
Copy link

It's the same hour, but there may be cases (I didn't read the whole data set) when it's currently the same time, but the rules did or will change. Imagine for example if it detects "Europe/London" for you, that is saved in settings and then next year the UK shift DST by one week; or worse: decides to go off DST entirely, then half a year you would have the wrong times. It's all theoretical and I can't give an example, but you get the point.

@marcalj
Copy link

marcalj commented Nov 16, 2015

Yes, but it's something you cannot save it. It doesn't make any sense. If you need the timezone of the user, just get it and send it by parameter.
It's the same if I register in San Francisco and then come back to Europe or some other timezone. It will be incorrect.
I use to get the timezone with jstz every time I need it. And then send it to server with a custom header.
PD: Also if I change time in the OS settings, the saved setting will be incorrect. You get the idea.

Thanks!

@jstoeffler
Copy link

What you said makes sense, but it only affects specific cases (user changing the time, or moving to another country) -> it's the user's responsibility and it will probably be less frequent.

Plus you can detect time zone changes.

That's why they call it "Guess", it's good if it's not really sensitive. And if it is, we just have to ask the users, and make sure they understand that if their situation change they have to update (that's what most websites do)

@marcalj
Copy link

marcalj commented Nov 18, 2015

Since it's something that you configure in your OS (sometimes it's automatic, also on mobile), in my perspective it should be transparent and automatic to users.

@danielgindi
Copy link

You guys know about https://bitbucket.org/pellepim/ right?

@TWiStErRob
Copy link

@danielgindi That's jstz, it was literally mentioned 3 comments above you... Try CTRL+F, jstz.

@danielgindi
Copy link

Oh yeah, sorry... I read many comments and then just skipped to the end :)

@timrwood timrwood deleted the current-timezone branch December 25, 2015 15:23
@timrwood
Copy link
Member Author

This has been released in moment-timezone@0.5.0 as moment.tz.guess().

@jstoeffler
Copy link

Congratulations!

@Shobana16
Copy link

Super. Thanks for these functionalities. But can someone suggest what exact i can use to get timezone ? i tried guess and zone but i didnt get any results. actually i am passing a string "Tue, 05 Jan 2016 18:51:18 PST", and trying to get timezone from this to stop converting to any other timezone by checking it. can u pls tell what to use?

@danielgindi
Copy link

@Shobana16 you are using it incorrectly. You should not pass anything - just call it, and get the current timezone string.

@Shobana16
Copy link

I am getting this string as an input from other source. so i am trying to get that timezone.

@mathieumg
Copy link

@Shobana16 You probably want to make a new Moment instance with that string:

console.log(moment(new Date("Tue, 05 Jan 2016 18:51:18 PST")).guess());

Edit: I was mistaken! See answer by @TWiStErRob below.

@Shobana16
Copy link

ok But i get guess is not a function. :( i have moment version 0.5. do i need any other lib?

@mathieumg
Copy link

You need moment (latest version is 2.11) as well as moment-timezone version 0.5.

@Shobana16
Copy link

yes i have that only. But still i get same error Uncaught TypeError: moment(...).guess is not a function

@TWiStErRob
Copy link

The last comment from Tim (author) was that it was released in 0.5 and he gave the exact usage: moment.tz.guess(). It is a "static" function no need to create a Moment object. It gives you what moment-tz can figure out the user's time zone is, not parsing a date string.

I think what you're looking for is significantly different than the purpose of the feature discussed here, I think you should ask a StackOverflow question to get help to prevent further hijacking this issue. From what I understood you want something ike this:

moment(new Date(yourString)).tz("America/Los_Angeles").format("ddd, DD MMM YYYY HH:mm:ss z") == yourString

@mathieumg
Copy link

Thanks @TWiStErRob for clarifying, my moment is a bit rusty! I agree that a SO post would be a better place to seek help with this.

@mattjohnsonpint
Copy link
Contributor

The moment.tz.guess() functionality is only about guessing the user's time zone. It is not at all about guessing the time zone from any sort of string input.

@josh-g3
Copy link

josh-g3 commented Sep 26, 2018

moment.tz.guess() gets TypeError: Cannot read property 'guess' of undefined now. What is the status of this PR?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet