PHP DateTime微秒始终返回0


71

此代码在PHP 5.2.5中始终以微秒返回0:

<?php
$dt = new DateTime();
echo $dt->format("Y-m-d\TH:i:s.u") . "\n";
?>

输出:

[root@www1 ~]$ php date_test.php
2008-10-03T20:31:26.000000
[root@www1 ~]$ php date_test.php
2008-10-03T20:31:27.000000
[root@www1 ~]$ php date_test.php
2008-10-03T20:31:27.000000
[root@www1 ~]$ php date_test.php
2008-10-03T20:31:28.000000

有任何想法吗?


20
也许您太幸运了;)
albertein

1
@Unkwntech:date()仅支持整数。本手册将您转到使用“ u”的date_format / DateTime :: format。
乔纳森·洛诺夫斯基

1
@Jonathan Lonowski:是的,我正在使用DateTime :: format
eydelber

11
这是荒唐的。出现了与OP相同的问题。真是一团糟。

7
真可笑... 2014年至今。
AdrianGünter2014年

Answers:


26

这似乎是可行的,尽管http://us.php.net/date记录了微秒说明符似乎并不合逻辑,但实际上并不支持它:

function getTimestamp()
{
        return date("Y-m-d\TH:i:s") . substr((string)microtime(), 1, 8);
}

17
通过进行单独的呼叫,您极有可能使两个时间戳失序:例如,呼叫日期为1:29:22.999999,而mircotime为1:29:23.000001。在我的服务器上,连续通话的间隔时间约为10 us。
幸运的

29
试试:list($ usec,$ sec)= explode(“”,microtime()); 返回日期(date(“ Ymd \ TH:i:s”,$ sec)。$ usec;
幸运

@eydelber:由于PHP的date()函数仅接受整数时间戳记,因此只有在将date_format()函数与通过date_create()创建的基于用户的时间戳记一起使用时,u格式字符才有用。
AlexV 2011年

一个很好的简单解决方案,可以解决我一直在寻找的简单问题。:)
YOMorales

1
这个公认的答案确实引起了比赛条件。@Lucky的解决方案是一种更好的解决方法,可以应对PHP的怪异现象。
e2-e4

19

您可以在构造DateTime对象时指定您的输入包含微秒,并microtime(true)直接用作输入。

不幸的是,如果您精确到秒,这将失败,因为.在微型时间输出中将没有输出。因此sprintf.0在这种情况下,可以强制其包含一个:

date_create_from_format(
    'U.u', sprintf('%.f', microtime(true))
)->format('Y-m-d\TH:i:s.uO');

或等效(更多的面向对象样式)

DateTime::createFromFormat(
    'U.u', sprintf('%.f', microtime(true))
)->format('Y-m-d\TH:i:s.uO');

2
如果microtime(true)命中一整秒,此命令将失败date_create_from_format('U.u', sprintf('%.f', microtime(true)))->format(...
hooblei 2012年

这也将在PHP 5.3.2(Ubuntu 10.04)上失败,当在此函数中使用“ Uu”作为格式时(错误)返回False
Martin Konecny 2012年

不幸的是,此解决方案需要PHP 5.3+
Alex Yaroshevich 2013年

17

此功能来自http://us3.php.net/date

function udate($format, $utimestamp = null)
{
    if (is_null($utimestamp))
        $utimestamp = microtime(true);

    $timestamp = floor($utimestamp);
    $milliseconds = round(($utimestamp - $timestamp) * 1000000);

    return date(preg_replace('`(?<!\\\\)u`', $milliseconds, $format), $timestamp);
}

echo udate('H:i:s.u'); // 19:40:56.78128

非常麻烦,您必须实现此功能才能使“ u”工作...:\


1
嗨,(string)microtime(),1、8呢?
eydelber

+1表示使用负向后看,以便不替换\u
h2ooooooo

h2ooooooo,无论如何这都不是正确的解决方案,它本身也不支持转义的反斜杠,即“ \\ u”
Misanthrope 2014年

14

试试看,它显示微秒:

$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");

相当优雅,我最喜欢这种解决方案。我做了一些测试,发现此实现没有任何问题。
Dynom 2013年

太好了,有没有什么优雅的东西可以转换回微型时间了?
malhal 2014年

既然你串接微秒到格式化字符串反正你不需要的DateTime对象:date('Y-m-d H:i:s.'.$micro,$t)是AREADY所需的字符串(或等价date('Y-m-d H:i:s.',$t).$micro
IMSOP

8
\DateTime::createFromFormat('U.u', microtime(true));

将给您(至少在大多数系统上):

object(DateTime)(
  'date' => '2015-03-09 17:27:39.456200',
  'timezone_type' => 3,
  'timezone' => 'Australia/Darwin'
)

但是由于PHP浮点舍入而导致精度下降。这不是真正的微秒。

更新资料

这可能是createFromFormat()选项的最佳折衷方案,并且可以提供完整的精度。

\DateTime::createFromFormat('0.u00 U', microtime());

gettimeofday()

更明确,也许更强大。解决Xavi发现的错误。

$time = gettimeofday(); 
\DateTime::createFromFormat('U.u', sprintf('%d.%06d', $time['sec'], $time['usec']));

我对“在大多数系统上”的声明感到好奇。在某些系统上是主机时间解析或DateTime实现的问题吗?
cernio

我不明白:您指的是“浮动精度问题”?我可以简单地安全使用\DateTime::createFromFormat('U.u', microtime(true));吗?
阿兰迪尔

我刚刚在另一个问题中回答了这个问题。基本上,因为microtime(true)存储为浮点数,所以它将失去精度。它实际上不会给您几微秒,而是十分之一毫秒。stackoverflow.com/questions/41390315/…–
瑞安

在浮点数舍入和00结束时非常好!该gettimeofday()解决方案就像一个魅力!
哈维·蒙特罗

糟糕!警告!!-我发现了一个错误。情况:当微秒数处于最低1/10时。例如:如果{seconds:1498953412,microseconds:13}2017-07-01T23:56:52.130000Z,那么表示的UTC是并且应该2017-07-01T23:56:52.000013Z仅仅是因为它串联了1498953412+ .+ 13yield in:1498953412.13而它应该导致1498953412.000013-如果日志事件和顺序事件彼此之间发生的距离太近,则这可能会成为问题。
哈维·蒙特罗

5

是的,我想一劳永逸地解决这个问题。

解释如何在PHP中以毫秒微秒显示ISO 8601格式的日期和时间...

毫秒或“ ms”在小数点后有4位数字,例如0.1234。微秒或“ µs”在小数点后有7位数字。秒分数/名称解释在这里

PHP的date()功能在毫秒或微秒内不能完全按预期运行,因为除了整数以外,PHP的功能只能像整数一样,如php date文档中格式字符“ u”下所述。

基于Lucky的注释思想(此处),但具有正确的PHP语法和正确的秒格式设置(Lucky的代码在秒之后添加了错误的额外“ 0”)

这些还消除了竞争条件,并正确设置秒数格式。

PHP日期以毫秒为单位

工作当量 date('Y-m-d H:i:s').".$milliseconds";

list($sec, $usec) = explode('.', microtime(true));
echo date('Y-m-d H:i:s.', $sec) . $usec;

输出= 2016-07-12 16:27:08.5675

PHP日期与

工作等效项,date('Y-m-d H:i:s').".$microseconds";或者date('Y-m-d H:i:s.u')日期函数的行为与预期一致,以微秒microtime()//'u'表示

list($usec, $sec) = explode(' ', microtime());
echo date('Y-m-d H:i:s', $sec) . substr($usec, 1);

输出= 2016-07-12 16:27:08.56752900


2
很好的答案,但实际上毫秒是小数点后3位,毫秒是6,并且从技术上讲,这两种格式在真正的ISO 8601格式中均无效。
杰夫·普基特

另外值得一提的是,DateTime::createFromFormat("U.u", $t) 甚至会失败,如果(而不仅仅是失去精度)$t有7个(或任何超过6)小数点后的数字,在PHP ...
SZ。

4

这对我有用,并且是一个简单的三层:

function udate($format='Y-m-d H:i:s.', $microtime=NULL) {
    if(NULL === $microtime) $microtime = microtime();
    list($microseconds,$unix_time) = explode(' ', $microtime);
    return date($format,$unix_time) . array_pop(explode('.',$microseconds));
}

默认情况下,这(不提供任何参数)将以这种格式返回一个字符串,持续时间为当前的微秒:

YYYY-MM-DD HH:MM:SS.UUUUUUUU

甚至更简单/更快(尽管只有一半的精度)将如下所示:

function udate($format='Y-m-d H:i:s.', $microtime=NULL) {
    if(NULL === $microtime) $microtime = microtime(true);
    list($unix_time,$microseconds) = explode('.', $microtime);
    return date($format,$unix_time) . $microseconds;
}

这将以以下格式打印:

YYYY-MM-DD HH:MM:SS.UUUU


1

date_create

time:采用strtotime()接受的格式的字符串,默认为“ now”。

时间

time:根据GNU»日期输入格式语法分析的字符串。在PHP 5.0.0之前,该时间不允许使用微秒,因为PHP 5.0.0允许,但可以忽略。


1

根据Lucky评论以及PHP bug数据库中的功能请求,我使用了类似的方法:

class ExtendedDateTime extends DateTime {
    /**
     * Returns new DateTime object.  Adds microtime for "now" dates
     * @param string $sTime
     * @param DateTimeZone $oTimeZone 
     */
    public function __construct($sTime = 'now', DateTimeZone $oTimeZone = NULL) {
        // check that constructor is called as current date/time
        if (strtotime($sTime) == time()) {
            $aMicrotime = explode(' ', microtime());
            $sTime = date('Y-m-d H:i:s.' . $aMicrotime[0] * 1000000, $aMicrotime[1]);
        }

        // DateTime throws an Exception with a null TimeZone
        if ($oTimeZone instanceof DateTimeZone) {
            parent::__construct($sTime, $oTimeZone);
        } else {
            parent::__construct($sTime);
        }
    }
}

$oDate = new ExtendedDateTime();
echo $oDate->format('Y-m-d G:i:s.u');

输出:

2010-12-01 18:12:10.146625

1

这个怎么样?

$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>";

样本输出

2013-07-17 08:23:37:0.88862400


1

这应该是最灵活和精确的:

function udate($format, $timestamp=null) {
    if (!isset($timestamp)) $timestamp = microtime();
    // microtime(true)
    if (count($t = explode(" ", $timestamp)) == 1) {
        list($timestamp, $usec) = explode(".", $timestamp);
        $usec = "." . $usec;
    }
    // microtime (much more precise)
    else {
        $usec = $t[0];
        $timestamp = $t[1];
    }
    // 7 decimal places for "u" is maximum
    $date = new DateTime(date('Y-m-d H:i:s' . substr(sprintf('%.7f', $usec), 1), $timestamp));
    return $date->format($format);
}
echo udate("Y-m-d\TH:i:s.u") . "\n";
echo udate("Y-m-d\TH:i:s.u", microtime(true)) . "\n";
echo udate("Y-m-d\TH:i:s.u", microtime()) . "\n";
/* returns:
2015-02-14T14:10:30.472647
2015-02-14T14:10:30.472700
2015-02-14T14:10:30.472749
*/

0

字符串以strtotime()接受的格式工作!


0

在我正在编写的应用程序内部,我需要在DateTime对象上设置/显示微时间。似乎使DateTime对象识别微秒的唯一方法是使用“ YYYY-MM-DD HH:MM:SS.uuuuuu”格式的时间对其进行初始化。和ISO8601格式一样,日期和时间部分之间的空格也可以是“ T”。

以下函数返回一个初始化为本地时区的DateTime对象(当然,可以根据需要修改代码以适应个人需求):

// Return DateTime object including microtime for "now"
function dto_now()
{
    list($usec, $sec) = explode(' ', microtime());
    $usec = substr($usec, 2, 6);
    $datetime_now = date('Y-m-d H:i:s\.', $sec).$usec;
    return new DateTime($datetime_now, new DateTimeZone(date_default_timezone_get()));
}

0

PHP文档明确指出“请注意,date()将始终生成000000,因为它需要一个整数参数... ”。如果要快速替换date()功能,请使用以下功能:

function date_with_micro($format, $timestamp = null) {
    if (is_null($timestamp) || $timestamp === false) {
        $timestamp = microtime(true);
    }
    $timestamp_int = (int) floor($timestamp);
    $microseconds = (int) round(($timestamp - floor($timestamp)) * 1000000.0, 0);
    $format_with_micro = str_replace("u", $microseconds, $format);
    return date($format_with_micro, $timestamp_int);
}

(可以在这里找到要点:date_with_micro.php


请注意,您可以在上面date_with_micro()使用与date()函数相同的参数。
Manu Manjunath

1
如果仔细阅读OP的案例,您会注意到它DateTime::format()还会生成000000
Twisted Whisper

@TwistedWhisper已更正
Manu Manjunath

0

基于Lucky的评论,我编写了一种简单的方法来在服务器上存储消息。过去,我使用散列和增量来获取唯一的文件名,但是带有微秒的日期对于此应用程序来说效果很好。

// Create a unique message ID using the time and microseconds
    list($usec, $sec) = explode(" ", microtime());
    $messageID = date("Y-m-d H:i:s ", $sec) . substr($usec, 2, 8);
    $fname = "./Messages/$messageID";

    $fp = fopen($fname, 'w');

这是输出文件的名称:

2015-05-07 12:03:17 65468400

0

一些答案使用了几个时间戳,这在概念上是错误的,并且可能会出现重叠的问题:21:15:05.999组合在一起的秒数与21:15:06.000给出的微秒数21:15:05.000

显然,最简单的就是使用DateTime::createFromFormat()U.u,但作为一个评论说,它如果没有微秒失败。

所以,我建议这段代码:

function udate($format, $time = null) {

    if (!$time) {
        $time = microtime(true);
    }

    // Avoid missing dot on full seconds: (string)42 and (string)42.000000 give '42'
    $time = number_format($time, 6, '.', '');

    return DateTime::createFromFormat('U.u', $time)->format($format);
}

-1

此方法比公认的答案更安全:

date('Y-m-d H:i:s.') . str_pad(substr((float)microtime(), 2), 6, '0', STR_PAD_LEFT)

输出:

2012-06-01 12:00:13.036613

更新:不推荐(请参阅评论)


馊主意。首先获取微时间(使用microtime(true)而不是将其强制转换为浮点数)。使用小数部分(微时间)并将整数部分作为第二个参数传递给date()。否则,您可能会失配。
Shane H

@Encoderer:不匹配风险是什么意思?你能举个例子吗?
f.ardelian

4
假设实际时间是2012-08-12 13:53:30.9999。您对date()的调用返回“ 2012-08-12 13:53:30”。在随后的千分之一秒之后,您对microtime()的后续调用将返回00(两次调用之间的第二次滴答)。虽然这是一个极端的情况,但仍然是您的代码将被破坏的情况。如果我使用此代码创建通过系统传播的事件的审核日志,则您刚刚更改了历史记录。正确的答案是对microtime(true)进行一次调用并使用该结果。
Shane H

oneliner:date(“ Ymd H:i:s。”,$ m = microtime(1))。str_pad(floor(fmod($ m,1)* 1e + 6),6,“ 0”,STR_PAD_LEFT);
Alex Yaroshevich 2013年

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.