当前系统在2038年之后datetime类也无法获取当前时间的解决-九游会




九游会 -> 专题栏目 -> 网络技术 找资讯
发布时间:2018-04-18 阅读次数:  
由于php 32位使用 int 类型保存时间戳,也就是从1970 00:00:00 到当前时间的秒数。
而32位int 数字的取值范围是 -2147483648 到 2147483647。
 
所以当 时间戳为最大值 2147483647 时,表示的时间是 2038-01-19 03:14:07 或北京时间 2038-01-19 11:14:07 (为了表述方便,下文中,将这个临界点时间称之为 t0)。
而当时间大于这个时间时,php很多内置函数都会出错。
比如
 
当日期和时间大于 北京时间 2038-01-19 11:14:07 时
time()函数,原本应该返回时间戳,现在会始终返回-1。
date("y-m-d h:i:s")函数,会返回 1970-01-01 07:59:59(北京时间),其实也是因为 time()=-1导致的,date默认的第二个参数就是time()。
同样,mktime() 等函数也会异常。
 
上网查了解决办法,
1、换用64位系统。这里说的64位系统,需要操作系统、web服务系统,以及php都要64位的。
2、使用php5.2之后推出的 datetime 类。
 
首先说第一种方法,因为我的服务器建设在windows系统上,然后又有几个自制插件,这些插件在php 64位下面可能不能使用,因此这个方法不能用。
再说datetime类,网上几乎几十篇文章都说使用datetime类就能解决2038年问题。
 
我在自己的服务器上测试了一下,使用datetime类似乎确实可以让日期超过2038上限,各种转换,都没问题,这里我不具体说明,大家网上搜"php datetime",都有说明。
但是,我在把服务器的时间设置为2040年4月18日的时候,发现,datetime 类依然无法获取当前时间。代码如下:
$date = new datetime();
echo $date->format('y-m-d h:i:s');
 
输出的还是 1970-01-01 07:59:59
 
但是,如果使用 $date->setdate(2040,4,18) 之后,再显示,再输出时间戳等,都是正常的代码如下:
$date = new datetime();
$date->setdate(2040,4,18);
$date->settime(10,24,11);
echo $date->format('y-m-d h:i:s')."
rn";
echo $date->format('u')."
rn";
 
这时 输出时间 2040-04-18 10:24:11 ,以及时间戳 2218328651 都是正常的。
 
问题在于,datetime 类可以解决 2038年之后的时间的各种运算和转换,但是当系统日期在2038年那个t0时间之后,php系统根本无法获取当前时间。
我还试了  new datetime("today");new datetime(' 2 days');new datetime('tomorrow'); 等等,都无法获取今天,明天,后天等日期。
这时,整个php 系统无法获取当前的年月日和时间。
 
然后我开始在php的系统数组 $_server 中寻找,看看哪里能找到和时间相关的内容,终于被我找到一个 $_server["request_time"],这个实际上是一个记录用户刷新页面时php相应时刻的时间。它的值,在t0之前,和time()是一致的,但是,当t0之后,它就变成负数了。那么,怎么通过 $_server["request_time"] 来获取真实的 时间戳呢?
 
很简单,32位int 数字的取值范围是 -2147483648 到 2147483647,转成2进制就会发现,其实是最高位用作符号位,最高位0表示正数,最高位1表示负数,当数字达到 2147483647后,二进制 就是 01111111 11111111 11111111 11111111(31个1),这时就是t0时刻的时间戳,继续 1 以后,变成了 10000000000000000000000000000000 (31个0),如果是无符号32位整数,就是 2147483648(正数) 但是在有符号的整数里,最高位1表示负数,就是 -2147483648(负数),而 $_server["request_time"] 的特性是根据时间的推移进行累加。所以,它的时间线如下:
t0 之前:它等于 1970 00:00:00 到当前时间的秒数,和time()相同
t0 时: 它等于 2147483647
t0 后1秒: 它等于 2147483647 1=2147483648  被表示为 -2147483648  我们把 -2147483648 记作 t1,t1=t0 1秒的时刻
t0 后n秒:-2147483648-1 n 
 
所以,当 $_server["request_time"]<0 时,真正的时间戳为  $_server["request_time"]-(-2147483648) 2147483647。
其中 $_server["request_time"]-(-2147483648)表示 t1(变成负数,即t0 1秒) 时刻到当前时间 过了多少秒。
 
据此,写出一个新的取代time()的函数,该函数在系统时间超过t0 时,也能返回正确的时间戳,但是它的范围是无符号32位上限 4294967295,北京时间 2106-02-07 06:28:15。在这个时间之前,应该都可以正常使用。
 
function suntime(){
  if($_server["request_time"]>0){
    $t=$_server["request_time"];
  }else{
    $t0=php_int_max; // 第 2147483647 秒 再过一秒为 2147483648秒,但最高位变成1,系统中为 -2147483648
    $t1=0-$t0-1;   // t0后面1秒,瞬间变成负数,值为 -2147483648
    $t2=($_server["request_time"]);//虽然$_server["request_time"]变成了负数,但是 系统依然通过 1秒 来计时
    $t=$t2-$t1 $t0."";  //t2-t1 就是变成负数后过了多少秒,t0就是变成负数前的秒数。
  }
  $date=new datetime("@".$t);
  $timemark=$date->format("u");
  return $timemark;
}
 
所以,目前网上很多人都以为使用 datetime类可以解决问题,殊不知等时间真正到了2038那个时间之后,php系统获得当前时间都会出错。而我这方法也是目前网上唯一存在的方法。
 
阳光浪子
2018-04-18
网络技术 (最近更新)

正在打开...
【提出建议、反馈错误,得红包奖励】

微信扫码访问获得更好体验
【营业执照、人力资源服务许可证】

刷新

返回
网站地图