views:

643

answers:

2

I have a scenario where I have a timezone offset (in minutes) and need to determine the timezone for it. I know that all the data is not available (for example, there may be several timezones with an offset of -240 minutes) but a "best guess" is acceptable.

My first pass looked like this:

foreach (var info in TimeZoneInfo.GetSystemTimeZones())
{
    if (info.BaseUtcOffset.TotalMinutes == timezoneOffset)
    {
         // do something here if this is a valid timezone
    }
}

This sorta works, but I need to account for daylight savings which is throwing me off somewhat. I added this terrible hack:

foreach (var info in TimeZoneInfo.GetSystemTimeZones())
{
    var extra = info.IsDaylightSavingTime(DateTime.Now) ? 60 : 0;
    if (info.BaseUtcOffset.TotalMinutes + extra == timezoneOffset)
    {
         // do something here if this is a valid timezone
    }
}

This works "well enough" in that I can show the user the correct time for them when daylight savings is not in effect and am about 70% correct during DST. Still... this is some awful code to my eyeballs.

Is there a better way to do this? More elegance would be good, and more accuracy would be better still.

Update

Technically I have access to any information Javascript can get regarding the date. I have a page on which I've placed a hidden field called "offset". I have a JQuery function that populates the offset field with the DateTime().getTimezoneOffset(). While I don't see anything on the DateTime object that will help, perhaps this will open other avenues for ideas.

+9  A: 

Short answer: you can't.

Daylight saving time make it impossible. For example, there is no way to determine, solely from UTC offset, the difference between Arizona and California in the summer, or Arizona and New Mexico in the winter (since Arizona does not observe DST).

There is also the issue of what time different countries observe DST. For example, in the US DST starts earlier and ends later than in Europe.

A close guess is possible (i.e. +/- an hour), but if you are using it to display time to users you will inevitably display the wrong time to some of them.


Update: From the comments, it looks like your primary goal is to display a timestamp in the user's local timezone. If that is what you want to do, you should send the time as a UTC timestamp, and then just rewrite it on the user's browser with Javascript. In the case that they don't have Javascript enabled, they would still see a usable UTC timestamp. Here is a function I came up with in this question, which I used in this Greasemonkey script. You may want to tweak it to suit your needs.

//@param timestamp An ISO-8601 timestamp in the form YYYY-MM-DDTHH:MM:SS±HH:MM
//Note: Some other valid ISO-8601 timestamps are not accepted by this function
function parseISO8601(timestamp)
{
  var regex = new RegExp("^([\\d]{4})-([\\d]{2})-([\\d]{2})T([\\d]{2}):([\\d]{2}):([\\d]{2})([\\+\\-])([\\d]{2}):([\\d]{2})$");
  var matches = regex.exec(timestamp);
  if(matches != null)
  {
    var offset = parseInt(matches[8], 10) * 60 + parseInt(matches[9], 10);
    if(matches[7] == "-")
      offset = -offset;

    return new Date(
      Date.UTC(
        parseInt(matches[1], 10),
        parseInt(matches[2], 10) - 1,
        parseInt(matches[3], 10),
        parseInt(matches[4], 10),
        parseInt(matches[5], 10),
        parseInt(matches[6], 10)
      ) - offset*60*1000
    );
  }
  return null;
}

Here is a function I use on my blog to display a parsed timestamp in the user's local timezone. Again, you can tweak it to the format you want.

var weekDays = new Array("Sunday", "Monday", "Tuesday", "Wednesday",
        "Thursday", "Friday", "Saturday");
var months = new Array("January", "February", "March", "April", "May", "June",
        "July", "August", "September", "October", "November", "December");

function toLocalTime(date)
{
  var hour = date.getHours();
  var ampm = (hour < 12 ? "am" : "pm");
  hour = (hour + 11)%12 + 1;

  var minutes = date.getMinutes();
  if(minutes < 10)
    minutes = "0" + minutes;

  return weekDays[date.getDay()] + ", "
       + months[date.getMonth()] + " "
       + date.getDate()          + ", "
       + date.getFullYear()      + " at "
       + hour                    + ":"
       + minutes                 + " "
       + ampm;
}
Kip
This is true, an offset alone is not enough info to determine time zone. Any other info you have access to? Can you ask a user for a time zone?
Matt Sherman
@Matt: Technically I can have access to anything Javascript can tell me but I don't see anything on the javascript DateTime object that will return if the client is currently in DST or not.
Sailing Judo
@Matt: Also, this whole excercise is an attempt to NOT bother the user for information if I can somehow get it for myself. As the user for a timezone is the absolute last resort. In fact, having a *mostly* accurate guess at the timezone is better than straight out asking (in my case).
Sailing Judo
@Kip: Thanks for the update. Unfortunately, my goal is actually to show timestamps for *other* users, not necessarily the user viewing them. For example, Alice (in Alaska) can see that Bob (in Bosnia) responded to a message at 3pm Bosnian time. That means I need to know Bob's timezone (preferably without bothering him about it) and transform the timestamp before I send the data to Alice. Your code is still handy. :)
Sailing Judo
@Sailing Judo: One option would be to have a hidden field on the form, which Javascript fills using `(new Date()).toString()`. When the user submits that, store it as a string (not a timestamp). That string *is* the user's local time that he made the post.
Kip
@Sailing Judo: of course, there are drawbacks. since it is client reported, he can still put whatever he wants in that string. And it's possible that *his* machine doesn't actually know the correct time. also, as i presented it, that would actually be the time that he loaded the page, not the time he made the post. You could have Javascript update the hidden field every 30 seconds, so that your timestamp would always be no more than 30 seconds off.
Kip
+4  A: 

Knowing just the offset from UTC, you can't tell what timezone you are in, because of DST. You could consider looking at the time part of the time to try to guess whether DST was in effect then or not, but political considerations make that nearly impossible, as different jurisdictions change the definition of DST.

Ned Batchelder