Search
Write a publication
Pull to refresh

Как правильно определить возраст, имея timestamp

Здравствуйте, Хаброжители.
Хотел бы рассказать о недостатке одного способа определения возраста на основе даты рождения в timestamp.

Самый очевидный способ


$age = (int)((time() - $borndate) / 31556926); // 31556926 -- количество секунд в одном году

Действительно, очевиднее некуда: определяем разницу в секундах, разделяем на количество секунд в году и скругляем, присваивая тип integer. Казалось бы, всё правильно. Но давайте проверим это на практике.


Проверка правильности


Я родился 02 Мая 1996. Не надо быть профессором математики, чтоб посчитать, что в 2 Мая 2030 года мне должно исполнится 34 года. Но давайте убедимся в этом на практике.
$borndate = 830984400; // timestamp соответсвует 2-му Мая 1996 00:00:00 по Киевскому времени, дню когда я родился.
echo (int)((strtotime('5/2/2030 0:0:0') - $borndate) / 31556926); // я заменил time() на strtotime(), чтобы узнать, сколько мне будет в 2030 году.
// Выводит 33

Странно, не так ли? Давайте протестируем ещё раз, для того же дня, но на несколько часов позже.
$borndate = 830984400;
echo (int)((strtotime('5/2/2030 5:0:0') - $borndate) / 31556926); 
// Опять 33!

Почему так произошло? Почему вывело 33, а не 34, как положено? На самом деле всё проще, чем кажется.

В чем дело?


На самом деле, год не всегда содержит 31556926 секунд (а именно 365.242199 * 24 * 3600), т.к. должны учитываться высокосные года. То есть, каждые 4 года количество секунд будет отличаться, от предыдущих трех годов (кстати, высокосного года нет раз в сто лет, за исключением тех, которые делятся на 400. Например, 1800, 1900 не были высокосными, а 2000 — был). Эта казалось бы, небольшая погрешность, значительно влияет на результаты выполнения скрипта, приведеного выше, заставив меня отмечать День рождение на несколько часов позже. Нас это не совсем устраивает, попробуем исправить ситуацию.

Решение проблемы


$borndate = 830984400;
$date = strtotime('5/2/2030 0:0:0');
            if (date('m', $borndate) > date('m', $date) || date('m', $borndate) == date('m', $date) && date('d', $borndate) > date('d')) {
                return (date('Y', $date) - date('Y', $borndate) - 1); // если в текущем году я ещё не отмечал День рождение, вернуть значение на 1 меньше
            }
                return (date('Y', $date) - date('Y', $borndate));
// 34!

То, что надо! А тепер давайте подкоректируем скрипт, чтобы он возвращал текущий возраст, в чем и была изначальная функция скрипта.
$borndate = 830984400;
            if (date('m', $borndate) > date('m') || date('m', $borndate) == date('m') && date('d', $borndate ) > date('d')) {
                return (date('Y') - date('Y', $borndate ) - 1); 
            }
                return (date('Y') - date('Y', $borndate));
// 17


Заключение


Скрипт с правильным отображением возраста получился на несколько строчек больше, да и выполняется несколько дольше, но это куда лучше чем отображение неправильного значения с погрешность в несколько часов. Советы по оптимизации/критику/пожелания/замечания жду в коментариях.

Спасибо за внимание!
Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.