DateTime::createFromFormat ('U.u', microtime (true)) вместо DateTime Object иногда возвращает false

<?php

/* Если ты кодер, то, братишка, я тебе покушать принёс. Забирай вот этот вариант, который всегда возвращает DateTime Object и продолжай кодить, а дальнейшую рефлексию читать не обязательно: */

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

/* Итак, сабж — не хочу повторять этот кусок вредоносного кода, который легко находится на stackoverflow по тематическим запросам в числе полезных ответов. Но почему вместо DateTime Object иногда получается false? Казалось бы, имеем функцию microtime, которая с параметром true возвращает float-значение: */

echo microtime(true);
# 1660905894.429

/* И есть метод createFromFormat, который с указанием формата 'U.u' должен корректно распознать переданное значение «1660905894.429» и создать дату: */

print_r(DateTime::createFromFormat('U.u'1660905894.429));
# DateTime Object
# (
# [date] => 2022-08-19 10:44:54.429000
# [timezone_type] => 1
# [timezone] => +00:00
# )

/* Но в 0.0003% случаев вместо объекта DateTime мы получим логическое false.

«Что эти ваши 0.0003% — вероятность этого ничтожно мала!» — Скажет гуманитарий и покинет чат кодеров. А специально для кодеров я написал пример, демонстрирующий, что даже такие редкие события могут приводить к 3 ошибкам в секунду! Каждую секунду! */

$k 0$e 0$max_delta 0$min_delta 1;
$t0 microtime(true);
while (
true) {
    
$k++;
    
$t microtime(true);
    
$date DateTime::createFromFormat('U.u'$t);
    if (!
$date) {
        
$e++;
        
$delta $t - (int)$t;
        
$min_delta min([$delta$min_delta]);
        
$max_delta max([$delta$max_delta]);
        
print_r([
            
'delta'             => $delta,
            
'min delta'         => $min_delta,
            
'max delta'         => $max_delta,
            
'e/k'               => "$e / $k",
            
'probability'       => ($e/$k*100).' %',
            
'errors per second' => $e/($t-$t0),
            
'time'              => $t,
            
'error'             => DateTime::getLastErrors(),
            
'alter'             => DateTime::createFromFormat('U'$t),
        ]);
    }
}
Array
(
    [delta] => 4.4107437133789E-5
    [min delta] => 6.9141387939453E-6
    [max delta] => 0.99995803833008
    [e/k] => 369 / 116160492
    [probability] => 0.00031766394377875 %
    [errors per second] => 2.2731272386197
    [time] => 1660905794
    [error] => Array
        (
            [warning_count] => 0
            [warnings] => Array
                (
                )

            [error_count] => 1
            [errors] => Array
                (
                    [10] => Data missing
                )

        )

    [alter] => DateTime Object
        (
            [date] => 2022-08-19 10:43:14.000000
            [timezone_type] => 1
            [timezone] => +00:00
        )
)
^C
[DateTime@createFromFormat]# php8.2 ./run.php 

/* В этом примере видно, что во время выполнения скрипта возникают ошибки со скоростью 2.27 штук в секунду (на момент старта было около 3-х, но со временем производительность падает). Возникают они на таких значениях времени, полученных от функции microtime, когда дробная часть времени больше 0.99995006 или меньше 0.00004994 — т.е близка к целому числу настолько, что получается число без точки: значение типа float имеет точность 14 цифр, 10 цифр занимает целая часть, потому на дробную часть остаётся 4 цифры. Потому вместо точного времени 1660905794.00004994 microtime вернёт 1660905794, а вместо 1660905794.99995006 вернет 1660905795 — число без точки, которое не подходит под формат 'U.u', который ожидает метод createFromFormat.

Когда важна точность, а ошибки неприемлемы (у кодера это примерно точно всегда), надо использовать такой вариант: */

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

/* Этот вариант я крутил в бесконечном цикле больше 0.5 миллиарда раз, и ни одной ошибки не возникло, хотя были итерации с 000000, и 999999 в дробной части microtime. */

/* P.S. Пришу на этот сайт, будто чужой забор обоссываю: редко и по особой нужде, и украдкой, чтобы процесс и результат по возможности никто не увидел. Ж) */

Запись опубликована в рубрике Web-мастеринг с метками . Короткая ссылка для добавления в закладки: DateTime::createFromFormat ('U.u', microtime (true)) вместо DateTime Object иногда возвращает false.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *