Nomar记录一段历史
确定用户的时区

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

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

询问用户。如果您从用户的计算机上获取时区,并且设置错误,那怎么办?
那么用户可能不在乎?
不。虽然看起来对其他事情有用,但对我目前的事业没有帮助。我需要做的是获取getTimezoneOffset()分钟,并将它们传递给一些东西,例如从中给我America/Los_Angeles 。或者将一些东西传递给一个可以给我那个或等效的函数。我不希望根据用户设置的时区显示时间,而不是基于发生某些事情的地方。
我认为没有提供时区名称的内置 API。为此,您必须求助于一些外部服务或图书馆。
不幸的是,这个问题的答案也允许用户分析和地理围栏。
为什么需要知道用户时区?
除非用户将信息发送到服务器,否则我认为这是不可能的。这应该在客户端处理。

26个回答

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

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

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

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

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

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

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

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

当用户下载的 .ics 文件应该具有特定于其位置的开始时间(例如,全国范围内的上午 9 点至 11 点)时,该怎么办?他们不应该必须说出他们的时区是什么。
为什么不接受这个作为正确答案? Unkwntech 给出的答案甚至不适用于非 IE 浏览器。
@Ishmaeel:但是用户确实在国际上旅行,他们每次从非本地时区登录时都不需要告诉他们的时区
这并没有回答问题,这显然意味着他正在寻找技术解决方案。
@gWiz OP 正在寻求标准解决方案。这是很标准的。
最好的解决方案可能是询问用户(例如在报告顶部提供时区下拉菜单)的组合,同时在用户使用移动设备时将下拉选择默认为 GPS 确定的时区提供位置信息的设备,否则默认为 UTC。
@user01 您不能做出这样的笼统声明并认为它适用于所有情况。有足够的具有用户配置文件的应用程序,其中用户不是每天都在旅行。因此,带有可供选择的时区的下拉菜单适用于“大多数”用户需求。

所有的魔法似乎都在

visitortime.getTimezoneOffset()

这很酷,我不知道这个。它在 Internet Explorer 等中工作吗?从那里你应该能够使用 JavaScript 到 Ajax,设置 cookie。我可能会自己走饼干路线。

不过,您需要允许用户更改它。不久前,我们尝试使用地理定位(通过maxmind )来做到这一点,但它的错误足以使它不值得做。所以我们只是让用户在他们的个人资料中设置它,并向尚未设置他们的用户显示通知。

如果您碰巧使用OpenID进行身份验证, Simple Registration Extension将解决经过身份验证的用户的问题(您需要将 tz 转换为数字)。

另一种选择是从用户代理的国家偏好推断时区。这是一种有点粗糙的方法(不适用于 en-US),但可以很好地近似。

-new Date().getTimezoneOffset()/60;

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

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

还要注意w3school说:

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

这对我来说最有意义。有什么理由不应该使用它,或者上面的每个人都只是让问题变得比他们需要的更困难。
@jordanstephens 我不是专家,所以我不知道是否在某些情况下这不起作用,但对我来说效果很好。
那些使用带有不支持 JavaScript 的浏览器的手机的用户呢?我喜欢这个问题,用户询问 HTTP 标头、用户代理......有没有办法让这个工作服务器端尽可能准确?
那很奇怪。对我来说,这返回-4但我在GET ,它应该是+4 - 我错过了什么?我已经检查过我的操作系统是否设置为正确的时区。
getTimezoneOffset() 返回从 GMT 时间减去当前时间时的分钟数。所以如果你住在加利福尼亚,那是 PST,也就是 GMT-8,所以你晚了 8 个小时。使用 getTimezoneOffset() 从 GMT 中减去它,它将返回 480 分钟。如果您想找到您的偏移量,请将其乘以 -1 并除以 60。
这不考虑夏令时/夏令时,但 Joseph Lust 发布的链接确实如此: stackoverflow.com/a/5492192/462162
这并不总是适用于 DST。获取时区偏移量正是它所说的。它得到了偏移量。时区实际上是一个地理区域。这不适用于夏令时,因为您不知道用户居住在哪个半球,或者他们的国家/地区是否有夏令时。为什么不改用这个: >>> date.toTimeString() "15:46:04 GMT+1200 (New Zealand Standard Time)"
我不同意Keyo。 getTimezoneOffset() 的定义(根据 ECMA 标准ecma-international.org/ecma-262/5.1/#sec-15.9.5.26 )是“以分钟为单位返回本地时间和 UTC 时间之间的差异。” - 换句话说,它应该考虑到夏令时。 Mozilla 的文档说“即使对于给定的语言环境,夏令时也阻止了这个值成为一个常数。”
@xgretsch:它从 GMT 获取用户当前的偏移量。如果您要展示同一天发生的另一个时间,那也没关系(除非当前日期是转换日期,否则可能会出错)。但是,有许多时区与 GMT 具有相同的偏移量,并且它们可能具有不同的转换日期或不使用夏令时。
@KeyotoTimeString的输出是实现定义的: ecma-international.org/ecma-262/5.1/#sec-15.9.5.4 。不能保证它会包含时区名称。如果它确实包含时区名称,也不能保证它映射到服务器知道的任何内容。它有时可能在某些浏览器中工作,但我不想依赖它。
@Mike Dimmick:虽然是真的,但问题本身询问是否有办法获得时区偏移量,而不是实际时区本身,所以这应该是正确的。
@Mike Dimmick:啊,我明白你的意思。我想“用户的时区偏移是多少”没有简单的答案——您需要问“用户在特定时间点的时区偏移是多少”。我开始认为这样做的唯一可靠方法是设置时区,打印本地时间并比较您得到的内容的可怕解决方案之一!
这里要注意一件事;某些地方(例如加拿大的纽芬兰)的时区相差半小时,因此除以 60 后,您的答案可能不是整数。
为了解决上述问题,为什么不获取浏览器的当前时间(即实际时间,而不是偏移量)然后,服务器端通过比较浏览器的当前时间和服务器的当前时间(假设您知道服务器自己的 GMT 偏移量)来确定 GMT 偏移量。这对我来说似乎是最不成问题的解决方案。您可以从那里确定时区。例如,在 .NET 中,您可以使用TimeZoneInfo循环浏览每个区域的当前时间,直到找到与浏览器报告的时间匹配的时间(或查询在线时间服务)。
用户的计算机时钟可能是错误的,在这种情况下会失败。例如,如果我向教师提供一份报告,列出学生对问题的回答及其时间,并且我的数据库以 UTC 存储时间,那么我需要知道时区偏移量,用户当前的计算机时间将是无关。可靠地获得此类偏移量的唯一方法是询问用户(即确保报告请求中包含时区,或在报告上方添加时区下拉菜单并在客户端进行转换),或通过 GPS 确定用户的物理位置。
问题问如何在服务器端确定它。这是客户端的代码。
w3scoolThe returned value is not a constant, because of the practice of using Daylight Saving Time.作为注释。
这甚至没有开始回答这个问题。

这是一篇文章(含源代码),解释了如何在 ASP.NET(VB.NET、C#)应用程序中确定和使用本地化时间:

是时候了

简而言之,所描述的方法依赖于 JavaScript getTimezoneOffset函数,该函数返回保存在会话 cookie 中并由代码隐藏用于调整 GMT 和本地时间之间的时间值的值。好处是用户不需要指定时区(代码会自动完成)。涉及更多(这就是我链接到文章的原因),但提供的代码使其非常易于使用。我怀疑您可以将逻辑转换为PHP和其他语言(只要您了解ASP.NET)。

链接已死。我认为这是替代链接: devproconnections.com/article/aspnet2/it-s-about-time-122778
该文章也可在此处以 PDF 格式获取: app.box.com/shared/bfvvmidtyg
本文描述的将UTC服务器时间转换为本地客户端时间的方法是错误的。使用当前客户端偏移量来调整服务器上的 UTC 时间将导致对于遵守夏令时的客户端语言环境,半年的“本地”时间不正确。考虑这种情况:2013 年 1 月 14 日(GMT+0000 标准时间)的英国客户将日期和时间设置为 2015 年 8 月 21 日 14:00(GMT+0100 夏令时间)。这在服务器上标准化为 2015 年 8 月 21 日 13:00 UTC。在发生这种情况的那天,客户端偏移量为 0,因此发送回客户端的时间将为 2015 年 8 月 21 日 13:00。
有道理,但我并没有声称这是一个防弹解决方案。如果您需要实施一个真正对时间敏感的解决方案,比如火车票预订应用程序,那么您需要找到一个更全面(和复杂的解决方案)。但是,对于许多应用程序来说,这不是问题。因为在许多情况下,我们希望本地化当前会话的 GMT 值。现在,如果您有一个应用程序需要为某些甚至将来保存时间戳并且它不能容忍 DTS,那么正确的方法是提供一个选项以直接在 GMT 中节省时间。如果您知道更好的选择,请分享。

使用 PHP date函数,您将获得站点所在服务器的日期时间。获取用户时间的唯一方法是使用 JavaScript。

但我建议您这样做,如果您的网站需要注册,那么最好的方法是询问用户是否将注册作为必填字段。您可以在注册页面中列出各种时区并将其保存在数据库中。在此之后,如果用户登录到该站点,则您可以根据用户选择的时区为该会话设置默认时区。

您可以使用 PHP 函数date_default_timezone_set设置任何特定的时区。这为用户设置了指定的时区。

基本上用户的时区是在客户端,所以我们必须为此使用 JavaScript。

下面是使用 PHP 和 JavaScript 获取用户时区的脚本。

 <?php #http://www.php.net/manual/en/timezones.php List of Time Zones function showclienttime() { if(!isset($_COOKIE['GMT_bias'])) { ?> <script type="text/javascript"> var Cookies = {}; Cookies.create = function (name, value, days) { if (days) { var date = new Date(); date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); var expires = "; expires=" + date.toGMTString(); } else { var expires = ""; } document.cookie = name + "=" + value + expires + "; path=/"; this[name] = value; } var now = new Date(); Cookies.create("GMT_bias",now.getTimezoneOffset(),1); window.location = "<?php echo $_SERVER['PHP_SELF'];?>"; </script> <?php } else { $fct_clientbias = $_COOKIE['GMT_bias']; } $fct_servertimedata = gettimeofday(); $fct_servertime = $fct_servertimedata['sec']; $fct_serverbias = $fct_servertimedata['minuteswest']; $fct_totalbias = $fct_serverbias – $fct_clientbias; $fct_totalbias = $fct_totalbias * 60; $fct_clienttimestamp = $fct_servertime + $fct_totalbias; $fct_time = time(); $fct_year = strftime("%Y", $fct_clienttimestamp); $fct_month = strftime("%B", $fct_clienttimestamp); $fct_day = strftime("%d", $fct_clienttimestamp); $fct_hour = strftime("%I", $fct_clienttimestamp); $fct_minute = strftime("%M", $fct_clienttimestamp); $fct_second = strftime("%S", $fct_clienttimestamp); $fct_am_pm = strftime("%p", $fct_clienttimestamp); echo $fct_day.", ".$fct_month." ".$fct_year." ( ".$fct_hour.":".$fct_minute.":".$fct_second." ".$fct_am_pm." )"; } showclienttime(); ?>

但根据我的观点,最好询问用户您的项目是否必须进行注册。

这是一个更完整的方法。

  1. 获取用户的时区偏移量
  2. 在夏令时边界上测试几天以确定它们是否在使用夏令时的区域中。

摘录如下:

 function TimezoneDetect(){ var dtDate = new Date('1/1/' + (new Date()).getUTCFullYear()); var intOffset = 10000; //set initial offset high so it is adjusted on the first attempt var intMonth; var intHoursUtc; var intHours; var intDaysMultiplyBy; // Go through each month to find the lowest offset to account for DST for (intMonth=0;intMonth < 12;intMonth++){ //go to the next month dtDate.setUTCMonth(dtDate.getUTCMonth() + 1); // To ignore daylight saving time look for the lowest offset. // Since, during DST, the clock moves forward, it'll be a bigger number. if (intOffset > (dtDate.getTimezoneOffset() * (-1))){ intOffset = (dtDate.getTimezoneOffset() * (-1)); } } return intOffset; }

从 JS 获取 TZ 和 DST (通过 Way Back Machine)

这对我有用!阅读博文下的评论,了解代码的一些更新。
这仍然只会返回时区的标准偏移量,例如 +02:00。它不会为您提供足够的信息来确定用户所在的时区,例如Africa/JohannesburgEurope/Istanbul 。请参阅时区标签维基

使用 Unkwntech 的方法,我使用 jQuery 和 PHP 编写了一个函数。这是经过测试的,确实有效!

在您希望将时区作为变量的 PHP 页面上,将这段代码放在页面顶部附近的某处:

 <?php session_start(); $timezone = $_SESSION['time']; ?>

这将读取我们现在将要创建的会话变量“time”。

在同一页面上,在 <head> 中,您首先需要包含 jQuery:

 <script type="text/javascript" src="http://code.jquery.com/jquery-latest.min.js"></script>

同样在 <head> 中,在 jQuery 下方,粘贴以下内容:

 <script type="text/javascript"> $(document).ready(function() { if("<?php echo $timezone; ?>".length==0){ var visitortime = new Date(); var visitortimezone = "GMT " + -visitortime.getTimezoneOffset()/60; $.ajax({ type: "GET", url: "http://example.org/timezone.php", data: 'time='+ visitortimezone, success: function(){ location.reload(); } }); } }); </script>

您可能注意到也可能没有注意到,但您需要将 URL 更改为您的实际域。

最后一件事。您可能想知道 timezone.php 到底是什么。嗯,就是这样:(创建一个名为timezone.php的新文件,并用上面的 URL 指向它)

 <?php session_start(); $_SESSION['time'] = $_GET['time']; ?>

如果这工作正常,它将首先加载页面,执行 JavaScript,然后重新加载页面。然后,您将能够读取 $timezone 变量并随意使用它!它返回当前的 UTC/GMT 时区偏移量 (GMT -7) 或您所在的任何时区。

我确实喜欢这个,但我可能有一些检查当前 $_SESSION['time'] 的东西,并且只有在它不同时才让 javascript 重新加载
使用 Cookie 传输它可能比使用 Session 更容易,因为锁定和反序列化 PHP 会话可能会导致应用程序变慢。为了获得最大效率,您可以将该值复制到会话中并删除 cookie,以便它不会在后续请求中发送。

JavaScript:

 function maketimus(timestampz) { var linktime = new Date(timestampz * 1000); var linkday = linktime.getDate(); var freakingmonths = new Array(); freakingmonths[0] = "jan"; freakingmonths[1] = "feb"; freakingmonths[2] = "mar"; freakingmonths[3] = "apr"; freakingmonths[4] = "may"; freakingmonths[5] = "jun"; freakingmonths[6] = "jul"; freakingmonths[7] = "aug"; freakingmonths[8] = "sep"; freakingmonths[9] = "oct"; freakingmonths[10] = "nov"; freakingmonths[11] = "dec"; var linkmonthnum = linktime.getMonth(); var linkmonth = freakingmonths[linkmonthnum]; var linkyear = linktime.getFullYear(); var linkhour = linktime.getHours(); var linkminute = linktime.getMinutes(); if (linkminute < 10) { linkminute = "0" + linkminute; } var fomratedtime = linkday + linkmonth + linkyear + " " + linkhour + ":" + linkminute + "h"; return fomratedtime; }

只需将您的时间以 Unix 时间戳格式提供给此函数; JavaScript 已经知道用户的时区。

像这样:

PHP:

 echo '<script type="text/javascript"> var eltimio = maketimus('.$unix_timestamp_ofshiz.'); document.write(eltimio); </script><noscript>pls enable javascript</noscript>';

这将始终根据该人在其计算机时钟上设置的时区正确显示时间。无需向任何人询问任何内容并将其保存到位,感谢上帝!

$unix_timestamp_ofshiz ?这里缺少一些东西并且不太有效,尽管这似乎是一个很好的答案。

一个简单的方法是使用:

 new Date().getTimezoneOffset();

这是我如何做到的。这会将 PHP 默认时区设置为用户的本地时区。只需将以下内容粘贴到所有页面的顶部:

 <?php session_start(); if(!isset($_SESSION['timezone'])) { if(!isset($_REQUEST['offset'])) { ?> <script> var d = new Date() var offset= -d.getTimezoneOffset()/60; location.href = "<?php echo $_SERVER['PHP_SELF']; ?>?offset="+offset; </script> <?php } else { $zonelist = array('Kwajalein' => -12.00, 'Pacific/Midway' => -11.00, 'Pacific/Honolulu' => -10.00, 'America/Anchorage' => -9.00, 'America/Los_Angeles' => -8.00, 'America/Denver' => -7.00, 'America/Tegucigalpa' => -6.00, 'America/New_York' => -5.00, 'America/Caracas' => -4.30, 'America/Halifax' => -4.00, 'America/St_Johns' => -3.30, 'America/Argentina/Buenos_Aires' => -3.00, 'America/Sao_Paulo' => -3.00, 'Atlantic/South_Georgia' => -2.00, 'Atlantic/Azores' => -1.00, 'Europe/Dublin' => 0, 'Europe/Belgrade' => 1.00, 'Europe/Minsk' => 2.00, 'Asia/Kuwait' => 3.00, 'Asia/Tehran' => 3.30, 'Asia/Muscat' => 4.00, 'Asia/Yekaterinburg' => 5.00, 'Asia/Kolkata' => 5.30, 'Asia/Katmandu' => 5.45, 'Asia/Dhaka' => 6.00, 'Asia/Rangoon' => 6.30, 'Asia/Krasnoyarsk' => 7.00, 'Asia/Brunei' => 8.00, 'Asia/Seoul' => 9.00, 'Australia/Darwin' => 9.30, 'Australia/Canberra' => 10.00, 'Asia/Magadan' => 11.00, 'Pacific/Fiji' => 12.00, 'Pacific/Tongatapu' => 13.00); $index = array_keys($zonelist, $_REQUEST['offset']); $_SESSION['timezone'] = $index[0]; } } date_default_timezone_set($_SESSION['timezone']); //rest of your code goes here ?>
这不考虑“夏令时”调整——都柏林的用户在冬天会匹配你的“欧洲/都柏林”,但在夏天会匹配“欧洲/贝尔格莱德”。如果您打算使用当前偏移量,您可以合理地假设是该偏移量,而不是地理标识符。

很简单,只需像这样使用 JavaScript getTimezoneOffset函数:

 -new Date().getTimezoneOffset()/60;
此外,它只返回当前时区偏移量 - 而不是time zone 。请参阅时区标签维基
这只是已接受答案的副本,为什么您甚至对其进行投票。

试试这个 PHP 代码:

 <?php $ip = $_SERVER['REMOTE_ADDR']; $json = file_get_contents("http://api.easyjquery.com/ips/?ip=" . $ip . "&full=true"); $json = json_decode($json,true); $timezone = $json['LocalTimeZone']; ?>

这是一个强大的 JavaScript 解决方案,用于确定浏览器所在的时区。

 >>> var timezone = jstz.determine(); >>> timezone.name(); "Europe/London"

https://github.com/pelepim/jstimezonedetect

不鼓励仅链接答案,因为如果链接消失,答案就不再有用;并且因为没有跟随链接,读者不知道它是否给出了一个好的答案。在这种情况下,您可以澄清这是一个采用基于数据库的方法进行识别的第 3 方库,并可能解释其操作的一些原则。
这!这是要走的路。简单的库,正确处理 DST。其他答案充满了 WTF,许多不做 DST。
这个图书馆很聪明。它的工作原理是查询当前与 UTC 的偏移量,然后通过添加或减去秒数来调整 JavaScript 日期对象时间,直到与 UTC 的偏移量发生变化。使用这种方法,该库计算出足够多的 DST 更改来唯一标识时区。我认为如果库进行二分搜索而不是线性搜索,它的性能会更好。返回值是一个 IANA 区域信息键(又名奥尔森时区数据库)。
不再需要这个库。现代浏览器支持 Intl API,它返回 IANA 时区字符串。看到这个答案

我仍然没有在这里看到获取时区的详细答案。您不应该需要按 IP 地址进行地理编码或使用 PHP (lol) 或从偏移量中错误地猜测。

首先,时区不仅仅是格林威治标准时间的偏移量。这是一个时间规则由当地标准制定的土地区域。一些国家/地区实行夏令时,会在不同时间开启 DST。获取实际区域通常很重要,而不仅仅是当前的偏移量。

如果您打算存储此时区,例如在用户首选项中,您需要时区而不仅仅是偏移量。对于实时转换,它不会有太大关系。

现在,要使用 javascript 获取时区,您可以使用以下命令:

 >> new Date().toTimeString(); "15:46:04 GMT+1200 (New Zealand Standard Time)" //Use some regular expression to extract the time.

但是我发现简单地使用这个强大的插件会更容易,它返回奥尔森格式的时区:

https://github.com/scottwater/jquery.detect_timezone

使用jQuery在 AJAX 请求上将时区偏移量作为 HTTP 标头提交

$.ajaxSetup({ beforeSend: function(xhr, settings) { xhr.setRequestHeader("X-TZ-Offset", -new Date().getTimezoneOffset()/60); } });

您还可以通过使用moment.tz.guess();来执行类似的操作以获取实际时区名称moment.tz.guess();来自http://momentjs.com/timezone/docs/#/using-timezones/guessing-user-timezone/

这仅返回当前时区偏移量 - 而不是时区。请参阅时区标签维基
编辑以包含有关对时区名称执行相同操作的信息。

不要使用 IP 地址来明确地确定位置(以及时区)——这是因为对于 NAT、代理(越来越流行)和 VPN,IP 地址不一定真实地反映用户的实际位置,而是实现这些协议的服务器驻留。

类似于美国区号在定位电话用户时不再有用,因为号码携带很流行。

上面显示的 IP 地址和其他技术对于建议用户可以调整/更正的默认值很有用。

首先,了解 JavaScript 中的时区检测是不完美的。您可以在Date对象的实例上使用getTimezoneOffset获取特定日期和时间的本地时区偏移量,但这与完整的IANA 时区(America/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(如果它可用),当它不可用时,他们询问Date对象的getTimezoneOffset函数,针对几个不同的时间点,使用结果从内部数据集中选择合适的时区。

    jsTimezoneDetectmoment-timezone都具有此功能。

     // 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/...

一种可能的选择是使用Date标头字段,它在RFC 7231 中定义并且应该包括时区。当然,不能保证该值确实是客户端的时区,但它可以是一个方便的起点。

不幸的是,该标头似乎主要用于响应,而不是请求:“用户代理可以在请求中发送 Date 标头字段,但通常不会这样做,除非它被认为向服务器传达了有用的信息。”我刚查了一下,Firefox 没有发送。

使用 JavaScript 和 PHP 很简单:

尽管用户可能会弄​​乱他/她的内部时钟和/或时区,但我迄今为止找到的最佳方式来获取偏移量,仍然是new Date().getTimezoneOffset(); .它是非侵入性的,不会让人头疼,并且无需依赖第三方。

假设我有一个表users ,它包含一个字段date_created int(13) ,用于存储 Unix 时间戳;

假设客户端creates a new account ,数据是通过post接收的,我需要使用客户端的 Unix 时间戳而不是服务器的时间戳insert/update date_created column

由于在插入/更新时需要 timezoneOffset,因此当客户端提交表单时,它会作为额外的 $_POST 元素传递,从而无需将其存储在会话和/或 cookie 中,并且也没有额外的服务器命中。

 var off = (-new Date().getTimezoneOffset()/60).toString();//note the '-' in front which makes it return positive for negative offsets and negative for positive offsets var tzo = off == '0' ? 'GMT' : off.indexOf('-') > -1 ? 'GMT'+off : 'GMT+'+off;

假设服务器接收tzo作为$_POST['tzo']

 $ts = new DateTime('now', new DateTimeZone($_POST['tzo']); $user_time = $ts->format("F j, Y, g:ia");//will return the users current time in readable format, regardless of whether date_default_timezone() is set or not. $user_timestamp = strtotime($user_time);

插入/更新date_created=$user_timestamp

检索 date_created 时,您可以像这样转换时间戳:

 $date_created = // Get from the database $created = date("F j, Y, g:ia",$date_created); // Return it to the user or whatever

现在,这个例子可能适合一个人的需要,当涉及到插入first时间戳时......当涉及到额外的时间戳或表时,您可能需要考虑将 tzo 值插入到用户表中以供将来参考,或设置它作为会话或 cookie。

PS 但是如果用户旅行并切换时区怎么办。在 GMT+4 登录,快速到达 GMT-1 并再次登录。最后登录将在未来。

我想……我们想太多了。

您可以使用moment-timezone在客户端上执行此操作并将值发送到服务器;示例用法:

 > moment.tz.guess() "America/Asuncion"

在 PHP 中获取有效的 TZ 数据库时区名称是一个两步过程:

  1. 使用 JavaScript,通过getTimezoneOffset以分钟为getTimezoneOffset获取时区偏移量。如果本地时区落后于 UTC,则该偏移量将为正,如果领先则为负。因此,您必须为偏移量添加一个相反的符号。

     var timezone_offset_minutes = new Date().getTimezoneOffset(); timezone_offset_minutes = timezone_offset_minutes == 0 ? 0 : -timezone_offset_minutes;

    将此偏移量传递给 PHP。

  2. 在 PHP 中,使用timezone_name_from_abbr函数将此偏移量转换为有效的时区名称。

     // Just an example. $timezone_offset_minutes = -360; // $_GET['timezone_offset_minutes'] // Convert minutes to seconds $timezone_name = timezone_name_from_abbr("", $timezone_offset_minutes*60, false); // America/Chicago echo $timezone_name;</code></pre>

我写了一篇关于它的博客文章: How to Detect User Timezone in PHP 。它还包含一个演示。

没有这样的方法来计算实际 HTML 代码或任何user-agent字符串中的时区,但您可以做的是使用 JavaScript 制作一个基本函数来获取它。

我还不知道如何用 JavaScript 编码,所以我的函数可能需要时间来制作。

但是,您也可以尝试使用 JavaScript 和Date部分中的getTzimezoneOffset()函数或简单地new Date().getTimezoneOffset();来获取实际时区new Date().getTimezoneOffset(); .

我认为@Matt Johnson-Pints是迄今为止最好的,并且 CanIuse 搜索显示它现在被广泛采用:

https://caniuse.com/?search=Intl.DateTimeFormat().resolvedOptions().timeZone

不过,挑战之一是考虑为什么要了解时区。因为我认为大多数人错过的一件事就是他们可以改变!如果用户带着他的笔记本电脑从欧洲旅行到美国,如果您之前将其存储在数据库中,那么他们的时区现在是不正确的(即使用户从未真正更新他们的设备时区)。这也是@Mads Kristiansen回答的问题,因为用户旅行 - 您不能依赖它。

例如,我的 Linux 笔记本电脑关闭了“自动时区”。虽然时间可能会更新我的时区不会。

所以我相信答案是 - 你需要它做什么?客户端当然似乎提供了一种更简单的方法来确定它,但是客户端和服务器端代码都将取决于用户更新时区或自动更新。我当然可能是错的。

随机文章