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.


From: [email protected]
[mailto:[email protected]] On Behalf Of Hammed M.
Sent: donderdag 11 januari 2007 5:24
To: [email protected]
Subject: [Rails] Re: Inferring timezone for HTTP headers

You could use javascript to transform the date into the client’s
timezone. This post on _why’s blog describes how to go about doing this:
http://redhanded.hobix.com/inspect/showingPerfectTime.html

Hammed

On 1/11/07, Veera [email protected] wrote:

Hello,

I am encountering the classic 'hosting timezone

different from customer
timezone’ problem. The hosting timezone is CST, but
users all over the
world are visiting the site.

I understand I can make the Timezone selection a user

preference and use
TZInfo (http://tzinfo.rubyforge.org/) to do the math.

But, is there anyway to infer the client timezone from

the HTTP headers
or the request object?

Thanks,
+Veera.

--
Posted via http://www.ruby-forum.com/.

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