RE: [Rails] Re: Inferring timezone for HTTP headers

You could also do a javascript redirect, sending the browser’s local time as a parameter and storing the difference with servertime in a session variable.

And then everytime you need to display a time, add (or subtract) the difference.

No example at hand, but I’ve done something like this in the past.

Piet.

hey, this is a tricky problem. What you are probably interested in is guessing someone's 'locale', not their timezone. The reason is that most locales change timezones once every year; but not all do. Your servers current timezone might be CST, but chances are that in a few months it will switch to CDT. Same with most of your clients. Then it gets even more complicated because when timezones switch is constantly changing. The USA, for one, is stipulating a change this year. And to make it even more complicated, you have 'odd' cases where places like Arizona don't change their timezone at all throughout the year, while other states like Indiana have 5 different locales (some areas don't change timezones, others are in easter, some in central, etc). It is _complicated!!!!_

So, with that in mind, one solution, that as far as I know works for almost all cases (see below for disclaimer :wink: is this: * have the client, in javascript, compute the UTC offset for two dates, '2005-06-30' and '2005-12-30' (or any date that will always be in one timezone and another date that will be in the other) * using the two offsets, search through all possible locales for that which has the same winter and summer UTC offsets.

For example: summer_offset == -21600 winter_offset == -20600 matches the America/Los_Angeles locale

summer_offset == -21600 winter_offset == -21600 matches the Pacific/Galapagos locale

summer_offset == 34200 winter_offset == 37800 matches the 'Australia/Yancowinna' locale

this works, but isn't perfect because it is possible to match multiple locales. But you might be able to use a trimmed down list of locales that highlight only the major ones. The other catch is that you are choosing two dates (in our case '2005-06-30' and '2005-12-30') which you assume will always be in one timezone or the other; but what if that is not always the case. As far as I know it is *currently* the case, but when timezones switch for different locales is always changing, so this solution might not be future proof.

I have my logic so when a user registers for an account, I figure out their current timezone. I have two hidden form fields called 'summer_offset' and 'winter_offset'. Then in javascript I do this: try {   $('summer_offset').value = -1 * (new Date(Date.UTC(2005, 6, 30, 0, 0, 0, 0))).getTimezoneOffset() * 60; // in seconds   $('winter_offset').value = -1 * (new Date(Date.UTC(2005, 12, 30, 0, 0, 0, 0))).getTimezoneOffset() * 60; //in seconds } catch (e) {}

in my controller, I pass the parameters to this method, which will spit out a string containing the locale identifier:

  # trying to automatically determin a users time zone   # this is possible, but note that the locale might be off (say the local is America/Boise, but it chooses America/Denver instead)   # if a timezone is not found, will return nil... you can then set the default timezone to whatever you want: UTC perhaps?   def find_time_zone(winter_offset, summer_offset)     default_tz = nil     return default_tz if winter_offset.blank? || summer_offset.blank?

    # look at two dates that will, by and large, reside *between* major time changes within a zone.     # ie, they won't fall on a day where the time is actually changing     summer_date = "2005-06-30".to_time     winter_date = "2005-12-30".to_time     winter_offset = winter_offset.to_i     summer_offset = summer_offset.to_i     # this comes from the client, where they should have something like so (for a html client):     # $('summer_offset').value = -1 * (new Date(Date.UTC(2005, 6, 30, 0, 0, 0, 0))).getTimezoneOffset() * 60;     sd_offset, wd_offset = nil     # need to go through each timezone and find a match for the summer and winter offsets     default_tz = nil     TZInfo::Timezone.all.reverse.each do |tz|       # get the summer date and winter date total utc offset for the timezone       # This includes the utc offset plus any change with daylight savings       sd_offset = tz.period_for_local(summer_date, false).utc_total_offset       wd_offset = tz.period_for_local(winter_date, false).utc_total_offset       # at first I thought I had to have this if PLUS another one where the == were mixed like:       # sd_offset == winter_offset && wd_offset == summer_offest. This check is not needed       # because the summer_date and winter_date are aligned between the client and the server.       if sd_offset == summer_offset && wd_offset == winter_offset         default_tz = tz         break       end     end

    unless sd_offset.blank? || wd_offset.blank?       #THIS is a _tad_ elitest. Ccan the US zones to try and better match the locale.       #If a timezone matches in the US, use THAT locale...otherwise use the original one       new_time_zone = TZInfo::Timezone.us_zones.find do |z|         z.period_for_local(summer_date, false).utc_total_offset == sd_offset &&         z.period_for_local(winter_date, false).utc_total_offset == wd_offset       end       #we can add other 'cleanup' loops here if needed....       default_tz = new_time_zone unless new_time_zone.blank?     end     default_tz.blank? ? nil : default_tz.identifier   end

its a heavy approach, but it is the most complete that I could come up with. Let me know if you think it would work for you.

cheers! Adam