我希望通过我的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;
我有一个小时表,可以在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);
}
}
答案 0 :(得分:1)
保持简单永远是最好的主意。独立于本地时间保存所有日期。在我看来,其他所有事情都是不好的做法,因为当地时间只会在以后导致混乱和问题。
假设我在美国有一个用户,例如,如果他们在时区点击了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)表示的时间以及带有猜测的时区短码的本地时间。任何实现都是有效的,只需明确结果在哪个时区以及用户如何正确查询即可。
答案 1 :(得分:0)
如果您在世界通用时间记录所有时间戳,则您的头痛会减轻。跟踪谁在哪个夏令时时间很快变老。
答案 2 :(得分:0)
最终思路(我的问题解答):
好吧,首先感谢Blackbam提供了确实有用的信息,我也一直在阅读这篇有帮助的文章。
根据我的理解,UTC是24小时时钟起点的参考,然后可以将其偏移以转换为正确的时区,而该时区以前是GMT或GMT不确定,但是它是起点。
当必须定义原始时间参考时,同意使其相对于地球上的某个点。格林威治是伦敦的一个子午线(经度为0o)的所在地。
然后用PHP或MySql中的函数表示偏移量,这些函数基于字符串参数计算偏移量,例如,这些可以更改并存储在全局数据库中。
MySQL:
SELECT CONVERT_TZ('2004-01-01 12:00:00','GMT','MET');
PHP:
date_default_timezone_set('America/Los_Angeles');
经过模糊的了解后,我现在可以看到针对我的设置的解决方案,因为我将有多个来自不同时区的用户注册我的服务,因此在他们的帐户资料下可以选择一个选项来设置正确的时区。 / p>
然后,这使我能够抓住他们的时区,然后更新mysql查询以在其时区中返回正确的数据,我同意Blackbam,我更喜欢使用mysql。
我的过程如下。
**更新:**
我想我已经使用以下命令登录到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_TZ中获取NULL,因此也只是想找出解决此问题的最佳方法,显然您需要将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
到那里时,如果我弄错了,请告诉我。