Как стать автором
Обновить

PHP: порт функции TO_DAYS() из MySQL

Время на прочтение3 мин
Количество просмотров825
Однажды мне понадобился аналог функции TO_DAYS() в PHP. Я, наивный, полагал что среди огромного набора функций обязательно должна быть реализующая TO_DAYS() из MySQL. Но поиски мои были тщетны. Я перерыл гугл, но так и не нашел нужную мне реализацию. Все что я нашел было из разряда

define(«SECONDS_PER_DAY», 86400);
$now = (int) (time() / SECONDS_PER_DAY);

Меня такой подход не устроил.
Во-первых, данное решение считало дни относительно начала эпохи UNIX, а в MySQL это от даты 0000-00-00, то есть числа мягко говоря не совпадали. Можно было, конечно, прибавить количество дней до начала эпохи UNIX, но это бы сильно сократило диапазон валидных дат.
Во-вторых не учитывалось то, что в некоторых днях 23 часа, а в некотоых 25 (при переходе на летнее и зимнее время).
Кроме того, по метке времени (timestamp) нельзя точно сказать какое сейчас число не зная часового пояса (хотя это не особая проблема).

Самым главным критерием было совпадение результатов выполнения функции с результатами TO_DAYS() из MySQL. Ну раз уж так, то я побрел искать эту функцию, в исходниках самой MySQL. С горем пополам, нашел функцию calc_daynr, которую вызывает функция Item_func_to_days::val_int() для подсчета этих самых дней.

После нехитрых преобразования код, написанный на C превратился в код на PHP. Я немного модернизировал входные параметры с учетом последних веяний работы с датой в PHP, и получил следующую функцию:

<?
    define 
(«YY_PART_YEAR»70); 
    
define («YY_MAGIC_BELOW»200);   
    
    function 
to_days(DateTime $date)
    {
        
$year $date->format(«Y»);
        
$month $date->format(«m»);
        
$day $date->format(«d»);
        
        if (
$year == && $month == && $day == 0)
        {
          return 
0;
        }
        if (
$year YY_MAGIC_BELOW)
        {
            if ((
$year $year+1900) < 1900+YY_PART_YEAR)
            {
                
$year+=100;
            }
        }
        
$delsum 365*$year+31*($month-1)+$day;
        if (
$month <= 2)
        {
            
$year--;
        }
        else
        {
            
$delsum -= floor(($month*4+23)/10);
        }
        
$temp floor((floor($year/100)+1)*3/4);
        return 
floor($delsum+$year/4-$temp);
    }
?>

Если вы думаете что я полностью понимаю этот код, то вы ошибаетесь. Чего стоит хотя бы эта сточка:

$delsum -= floor(($month*4+23)/10);

Но этого на данном этапе и не требуется, главное чтобы работало.
Я проверил на совпадение с TO_DAYS() с 1700 г. по 2300 г. — ни единой ошибки.
Так что если кому надо, пользуйте на здоровье.

P.S. Теперь надо то же самое сделать для FROM_DAYS(). Потом поделюсь, если будет чем :)
Теги:
Хабы:
Всего голосов 5: ↑4 и ↓1+3
Комментарии13

Публикации