PHP时间日期和毫秒数之间的处理

高坚果兄弟 2016-10-04 12:49:43
最近打算学PHP,用PHP搞网站,先准备从一些通用公共函数写起,把那些脏活累活用函数封装好,第一个就是时间日期处理了。

要搞时间日期没有什么别的要求,就是要和前端js实现结果一致,至少支持1800-2100年(以前定的一个数据库字段默认值),同一个长整形毫秒数(js:Date.now()这样的值)转换成时间日期,同一个时间日期转换成长整形毫秒数,两个操作结果必须完全一致,也就是必须准确,时区这个细节,各自语言自己处理好 北京时间呗

然后就发现,php时间里面没有现成的获取毫秒数方法,不过研究了一下还是有方法计算出这个值

static function Date1970($zone){
return new DateTime('1970-01-01T00:00:00.0Z', $zone);
}
static function Date_MS($date){
$c=$date->diff(EDate::Date1970($date->getTimezone()));
$f=$c->invert?1:-1;
return ($c->days*24*60*60*1000+$c->h*60*60*1000+$c->i*60*1000+$c->s*1000+$f*round($date->format('u')/1000)-$f*8*60*60*1000)*$f;
}

这个转换测试没有问题,能够将datetime准确转换成毫秒数


那就差一个毫秒数转时间日期了,这个搞魔了,所有代码都是用的DateTime类,测试发现1901后面的时间会有固定误差,这个补一下就好了,但是测试发现有一个严重问题,1940年和1986-1991中间时间有些转换会有偏差,差几个小时,出现错误有点像随机的,中间有的是好的有的是坏的,这个两个区间外的时间测试都是好的,这是严重问题,直接导致代码完全没卵用,不清楚是不是代码问题,按道理说如果是代码问题不应该还会有好的啊

static function MS_Date($ms){
$zone=new DateTimeZone('PRC');

if($ms<-2147483648000){
$ms-=343000;
}

$mi=intval(fmod($ms,1000));
if($mi<0){
$mi=1000+$mi;
$ms-=2*$mi;
}


$h=intval(abs($ms)/(60*60*1000));
$s=intval(fmod(abs($ms),60*60*1000)/1000);
$c=new DateInterval('PT'.$h.'H'.$s.'S');
$d=EDate::Date1970($zone);
if($ms>0){
$d->add($c);
}else{
$d->sub($c);
}
$d->setTimezone($zone);

$mi=substr('00000'.$mi*1000,-6);
$ds=$d->format('Y-m-d\TH:i:s').'.'.$mi.'Z';
$d=new DateTime($ds, $zone);
return $d;
}


特此发帖请教:
1、PHP时间日期 上面需求这样的处理是否有什么需要规避的细节
2、别人时间日期和毫秒数转换如何处理
3、有没有什么现成可用的东西能拿过来直接用

新手 望多多指点
...全文
1593 18 打赏 收藏 转发到动态 举报
写回复
用AI写文章
18 条回复
切换为时间正序
请发表友善的回复…
发表回复
高坚果兄弟 2016-10-09
  • 打赏
  • 举报
回复
引用 11 楼 fdipzone 的回复:

我电脑是IIS里面跑的PHP,IIS是32、64位模式都试过,PHP是64位的,1800的时间date方法好像处理不了,可能跨平台时间处理也是坑



引用 12 楼 xuzuning 的回复:
不知道你打算做什么,所以也不想参与
不过夏令时是 1907 年才首次再美国提出动议,与 1800 年毫无关系
再说夏令时和冬令时也只是至多在一天之内产生一小时的时差,与时间计算毫无关系,并不因为你实现了夏令时,地球就停一小时不转了

精辟
我打算做的还是画张图表述一下

对浏览器来说,中间虚线框中的细节可以不用管,浏览器看到的只有长整型的时间(毫秒数)
php作为服务器连接数据库和浏览器的中间部分,我想要的就是实现长整型时间转换成DateTime,DateTime转换成长整型时间

可能这样的框架设计就不合理,新手不要笑哈
xuzuning 2016-10-09
  • 打赏
  • 举报
回复
不知道你打算做什么,所以也不想参与 不过夏令时是 1907 年才首次再美国提出动议,与 1800 年毫无关系 再说夏令时和冬令时也只是至多在一天之内产生一小时的时差,与时间计算毫无关系,并不因为你实现了夏令时,地球就停一小时不转了
傲雪星枫 2016-10-09
  • 打赏
  • 举报
回复
我的代码是在本机跑的结果 $utime = -5364691199.877000; echo udate('Y-m-d H:i:s.u', $utime).'<br>'; // 1800-01-01 00:00:00.123000 $utime = 1476072000.123000; echo udate('Y-m-d H:i:s.u', $utime).'<br>'; // 2016-10-10 12:00:00.123000 夏令时就是个坑,中国貌似不用的。
xuzuning 2016-10-09
  • 打赏
  • 举报
回复
日期部分不对的话,你就 日期、时间、毫秒 分三块分别计算就是了
高坚果兄弟 2016-10-09
  • 打赏
  • 举报
回复
引用 16 楼 xuzuning 的回复:
简化一下
function MS_Date($n) {
  if(is_numeric($n))
    return date('Y-m-d H:i:s.', substr($n, 0, -3)) . substr($n, -3);
  return substr((new DateTime($n))->format('Uu'), 0, -3);
}
和8楼的群主发的一样,我电脑上运行最小时间只能到1936年

echo MS_Date(-5364691199877);//1936-02-08 06:28:17.877
我电脑是IIS里面跑的PHP,IIS是32、64位模式都试过,PHP是64位的,1800的时间date方法好像处理不了,可能跨平台时间处理也是坑 PHP date方法介绍里面写的: 时间戳的有效取值范围为 GMT 时间的 1901 年 12 月 13 日至 GMT 时间的 2038 年 1 月 19 日。 (32 位有符号整数的取值范围)。 但是,在 PHP 5.1.0 之前的版本,在某些系统(例如 Windows)上有效取值范围为 1970 年 1 月 1 日至 2038 年 1 月 19 日。
xuzuning 2016-10-09
  • 打赏
  • 举报
回复
简化一下
function MS_Date($n) {
  if(is_numeric($n))
    return date('Y-m-d H:i:s.', substr($n, 0, -3)) . substr($n, -3);
  return substr((new DateTime($n))->format('Uu'), 0, -3);
}
xuzuning 2016-10-09
  • 打赏
  • 举报
回复
$inp = 1451621405123;

echo $s = MS_Date($inp); //2016-01-01 12:10:05.123
echo MS_Date($s); //1451621405123

function MS_Date($n) {
  if(is_numeric($n)) {
    $r = date('Y-m-d H:i:s.', intval($n/1000));
    $u = substr($n, -3);
    return $r . $u;
  }
  $d = new DateTime($n);
  return substr($d->format('Uu'), 0, -3);
}
qq_14831791 2016-10-09
  • 打赏
  • 举报
回复
几乎没考虑过这种问题..也赞个先,以后有需要再来取
高坚果兄弟 2016-10-08
  • 打赏
  • 举报
回复
刚才灵光一现,想起了夏令时这个玩意,看了一下百度百科

1986年至1991年,每年四月的第2个星期日早上2点,到九月的第2个星期日早上2点之间。
1986年5月4日至9月14日(1986年因是实行夏令时的第一年,从5月4日开始到9月14日结束)
1987年4月12日至9月13日,
1988年4月10日至9月11日,
1989年4月16日至9月17日,
1990年4月15日至9月16日,
1991年4月14日至9月15日。
1986-1991,看样子是夏令时搞的鬼,DateTime没有问题,错怪它了,只怪自己不会用 JavaScript、asp、c#刚才测了一下,默认的都没考虑夏令时,他们之间的时间转换都能互通,php这样感觉是个坑
高坚果兄弟 2016-10-08
  • 打赏
  • 举报
回复
引用 8 楼 fdipzone 的回复:
谢谢版主的回答,你发的这段代码测试发现没有预期效果哦

1936-02-08 06:28:16.123000
2016-10-10 12:00:00.123000
是我服务器哪里要配置一下还是?

new Date(-5364691199877).toLocaleString()//"1800/1/1 上午12:05:43"
第一个测试值,js算出来的和你算出来的有点出入
傲雪星枫 2016-10-08
  • 打赏
  • 举报
回复

function udate($format, $timestamp=null) {
    if (!isset($timestamp)) $timestamp = microtime();

    if (count($t = explode(" ", $timestamp)) == 1) {
        list($timestamp, $usec) = explode(".", $timestamp);
        $usec = "." . $usec;
    }else {
        $usec = $t[0];
        $timestamp = $t[1];
    }

    if($timestamp<0){
        $usec = 1-$usec;
        $timestamp = $timestamp-1;
    }

    $date = new DateTime(date('Y-m-d H:i:s' . substr(sprintf('%.7f', $usec), 1), $timestamp));
    $result = $date->format($format);
    return $result;

}

$utime = -5364691199.877000;
echo udate('Y-m-d H:i:s.u', $utime).'<br>'; // 1800-01-01 00:00:00.123000

$utime = 1476072000.123000;
echo udate('Y-m-d H:i:s.u', $utime).'<br>'; // 2016-10-10 12:00:00.123000
高坚果兄弟 2016-10-08
  • 打赏
  • 举报
回复
没人回答,我就自问自答了 测试发现原因所在了,PHP Version 5.6.14的DateTime实现有严重错误,重现代码:

//1992年是好的
$d=new DateTime("1992-05-30 15:50:00",new DateTimeZone('UTC'));
echo $d->format("Y-m-d H:i:s")."\n";//1992-05-30 15:50:00
$d->setTimezone(new DateTimeZone('PRC'));
echo $d->format("Y-m-d H:i:s")."\n";//1992-05-30 23:50:00

//1991年出现错误,时区是9小时?
$d=new DateTime("1991-05-30 15:50:00",new DateTimeZone('UTC'));
echo $d->format("Y-m-d H:i:s")."\n";//1991-05-30 15:50:00
$d->setTimezone(new DateTimeZone('PRC'));
echo $d->format("Y-m-d H:i:s")."\n";//1991-05-31 00:50:00
也许这就是导致我写的MS_Date函数出现“1940年和1986-1991中间时间有些转换会有偏差”的原因,坑爹不是 这个错误还是我写完EDate类测试好之后抽空发现的,EDate类已经写好了,完全满足要求了,把新的MS_Date代码贴下面,这个是完是自己代码计算的,没有用PHP自带的时间方法,如果谁要用就拿去用好了。 类似的用法

MS_Date(-30610252800000)//1000-01-01 00:00
MS_Date(32503651200000)//3000-01-01 00:00
MS_Date(1475925060000)//2016-10-08 19:11

	static function MS_Date($ms){
		$z=8;
		$ms+=$z*60*60*1000;
		
		$t=$ms;
		$ss=intval(fmod($t,1000));
		$t=floor($t/1000);
		$s=intval(fmod($t,60));
		$t=floor($t/60);
		$m=intval(fmod($t,60));
		$t=floor($t/60);
		$h=intval(fmod($t,24));
		$t=ceil($t/24);
		$day=intval($t);
		
		if($ms<0){
			$h=24+$h;
			$m=60+$m;
			$s=60+$s;
			$ss=1000+$ss;
			
			if($ss==1000){
				$ss=0;
			}
			if($s==60){
				$s=0;
			}
			if($m==60){
				$m=0;
			}
			if($h==24){
				$h=0;
			}
		}
		if($h==0){
			$day++;
		}
		$day=719527+$day;//day=0;for(var i=1;i<=1970;i++){if(i%400==0||i%100>0&&i%4==0){day+=366}else{day+=365}};day
		
		$y=0;
		$ydPrev=0;
		while(true){
			$y=$y+intval($day/366);
			$day=$day%366;
			$yd=$y-(intval($y/4)-intval($y/100)+intval($y/400));
			$day+=$yd-$ydPrev;
			if($day==366){
				if($ms>0 && EDate::IsLeapYear($y+1)){
					$day=0;
					$y++;
					break;
				}
			}else if($day==365){
				if($ms>0 && !EDate::IsLeapYear($y+1)){
					$day=0;
					$y++;
				}
				break;
			}else if($day<365){
				break;
			}
			$ydPrev=$yd;
		}
		$Y=$y;
		$M=0;
		$D=0;
		while(true){
			$isLeap=EDate::IsLeapYear($Y);
			if($isLeap){
				$day++;
			}
			if($day==0){
				if($ms<0){
					$M=1;
					$D=1;
				}else{
					$Y--;
					$M=12;
					$D=31;
				}
				break;
			}else if($day>=60 && $isLeap){
				if($day>60){
					$day--;
				}else if($day==60){
					$M=2;
					$D=29;
					break;
				}
			}
			if($day<=31){
				$M=1;
				$D=$day;
			}else if($day<=59){
				$M=2;
				$D=$day-31;
			}else if($day<=90){
				$M=3;
				$D=$day-59;
			}else if($day<=120){
				$M=4;
				$D=$day-90;
			}else if($day<=151){
				$M=5;
				$D=$day-120;
			}else if($day<=181){
				$M=6;
				$D=$day-151;
			}else if($day<=212){
				$M=7;
				$D=$day-181;
			}else if($day<=243){
				$M=8;
				$D=$day-212;
			}else if($day<=273){
				$M=9;
				$D=$day-243;
			}else if($day<=304){
				$M=10;
				$D=$day-273;
			}else if($day<=334){
				$M=11;
				$D=$day-304;
			}else{
				$M=12;
				$D=$day-334;
			}
			break;
		}
		
		$Y=substr('000'.$Y,-4);
		$M=substr('0'.$M,-2);
		$D=substr('0'.$D,-2);
		$h=substr('0'.$h,-2);
		$m=substr('0'.$m,-2);
		$s=substr('0'.$s,-2);
		$ss=substr('00000'.$ss*1000,-6);
		
		$date=new DateTime($Y.'-'.$M.'-'.$D.'T'.$h.':'.$m.':'.$s.'.'.$ss.'Z', new DateTimeZone('PRC'));
		return $date;
	}
最后还是要请教一下: 1、PHP时间日期 上面需求这样的处理是否有什么需要规避的细节 2、别人时间日期和毫秒数转换如何处理 3、有没有什么现成可用的东西能拿过来直接用
高坚果兄弟 2016-10-06
  • 打赏
  • 举报
回复
大神呢,来分享分享撒
高坚果兄弟 2016-10-05
  • 打赏
  • 举报
回复
引用 4 楼 lanshs 的回复:
lanshs 2016-10-05
  • 打赏
  • 举报
回复
复制过来的,没试过

function microtime_format($tag, $time)
{
   list($usec, $sec) = explode(".", $time);
   $date = date($tag,$usec);
   return str_replace('x', $sec, $date);
}
microtime_format('Y年m月d日 H时i分s秒 x毫秒', 1270626578.66000000);
高坚果兄弟 2016-10-04
  • 打赏
  • 举报
回复
引用 1 楼 hyurl 的回复:
引用 2 楼 jam00 的回复:
可能没说清楚哈,转毫秒数是没一点问题的,包括获取当前时间

round(microtime(true)*1000);
其实如果不纠,结秒*1000也可以(丢失了时间的毫秒部分,一秒可以做很多事情嘞),就是感觉不舒服,网页传过来的值可以达到毫秒精度,php里一处理就丢了,这个就不提了是好的。 现在问题在毫秒数转成时间上,比如-5364691199877成时间1800-01-01 0:0:0.123,有没有什么好方法 电脑上装的PHP Version 5.6.14
果酱很好吃 2016-10-04
  • 打赏
  • 举报
回复
向上取整的毫秒

echo ceil(microtime(true) * 1000);
hyurl 2016-10-04
  • 打赏
  • 举报
回复
搞不懂你弄这么复杂做什么,秒乘上1000就是毫秒

21,886

社区成员

发帖
与我相关
我的任务
社区描述
从PHP安装配置,PHP入门,PHP基础到PHP应用
社区管理员
  • 基础编程社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧