关于html:确定用户的时区

Determine a user's timezone

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

可能来自HTTP头或部分user-agent字符串?


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

方法getTimezoneOffset()将从gmt中减去您的时间,并返回分钟数。所以如果你住在格林尼治标准时间8,它将返回480。

把这个数小时,除以60。另外,请注意,符号与您需要的符号相反-它计算的是格林尼治标准时间与您所在时区的偏移量,而不是您所在时区与格林尼治标准时间的偏移量。要解决此问题,只需乘以-1。

还要注意W3School说:

The returned value is not a constant, because of the practice of using
Daylight Saving Time.


最流行(=标准?)确定我所看到的时区的方法只是询问用户自己。如果您的网站需要订阅,可以将其保存在用户的配置文件数据中。对于anon用户,日期可以显示为UTC或GMT等。

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


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

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


javascript是获取客户端本地时间的最简单方法。我建议使用xmlhttpRequest发送回本地时间,如果失败,则返回到基于其IP地址检测到的时区。

至于地理位置,我已经在几个项目中使用了MaxMindGeoIP,它工作得很好,尽管我不确定它们是否提供时区数据。这是一项你付费的服务,他们每月更新你的数据库。它们提供多种Web语言的包装器。


这里有一个健壮的javascript解决方案来确定浏览器所在的时区。

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

https://bitback.org/pellepim/jstimezonedetet


这里有一个更完整的方法。

  • 获取用户的时区偏移量
  • 在日光节约边界上测试几天,以确定它们是否位于使用日光节约的区域中。
  • 摘录如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    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(通过回程机器)


    首先,要了解javascript中的时区检测是不完善的。您可以使用getTimezoneOffsetDate对象的实例上获得特定日期和时间的本地时区偏移量,但这与America/Los_Angeles等完整的IANA时区不同。

    但也有一些选择是可行的:

    • 大多数现代浏览器在实现EcmaScript国际化API时都支持IANA时区,因此您可以这样做:

      1
      const tzid = Intl.DateTimeFormat().resolvedOptions().timeZone;

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

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

      • 一些库(如Luxon)使用此API通过诸如luxon.Settings.defaultZoneName之类的函数来确定时区。
    • 如果您需要支持更广泛的环境,例如旧的Web浏览器,您可以使用库在时区进行有根据的猜测。他们首先尝试IntlAPI(如果它可用),当它不可用时,他们询问Date对象的getTimezoneOffset函数几个不同的时间点,使用结果从内部数据集中选择适当的时区。

      JSTimeZoneDetect和Moment TimeZone都具有此功能。

      1
      2
      3
      4
      5
      // using jsTimeZoneDetect
      var tzid = jstz.determine().name();

      // using moment-timezone
      var tzid = moment.tz.guess();

      在这两种情况下,结果只能被认为是猜测。在许多情况下,猜测可能是正确的,但并非全部正确。

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

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

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


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

    在希望将时区作为变量的php页面上,在页面顶部附近的某个位置包含以下代码片段:

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

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

    在同一页的中,首先需要包含jquery:

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

    同样在jquery下面的中,粘贴此:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <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();
                    }
                });
            }
        });

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

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

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

    如果操作正确,它将首先加载页面,执行javascript,然后重新加载页面。然后您就可以读取$timezone变量,并使用它来满足您的需要!它返回当前的UTC/GMT时区偏移量(GMT-7)或您所在的任何时区。


    使用jquery将时区偏移量作为Ajax请求的HTTP头提交

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

    您还可以通过使用http://momentjs.com/time zone/docs//using timezones/guessing user time zone中的moment.tz.guess();来获取实际时区名称。/


    我仍然没有看到一个详细的答案,得到时区。您不需要按IP地址地理编码,也不需要使用PHP(LOL),也不需要从偏移量中进行错误的猜测。

    首先,时区不仅仅是与格林尼治标准时间的偏移,它是一块由当地标准制定时间规则的土地。一些国家有日光节约,并将在不同的时间开启DST。通常重要的是获得实际区域,而不仅仅是当前偏移。

    如果您打算存储这个时区,例如在用户首选项中,您需要的是该时区,而不仅仅是偏移量。对于实时转换来说没什么关系。

    现在,要使用javascript获取时区,可以使用以下方法:

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

    但是,我发现简单地使用这个健壮的插件会更容易,它返回olsen格式的时区:

    https://github.com/scottwater/jquery.detect_时区


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

    但我建议你,如果你的网站有注册要求,那么最好的方法是要求用户同时注册为强制字段。您可以在注册页面中列出各种时区,并将其保存到数据库中。之后,如果用户登录到站点,则可以根据用户选择的时区设置会话的默认时区。

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

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

    下面是使用php和javascript获取用户时区的脚本。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    <?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'];?>";
               

                <?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();
    ?>

    但是根据我的观点,最好向用户询问注册是否在您的项目中是必需的。


    JavaScript:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    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:

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

    这将始终根据用户在其计算机时钟上设置的时区正确显示时间。没有必要向任何人要求任何东西,把它保存到某个地方,感谢上帝!


    简单,只需使用javascript getTimezoneOffset函数,如下所示:

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


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

    与美国区号在定位电话用户时不再有用的方式类似,考虑到数字便携性的普及。

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


    所有的魔法似乎都在

    1
    visitortime.getTimezoneOffset()

    很酷,我不知道。它在Internet Explorer等中工作吗?从那里,您应该能够使用javascript到Ajax,设置cookie,等等。我可能会自己去吃饼干。

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


    如果您碰巧使用OpenID进行身份验证,那么简单的注册扩展将为经过身份验证的用户解决这个问题(您需要将TZ转换为数字)。

    另一种选择是根据用户代理的国家/地区偏好推断时区。这是一个有点粗糙的方法(对en-us不起作用),但它是一个很好的近似值。


    以下是一篇文章(包含源代码),介绍如何确定和使用ASP.NET(VB.NET,C)应用程序中的本地化时间:

    是时候了

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


    使用javascript和php很简单:

    尽管用户可以处理他/她的内部时钟和/或时区,但到目前为止我找到的获得偏移量的最佳方法仍然是new Date().getTimezoneOffset();。它是无创的,不会引起头痛,也不需要依赖第三方。

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

    假设客户机为creates a new account,数据由post接收,我需要insert/updatedate_created column带有客户机的unix时间戳,而不是服务器的。

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

    1
    2
    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']

    1
    2
    3
    $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

    在检索创建日期时,可以像这样转换时间戳:

    1
    2
    $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值插入到用户表中以供将来参考,或者将其设置为会话或cookie。

    还有,但是如果用户移动并切换时区呢?在格林尼治标准时间+4登录,快速到达格林尼治标准时间-1并再次登录。最后一次登录是在将来。

    我想。。。我们想得太多了。


    您可以在具有即时时区的客户机上执行此操作,并将值发送到服务器;示例用法:

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


    一个简单的方法是使用:

    1
    new Date().getTimezoneOffset();


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

  • 使用javascript,通过getTimezoneOffset获得以分钟为单位的时区偏移量。如果本地时区落后于UTC,则此偏移量为正;如果超前,则为负。因此,必须在偏移量中添加一个相反的符号。

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

    将此偏移量传递给php。

  • 在php中,将这个偏移量转换成一个有效的时区名称,其中timezone的名称是u abbr函数中的u name。

    1
    2
    3
    4
    5
    6
    7
    8
    // 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;

    < /代码>

  • 我在上面写了一篇博文:如何在PHP中检测用户时区。它还包含一个演示。


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


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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    <?php
    session_start();

    if(!isset($_SESSION['timezone']))
    {
        if(!isset($_REQUEST['offset']))
        {
        ?>
           
            var d = new Date()
            var offset= -d.getTimezoneOffset()/60;
            location.href="<?php echo $_SERVER['PHP_SELF']; ?>?offset="+offset;
           
            <?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
    ?>


    尝试以下PHP代码:

    1
    2
    3
    4
    5
    6
    <?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'];
    ?>