确定用户的时区

IT技术 html browser timezone user-agent timezone-offset
2021-01-20 11:01:50

Web 服务器是否有一种标准方法可以在网页中确定用户的时区?

也许来自 HTTP 标头或user-agent字符串的一部分

6个回答
-new Date().getTimezoneOffset()/60;

该方法getTimezoneOffset()将从 GMT 中减去您的时间并返回分钟数。所以如果你住在 GMT-8,它会返回 480。

要将其转换为小时,请除以 60。另外,请注意该符号与您需要的符号相反 - 它正在计算 GMT 与您的时区的偏移量,而不是您的时区与 GMT 的偏移量。要解决这个问题,只需乘以 -1。

还要注意w3school说:

由于使用夏令时的做法,返回值不是常量。

我不同意Keyo。getTimezoneOffset() 的定义(根据 ECMA 标准ecma-international.org/ecma-262/5.1/#sec-15.9.5.26)是“以分钟为单位返回本地时间和 UTC 时间之间的差异。” - 换句话说,它应该考虑到夏令时。Mozilla 的文档说“即使对于给定的语言环境,夏令时也阻止了这个值成为一个常数。”
2021-03-19 11:01:50
这并不总是适用于 DST。获取时区偏移量正是它所说的。它得到了偏移量。时区实际上是一个地理区域。这不适用于夏令时,因为您不知道用户居住在哪个半球,或者他们的国家/地区是否有夏令时。为什么不改用这个:>>> date.toTimeString() "15:46:04 GMT+1200 (New Zealand Standard Time)"
2021-03-20 11:01:50
@xgretsch:它从 GMT获取用户当前的偏移量。如果您要展示发生在同一天的另一个时间,那很好(除非当前日期是转换日期,否则可能会出错)。但是,有许多时区与 GMT 的偏移量相同,并且它们可能具有不同的转换日期或不使用夏令时。
2021-03-20 11:01:50
这里要注意一件事;某些地方(例如加拿大的纽芬兰)的时区相差半小时,因此除以 60 后,您的答案可能不是整数。
2021-03-24 11:01:50
那些使用带有不支持 JavaScript 的浏览器的手机的用户呢?我喜欢这个问题,用户询问 HTTP 标头、用户代理......有没有办法让这个工作服务器端尽可能准确?
2021-04-02 11:01:50

我见过的最流行的(==标准?)确定时区的方法就是简单地询问用户自己。如果您的网站需要订阅,这可以保存在用户的个人资料数据中。对于匿名用户,日期可以显示为 UTC 或 GMT 等。

我不是想成为一个聪明的人。只是有时某些问题在任何编程上下文之外都有更好的解决方案。

这并没有回答问题,这显然意味着他正在寻找技术解决方案。
2021-03-21 11:01:50
@gWiz OP 正在寻求标准解决方案。这是很标准的。
2021-03-23 11:01:50
最好的解决方案可能是询问用户的组合(例如在报告顶部提供时区下拉菜单),同时在用户使用移动设备时将下拉选择默认设置为 GPS 确定的时区提供位置信息的设备,否则默认为 UTC。
2021-03-24 11:01:50
@Ishmaeel:但是用户确实在国际上旅行,他们每次从非本地时区登录时都不需要告诉他们的时区
2021-03-31 11:01:50
当用户下载的 .ics 文件应该具有特定于其位置的开始时间(例如,全国范围内的上午 9 点至 11 点)时会怎样?他们不应该必须说出他们的时区是什么。
2021-04-09 11:01:50

到目前为止,还没有 HTTP 标头会报告客户端时区,尽管有人建议将其包含在 HTTP 规范中。

如果是我,我可能会尝试使用客户端 JavaScript 获取时区,然后使用 Ajax 或其他方式将其提交给服务器。

这实际上是实际解决所提出问题的唯一答案。
2021-03-12 11:01:50
我相信最好的方法是使用 geo-ip 位置对可能的时区进行排序,并默认选择第一个(接近)匹配用户代理时间偏移(需要 JavaScript)的时区。即使在那之后,您也必须提供一种方法来修复时区,即使这种方法会选择不正确的时区。
2021-03-19 11:01:50
奇怪的是,这是该问题的唯一正确答案,该问题询问如何在服务器端进行。我怀疑有更多投票的其他答案的原因是因为一旦你意识到你需要在客户端做它,你最终会使用其他答案。但恕我直言,任何支持另一个答案的人也应该支持这个答案。
2021-03-24 11:01:50
@MikkoRantalainen 在使用代理时要小心,因为它们并不总是在标题中宣传自己。
2021-03-30 11:01:50
@Matthieu 现在有更好的答案:stackoverflow.com/a/11836123/334451
2021-04-09 11:01:50

首先,了解 JavaScript 中的时区检测是不完美的。您可以使用对象实例获取特定日期和时间的本地时区偏移量,但这与完整的IANA 时区(.getTimezoneOffsetDateAmerica/Los_Angeles

有一些选项可以工作:

const tzid = Intl.DateTimeFormat().resolvedOptions().timeZone;
console.log(tzid);

结果是一个字符串,其中包含运行代码的计算机的 IANA 时区设置。

支持的环境在国际兼容性表中列出展开该DateTimeFormat部分,然后查看名为 的功能resolvedOptions().timeZone defaults to the host environment

  • 一些库(例如Luxon)使用此 API 通过诸如luxon.Settings.defaultZoneName.

  • 如果您需要支持更广泛的环境,例如较旧的 Web 浏览器,您可以使用库对时区进行有根据的猜测Intl如果 API 可用,他们首先尝试API,当 API 不可用时,他们询问对象getTimezoneOffset功能Date,针对几个不同的时间点,使用结果从内部数据集中选择合适的时区。

    无论jsTimezoneDetect时刻,时区具有此功能。

      // using jsTimeZoneDetect
      var tzid = jstz.determine().name();
    
      // using moment-timezone
      var tzid = moment.tz.guess();
    

    在这两种情况下,结果只能被认为是一种猜测。在许多情况下,猜测可能是正确的,但不是所有情况。

    此外,这些库必须定期更新,以抵消许多较旧的 JavaScript 实现只知道其本地时区当前夏令时规则的事实更多细节在这里。

最终,更好的方法是实际询问您的用户他们的时区。提供他们可以更改的设置。您可以使用上述选项之一来选择默认设置,但不要让您的应用程序无法偏离默认设置。

还有一种完全不同的方法,即完全依赖于用户计算机的时区设置。相反,如果您可以收集纬度和经度坐标,则可以使用以下方法之一将它们解析为时区这在移动设备上运行良好。

附上一个术语说明:“语言环境”显然不是用户的地理位置。“Locale”是一组语言、数字格式、日历等设置,例如de_DE locale指定德语为默认语言,欧元为默认货币,逗号为小数分隔符,句点为千位分隔符,公历作为日历。参见developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
2021-03-24 11:01:50
不知道你是不是在欺骗我罗布。;) 但是这些方法都没有使用它们的语言环境,而是使用设备上的设置,因此与您的观点一致。(只有最后一段中提到的替代方法会使用当前位置。)
2021-04-06 11:01:50
使用用户的地理位置(即他们的语言环境)来推断时区也是有缺陷的。用户可能希望在他们的设备上使用不是本地时区的特定时区,甚至认为它可能会显示相同的时间(或不同时区)。例如,旅行者经常将他们的设备时区设置为他们通常所在的位置,并且不希望看到日期和时间在没有得到建议的情况下使用不同的偏移量。我可能是根据个人经验在这里谈论的...... ;-)
2021-04-08 11:01:50

JavaScript 是获取客户端本地时间的最简单方法。我建议使用XMLHttpRequest发回本地时间,如果失败,则退回到根据其 IP 地址检测到的时区。

至于地理定位,我已经在几个项目中使用了MaxMind GeoIP,它运行良好,但我不确定它们是否提供时区数据。这是一项您付费的服务,他们每月为您的数据库提供更新。他们提供多种网络语言的包装器。

我对这个答案投了赞成票,因为从 GeoIP 等数据库(目前有免费版本)获得的纬度和经度可以与将此类坐标转换为时区的数据库结合使用。我认为 GeoNames 有一个这样的数据库。
2021-03-19 11:01:50
MaxMind GeoIP 数据库/ API 的当前版本(免费和付费)确实提供了时区信息(它为我的时区返回“欧洲/伦敦”。)我不记得他们的 GeoIP 系统的旧版本是否做了同样的事情,但现在效果很好!MaxMind 字段被命名为“time_zone”、“time_zone_name”。
2021-03-27 11:01:50