【问题标题】:PHP DateTime microseconds always returns 0PHP DateTime 微秒总是返回 0
【发布时间】:2010-09-15 05:28:11
【问题描述】:

此代码在 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

有什么想法吗?

【问题讨论】:

  • 也许你太幸运了;)
  • @Unkwntech: date() 只支持整数。手册将您转发到 date_format/DateTime::format 以使用 'u'。
  • @Jonathan Lonowski:是的,我正在使用 DateTime::format
  • 这太荒谬了。来到这里遇到与OP相同的问题。真是一团糟。
  • 确实很可笑......现在是 2014 年。

标签: php datetime


【解决方案1】:
\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 发现的 bug。

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

【讨论】:

  • 我对“在大多数系统上”的说法感到好奇。是主机时间分辨率问题还是某些系统上的 DateTime 实现问题?
  • 我不明白:您指的是哪个“浮点精度问题”?我可以简单地使用\DateTime::createFromFormat('U.u', microtime(true));吗?
  • 我刚刚在另一个问题中回答了这个问题。基本上, microtime(true) 将失去精度,因为它存储为浮点数。它实际上不会给你微秒,而是十分之一毫秒。 stackoverflow.com/questions/41390315/…
  • 非常好的浮点舍入并在最后得到00gettimeofday() 解决方案非常有效!
  • 糟糕错误!!警告!! - 我发现了一个错误。案例:当微秒数在最低的1/10时。例如:如果{seconds:1498953412,microseconds:13},那么表示的UTC是2017-07-01T23:56:52.130000Z,它应该是2017-07-01T23:56:52.000013Z,因为它连接1498953412+.+13产生:1498953412.13,而它应该导致@ 987654337@ - 如果它们发生的距离太近,这在记录和排序事件中可能会出现问题。
【解决方案2】:

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

说明如何在 PHP 中以 milli 秒和 micro 秒显示 ISO 8601 格式的日期和时间...

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

PHP 的 date() 函数在毫秒或微秒方面的表现并不完全符合预期,因为它只会除整数之外,如 php date docs 格式字符“u”下所述。

基于 Lucky 的评论想法 (here),但更正了 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日期

如果日期函数在微秒/microtime()/'u' 中按预期运行,则工作等效于 date('Y-m-d H:i:s').".$microseconds";date('Y-m-d H:i:s.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

【讨论】:

  • 很好的答案,但实际上一毫秒是小数点后 3 位,微秒是小数点后 6,在真正的 ISO 8601 格式中,这两者在技术上都不是有效的。
  • 另外值得注意的是,如果$t 小数点后有 7 个(或多于 6 个)位,DateTime::createFromFormat("U.u", $t) 甚至会失败(而不仅仅是失去精度) , 在 PHP 中...
【解决方案3】:

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

不幸的是,如果你精确到一秒,这将失败,因为微时间输出中不会有.;所以在这种情况下使用sprintf 强制它包含.0

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

或等价(更多 OO 风格)

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

【讨论】:

  • 如果microtime(true) 达到一整秒 - date_create_from_format('U.u', sprintf('%.f', microtime(true)))-&gt;format(...,此操作将失败
  • 这也将在 PHP 5.3.2 (Ubuntu 10.04) 上失败,当 'U.u' 用作此函数中的格式时返回 False(错误)
  • 很遗憾,这个解决方案需要 PHP 5.3+
【解决方案4】:

有些答案使用了多个时间戳,这在概念上是错误的,并且可能会出现重叠问题:21:15:05.999 的秒数与21:15:06.000 的微秒相加得到21:15:05.000

显然最简单的方法是使用DateTime::createFromFormat()U.u,但是as stated in a comment,如果没有微秒就会失败。

所以,我建议使用以下代码:

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);
}

【讨论】:

    【解决方案5】:

    基于 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
    

    【讨论】:

      【解决方案6】:

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

      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
      */
      

      【讨论】:

        【解决方案7】:

        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() 函数相同的参数。
        • 如果您仔细阅读 OP 的案例,您会注意到 DateTime::format() 也会生成 000000
        • @TwistedWhisper 已更正
        【解决方案8】:

        这个怎么样?

        $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

        【讨论】:

          【解决方案9】:

          这对我有用,是一个简单的三线:

          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

          【讨论】:

            【解决方案10】:

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

            以下函数返回一个初始化为本地时区的 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()));
            }
            

            【讨论】:

              【解决方案11】:

              试试这个,它会显示微秒:

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

              【讨论】:

              • 相当优雅,我最喜欢这个解决方案。我做了一些测试,没有发现这个实现有任何问题。
              • 太好了,有什么优雅的东西可以转换回微时间吗?
              • 由于无论如何您都将微秒连接到格式化字符串上,因此您不需要 DateTime 对象:date('Y-m-d H:i:s.'.$micro,$t) 是所需的字符串(或等效的 date('Y-m-d H:i:s.',$t).$micro
              【解决方案12】:

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

              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
              

              更新:不推荐(参见 cmets)

              【讨论】:

              • 坏主意。首先获取 microtime(使用 microtime(true) 而不是将其转换为浮点数)。使用小数部分(微时间)并将整数部分作为第二个参数传递给 date()。否则,您将面临不匹配的风险。
              • @Encoderer:错配风险是什么意思?你能举个例子吗?
              • 假设实际时间是2012-08-12 13:53:30.9999。您对 date() 的调用返回“2012-08-12 13:53:30”。您随后对 microtime() 的调用(万分之一秒后)返回 00(两次调用之间的第二次标记)。虽然这是一个边缘情况,但它仍然是您的代码将被破坏的情况。如果我使用此代码创建通过我的系统传输的事件的审核日志,那么您只是更改了历史记录。这里的正确答案是对 microtime(true) 进行一次调用并使用该结果。
              • oneliner: date("Y-m-d H:i:s.", $m = microtime(1)) 。 str_pad(地板(fmod($m, 1)*1e+6), 6, "0", STR_PAD_LEFT);
              【解决方案13】:

              Luckycomment 和这个feature request in the PHP bug database 工作,我使用这样的东西:

              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
              

              【讨论】:

                【解决方案14】:

                date('u') 仅从 PHP 5.2 开始受支持。您的 PHP 可能较旧!

                【讨论】:

                  【解决方案15】:

                  strtotime() 接受的格式的字符串 行得通!

                  【讨论】:

                    【解决方案16】:

                    date_create

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

                    strtotime

                    time:要解析的字符串,根据 GNU » Date Input Formats 语法。在 PHP 5.0.0 之前,微秒是不允许的,从 PHP 5.0.0 开始是允许但忽略的。

                    【讨论】:

                      【解决方案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”工作......:\

                      【讨论】:

                      • 嗨,(string)microtime(), 1, 8 怎么样?
                      • +1 用于使用否定的lookbehind 以不替换\u
                      • h2oooooooo,这不是正确的解决方案,它本身不支持转义的反斜杠,即“\\u”
                      【解决方案18】:

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

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

                      【讨论】:

                      • 通过单独调用,您有两个时间戳出现故障的可能性很小:例如调用日期在 1:29:22.999999 和 mircotime 在 1:29:23.000001。在我的服务器上,连续调用相隔大约 10 次。
                      • 尝试:list($usec, $sec) = explode(" ", microtime());返回日期 (date("Y-m-d\TH:i:s", $sec) . $usec;
                      • @eydelber : 由于 PHP date() 函数只接受整数时间戳,因此 u 格式字符仅在将 date_format() 函数与使用 date_create() 创建的基于用户的时间戳一起使用时才有用。
                      • 针对我正在寻找的简单问题的简单解决方案。 :)
                      • 这个公认的答案确实引发了竞争条件。 @Lucky 的解决方案是应对 PHP 怪异的更好解决方法。
                      猜你喜欢
                      • 1970-01-01
                      • 1970-01-01
                      • 2011-09-26
                      • 1970-01-01
                      • 1970-01-01
                      • 2014-10-07
                      • 1970-01-01
                      • 1970-01-01
                      • 2020-10-30
                      相关资源
                      最近更新 更多