Is there a standard way for a web server to be able to determine a user's timezone within a web page?
Perhaps from an HTTP header or part of the user-agent
string?
Is there a standard way for a web server to be able to determine a user's timezone within a web page?
Perhaps from an HTTP header or part of the user-agent
string?
-new Date().getTimezoneOffset()/60;
该方法getTimezoneOffset()
将从 GMT 中减去您的时间并返回分钟数。因此,如果您居住在 GMT-8,它将返回 480。
要将其计算为小时,除以 60。另外,请注意符号与您需要的相反 - 它计算的是 GMT 与您的时区的偏移量,而不是您的时区与 GMT 的偏移量。要解决此问题,只需乘以 -1。
另请注意,w3school说:
由于使用夏令时的做法,返回的值不是常数。
我见过的最流行的(==标准?)确定时区的方法是简单地询问用户自己。如果您的网站需要订阅,这可以保存在用户的个人资料数据中。对于匿名用户,日期可以显示为 UTC 或 GMT 等。
我不是想成为一个聪明的家伙。只是有时某些问题在任何编程上下文之外都有更好的解决方案。
到目前为止,还没有 HTTP 标头会报告客户端时区,尽管已建议将其包含在 HTTP 规范中。
如果是我,我可能会尝试使用客户端 JavaScript 获取时区,然后使用 Ajax 或其他方式将其提交到服务器。
首先,了解 JavaScript 中的时区检测是不完善的。您可以使用对象实例获取特定日期和时间的本地时区偏移量,但这与完整的IANA 时区(如.getTimezoneOffset
Date
America/Los_Angeles
不过,有一些选项可以工作:
const tzid = Intl.DateTimeFormat().resolvedOptions().timeZone;
console.log(tzid);
结果是一个字符串,其中包含运行代码的计算机的 IANA 时区设置。
支持的环境列在 Intl 兼容性表中。展开该DateTimeFormat
部分,并查看名为resolvedOptions().timeZone defaults to the host environment
.
一些库,比如Luxon使用这个 API 通过函数来确定时区,比如luxon.Settings.defaultZoneName
.
如果您需要支持更广泛的环境集,例如较旧的 Web 浏览器,您可以使用库对时区进行有根据的猜测。他们首先尝试Intl
API(如果可用),当它不可用时,他们会在几个不同的时间点询问对象的getTimezoneOffset
功能Date
,使用结果从内部数据集中选择合适的时区。
jsTimezoneDetect和moment -timezone都具有此功能。
// using jsTimeZoneDetect
var tzid = jstz.determine().name();
// using moment-timezone
var tzid = moment.tz.guess();
在这两种情况下,结果只能被认为是一种猜测。在许多情况下,猜测可能是正确的,但并非全部。
此外,这些库必须定期更新以抵消许多较旧的 JavaScript 实现只知道其本地时区的当前夏令时规则的事实。 更多细节在这里。
最终,更好的方法是实际询问您的用户他们的时区。提供他们可以更改的设置。您可以使用上述选项之一来选择默认设置,但请不要在您的应用程序中偏离该设置。
还有完全不同的方法,根本不依赖用户计算机的时区设置。相反,如果您可以收集纬度和经度坐标,则可以使用其中一种方法将其解析为时区。这在移动设备上运行良好。
JavaScript 是获取客户端本地时间的最简单方法。我建议使用XMLHttpRequest发回本地时间,如果失败,则回退到根据其 IP 地址检测到的时区。
至于地理定位,我在几个项目中使用了 MaxMind GeoIP,它运行良好,但我不确定它们是否提供时区数据。这是一项您付费的服务,他们会为您的数据库提供每月更新。它们提供多种网络语言的包装器。
这是一个强大的 JavaScript 解决方案,用于确定浏览器所在的时区。
>>> var timezone = jstz.determine();
>>> timezone.name();
"Europe/London"
这是一个更完整的方法。
摘录如下:
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)
使用 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>
同样在 jQuery 下方的 <head> 中,粘贴以下内容:
<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) 或您所在的任何时区。
使用jQuery在 AJAX 请求上将时区偏移量作为 HTTP 标头提交
$.ajaxSetup({
beforeSend: function(xhr, settings) {
xhr.setRequestHeader("X-TZ-Offset", -new Date().getTimezoneOffset()/60);
}
});
您还可以通过使用 http://momentjs.com/timezone/docs/#/using-timezones/guessing-user-timezone/ 执行类似的操作来获取实际时moment.tz.guess();
区名称
我仍然没有在这里看到获取时区的详细答案。您不需要按 IP 地址进行地理编码或使用 PHP (lol) 或从偏移量中错误地猜测。
首先,时区不仅仅是与 GMT 的偏移量。这是一块土地,其时间规则由当地标准制定。一些国家有夏令时,并且会在不同的时间开启 DST。获取实际区域通常很重要,而不仅仅是当前偏移量。
如果您打算存储此时区,例如在用户首选项中,您需要该时区而不仅仅是偏移量。对于实时转换,这并不重要。
现在,要使用 javascript 获取时区,您可以使用以下命令:
>> new Date().toTimeString();
"15:46:04 GMT+1200 (New Zealand Standard Time)"
//Use some regular expression to extract the time.
然而,我发现简单地使用这个返回奥尔森格式时区的强大插件更容易:
使用 PHPdate
函数,您将获得站点所在服务器的日期时间。获得用户时间的唯一方法是使用 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();
?>
但根据我的观点,最好询问用户在您的项目中是否必须注册。
不要使用 IP 地址来明确确定位置(以及因此时区)——这是因为对于 NAT、代理(越来越流行)和 VPN,IP 地址不一定真实地反映用户的实际位置,而是实现这些协议的服务器驻留。
鉴于号码可携带性的普及,类似于美国区号不再用于定位电话用户。
上面显示的 IP 地址和其他技术对于建议用户可以调整/更正的默认值很有用。
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>';
这将始终根据该人在他/她的计算机时钟上设置的时区正确显示时间。无需向任何人询问任何内容并将其保存到地方,感谢上帝!
很简单,只需像这样使用 JavaScriptgetTimezoneOffset
函数:
-new Date().getTimezoneOffset()/60;
所有的魔法似乎都在
visitortime.getTimezoneOffset()
很酷,我不知道。它可以在 Internet Explorer 等中使用吗?从那里你应该能够使用 JavaScript 到 Ajax,设置任何 cookie。我可能会自己走饼干路线。
您需要允许用户更改它。不久前,我们尝试使用地理位置(通过maxmind
)来执行此操作,但它是错误的,以至于不值得这样做。所以我们只是让用户在他们的个人资料中设置它,并向尚未设置的用户显示通知。
如果您碰巧使用OpenID进行身份验证,Simple Registration Extension将解决经过身份验证的用户的问题(您需要从 tz 转换为 numeric)。
另一种选择是从用户代理的国家偏好中推断时区。这是一种有点粗略的方法(不适用于 en-US),但可以很好地近似。
这是一篇文章(带有源代码),它解释了如何在 ASP.NET(VB.NET、C#)应用程序中确定和使用本地化时间:
简而言之,所描述的方法依赖于 JavaScriptgetTimezoneOffset
函数,该函数返回保存在会话 cookie 中并由代码隐藏用于调整 GMT 和本地时间之间的时间值的值。好处是用户不需要指定时区(代码会自动指定)。涉及的内容更多(这就是我链接到该文章的原因),但提供的代码使其非常易于使用。我怀疑您可以将逻辑转换为 PHP 和其他语言(只要您了解 ASP.NET)。
使用 JavaScript 和 PHP 很简单:
即使用户可能会弄乱他/她的内部时钟和/或时区,但到目前为止我找到的获得偏移量的最佳方法仍然是new Date().getTimezoneOffset();
. 它是非侵入性的,不会让人头疼,并且无需依赖第三方。
假设我有一个表,users
其中包含一个字段date_created int(13)
,用于存储 Unix 时间戳;
假设一个客户端creates a new account
,数据由 接收post
,我需要客户端insert/update
的date_created column
Unix 时间戳,而不是服务器的。
由于在插入/更新时需要 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:i a");//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:i a",$date_created); // Return it to the user or whatever
现在,这个例子可能适合一个人的需要,当涉及到插入first
时间戳时......当涉及到额外的时间戳或表时,您可能需要考虑将 tzo 值插入到 users 表中以供将来参考,或者设置它作为会话或cookie。
PS 但是,如果用户旅行并切换时区怎么办。在 GMT+4 登录,快速到达 GMT-1 并再次登录。最后一次登录将在将来。
我想……我们想太多了。
您可以使用时刻时区在客户端上执行此操作并将值发送到服务器;示例用法:
> moment.tz.guess()
"America/Asuncion"
在 PHP 中获取有效的 TZ 数据库时区名称是一个两步过程:
使用 JavaScript,通过getTimezoneOffset
. 如果本地时区落后于 UTC,则此偏移量将为正数,如果早于 UTC,则此偏移量将为负数。因此,您必须在偏移量中添加一个相反的符号。
var timezone_offset_minutes = new Date().getTimezoneOffset();
timezone_offset_minutes = timezone_offset_minutes == 0 ? 0 : -timezone_offset_minutes;
将此偏移量传递给 PHP。
在 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>
我写了一篇关于它的博客文章:如何在 PHP 中检测用户时区。它还包含一个演示。
试试这个 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'];
?>
一个简单的方法是使用:
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
?>
一种可能的选择是使用Date
标头字段,该字段在RFC 7231中定义并且应该包含时区。当然,不能保证该值确实是客户端的时区,但它可以是一个方便的起点。
有几种方法可以确定浏览器中的时区。如果您的浏览器有可用且支持的标准功能,那么您应该使用该功能。以下是以不同格式获取相同信息的三种方法。避免使用基于某些假设或硬编码区域列表进行任何猜测的非标准解决方案,尽管如果无法做其他事情,它们可能会有所帮助。
获得此信息后,您可以将其作为非标准请求标头传递给服务器并在那里使用它。如果您还需要时区偏移量,您还可以将其传递给服务器的标头或请求有效负载,可以使用 dateObj.getTimezoneOffset() 进行检索。
Intl.DateTimeFormat().resolvedOptions().timeZone
let dateObj = new Date(2021, 11, 25, 09, 30, 00);
//then
dateObj.toString()
//yields
Sat Dec 25 2021 09:30:00 GMT+0530 (India Standard Time) //I am located in India (IST)
请注意,该字符串包含长短格式的时区信息。您现在可以使用正则表达式来获取此信息:
let longZoneRegex = /\((.+)\)/;
dateObj.toString().match(longZoneRegex);
//yields
['(India Standard Time)', 'India Standard Time', index: 34, input: 'Sat Dec 25 2021 09:30:00 GMT+0530 (India Standard Time)', groups: undefined]
//Note that output is an array so use output[1] to get the timezone name.
同样,您也可以获取短格式:
let shortZoneRegex = /GMT[+-]\d{1,4}/;
dateObj.toString().match(shortZoneRegex);
//yields
['GMT+0530', index: 25, input: 'Sat Dec 25 2021 09:30:00 GMT+0530 (India Standard Time)', groups: undefined]
//Note that output is an array so use output[0] to get the timezone name.
我认为这@Matt Johnson-Pints
是迄今为止最好的,CanIuse 搜索显示现在它已被广泛采用:
https://caniuse.com/?search=Intl.DateTimeFormat().resolvedOptions().timeZone
但是,挑战之一是考虑为什么要知道时区。因为我认为大多数人错过的一件事就是他们可以改变!如果用户带着他的笔记本电脑从欧洲旅行到美国,如果您之前将其存储在数据库中,那么他们的时区现在是不正确的(即使用户从未真正更新他们的设备时区)。这也是@Mads Kristiansen
答案的问题,因为用户旅行 - 你不能依赖它作为给定的。
例如,我的 Linux 笔记本电脑关闭了“自动时区”。虽然时间可能会更新我的时区,但不会。
所以我相信答案是——你需要它做什么?客户端当然似乎提供了一种更简单的方法来确定它,但是客户端和服务器端代码都将取决于用户更新他们的时区还是自动更新。我当然可能是错的。
没有办法在实际的 HTML 代码或任何user-agent
字符串中计算时区,但您可以做一个使用 JavaScript 获取它的基本函数。
我还不知道如何用 JavaScript 编写代码,所以我的函数可能需要时间来完成。
但是,您也可以尝试使用 JavaScript 和部分getTzimezoneOffset()
中的函数Date
或简单地获取实际时区new Date().getTimezoneOffset();
。