DateTime с микросекундами

В моем коде я использую объекты DateTime для управления датами, а затем конвертирую их в метку времени, чтобы сохранить их в некоторых файлах JSON.

По некоторым причинам я хочу иметь то же самое, что и DateTime (или что-то близкое), но с точностью до микросекунды (что я бы преобразовал в float при вставке внутри файлов JSON).

Мой вопрос: есть ли объект PHP, который похож на DateTime, но может обрабатывать микросекунды тоже?

Цель состоит в том, чтобы иметь возможность манипулировать микротимами с объектами.

В документации date() есть что-то, что указывает на то, что DateTime может быть создано с помощью микросекунд, но я не смог найти как.

u Microseconds (добавлено в PHP 5.2.2). Обратите внимание, что date() всегда будет сгенерируйте 000000, так как он принимает целочисленный параметр, тогда как DateTime:: format() поддерживает микросекунды, если была создана DateTime с микросекундами.

Я попытался установить временную метку объекта DateTime с плавающим значением (microtime(true)), но он не работает (я думаю, что он преобразует метку времени в int, вызывая потерю микросекунд).

Вот как я пробовал

$dt = new DateTime();
$dt->setTimestamp(3.4); // I replaced 3.4 by microtime(true), this is just to give an example
var_dump($dt);
var_dump($dt->format('u'));

.4 не учитывается, как вы можете видеть здесь (хотя мы можем использовать формат u, который соответствует микросекундам).

object(DateTime)[1]
  public 'date' => string '1970-01-01 01:00:03' (length=19)
  public 'timezone_type' => int 3
  public 'timezone' => string 'Europe/Berlin' (length=13)

string '000000' (length=6)

EDIT: я видел этот код, который позволяет добавлять микросекунды к DateTime, но мне нужно будет применить много изменений к microtime перед созданием DateTime. Поскольку я буду использовать это много, я хочу сделать как можно меньше модификаций микрочаса, прежде чем получить "объект microtime".

$d = new DateTime("15-07-2014 18:30:00.111111");

Ответ 1

/!\ РЕДАКТИРОВАТЬ /!\

Сейчас я использую https://github.com/briannesbitt/Carbon, остальная часть этого ответа только здесь по историческим причинам.

КОНЕЦ РЕДАКТИРОВАНИЯ

Я решил расширить класс DateTime используя советы, которые вы мне дали.

Конструктор берет значение с плавающей запятой (из microtime) или ничего (в этом случае он будет инициализирован с текущей "микровременной меткой"). Я также переопределил 2 важные функции: setTimestamp и getTimestamp.

К сожалению, я не смог решить проблему с выступлениями, хотя и не так медленно, как я думал.

Здесь весь класс:

<?php
class MicroDateTime extends DateTime
{
    public $microseconds = 0;

    public function __construct($time = 'now')
    {
        if ($time == 'now')
            $time = microtime(true);

        if (is_float($time + 0)) // "+ 0" implicitly converts $time to a numeric value
        {
            list($ts, $ms) = explode('.', $time);
            parent::__construct(date('Y-m-d H:i:s.', $ts).$ms);
            $this->microseconds = $time - (int)$time;
        }
        else
            throw new Exception('Incorrect value for time "'.print_r($time, true).'"');
    }

    public function setTimestamp($timestamp)
    {
        parent::setTimestamp($timestamp);
        $this->microseconds = $timestamp - (int)$timestamp;
    }

    public function getTimestamp()
    {
        return parent::getTimestamp() + $this->microseconds;
    }
}

Ответ 2

Здесь очень простой метод создания объекта DateTime, который включает в себя microtime.

Я не углубился в этот вопрос слишком глубоко, поэтому, если я что-то пропустил, я прошу прощения, но надеюсь, что вы найдете это полезным.

$date = DateTime::createFromFormat('U.u', microtime(TRUE));
var_dump($date->format('Y-m-d H:i:s.u')); 

Я проверил это и попробовал другие способы сделать эту работу логичной, но это был единственный метод, который работал. Однако была проблема, это возвращало правильную часть времени, но не правильную часть дня (из-за времени UTC наиболее вероятно) Вот что я сделал (все еще кажется более простым ИМХО):

$dateObj = DateTime::createFromFormat('U.u', microtime(TRUE));
$dateObj->setTimeZone(new DateTimeZone('America/Denver'));
var_dump($dateObj->format('Y-m-d H:i:s:u'));

Вот рабочий пример: http://sandbox.onlinephpfunctions.com/code/66f20107d4adf87c90b5c8c914393d4edef180a2

ОБНОВИТЬ
Как отмечено в комментариях, начиная с PHP 7.1, метод, рекомендованный Planplan, по-видимому, превосходит метод, показанный выше.

Итак, снова для PHP 7.1 и более поздних версий может быть лучше использовать приведенный ниже код вместо вышеупомянутого:

$dateObj = DateTime::createFromFormat('0.u00 U', microtime());
$dateObj->setTimeZone(new DateTimeZone('America/Denver'));
var_dump($dateObj->format('Y-m-d H:i:s:u'));

Обратите внимание, что вышесказанное работает только для PHP версии 7.1 и выше. Предыдущие версии PHP будут возвращать 0 вместо микровремени, поэтому теряют все данные микровремени.

Вот обновленная песочница, показывающая оба: http://sandbox.onlinephpfunctions.com/code/a88522835fdad4ae928d023a44b721e392a3295e

ПРИМЕЧАНИЕ: при тестировании вышеупомянутой песочницы я никогда не видел сбой микротайма (ИСТИНА), который Планпол упомянул, что он испытал. Однако обновленный метод, по-видимому, фиксирует более высокий уровень точности, как это было предложено KristopherWindsor.

Ответ 3

Глядя на ответ в руководстве PHP DateTime:

DateTime не поддерживает разделенные секунды (микросекунды или миллисекунды и т.д.)Я не знаю, почему это не задокументировано. Конструктор класса примет их без жалобы, но они будут отброшены. Кажется, что нет способа взять строку типа "2012-07-08 11:14: 15.638276" и сохранить ее в объективной форме в полном объеме.

Таким образом, вы не можете выполнять математику по дате в двух строках, например:

<?php
$d1=new DateTime("2012-07-08 11:14:15.638276");
$d2=new DateTime("2012-07-08 11:14:15.889342");
$diff=$d2->diff($d1);
print_r( $diff ) ;

/* returns:

DateInterval Object
(
    [y] => 0
    [m] => 0
    [d] => 0
    [h] => 0
    [i] => 0
    [s] => 0
    [invert] => 0
    [days] => 0
)

*/
?>

Вы вернетесь 0, когда на самом деле хотите получить 0.251066 секунд.


Однако, отвечая от здесь:

$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 "u" is microseconds (1 seconds = 1000000 µs).

Ссылка dateTime() на php.net: http://php.net/manual/en/datetime.construct.php#

Ответ 4

Существует несколько вариантов. Но, как уже сказал Бен, я постараюсь дать вам другое решение.

Если вы предоставили более подробную информацию о том, какие вычисления вы хотите сделать, это может быть изменено далее.

$time =microtime(true);
$micro_time=sprintf("%06d",($time - floor($time)) * 1000000);
$date=new DateTime( date('Y-m-d H:i:s.'.$micro_time,$time) );
print "Date with microseconds :<br> ".$date->format("Y-m-d H:i:s.u");

или

$time =microtime(true);
var_dump($time);

$micro_time=sprintf("%06d",($time - floor($time)) * 1000000);
$date=new DateTime( date('Y-m-d H:i:s.'.$micro_time,$time) );
print "Date with microseconds :<br> ".$date->format("Y-m-d H:i:s.u");

или

list($ts,$ms) = explode(".",microtime(true));
$dt = new DateTime(date("Y-m-d H:i:s.",$ts).$ms);
echo $dt->format("Y-m-d H:i:s.u");

или

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

Ответ 5

/*
 * More or less standard complete example. Tested.
 */
  private static function utime($format, $utime = null, $timezone = null) {
    if(!$utime) {
      // microtime(true) had too fiew digits of microsecconds
      $time_arr = explode( " ", microtime( false ) );
      $utime = $time_arr[1] . substr( $time_arr[0], 1, 7 );
    }
    if(!$timezone) {
      $timezone = date_default_timezone_get();
    }
    $date_time_zone = timezone_open( $timezone );
    //date_create_from_format( "U.u", $utime ) - had a bug with 3-rd parameter
    $date_time = date_create_from_format( "U.u", $utime );
    date_timezone_set( $date_time, $date_time_zone );
    $timestr = date_format( $date_time, $format );
    return $timestr;
  }

Ответ 6

Поскольку я решил свою проблему, я хочу поделиться ею с Вами. Php71+ имеют точность в микросекунды, если вы хотите преобразовать его в нано-точность, просто умножьте его на 1000 (10 ^ 3).

$nsTimestamp = (int) (new \DateTime())->getTimestamp() * 1000