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

Posted

技术标签:

【中文标题】如何通过 UTC 偏移量确定时区?【英文标题】:How can I determine a timezone by the UTC offset? 【发布时间】:2010-11-19 11:19:57 【问题描述】:

我有一个时区偏移量(以分钟为单位)并且需要为其确定时区的情况。我知道并非所有数据都可用(例如,可能有多个时区偏移为 -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”的隐藏字段。我有一个使用 DateTime().getTimezoneOffset() 填充偏移字段的 JQuery 函数。虽然我在 DateTime 对象上看不到任何有用的东西,但也许这会为想法打开其他途径。

【问题讨论】:

@Sailing Judo:我在答案中添加了一些代码,它可以让 Javascript 将通用时间戳转换为本地时间戳。 【参考方案1】:

简短的回答:你不能。

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

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

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


更新:从 cmets 看来,您的主要目标是在用户的本地时区显示时间戳。如果这是您想要做的,您应该将时间作为 UTC 时间戳发送,然后使用 Javascript 在用户的浏览器上重写它。如果他们没有启用 Javascript,他们仍然会看到可用的 UTC 时间戳。这是我在this question 中提出的一个函数,我在this Greasemonkey script 中使用了它。您可能需要对其进行调整以满足您的需要。

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

【讨论】:

这是真的,仅靠偏移量不足以确定时区。您可以访问的任何其他信息?您可以向用户询问时区吗? @Matt:从技术上讲,我可以访问 Javascript 可以告诉我的任何内容,但是如果客户端当前是否在 DST 中,我在 javascript DateTime 对象上看不到任何返回的内容。 @Matt:此外,如果我能以某种方式为自己获取信息,那么整个练习都是为了不打扰用户获取信息。由于时区的用户是绝对的最后手段。事实上,对时区进行大部分准确的猜测比直接询问(在我的情况下)要好。 @Sailing Judo:当然也有缺点。由于是客户报告的,他仍然可以在该字符串中放入任何他想要的东西。 他的 机器可能实际上并不知道正确的时间。另外,正如我介绍的那样,那实际上是他加载页面的时间,而不是他发帖的时间。您可以让 Javascript 每 30 秒更新一次隐藏字段,这样您的时间戳就不会超过 30 秒。 如果我们假设没有夏令时怎么办?毕竟 DST 会影响 +-1 小时。【参考方案2】:

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

【讨论】:

以上是关于如何通过 UTC 偏移量确定时区?的主要内容,如果未能解决你的问题,请参考以下文章

.NET 通过时区名称获取时区偏移量

给定 UTC 时间戳和 UTC 偏移量,是不是可以在 Python 中获取时区?

如何将系统时间转换成utc时间

如何从时区名称中获取时区偏移量

如何使用 pytz 从时区字符串中获取 UTC 偏移量?

Python & pytz:确定时区是不是支持 DST 以及 DST 和非 DST UTC 偏移量是多少