• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    迪恩网络公众号

关于PHP函数time()date()和strtotime()的时区问题

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

最近项目中有个需求, 要记录新注册用户的次日登录情况, 于是写出了如下代码:

$create_time = '用户注册时间'; //格式 Y-m-d H:i:s

$time = time();
                
$lasttime = date('Y-m-d H:i:s', $time);
                
$current_day = floor($time / 86400);

$create_day = floor( strtotime($create_time) / 86400 );

$days = $current_day - $create_day;

switch ($days)
{
    case 1: 
        $values['2day'] = 1; break; //次日登陆
    case 6:
        $values['7day'] = 1; break; //七日登陆
    case 14:
        $values['15day'] = 1; break; //十五日登陆
} 

//执行SQL修改数据库相关字段

这段代码放到线上后, 出现了奇怪的BUG, 明明是当天注册的用户, 却出现了有次日登录的情况. 排查代码没有发现问题, 于是暂时搁置去忙其它事情. 然后在第6天时, 竟然又出现了有七日登陆的数据. 于是开始和同事正式解决这个问题, 最终发现是由于函数的时区原因导致, 具体如下:

time()  返回自从 Unix 纪元(格林威治时间 1970 年 1 月 1 日 00:00:00)到当前时间的秒数.

上面是 time() 函数在手册中的说明, 重点是格林威治时间, time() 始终返回的是格林威治时间的时间戳. 当PHP设置过时区后, date() 在格式化时间的操作中会将 (当前时区的时间 - 格林威治时间) 的偏移量自动添加进去, 按东八区的时间算也就是8小时. strtotime() 同样会自动将时区的偏移量加入处理操作中. 所以这时上面代码中 strtotime($create_time) 得到的同样是格林威治时间. $current_day 与 $create_day 现在都是按照格林威治时间计算的天数, 而BUG也就出现在这里.

比如当前时间为 2015-02-02 07:00:00 那么格林威治时间为 2015-02-01 23:00:00 (当前时间减去8小时)

当前时间为  2015-02-02 09:00:00  格林威治时间为 2015-02-02 01:00:00

再通过 floor() 处理后, 就相当于格林威治时间的 2015-02-02 与 2015-02-01, 中间相差一天.

所以如果用户在7点多注册, 而在9点再次登录的情况下, $current_day - $create_day = 1.

测试代码如下:

//date_default_timezone_set('UTC'); //设置为格林威治时间

date_default_timezone_set('Asia/Shanghai'); //设置为东八区上海时间

$a = floor( strtotime('2015-02-02 07:00:00') / 86400 );

$b = floor( strtotime('2015-02-02 09:00:00') / 86400 );

echo $b - $a; // 结果 1 

//将格林威治时间打开, 注释掉上海时间, 结果输出为 0.

 

最终解决BUG后的代码如下:

$create_time = '用户注册时间'; //格式 Y-m-d H:i:s

$time = time();
                
$lasttime = date('Y-m-d H:i:s', $time);

//时间戳总是获取的格林威治时间, strtotime()会自动添加当前时区的偏移量, 这里因时区问题导致天数计算出现一天的误差, 所以在处理时间戳时增加时区的偏移量
$current_day = floor( ($time + date('Z')) / 86400 );

$create_day = floor( (strtotime($create_time) + date('Z')) / 86400 );

$days = $current_day - $create_day;

switch ($days)
{
    case 1: 
        $values['2day'] = 1; break; //次日登陆
    case 6:
        $values['7day'] = 1; break; //七日登陆
    case 14:
        $values['15day'] = 1; break; //十五日登陆
} 

//执行SQL修改数据库相关字段

 


鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
php提供service总结---wsdl篇发布时间:2022-07-10
下一篇:
PHP中关于位运算符与或异或取反发布时间:2022-07-10
热门推荐
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap