2038年以后,在64位Windows中,PHP获取真实时间的一种解决方法
作者:于振海 文章ID:40763 浏览:
最近无聊,考虑到2038年之后,PHP如何获取当前时间的问题。
比如把电脑时间改成2050年,PHP时间系统乱套了。
本以为在windows系统中,只需把操作系统、Apache和PHP都换成64位的就能解决问题,经过测试,其实不是。
不同的操作系统和不同版本的Apache、PHP会出现不同的结果:
64位windows2003、32位Apache2.2.31、32位PHP5.4.45,2038年之后PHP的date()始终返回1970-01-01 07:59:59,不会变化。
64位windows2003、64位Apache2.2.34、64位PHP5.4.45,系统时间2050年,PHP的date()返回1914年,是变化的,time()是负数,但是会随着时间不断加1。PHP_INT_SIZE是4,PHP_INT_MAX是2147483647。date()超过2147483647还是出错。
64位windows7、64位Apache2.4.41、64位PHP7.2.23,系统时间2050年,PHP的date()返回1914年,是变化的,time()是负数,但是会随着时间不断加1。PHP_INT_SIZE是8,PHP_INT_MAX是9223372036854775807。date()超过2147483647不会出错。唯独time()还是负数,获取时间还是出错。
虽然是负数,但正常走时,这就好办了。下面是两张原理图:
过了47秒之后,有如下规律:
真实时间c = 47 + 多出的秒数b
多出的秒数b = 47 + PHP时间a + 2
整理一下:
因为c = 47 + b ,b = 47 + a + 2
所以c = 47 + 47 + 2 + a
暂且叫这种方法为振海法吧,运用到PHP,写了realtime()函数代替time(),并附上测试代码和测试结果:
//////////PHP代码开始//////////
<?php
if(!function_exists("realtime"))
{
function realtime()
{
$nowtime = time();
if($nowtime < 0)
{
return 2147483647 + 2147483647 + 2 + $nowtime;
}
else
{
return $nowtime;
}
}
}
//64位php7.0之后的用法
echo time()." ".realtime()." //time()和realtime()的值,系统时间调到2050年,time()负数说明是32位的。<br />\r\n";
echo date("Y-m-d H:i:s",2556115199)." //如果返回2050-12-31 23:59:59,那么date()没有问题,能处理64位。如果返回1914-11-25 09:31:43,那么date()只能处理32位。<br />\r\n";
echo date("Y-m-d H:i:s", time())." //date('Y-m-d H:i:s', time())的时间。<br />\r\n";
echo date("Y-m-d H:i:s", realtime())." //date('Y-m-d H:i:s', realtime())的时间。<br />\r\n";
echo gmdate("Y-m-d H:i:s", time())." //gmdate('Y-m-d H:i:s', time())的时间。<br />\r\n";
echo gmdate("Y-m-d H:i:s", realtime())." //gmdate('Y-m-d H:i:s', realtime())的时间。<br />\r\n";
echo PHP_INT_SIZE." ".PHP_INT_MAX." //PHP_INT_SIZE和PHP_INT_MAX的值。<br />\r\n";
//64位PHP7.0之前的用法
$newdate = new DateTime("@".realtime());
$realdate = $newdate->format("Y-m-d H:i:s");
echo $realdate." //DateTime类的时间。";
//////////PHP代码结束//////////
//////////执行结果开始//////////
环境:Windows2003-x64 Apache-2.2.34-VC10-x64 php-5.4.45-VC9-x64
Windows系统时间:2050-10-16 13:28
执行结果:
-1745456344 2549510952 //time()和realtime()的值,系统时间调到2050年,time()负数说明是32位的。
1914-11-25 17:31:43 //如果返回2050-12-31 23:59:59,那么date()没有问题,能处理64位。如果返回1914-11-25 09:31:43,那么date()只能处理32位。
1914-09-10 07:00:56 //date('Y-m-d H:i:s', time())的时间。
1914-09-10 07:00:56 //date('Y-m-d H:i:s', realtime())的时间。
1914-09-09 23:00:56 //gmdate('Y-m-d H:i:s', time())的时间。
1914-09-09 23:00:56 //gmdate('Y-m-d H:i:s', realtime())的时间。
4 2147483647 //PHP_INT_SIZE和PHP_INT_MAX的值。
2050-10-16 05:29:12 //DateTime类的时间。
----
环境:Windows7-x64 Apache-2.4.41-VC15-x64 PHP-7.2.23-VC15-x64
Windows系统时间:2050-10-16 13:07
执行结果:
-1745457669 2549509627 //time()和realtime()的值,系统时间调到2050年,time()负数说明是32位的。
2050-12-31 15:59:59 //如果返回2050-12-31 23:59:59,那么date()没有问题,能处理64位。如果返回1914-11-25 09:31:43,那么date()只能处理32位。
1914-09-09 22:38:51 //date('Y-m-d H:i:s', time())的时间。
2050-10-16 05:07:07 //date('Y-m-d H:i:s', realtime())的时间。
1914-09-09 22:38:51 //gmdate('Y-m-d H:i:s', time())的时间。
2050-10-16 05:07:07 //gmdate('Y-m-d H:i:s', realtime())的时间。
8 9223372036854775807 //PHP_INT_SIZE和PHP_INT_MAX的值。
2050-10-16 05:07:07 //DateTime类的时间。
//////////执行结果结束//////////
不考虑32位版本,从结果来看,在64位PHP7.0之前,realtime()函数和DateTime类一起用没有问题。在64位PHP7.0之后,realtime()函数和date()函数一起用没有问题,起码还能用到2038 + (2038 - 1970) = 2106年。
不知道64位windows2008、2012、2016等time()是不是返回负数。抽空再测试。
----------分割线----------
2019.10.19更新
今天突然发现64位7.0之后的php中,$_SERVER["REQUEST_TIME"]是完整的64位时间戳,超过2038年不会返回负数。在当前的php版本中,用下面的$rtime变量替换time()函数,可以完美解决2038年问题。
//////////php代码开始//////////
<?php
//64位php7.0之后的用法
$rtime = $_SERVER["REQUEST_TIME"];
echo $rtime." //rtime的值。<br />\r\n";
echo date("Y-m-d H:i:s", $rtime)." //date('Y-m-d H:i:s', rtime)的时间。<br />\r\n";
echo gmdate("Y-m-d H:i:s", $rtime)." //gmdate('Y-m-d H:i:s', rtime)的时间。<br />\r\n";
//////////php代码结束//////////
//////////执行结果开始//////////
环境:Windows7-x64 Apache-2.4.41-VC15-x64 PHP-7.2.23-VC15-x64
Windows系统时间:2099-10-19
执行结果:
4096073963 //rtime的值。
2099-10-19 06:19:23 //date('Y-m-d H:i:s', rtime)的时间。
2099-10-19 06:19:23 //gmdate('Y-m-d H:i:s', rtime)的时间。
//////////执行结果结束//////////
-----
2019.10.22更新
在32位windows2003系统和32位PHP5.3.45中,用$_SERVER['REQUEST_TIME']可以获取正常的负数时间。和64位版本中time()的值一样。写了一个函数,支持32位或64位老版本的windows2003和PHP5.4.45。
用下面的用time2()替换time()函数,用date2()替换date()函数,用gmdate2()替换gmdate()函数,顺便改了织梦CMS的MyDate()函数,全部能支持到2038年以后。
/////PHP代码开始/////
<?php
//时区
$cfg_cli_time = 8;
//支持2038年之后的正确时间,用time2()替换time()函数
if(!function_exists('time2'))
{
function time2()
{
$currenttime = time();//64位PHP用time()或$_SERVER['REQUEST_TIME']都可以,32位PHP只能用用$_SERVER['REQUEST_TIME']
if($currenttime < 0)
{
return 2147483647 + 2147483647 + 2 + $currenttime;
}
else
{
return $currenttime;
}
}
}
//支持2038年之后的正确时间,用date2()替换date()函数
if (!function_exists('date2'))
{
function date2($format='Y-m-d H:i:s', $timest=0)
{
global $cfg_cli_time;
if(empty($format))
{
$format = 'Y-m-d H:i:s';
}
$addtime = $cfg_cli_time * 3600;
if(empty($timest))
{
$newtimestamp = time2() + $addtime;
}
else
{
$newtimestamp = $timest + $addtime;
}
$newdatetime = new DateTime("@".$newtimestamp);
return $newdatetime->format($format);
}
}
//支持2038年之后的正确时间,用gmdate2()替换gmdate()函数
if (!function_exists('gmdate2'))
{
function gmdate2($format='Y-m-d H:i:s', $timest=0)
{
if(empty($format))
{
$format = 'Y-m-d H:i:s';
}
if(empty($timest))
{
$newtimestamp = time2();
}
else
{
$newtimestamp = $timest;
}
$newdatetime = new DateTime("@".$newtimestamp);
return $newdatetime->format($format);
}
}
//返回格林威治标准时间
if (!function_exists('MyDate'))
{
function MyDate($format='Y-m-d H:i:s', $timest=0)
{
global $cfg_cli_time;
$addtime = $cfg_cli_time * 3600;
if(empty($format))
{
$format = 'Y-m-d H:i:s';
}
return gmdate2($format, $timest+$addtime);
}
}
echo time()." //time()<br />\r\n";
echo time2()." //time2()<br />\r\n";
echo date2()." //date2()<br />\r\n";
echo gmdate2()." //gmdate2()<br />\r\n";
echo MyDate('', 2549948733)." //正常返回2050-10-21 15:05:33<br />\r\n";
/////PHP代码结束/////
////执行结果开始/////
环境:Windows2003-x64 Apache-2.2.34-VC10-x64 php-5.4.45-VC9-x64
当前系统时间:2050-10-21 15:08:18
-1745018398 //time()
2549948898 //time2()
2050-10-21 15:08:18 //date2()
2050-10-21 07:08:18 //gmdate2()
2050-10-21 15:05:33 //正常返回2050-10-21 15:05:33
/////执行结果结束/////
**********20210825更新**********
今天发现在低版本Apache和PHP中,$_SERVER["REQUEST_TIME_FLOAT"]可以正确获取时间,唯一一个正确的,非常难得。
****测试代码开始****
<?php
echo time()."<br />\r\n";
echo microtime()."<br />\r\n";
echo $_SERVER["REQUEST_TIME"]."<br />\r\n";
echo $_SERVER["REQUEST_TIME_FLOAT"]."<br />\r\n";
//支持2038年之后的正确时间,用time2()替换time()函数
if(!function_exists('time2'))
{
function time2()
{
$currenttime = explode('.', $_SERVER['REQUEST_TIME_FLOAT']);
return $currenttime[0];
}
}
echo time2()."<br />\r\n";
$newdate = new DateTime("@".time2());
$realdate = $newdate->format("Y-m-d H:i:s");
echo $realdate."<br />\r\n";
****测试代码结束****
****执行结果开始****
日期:2021-08-23 Windows-2008-r2-企业版-64位 Apache-2.2.34-64位 PHP-5.4.45-64位
结果:
1629692951
0.23484500 1629692951
1629692951
1629692951.234
1629692951
2021-08-23 04:29:11
日期:2050-08-23 Windows-2008-r2-企业版-64位 Apache-2.2.34-64位 PHP-5.4.45-64位
结果:
-1750125419
0.30849000 -1750125419
-1750125419
2544841877.308
2544841877
2050-08-23 04:31:17
****执行结果结束****