Микросекунды PHP DateTime всегда возвращают 0

этот код всегда возвращает 0 в PHP 5.2.5 в течение микросекунд:

<?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

Есть идеи?

Разве дата ("Y-m-d \ TH: i: s.u") не имеет меньше накладных расходов?

UnkwnTech 04.10.2008 04:37

Может тебе слишком повезло;)

albertein 04.10.2008 04:37

@Unkwntech: date () поддерживает только целые числа. Руководство направляет вас к date_format / DateTime :: format для использования 'u'.

Jonathan Lonowski 04.10.2008 04:41

@Jonathan Lonowski: да, я использую DateTime :: format

eydelber 04.10.2008 04:51

Из us.php.net/date: Примечание. Поскольку эта функция принимает только целочисленные метки времени, символ формата u полезен только при использовании функции date_format () с пользовательскими метками времени, созданными с помощью date_create (). Означает ли это, что мне нужно вручную создать его с помощью microtime ()?

eydelber 04.10.2008 04:56

Это смешно. Пришел сюда с той же проблемой, что и OP. Какой беспорядок.

ken 15.11.2011 22:23

Действительно, смешно ... 2014 год.

Adrian Günter 23.01.2014 23:43

можно предположить, что в 2015 году будет так же

user652649 27.10.2014 17:41

Казалось бы, user652649 был прав ...

TMH 13.07.2015 15:01

мы можем увидеть то же самое в 2016 году

linagee 10.01.2017 21:35

Обратите внимание, что PHP 7 (я пробовал только PHP 7.1) по умолчанию добавляет микросекунды, так что проблем больше нет.

Michaël Perrin 11.07.2017 11:03
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
71
11
48 426
18
Перейти к ответу Данный вопрос помечен как решенный

Ответы 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 нас.

Lucky 07.08.2009 02:18

попробуйте: list ($ usec, $ sec) = explode ("", microtime ()); дата возврата (date ("Y-m-d \ TH: i: s", $ sec). $ usec;

Lucky 07.08.2009 02:27

@eydelber: поскольку функция PHP date () принимает только целочисленные метки времени, символ формата u полезен только при использовании функции date_format () с пользовательскими метками времени, созданными с помощью date_create ().

AlexV 13.07.2011 00:01

Хорошее простое решение той простой проблемы, которую я искал. :)

YOMorales 16.07.2013 02:05

Этот принятый ответ действительно вызывает состояние гонки. Решение @ Lucky - гораздо лучший способ справиться со странностями PHP.

e2-e4 16.07.2014 11:54

@ К счастью, ваш код там неправильно отформатирован и добавляет дополнительный ноль к секундам, см. Мой ответ для исправлений. :) stackoverflow.com/a/38334226/614616

hozza 12.07.2016 19:53

Эта функция вытащена из 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?

eydelber 04.10.2008 07:22

+1 за использование отрицательного просмотра назад, чтобы не заменять \u.

h2ooooooo 18.07.2014 21:57

h2ooooooo, в любом случае это неправильное решение, оно не поддерживает экранированную обратную косую черту, т.е. "\\ u"

Misanthrope 30.10.2014 10:15

date_create

time: String in a format accepted by strtotime(), defaults to "now".

strtotime

time: The string to parse, according to the GNU » Date Input Formats syntax. Before PHP 5.0.0, microseconds weren't allowed in the time, since PHP 5.0.0 they are allowed but ignored.

Строка в формате, принятом strtotime () Работает!

date ('u') поддерживается только с PHP 5.2. Ваш PHP может быть старше!

Работая с комментарийУдачливый и этим запрос функции в базе данных ошибок PHP, я использую что-то вроде этого:

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

Попробуйте это, и он покажет микросекунды:

$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 26.06.2013 16:04

Отлично, есть ли что-нибудь столь же элегантное, чтобы преобразовать обратно в микровремени?

malhal 14.11.2014 20:21

Поскольку вы все равно объединяете микросекунды в форматированную строку, вам не нужен объект DateTime: date('Y-m-d H:i:s.'.$micro,$t) - это желаемая строка (или эквивалентно date('Y-m-d H:i:s.',$t).$micro)

IMSoP 24.05.2016 14:19

Вы можете указать, что ваш ввод содержит микросекунды при создании объекта 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)))->format(...

hooblei 09.03.2012 07:49

Это также не сработает на PHP 5.3.2 (Ubuntu 10.04), который возвращает False, когда в качестве формата в этой функции используется 'U.u' (ошибка)

Martin Konecny 06.05.2012 23:42

К сожалению, для этого решения требуется PHP 5.3+.

Alex Yaroshevich 08.10.2013 21:41

Этот метод безопаснее принятого ответа:

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 (используйте microtime (true) вместо того, чтобы преобразовывать его как float). Используйте дробную часть (микровремя) и передайте целую часть в качестве 2-го параметра в date (). В противном случае вы рискуете несоответствием.

Shane H 01.08.2012 02:47

@Encoderer: Что вы имеете в виду под риском несоответствия? Не могли бы вы привести пример?

f.ardelian 12.08.2012 21:05

Предположим, что фактическое время - 2012-08-12 13: 53: 30.9999. Ваш вызов в date () возвращает "2012-08-12 13:53:30". Ваш последующий вызов microtime () через одну десятитысячную секунды вернет 00 (вторая отметка между двумя вызовами). Хотя это крайний случай, это все же обстоятельство, при котором ваш код будет сломан. Если я использую этот код для создания журнала аудита событий, проходящих через мою систему, вы только что изменили историю. Правильный ответ здесь - сделать один вызов microtime (true) и использовать этот результат.

Shane H 12.08.2012 23:57

oneliner: date ("Y-m-d H: i: s.", $ m = microtime (1)). str_pad (этаж (fmod ($ m, 1) * 1e + 6), 6, «0», STR_PAD_LEFT);

Alex Yaroshevich 08.10.2013 21:39

Внутри приложения, которое я пишу, мне необходимо установить / отобразить микровремени для объектов DateTime. Кажется, единственный способ заставить объект DateTime распознавать микросекунды - это инициализировать его временем в формате «ГГГГ-ММ-ДД ЧЧ: ММ: 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()));
}

Это сработало для меня и представляет собой простой трехстрочный вариант:

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

Как насчет этого?

$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

В документации 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 29.08.2013 09:01

Если вы внимательно прочитаете случай OP, вы заметите, что DateTime::format() также генерирует 000000

Twisted Whisper 23.12.2013 13:06

@TwistedWhisper Исправлено

Manu Manjunath 23.12.2013 16:04

Это должно быть максимально гибким и точным:

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

\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 ()

Более явный и, возможно, более надежный. Решает ошибку, обнаруженную Хави.

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

Мне любопытно утверждение "в большинстве систем". Это проблема разрешения времени хоста или реализации DateTime в определенных системах?

cernio 20.09.2016 11:06

Я не понимаю: что это за "проблема точности с плавающей запятой"? Могу ли я безопасно использовать просто \DateTime::createFromFormat('U.u', microtime(true));?

Aerendir 29.12.2016 21:27

Я только что ответил на это в другом вопросе. По сути, microtime (true) потеряет точность, потому что хранится как float. На самом деле он даст вам не микросекунды, а десятые доли миллисекунд. stackoverflow.com/questions/41390315/…

Ryan 30.12.2016 08:12

Очень хороший момент при округлении числа с плавающей запятой и получении 00 в конце! Решение gettimeofday() сработало отлично!

Xavi Montero 22.03.2017 22:15

OOPS ERROR !! ПРЕДУПРЕЖДЕНИЕ!! - Я обнаружил ошибку. Случай: когда количество микросекунд меньше 1/10. Например: если {seconds:1498953412,microseconds:13}, то представлен UTC - 2017-07-01T23:56:52.130000Z, и он должен быть 2017-07-01T23:56:52.000013Z только потому, что он объединяет 1498953412 + . + 13, что дает: 1498953412.13, в то время как это должно приводить к 1498953412.000013 - это может быть проблематичным при регистрации и упорядоченных событиях, если они происходят слишком близко друг от друга.

Xavi Montero 02.07.2017 11:40

Думаю, я нашел более изящное решение проблемы с заполнением нулями. Но я также нашел более аккуратное решение, используя microtime () ...

Ryan 09.01.2018 02:56

Основываясь на комментарии Лаки, я написал простой способ хранить сообщения на сервере. Раньше я использовал хеши и приращения для получения уникальных имен файлов, но дата с микросекундами хорошо подходит для этого приложения.

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

В некоторых ответах используется несколько меток времени, что концептуально неверно, и могут возникнуть проблемы с перекрытием: секунды из 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);
}

Хорошо, я хотел бы прояснить это раз и навсегда.

Объяснение того, как отображать дату и время в формате ISO 8601 в PHP с Миллиseconds и микроseconds ...

Миллиseconds или 'ms' имеют 4 цифры после десятичной точки, например 0,1234. микроseconds или "µs" имеют 7 цифр после запятой. Объяснение долей / названий секунд здесь

Функция PHP date() в миллисекундах или микросекундах ведет себя не совсем так, как ожидалось, поскольку она будет только за исключением целого числа, как объяснено в документация даты php под символом формата 'u'.

На основе идеи комментария Лаки (здесь), но с исправленным синтаксисом PHP и правильной обработкой секунд форматирования (Код Лаки добавил неправильный лишний "0" после секунд)

Это также устраняет условия гонки и правильно форматирует секунды.

Дата PHP с Миллиseconds

Рабочий эквивалент 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 с микроseconds

Рабочий эквивалент 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

хороший ответ, но на самом деле миллисекунда соответствует 3 десятичным знакам, а микросекунда - 6, и ни один из них технически не действителен в истинном формате ISO 8601.

Jeff Puckett 31.05.2017 19:50

Также стоит отметить, что DateTime::createFromFormat("U.u", $t)даже потерпит неудачу (а не просто потеря точности), если $t имеет 7 (или более 6) цифр после десятичной точки, в PHP ...

Sz. 02.06.2020 18:35

Другие вопросы по теме