如何通过 UTC 偏移量确定时区?

发布于 2024-08-02 01:01:08 字数 1021 浏览 6 评论 0原文

我有一个场景,我有一个时区偏移(以分钟为单位),需要确定它的时区。 我知道所有数据都不可用(例如,可能有几个时区的偏移量为 -240 分钟),但“最佳猜测”是可以接受的。

我的第一遍看起来像这样:

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

这有点有效,但我需要考虑夏令时,这让我有些失望。 我添加了这个可怕的 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
    }
}

这“足够好”,因为我可以在夏令时未生效时向用户显示正确的时间,并且在 DST 期间大约 70% 正确。 不过……这对我来说是一些糟糕的代码。

有一个更好的方法吗? 更优雅就更好了,更准确就更好了。

更新

从技术上讲,我可以访问 Javascript 可以获得的有关日期的任何信息。 我有一个页面,在其中放置了一个名为“offset”的隐藏字段。 我有一个 JQuery 函数,它使用 DateTime().getTimezoneOffset() 填充偏移字段。 虽然我在 DateTime 对象上没有看到任何有帮助的内容,但也许这会打开其他的想法途径。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

○愚か者の日 2024-08-09 01:01:08

简短的回答:你不能。

夏令时使其不可能。 例如,仅根据 UTC 偏移量,无法确定夏季亚利桑那州和加利福尼亚州之间的差异,或者冬季亚利桑那州和新墨西哥州之间的差异(因为亚利桑那州不遵守 DST)。

还有不同国家遵守夏令时的时间问题。 例如,美国的 DST 比欧洲更早开始并更晚结束。

可以进行近似猜测(即+/-一小时),但如果您使用它向用户显示时间,您将不可避免地向其中一些人显示错误的时间。


更新:从评论来看,您的主要目标似乎是显示用户本地时区的时间戳。 如果这就是您想要做的,您应该将时间作为 UTC 时间戳发送,然后使用 Javascript 在用户的浏览器上重写它。 如果他们没有启用 Javascript,他们仍然会看到可用的 UTC 时间戳。 这是我在这个问题,我在这个 Greasemonkey 脚本。 您可能需要对其进行调整以满足您的需求。

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

这是我在博客上使用的一个函数,用于显示用户本地时区中已解析的时间戳。 同样,您可以将其调整为您想要的格式。

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

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;
}
如果没有 2024-08-09 01:01:08

由于 DST,仅知道与 UTC 的偏移量,您无法判断您所在的时区。 您可以考虑查看时间的一部分来尝试猜测当时 DST 是否有效,但政治考虑使这几乎不可能,因为不同的司法管辖区会改变 DST 的定义。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文