从毫秒中获取日期格式为m-d-Y H:i:s.u

155

我正在尝试获取一个格式化的日期,包括以毫秒为单位指定的UNIX时间戳中的微秒。

唯一的问题是我一直得到000000,例如:

$milliseconds = 1375010774123;
$d = date("m-d-Y H:i:s.u", $milliseconds/1000);
print $d;

2013年07月28日 11点26分14秒


10
奇怪的是问题要求转换毫秒,但大家都回答如何转换微秒。我知道你可以在两者之间转换,但实际上回答问题而不是从PHP文档或其他地方复制粘贴不相关的代码会更好。 - laurent
18个回答

202

你可以使用输入格式U.u轻松地完成这个任务。

$now = DateTime::createFromFormat('U.u', microtime(true));
echo $now->format("m-d-Y H:i:s.u");

这会产生以下输出:

04-13-2015 05:56:22.082300

来自PHP手册日期格式页面:

  • U = Unix纪元以来的秒数
  • u = 微秒

http://php.net/manual/en/function.date.php


感谢giggsey指出我原来回答中的一个缺陷,将number_format()添加到代码行中可以解决确切秒数的问题。遗憾的是,它现在感觉不那么优雅了...

$now = DateTime::createFromFormat('U.u', number_format(microtime(true), 6, '.', ''));

http://php.net/manual/zh/function.number-format.php


关于时区的说明,回应 DaVe 的问题。

createFromFormat() 方法通常会使用本地时区(如果没有指定的话)。

http://php.net/manual/zh/datetime.createfromformat.php

然而,这里描述的技术使用 microtime() 初始化 DateTime 对象,它返回自 Unix 纪元(1970 年 1 月 1 日 00:00:00 GMT)以来经过的秒数。

http://php.net/manual/zh/function.microtime.php

这意味着 DateTime 对象被隐式地初始化为 UTC,这对于仅想跟踪经过时间的服务器内部任务是可以的。

如果您需要显示特定时区的时间,则需要相应地设置它。但是,由于上述原因,这应该作为初始步骤之后的单独步骤完成(不要使用 createFromFormat() 的第三个参数)。

可以使用 setTimeZone() 方法来实现此需求。

http://php.net/manual/zh/datetime.settimezone.php

例如:

$now = DateTime::createFromFormat('U.u', number_format(microtime(true), 6, '.', ''));
echo $now->format("m-d-Y H:i:s.u") . '<br>';

$local = $now->setTimeZone(new DateTimeZone('Australia/Canberra'));
echo $local->format("m-d-Y H:i:s.u") . '<br>';

产生以下输出:

10-29-2015 00:40:09.433818
10-29-2015 11:40:09.433818

请注意,如果您想将时间输入到MySQL中,则时间格式需要如下:

format("Y-m-d H:i:s.u")

27
注意:如果 microtime(true) 正好在整秒时运行,DateTime::createFromFormat('U.u', microtime(true)); 将返回 false。它会返回 '1438616239' 而不是 '1438616239.000000'。 - giggsey
1
谢谢giggsey,总是边缘情况会让你出错! - ArchCodeMonkey
3
好的解决方案,谢谢。看起来我在使用microtime创建$now时丢失了本地时间。你有什么想法吗? - David Gras
2
@daVe,我添加了一些有关时区的信息,希望这正是你想要的。 - ArchCodeMonkey
2
number_format 始终是必需的,即使它不在确切的第二位。否则,您将受到 precision PHP 配置选项(用于“显示”)的限制,默认情况下 PHP 7.0 的精度不足,导致精度损失(不准确到微秒)。 - Teoh Han Hui
显示剩余4条评论

139

PHP官网上提到:

微秒(在 PHP 5.2.2 中添加)。请注意,由于 date() 接受整数参数,所以它始终会生成 000000,而 DateTime::format() 支持微秒,如果 DateTime 是使用微秒创建的。

因此,简单使用:

$micro_date = microtime();
$date_array = explode(" ",$micro_date);
$date = date("Y-m-d H:i:s",$date_array[1]);
echo "Date: $date:" . $date_array[0]."<br>";

建议使用引用中的dateTime()类:

$t = microtime(true);
$micro = sprintf("%06d",($t - floor($t)) * 1000000);
$d = new DateTime( date('Y-m-d H:i:s.'.$micro, $t) );

print $d->format("Y-m-d H:i:s.u"); // note at point on "u"

注意:u代表微秒(1秒 = 1000000 µs)。

另一个来自php.net的例子:

$d2=new DateTime("2012-07-08 11:14:15.889342");

dateTime() 的参考文档位于 php.net

我已经简短地回答了作者的问题。更多信息请参见: 获取毫秒数的日期格式 m-d-Y H:i:s.u


4
通常我选择第二种形式,因为它更容易阅读和理解。此外,DateTime比日期/时间函数更加灵活和强大。处理微秒是一个很好的例子。 - Herbert
3
@Geo和AlBundy,你们是否在使用PHP版本低于5.2.2的计算机上尝试过这个?PHP 5.4.39可以完美地运行这个。 - RichardBernards
这不是很容易吗?date('m-d-Y H:i:s').(int)microtime(true); - Anant
@Anant,确实很容易。但是不正确。;)你得到的只是以秒为单位的时代。 (不幸的是,microtime(false)也无法使用。) - Sz.
这段代码在所有语言环境下都不能正确运行。一些国家,如德国,会写成“0,1234”或“0.1234”,而不是“.1234”,因此你的代码会给我输出以下结果:2012-07-08 11:14:15.0.889342 - Daniel Marschall
这也会返回一个错误:Deprecated: Implicit conversion from float 1664568100.74341 to int loses precision,因为 microtime(true) 返回了这个值。 - Parminder Singh

19

我在使用

echo date("Y-m-d H:i:s.").gettimeofday()["usec"];

output: 2017-09-05 17:04:57.555036

一行代码。优雅。 - Misunderstood

17

这里有一个稍微简短的方法。不需要创建高精度的日期/时间,我将微秒值转换为字符串,去掉0,然后将其添加到日期/时间字符串的末尾。通过调整字符串长度参数,可以轻松地裁剪小数位数;在这里,我使用4来获取毫秒,但您也可以使用7来获取微秒。

$t = explode(" ",microtime());
echo date("m-d-y H:i:s",$t[1]).substr((string)$t[0],1,4);

对于 microtime() 值为 0.98236000 1407400573,此代码将返回 08-07-14 01:08:13.982

难道不应该像这样吗? date("m-d-y H:i:s") . substr((string)$t,1,4); 我对数组引用、explode等感到困惑... - Chris Pugmire

8
# PHP 8 type safe when using declare(strict_types=1);
echo date('m-d-Y H:i:s').substr((string) fmod(microtime(true), 1), 1, 4);

示例输出:

02-06-2019 16:45:03.53811192512512

如果您需要限制小数点位数,则下面这行代码(来自mgutt的贡献)是一个不错的选择。(在下面的代码中,数字6限制小数点位数为6。)

echo date('m-d-Y H:i:').sprintf('%09.6f', date('s')+fmod(microtime(true), 1));

示例输出:

02-11-2019 15:33:03.624493

你忘了秒数的精度和前导零。因此,正确的版本应该是 echo date('m-d-Y H:i:').sprintf('%09.6f', date('s')+fmod(microtime(true), 1));。为什么我们需要 09 的解释:https://dev59.com/614b5IYBdhLWcg3wvT6w#28739819。 - mgutt
你又错过了精度 ;) echo date('H:i:s').substr(fmod(microtime(true), 1), 1, 7); - mgutt
但要注意,substr() 不会四舍五入。而且与 round() 结合使用时,它会截断尾随的零:https://dev59.com/wWs05IYBdhLWcg3wB9ef 这就是我建议使用 sprintf() 的原因。 - mgutt
@mgutt 第一个数字什么时候不是零?基本上只需要在通过fmod除以1得到余数时删除始终存在的零。问题要求微秒,但以秒的小数形式表示。没有理由限制小数位数。更多的小数位数只是更好的精度。 - ghbarratt
@mgutt 我已经将你提出的第一个替代方案添加到我的答案中,并解释了如果需要限制小数位数,那将是一个不错的方法。 - ghbarratt
现在它完美了 ;) - mgutt

8

适用于PHP 8.0+

该错误最近已修复,现在您可以使用带有小数部分的UNIX时间戳。

$milliseconds = 1375010774123;

$d = new DateTime( '@'. $milliseconds/1000 );
print $d->format("m-d-Y H:i:s.u");

输出:

2013年07月28日 11:26:14.123000

对于 PHP < 8.0

在使用 DateTime 对象之前,您需要指定 UNIX 时间戳的格式。正如其他答案中所指出的那样,您可以通过使用 createFromFormat()number_format() 来解决这个问题。

$milliseconds = 1375010774123;

$d = DateTime::createFromFormat('U.v', number_format($milliseconds/1000, 3, '.', ''));
print $d->format("m-d-Y H:i:s.u");

输出:

2013年7月28日 11:26:14.123000

对于PHP < 7.3

如果你仍在使用PHP早于7.3的版本,可以将U.v替换为U.u。这不会有任何影响,但是毫秒格式标识符只出现在PHP 7.3之后。

$milliseconds = 1375010774123;
//                                 V - Use microseconds instead of milliseconds
$d = DateTime::createFromFormat('U.u', number_format($milliseconds/1000, 3, '.', ''));
print $d->format("m-d-Y H:i:s.u");

输出:

2013年7月28日11点26分14.123秒


6
从PHP 7.1开始,您可以简单地执行以下操作:
$date = new DateTime( "NOW" );
echo $date->format( "m-d-Y H:i:s.u" );

它将显示为:
04-11-2018 10:54:01.321688

简写回显 (new DateTime())->format('Y-m-d H:i:s.u'); - mariovials

4
如果您想以类似JavaScript中的 (new Date()).toISOString() 的格式来格式化日期,这是您可以在PHP中执行的操作:
$now = microtime(true);
gmdate('Y-m-d\TH:i:s', $now).sprintf('.%03dZ',round(($now-floor($now))*1000));

样例输出:

2016-04-27T18:25:56.696Z

仅为证明减去整数不会降低小数部分的精度:

>>> number_format(123.01234567890123456789,25)
=> "123.0123456789012408307826263"
>>> number_format(123.01234567890123456789-123,25)
=> "0.0123456789012408307826263"

PHP确实对小数位进行了四舍五入,但在这两种情况下它采用了相同的方式进行四舍五入。


1
不要对浮点数使用数学运算,这样会导致不准确。同时也不要浪费时间调用多个函数,只需使用substr()将格式化日期截断到三位小数即可。 - Mr. Lance E Sloan
@LS 截断不会将第三位小数四舍五入,这可能会更加不准确。只有当你进入非常小的精度时,浮点数才会崩溃;3个小数位应该没问题。实际上,我认为我根本没有改变精度,浮点数越接近0,就越准确。据我所知,减去整数不应该改变小数位数。 - mpen
那么 date('Y-m-d\TH:i:s', $time / 1000) . sprintf('.%03dZ', substr($time, 10)); 呢? - Artfaith
如果您的“$time”已经是毫秒,那么从技术上讲,这应该可以工作。我不喜欢“substr”方法,但我猜它对于2001年到2286年之间的日期应该可以工作。 - mpen
是的,$time 是毫秒,但 2286 又怎么样呢?我的意思是,你为什么这么认为,如何才能避免这种情况? - Artfaith
@F8ER,你的 substr(..., 10) 假设了一个特定的数字数量。在2286年,时间戳将会增加一位数字。 - mpen

4
这基于 ArchCodeMonkey 的回答,简化版的话,如果您只想要一个快速可行的解决方案。
function DateTime_us_utc(){
    return DateTime::createFromFormat('U.u', number_format(microtime(true), 6, '.', ''));
}
function DateTime_us(){
    $now = DateTime_us_utc();
    return $now->setTimeZone(new DateTimeZone(date_default_timezone_get()));
}

所以对我来说,
$now = DateTime_us();
$now->format("m-d-Y H:i:s.u");

3
这里有另一种我觉得更加优雅/简单的方法:
echo date('Y-m-d-H:i:s.').preg_replace("/^.*\./i","", microtime(true));

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接