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
Ответ 7
$micro_seconds = microtime(false) * 1000000;
echo date('Y-m-d H:i:s.'. floor($micro_seconds));
больше о дате: https://www.php.net/manual/en/function.date.php
больше о microtime: https://www.php.net/manual/en/function.microtime.php