不同时区的每小时日志php和mysql

r7knjye2  于 2021-06-20  发布在  Mysql
关注(0)|答案(3)|浏览(382)

我期待着提供简单的日志,我的用户通过我的api从不同的时区,我有点迷路,希望能得到一些帮助。
我有一个名为logs的下表:

CREATE TABLE `logs` (
   `id` INT(11) NOT NULL AUTO_INCREMENT,
   `userId` int(11) NOT NULL,
   `ip` VARCHAR(45) NOT NULL,
   `timestamp` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
   PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

我有一个小时表可以在24小时内绑定我的查询。

CREATE TABLE `log_hours` (
   `id` INT(11) NOT NULL AUTO_INCREMENT,
   `hourId` int(11) NOT NULL,
   PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO log_hours
    (hourId)
VALUES
    (1),
    (2),
    (3),
    (4),
    (5),
    (6),
    (7),
    (8),
    (9),
    (10),
    (11),
    (12),
    (13),
    (14),
    (15),
    (16),
    (17),
    (18),
    (19),
    (20),
    (21),
    (22),
    (23),
    (24);

每次我得到一个api命中我存储日志采取以下数据。

id | userId |   ip    | timestamp
1    1        0.0.0.0   2018-08-23 14:20:34
2    1        0.0.0.0   2018-08-23 14:20:34
3    1        0.0.0.0   2018-08-23 14:20:34
4    1        0.0.0.0   2018-08-23 14:20:34
5    1        0.0.0.0   2018-08-23 14:20:34

现在,如果我像下面这样运行我的查询。

SELECT DW.hourId AS hour, ifnull(t.hits,0) hits from log_hours DW left join (            
    SELECT COUNT( * ) AS hits, HOUR( logs.timestamp ) AS `hour` 
    FROM logs WHERE DAY( logs.timestamp ) = DAY(CURDATE()) 
    AND MONTH( logs.timestamp ) = MONTH(CURDATE()) 
    AND `userId` = 1 GROUP BY HOUR( logs.timestamp )) t on DW.hourId = t.hour 
    ORDER BY hour ASC

这工作很好,给我的日志,每小时像这样。

现在我被困在时区我的数据库时区设置为英国夏令时。
比如说,我在美国有一个用户,如果他们在自己的时区点击了api,mysql会自动将他们的时区转换成我的bst时区,这样输出的数据才有意义吗?
我应该使用datetime not null default current\u timestamp还是应该使用int并使用php time()函数插入记录?
我是否应该首先通过php从我的时区返回数据,然后转换成他们的时区?在这种情况下,什么是最佳实践?
编辑:
首先很抱歉,如果我问了一些愚蠢的问题,但我真的想完全理解这个过程。
在什么时候检索用户的本地时区?我应该把它作为必需的参数添加到api中。
这是我的php代码,我只是把所有的东西都添加到一个函数中,这样更容易阅读我的进程是mvc。

public function usage_get(){

        $userId = $this->get('userId'); // i am obviously not getting the users info this way this is just for the example

        // DO I MAKE THEM SEND THEIR TIMEZONE TO THE API AS A PARAM THEN USE IT IN MYSQL
        //https://dev.mysql.com/doc/refman/8.0/en/date-and-time-functions.html#function_convert-tz?
        $timezone = $this->get('timezone');

        // DO I SOMEHOW RETRIEVE THE TIME ZONE BASE ON IP AND GET IT THAT WAY?

        $query = $this->db->query("SELECT DW.hourId AS hour, ifnull(t.hits,0) hits
        from log_hours DW 
        left join (

            SELECT COUNT( * ) AS hits, HOUR( logs.timestamp ) AS `hour`
            FROM logs
            WHERE DAY( logs.timestamp ) = DAY(CURDATE())
            AND MONTH( logs.timestamp ) = MONTH(CURDATE())
            AND `userId` = ".$this->db->escape_str($userId)."
            GROUP BY HOUR( logs.timestamp )

        ) t  on DW.hourId = t.hour ORDER BY hour ASC");

        // DO I RUN A PHP FUNCTION AFTER THE MYSQL BASED ON TIMEZONE?

        if($query->num_rows() > 0){

            $this->response([
                'status'  => TRUE,
                'message' => 'Success',
                'data'    => $query->result(),
            ], REST_Controller::HTTP_OK);

        }else{ 

            $this->response([
                'status' => FALSE,
                'message' => 'Error'
            ], REST_Controller::HTTP_OK);

        }

    }
gzjq41n4

gzjq41n41#

保持简单永远是最好的主意。独立于本地时间保存所有日期。在我看来,其他一切都是不好的做法,因为当地时间只会导致后来的混乱和问题。
比如说,我在美国有一个用户,如果他们在自己的时区点击了api,mysql会自动将他们的时区转换成我的bst时区,这样输出的数据才有意义吗?
如果要将日志时间传递为本地时间,请将其转换为本地时间。在数据库中,应始终存储utc(+/-0)。在mysql中,您可以使用 CONVERT_TZ 功能(https://dev.mysql.com/doc/refman/8.0/en/date-and-time-functions.html#function_convert-在查询过程中。
我应该使用datetime not null default current\u timestamp还是应该使用int并使用php time()函数插入记录? CURRENT_TIMESTAMP 这似乎是正确的方法,因为它是独立的(mysql是否应该将其时区设置为utc?)。我看不出有什么好的理由使用php time() 功能因为 CURRENT_TIMESTAMP 如果服务器时间设置正确的话,它是最精确的,而且总是正确的,但是基本上除了从php调用和实际的数据库插入之间的毫秒数之外,应该没有什么区别。
我是否应该首先通过php从我的时区返回数据,然后转换成他们的时区?在这种情况下,什么是最佳实践?
在我看来,使用数据库进行转换是最好的(最快的)-至少如果您可以编写自己的sql查询。否则,通过php进行转换是完全合法的,但是会有一些最小的开销。如何做到这一点已经被问过:转换utc日期当地时间在php
在什么时候检索用户的本地时区?我应该把它作为必需的参数添加到api中吗?
这是一个应用程序架构的问题,没有“规则”。检索用户时区的可能性:
geoip:使用geoip和基于位置的匹配将ip与区域匹配。与许多解决方案一样,这里的缺点是,如果用户使用vpn或代理,它将显示vpn或代理ip的区域,而不是用户背后的物理ip。
javascript:javascript是客户端的,虽然很像geoip,但是如果客户端在vpn或代理之后,同样的问题仍然存在。您将不会显示用户的时区,而是显示vpn或代理的时区(以javascript获取客户端的时区)。
询问用户:时区以某种方式作为参数传递。
对于一个api,我会明确地推荐后者——要求时区作为参数,或者简单地将时间utc(+-0)传递给它们。也许正在使用你的api的用户有一个他们设置时区的配置文件?
如果不是,请考虑向他们传递一个结果,其中包含utc(+-0)时间以及带有猜测时区短码的本地时间。任何实现都是有效的,只需明确结果是哪个时区,以及用户如何正确查询即可。

vi4fp9gy

vi4fp9gy2#

如果你把所有的时间戳都记录在世界时间里,你的头痛就会少一些。追踪谁有哪一个夏时制的地方变老很快。

p3rjfoxz

p3rjfoxz3#

最后的想法(我的问题解决方案是):
好的,首先感谢blackam提供的信息,我也一直在读这篇文章。
http://www.vertabelo.com/blog/technical-articles/the-proper-way-to-handle-multiple-time-zones-in-mysql
据我所知,utc是24小时时钟起始点的参考,然后可以将其偏移以转换为正确的时区这与以前的gmt或gmt相同不确定,但它是一个起始点。
当最初的时间基准必须被定义时,人们同意使它相对于地球上的某一点。格林威治是伦敦的一个自治市,是本初子午线(0度经度)的所在地。
然后用函数来表示偏移量,不管是在php还是mysql中,这些函数都是基于字符串参数来计算偏移量的,例如,这些参数可以更改并存储在全局数据库中。
mysql数据库:

SELECT CONVERT_TZ('2004-01-01 12:00:00','GMT','MET');

PHP:

date_default_timezone_set('America/Los_Angeles');

在模糊地理解了这一点后,我现在可以看到一个解决方案,为我的设置,因为我将有来自不同时区的多个用户注册我的服务,这对我来说是有意义的,在他们的帐户配置文件下有一个选项,以设置其正确的时区。
这样我就可以抓取他们的时区,然后更新mysql查询以返回他们时区中的正确数据我同意blackam我更喜欢使用mysql。
我的过程如下。
mysql数据库存储时间或设置为utc(仍在寻找正确的方法)

更新:

我想我已经完成了以下命令登录到mysql并在命令行运行。 SELECT @@global.time_zone, @@session.time_zone ;
然后返回你的时区我的。

mysql> SELECT @@global.time_zone, @@session.time_zone;
+--------------------+---------------------+
| @@global.time_zone | @@session.time_zone |
+--------------------+---------------------+
| SYSTEM             | SYSTEM              |
+--------------------+---------------------+
1 row in set (0.00 sec)

然后我跑了。

mysql> SET GLOBAL time_zone = '+00:00';

现在我明白了。

mysql> SELECT @@global.time_zone, @@session.time_zone;
+--------------------+---------------------+
| @@global.time_zone | @@session.time_zone |
+--------------------+---------------------+
| +00:00             | SYSTEM              |
+--------------------+---------------------+
1 row in set (0.00 sec)

用户通过他们的帐户设置时区,就像你在博客平台(比如wordpress)中设置时区一样。
时区存储在数据库中,然后通过api检索以进行转换。
比如说这个。

$timezone = $this->get('timezone');
$query = $this->db->query("SELECT DW.hourId AS hour, ifnull(t.hits,0) hits
from log_hours DW 
left join (

    SELECT COUNT( * ) AS hits, HOUR( CONVERT_TZ(logs.timestamp,'GMT','".$this->db->escape_str($timezone)."') ) AS `hour`
    FROM logs
    WHERE DAY( CONVERT_TZ(logs.timestamp,'GMT','".$this->db->escape_str($timezone)."') ) = DAY(CURDATE())
    AND MONTH( CONVERT_TZ(logs.timestamp,'GMT','".$this->db->escape_str($timezone)."') ) = MONTH(CURDATE())
    AND `userId` = ".$this->db->escape_str($userId)."
    GROUP BY HOUR( CONVERT_TZ(logs.timestamp,'GMT','".$this->db->escape_str($timezone)."') )

) t  on DW.hourId = t.hour ORDER BY hour ASC");

我现在从convert_z得到空值,所以也只是想找出解决这个问题的最佳方法,显然你需要将zoneinfo加载到mysql中。
这解决了我的时区问题:

./mysql_tzinfo_to_sql /usr/share/zoneinfo | ./mysql -uroot -proot mysql

这意味着我现在可以跑了。

SELECT CONVERT_TZ(NOW(), @@session.time_zone, 'America/Los_Angeles');
SELECT CONVERT_TZ(NOW(), @@session.time_zone, 'Indian/Antananarivo');
SELECT CONVERT_TZ(NOW(), @@session.time_zone, 'Asia/Dhaka');
SELECT CONVERT_TZ(NOW(), @@session.time_zone, 'Antarctica/Troll');

输出:

CONVERT_TZ(NOW(), @@session.time_zone, 'America/Los_Angeles')
2018-08-24 02:07:36

CONVERT_TZ(NOW(), @@session.time_zone, 'Indian/Antananarivo')
2018-08-24 12:07:36

CONVERT_TZ(NOW(), @@session.time_zone, 'Asia/Dhaka')
2018-08-24 15:07:36

CONVERT_TZ(NOW(), @@session.time_zone, 'Antarctica/Troll')
2018-08-24 11:07:36

您可以从这里获得时区:http://php.net/manual/en/timezones.php
到了那里请让我知道如果我把这一切都弄错了。

相关问题