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

  1. $create_time = '用户注册时间'; //格式 Y-m-d H:i:s
  2.  
  3. $time = time();
  4.  
  5. $lasttime = date('Y-m-d H:i:s', $time);
  6.  
  7. $current_day = floor($time / 86400);
  8.  
  9. $create_day = floor( strtotime($create_time) / 86400 );
  10.  
  11. $days = $current_day - $create_day;
  12.  
  13. switch ($days)
  14. {
  15. case 1:
  16. $values['2day'] = 1; break; //次日登陆
  17. case 6:
  18. $values['7day'] = 1; break; //七日登陆
  19. case 14:
  20. $values['15day'] = 1; break; //十五日登陆
  21. }
  22.  
  23. //执行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.

测试代码如下:

  1. //date_default_timezone_set('UTC'); //设置为格林威治时间
  2.  
  3. date_default_timezone_set('Asia/Shanghai'); //设置为东八区上海时间
  4.  
  5. $a = floor( strtotime('2015-02-02 07:00:00') / 86400 );
  6.  
  7. $b = floor( strtotime('2015-02-02 09:00:00') / 86400 );
  8.  
  9. echo $b - $a; // 结果 1
  10.  
  11. //将格林威治时间打开, 注释掉上海时间, 结果输出为 0.

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

  1. $create_time = '用户注册时间'; //格式 Y-m-d H:i:s
  2.  
  3. $time = time();
  4.  
  5. $lasttime = date('Y-m-d H:i:s', $time);
  6.  
  7. //时间戳总是获取的格林威治时间, strtotime()会自动添加当前时区的偏移量, 这里因时区问题导致天数计算出现一天的误差, 所以在处理时间戳时增加时区的偏移量
  8. $current_day = floor( ($time + date('Z')) / 86400 );
  9.  
  10. $create_day = floor( (strtotime($create_time) + date('Z')) / 86400 );
  11.  
  12. $days = $current_day - $create_day;
  13.  
  14. switch ($days)
  15. {
  16. case 1:
  17. $values['2day'] = 1; break; //次日登陆
  18. case 6:
  19. $values['7day'] = 1; break; //七日登陆
  20. case 14:
  21. $values['15day'] = 1; break; //十五日登陆
  22. }
  23.  
  24. //执行SQL修改数据库相关字段

关于PHP函数time() date() 和 strtotime() 的时区问题的更多相关文章

  1. php中的date和strtotime函数妙用

    php中的两个常用的日期相关函数date和strtotime,相信大家一定不陌生.但我们平时使用都只是基本功能,什么时间戳变日期格式,日期格式变时间戳. 其实这两个函数还有更深的用法: 1.date函 ...

  2. php时间函数time(),date(),mktime()区别

    php时间函数time(),date(),mktime()区别   浏览:1161 发布日期:2014/12/18 分类:系统代码 关键字: php时间函数 time() date()mktime() ...

  3. PHP 时间 date,strtotime ,time计算1970开始的第几天

    首先,需要看你的php时区配置参数 方式1:更改php配置文件,然后从其fast-cgi或者php调用的地方: 方式2:date_default_timezone_set('PRC'); date函数 ...

  4. JavaScript 基础(七) 箭头函数 generator Date JSON

    ES6 标准新增了一种新的函数: Arrow Function(箭头函数). x => x *x 上面的箭头相当于: function (x){ return x*x; } 箭头函数相当于匿名函 ...

  5. js中的函数,Date对象,Math对象和数组对象

    函数就是完成某个功能的一组语句,js中的函数由关键字 function + 函数名 + 一组参数定义;函数在定义后可以被重复调用,通常将常用的功能写成一个函数,利用函数可以使代码的组织结构更多清晰. ...

  6. 玩转PHP(二)--PHP强大的时间函数:date()

    PHP具有相对来说强大的时间函数date(),该方法有下列一系列参数: 例如: echo date("Y-m-d H:i:s"); //2015-01-09 13:03:30 如果 ...

  7. xe7 c++builder 日期时间头文件函数大全 date

    c++builde r时间日期函数大全,在头文件System.DateUtils.hpp,不过没有IncMonth,因为这个函数定义在System.SysUtils.hpp里头了,唉 date,dat ...

  8. js匿名函数和date对象,math对象

    匿名函数: <script type="text/javascript"> function (参数列表){ 要执行的语句块; } </script> 对象 ...

  9. 日期函数new Date()浏览器兼容性问题

    项目上与时间相关的地方特别多,与时间格式相关都使用了moment.js轻量级日期处理库,在开发中出现了几次浏览器兼容性问题,所以总结一下new Date()和moment.js在各大浏览器中兼容性问题 ...

随机推荐

  1. activity的四种加载模式

    在android里,有4种activity的启动模式,分别为: standard, singleTop, singleTask和singleInstance, 其中standard和singleTop ...

  2. jQ中对attr()方法的理解

    在JS中设置节点的属性与属性值用到setAttribute(),获得节点的属性与属性值用到getAttribute(),而在jquery中,用一个attr()就可以全部搞定了,赞一个先 ^^jquer ...

  3. wsdl学习

    本文来自 :迹忆 原文地址:http://www.onmpw.com/tm/xwzj/network_47.html 在刚开始学习Webservice的时候,发现里面涉及到的知识点还真不少,每一点单拿 ...

  4. iOS开发,URL编码和解码

    URL传递数据中,如果含有中文,需要进行编码: NSString *urlEncodeString = [urlStr stringByAddingPercentEncodingWithAllowed ...

  5. django之分页、cookie装饰器

    一.分页代码如下 from django.utils.safestring import mark_safe class Page: def __init__(self, current_page, ...

  6. 使Docker Container支持运行SWT程序

    1, 下载安装JDK的docker container 我是从这个源下载已经做好的JDK8的container: https://registry.hub.docker.com/u/dockerfil ...

  7. Linux截屏工具scrot用法详细介绍

    Scrot是Linux命令行中使用的截图工具,能够进行全屏.选取等操作,下面小编将针对Scrot截图工具的用法给大家做个详细介绍,通过操作实例来学习Scrot的使用.   在Linux中安装Scrot ...

  8. nginx 日志变量含义

    log_format logstash "remote_addr | $time_local | $request | $status | $body_bytes_sent | " ...

  9. 【转载】 input 输入格式化

    不多说直接 发链接 http://nosir.github.io/cleave.js/

  10. 有关uploadifive的使用经验

    这段时间做了一个项目用到uploadifive上传控件,和uploadify不同,这个控件是基于HTML5的版本而不用支持falsh,因而移动端也可以使用. 整理用过的相关属性与方法: 属性 作用 a ...