我希望通过我的 api 从不同时区向我的用户提供简单的日志,我有点迷茫,希望能得到一些帮助。
我有下表称为日志:
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;
我有一个小时表来绑定(bind)我 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
这工作正常,并给我这样的每个小时的日志。
现在我卡在时区上了,我的数据库时区设置为 BST。
例如,假设我有一个美国用户,如果他们在他们的时区访问 api,mysql 会自动将他们的时区转换为我的 BST 时区,以便输出数据有意义吗?
我应该使用 datetime NOT NULL DEFAULT CURRENT_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);
}
}
最佳答案
保持简单永远是最好的主意。保存所有独立于本地时间的日期。在我看来,其他一切都是不好的做法,因为本地时间只会导致以后的困惑和问题。
假设我有一个美国用户,如果他们在他们的时区访问 api,mysql 会自动将他们的时区转换为我的 BST 时区以便输出数据有意义吗?
如果您想将日志时间作为本地时间进行转换。在数据库中,应始终存储 UTC(+/-0)。在 MySQL 中,您可以在查询期间使用 CONVERT_TZ
函数 ( https://dev.mysql.com/doc/refman/8.0/en/date-and-time-functions.html#function_convert-tz )。
我应该使用 datetime NOT NULL DEFAULT CURRENT_TIMESTAMP 还是应该使用 INT 并使用 php time() 函数来插入记录?
CURRENT_TIMESTAMP
似乎是正确的方法,因为它是独立的 ( Should MySQL have its timezone set to UTC? )。我看不出有任何理由使用 PHP time()
函数,因为 CURRENT_TIMESTAMP
是最精确的,并且如果服务器时间设置正确则始终正确,但是基本上应该有除了 PHP 调用和实际数据库插入之间的毫秒数之外没有区别。
我是否应该先通过 php 从我的时区返回数据,然后将其转换为他们的数据?在这种情况下,最佳做法是什么?
在我看来,使用数据库进行转换是最好的(最快的)- 至少如果您可以编写自己的 SQL 查询。否则通过 PHP 的转换是完全合法的,但是有一些最小的开销。之前有人问过如何做到这一点:Convert UTC dates to local time in PHP
我在什么时候检索用户的本地时区?我应该将它作为必需的参数添加到 API 中吗?
这是一个应用程序架构的问题,没有“规则”。检索用户时区的可能性:
GeoIP:使用 GeoIP 和基于位置的匹配将 IP 与区域匹配。与许多解决方案一样,这里的缺点是,如果他们使用 VPN 或代理,它将显示 VPN 或代理 IP 的区域,而不是其背后的用户物理 IP。
JavaScript:Javascript 是客户端,虽然很像 GeoIP,但如果客户端位于 VPN 或代理之后,同样的问题仍然存在。您不会显示用户的时区,您将显示 VPN 或代理的时区 ( Getting the client's timezone in JavaScript )。
询问用户:时区以某种方式作为参数传递。
对于 API,我肯定会推荐后者 - 要求时区作为参数或简单地向它们传递时间 UTC (+-0)。也许正在使用您的 API 的用户有一个配置文件,他们可以在其中设置时区?
如果不考虑向他们传递一个结果,其中包含 UTC (+-0) 时间以及本地时间和猜测的时区简码。任何实现都是有效的,只要明确结果是针对哪个时区以及用户如何正确查询即可。
关于php - 不同时区的每小时日志 PHP 和 MySQL,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51987448/