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

Get the browsers current time zone. #138

Closed
niemyjski opened this issue Oct 16, 2014 · 40 comments
Closed

Get the browsers current time zone. #138

niemyjski opened this issue Oct 16, 2014 · 40 comments

Comments

@niemyjski
Copy link

Unless I'm missing something it seems that there is no way to get the current time zone?? I know there are third party libs to get the olson name.. I just thought this would be included.

moment.tz().zoneName()
moment.tz(new Date()).zoneName()
moment.tz(new Date()).zone()

@timrwood
Copy link
Member

See #55 as well.

This is planned, but I haven't had time to implement it yet.

@elad
Copy link
Contributor

elad commented Nov 1, 2014

Hi, I'd like this feature as well. A couple of questions:

  • Should the API return a single guessed timezone or all suitable timezones? Maybe have both zoneName() and zoneNames()?
  • Any thoughts on how to implement it? I thought about getting the offset, adjusting it if DST is in effect, and then using the result to find suitable timezones. Or is that too naive? ;)

@timrwood
Copy link
Member

timrwood commented Nov 3, 2014

Yeah, I think the api would be something like moment.tz.localZones() to return a list of possible matches and moment.tz.localZone() to return the first item from that list.

Fortunately, this can be a one-time calculation, as the user's timezone won't change without a refresh.

var cachedLocalZones;

function localZones () {
  if (!cachedLocalZones) {
    cachedLocalZones = calculateLocalZones()
  }
  return cachedLocalZones.slice(); // always return a copy
}

function localZone () {
  return localZones().pop();
}

As far as the actual logic for calculating the local timezone, I was thinking something along these lines.

First Pass

Create Date objects for Jan 1 and Jul 1 of the current year.

var currYear = new Date().getFullYear();
var jan = new Date(currYear, 0, 1);
var jul = new Date(currYear, 6, 1);

Get the offsets for those dates using getTimezoneOffset.

var janOffset = jan.getTimezoneOffset();
var julOffset = jul.getTimezoneOffset();

Filter out zones which do not match the Jan and Jul offsets. See http://momentjs.com/timezone/docs/#/zone-object/offset/

zone.offset(jan) === janOffset && zone.offset(jul) === julOffset

That should filter down the list pretty significantly.

Second Pass

This pass is where things can get a bit more hairy, @mj1856, you should weigh in on this as I think you have more knowledge here. I don't know if we can check any more than just the current year in this step as browser timezone rules are unreliable for previous years.

For each zone that made it through the first pass, check the offset right before and after the untils for that year.

for (var i = 0; i < zone._untils.length; i++) {
  if (
    isCurrentYear(zone._untils[i])) {
    (new Date(zone._untils[i] - 1000).getTimezoneOffset() === zone._offsets[i]) &&
    (new Date(zone._untils[i] + 1000).getTimezoneOffset() === zone._offsets[i + 1])
  ) {
    // zone makes it through pass
  }
}

I think that should get most of the way there. This doesn't provide a way to prefer one zone alias over another, so we may have to add support for adding a list of preferred names to get America/Los_Angeles instead of US/Pacific for example.

As far as testing this functionality, I think we should mock Date.prototype.getTimezoneOffset to output the offsets that we want.

var getTimezoneOffset = Date.prototype.getTimezoneOffset;
exports.localZones = {
  tearDown : function () {
    Date.prototype.getTimezoneOffset = getTimezoneOffset;
  },
  someTest : function () {
    Date.prototype.getTimezoneOffset = function () {
      return this > someTimestamp ? 420 : 480;
    };
  }
};

@elad
Copy link
Contributor

elad commented Nov 3, 2014

I found this, also see the first comment to that answer...

I wonder, wouldn't it work if we just retrieved the standard timezone offset (without DST if it's in effect) and returned all zones with the same timezone offset? To borrow from the example in the link above:

  • Assume: Browser is in New York and DST is in effect, so its offset is -04:00
  • Get the standard timezone offset (-05:00 in this case)
  • Look for zones with that timezone offset (get an array that includes America/New_York)

@timrwood
Copy link
Member

timrwood commented Nov 3, 2014

We are trying to get the smallest array possible with localZones, so we should try to eliminate as many zones as we can. The southern hemisphere has DST during the northern hemisphere's winter, so checking both Jan and Jul will help eliminate those.

moment.tz([2014, 0, 1], 'Africa/Windhoek').format('MMM Z'); // Jan +02:00
moment.tz([2014, 6, 1], 'Africa/Windhoek').format('MMM Z'); // Jul +01:00
moment.tz([2014, 0, 1], 'Europe/Berlin').format('MMM Z');   // Jan +01:00
moment.tz([2014, 6, 1], 'Europe/Berlin').format('MMM Z');   // Jul +02:00

@elad
Copy link
Contributor

elad commented Nov 5, 2014

Alright. I'll go ahead an proceed with an initial implementation in a couple of days unless @mj1856 (or someone else) gets there first. :)

@timrwood
Copy link
Member

timrwood commented Nov 5, 2014

Another pass we could add is to check if the Jan and Jul Date objects contain the timezone abbreviation in their toString value.

new Date().toString(); //Wed Nov 05 2014 09:25:33 GMT-0800 (PST)

Date.prototype.toString does not always contain a timezone abbreviation, so we'll have to ignore this pass if it doesn't yield any results.

@mattjohnsonpint
Copy link
Contributor

@elad - Go for it. I'm swamped at the moment. :)

@franleplant
Copy link

Hi Guys! You can check how jstimezonedetect doest it!

@mattjohnsonpint
Copy link
Contributor

@franleplant - Yes, we're well aware of jstimezonedetect. There's also an effort at https://github.com/Canop/tzdetect.js to take the same approach within the moment-timezone data.

The bigger problem is that as time goes on, the detection tests have to be updated. Due to the ECMAScript issue, tests around historical dates don't always work. So jstz, and anything else, has to rely on current tz rules. (see moment/moment#831 and http://codeofmatt.com/2013/06/07/javascript-date-type-is-horribly-broken/).

Some browsers that support the ECMAScript Internationalization API (ECMA-402) can easily get the time zone from the host directly. (I say some because Internet Explorer supports that API, but doesn't support the provision for reporting IANA time zones.)

Intl.DateTimeFormat().resolvedOptions().timeZone

@franleplant
Copy link

Thanks for all the information @mj1856
Im currently working on a private app that needs to support Different timezones, and timezone selection, and let me tell you that it is no trivial task.
Thanks to moment.js and moment-timezone its easier though!

Fran

@seawatts
Copy link

Would love to see this get put in place! I just went through some troubles trying to figure this out today. I ended up using jstz with moment-timezone as follows

var someTimeFromUTC = moment('2014-11-19T08:00:00Z');
var tzName = jstz.determine().name(); // America/Los_Angeles
var localTime = moment(someTimeFromUTC).tz(tzName)
var formatedTime = localTime.format('llll z'); // Tue, Nov 19, 2014 8:00AM PST

All I really wanted to do was to be able to format my time with the abbreviated time zone. I saw that was depreciated in moment.js in lieu of creating moment-timezone however the functionality doesn't seem to exist anymore.

@kashifshamaz21
Copy link
Contributor

@elad Did you get a chance to wire up a working protoype for this feature.. I can give it a shot, if you haven't done a lot of progress on it already. Thanks!

@elad
Copy link
Contributor

elad commented Jan 31, 2015

@kashifshamaz21 please do! Unfortunately I haven't had a chance to get to it yet. :(

@okonon
Copy link

okonon commented Feb 8, 2015

Hello,
This feature would be awsome!
+1000

@RouR
Copy link

RouR commented Mar 8, 2015

Check this code
http://stackoverflow.com/questions/19420582/detect-the-id-of-the-current-user-timezone-using-moment-js
https://github.com/Canop/tzdetect.js

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;
    }
};

If you just want one timezone id, simply use
var tzid = tzdetect.matches()[0];

@mattjohnsonpint
Copy link
Contributor

I love the idea of having a function that can be used directly against the moment-timezone data, rather than jsTimeZoneDetect, which has it's own data. However, I'm not convinced that the Canop/tzdetect solution is complete enough to be useful.

Running it on my computer, which is Windows 8.1, Chrome latest, in US Pacific time, I get:

["America/Dawson", "America/Ensenada", "America/Los_Angeles", "America/Tijuana",
 "America/Vancouver", "America/Whitehorse", "Canada/Pacific", "Canada/Yukon",
 "Mexico/BajaNorte", "PST8PDT", "US/Pacific", "US/Pacific-New"]

jsTimeZoneDetect gives the correct result of America/Los_Angeles.

I'd love to be proven wrong, but I'm fairly certain that any solution other than the Ecma Intl API (in supported browsers) will have problems. The mere fact that ECMAScript 5.1 hides all non-current DST transitions makes it near impossible to properly interrogate the Date object for time zone information.

@emorikawa
Copy link

Until this gets fixed you can use Intl.DateTimeFormat().resolvedOptions().timeZone on latest Chrome & FF

@mattjohnsonpint
Copy link
Contributor

@emorikawa - Chrome, yes. FF - no. I also believe it works in Opera, but not IE. Not sure about Safari.

@pierre-b
Copy link

+1 ! with todays SaaS apps displaying analytics everywhere it's a must-have feature. Thank you if you cant make it !

@austinpray
Copy link

Gonna try and jump in and implement this. I have a lot of docs to read to orient myself though. @elad @timrwood if you have any updates other than what you have specified above let me know!

@timrwood
Copy link
Member

I opened a PR for this at #220.

@bcherny
Copy link

bcherny commented May 22, 2015

@mj1856 good call on Intl.DateTimeFormat().resolvedOptions().timeZone!

@gnansu
Copy link

gnansu commented Jul 22, 2015

@timrwood I'm using moment-timezone 0.3.0. Is the capability of getting client/browser timezone implemented? I'm currently using jstz-detect to detect the timezone (Country/City) and using that with moment.tz.* functions.

@mattjohnsonpint
Copy link
Contributor

@gnansu - no, it is not yet implemented in the release. You can try the code in PR #220, but we've still got some things to work out before we are ready to merge and release it.

@gnansu
Copy link

gnansu commented Jul 23, 2015

@mj1856 Thanks Matt for the information. Looking forward to the new additions.

@avetisk
Copy link

avetisk commented Sep 1, 2015

Fortunately, this can be a one-time calculation, as the user's timezone won't change without a refresh.

@timrwood that's not true all the time: try on Chrome (OSX), it will update the timezone dynamically without refresh.

@timrwood
Copy link
Member

@avetisk, that's a bummer, we'll have to add some cache checks to make sure we aren't getting stale data.

@timrwood
Copy link
Member

Closing in favor of #220

@allan-bonadio
Copy link

the great white hope:
Intl.DateTimeFormat().resolvedOptions().timeZone

Unfortunately I tried it with my 4 browsers. Safari (today's) has no Intl, FF41 and IE11 have it but returns undefined (which is documented as 'the current timezone' thanx a lot). Chrome has it but use 'resolved' in place of 'resolvedOptions()'. So the only one that works, is the improperly implemented one. :-(

Why is this so hard? It would be easy to implement once you've done the rest of Intl. EZ.

@argb
Copy link

argb commented Dec 13, 2015

Have implemented this feature,i noted that is issue have been posted more than one year,but i can find any info about this in moment-timezone's documents.

@TWiStErRob
Copy link

@argb is right, it looks like it's done.
@timrwood @mj1856 this was closed in favor of #220. #220 is now closed because it was moved to #256 which is merged, but only to guess-timezone. I just saw there's progress (#274), but there's no open tracking issue for this as a feature, or is there?

@timrwood
Copy link
Member

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

@Ihs88999
Copy link

@timrwood I tried using moment.tz.guess() it works however it doesn't detect it automatically the page has to reload first. is there a way to get the timezone immediately without having to reload the page?

@mattjohnsonpint
Copy link
Contributor

@Ihs88999 - There's nothing special about that function in terms of loading. Perhaps you just aren't loading moment and moment-timezone correctly? We don't know anything about your code or environment, so it's hard to say.

@kevinSuttle
Copy link

kevinSuttle commented Sep 29, 2016

@timrwood Suggestions for how to use this outside the browser?

@bcherny
Copy link

bcherny commented Sep 29, 2016

@kevinSuttle process.env.TZ

@Husterknupp
Copy link

@bcherny is there a trick to get TZ on OSX?

$ node
> process.env.TZ
undefined

I'm using OS X 10.11.6

$ node --version
v5.4.1

@bcherny
Copy link

bcherny commented Sep 29, 2016

Looks like OSX doesn't define it. I just published node-timezone, which I tested on OSX and Ubuntu.

@kevinSuttle
Copy link

This works for me inside of Webpack: #138 (comment)

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

No branches or pull requests