Zjištění věku z data narození
Jak postupovat, pokud máme v databázi uloženo datum narození a chceme z něj zjistit věk? MySQL nabízí funkci DATEDIFF, která vrátí rozdíl dvou dat ve dnech, převod na roky je ovšem kvůli přestupným rokům značně pracný. Ze stejného důvodu se nedá použít ani rozdíl dvou timestampů vydělených 365*24*60*60. Další funkce, která je v MySQL k dispozici, je PERIOD_DIFF, ta ale nepočítá se dny.
Úkol dokonale řeší funkce TIMESTAMPDIFF, která s parametrem YEAR
udělá přesně to, co potřebujeme. K dispozici je od MySQL 5.0.
Ve starších verzích můžeme využít vlastnosti MySQL, že při matematických operacích s daty se data převedou na číslo zřetězením roků, měsíců a dnů. Pokud tedy provedeme CURDATE() - narozeni
, získáme jakési podivné číslo, jehož spodní celá část po vydělení 10 000 ovšem dá požadovaný věk. Stejný trik se dá použít i v PHP:
<?php
echo floor((date("Ymd") - date("Ymd", $narozeni)) / 10000);
?>
Diskuse
Jeremy88:
Já jsem si na to kdysi dosti pracně napsal v dvě fkc a teď vidím, že to jde o dost jednodušeji.
To jsem byl ale ještě "PHP-začínající", tak jsem si na tom alespoň procvičil základy (substr, strstr, strlen, ...).
2.2.2007 05:12:47
finc:
Pro hardcore vývojáře doporučuji cyklus a pomocí mktime přičítat, než se dostanu na stejnou hodnotu :D
Pokud zjišťuji věk, tak mi stačí počet let, ne? Na to myslím není třeba speciálních funkcí.
2.2.2007 09:36:29
Karaya1:
»Pokud zjišťuji věk, tak mi stačí počet let, ne? Na to myslím není třeba speciálních funkcí.«
To není tak úplně pravda, když se uživatel narodí třeba v červenci a ty to kontroluješ třeba v únoru, tak by Tvá metoda vrátila věk o jedna větší než být má.
2.2.2007 10:47:44
Já to taky tak dělám, akorát ještě kontroluji jestli uživatel neměl narozeniny. Pokud ne, tak tu jedničku zase odečtu.
2.2.2007 11:15:00
Takhle se měří věk u filmů a u koní. Film uvedený 31.12. je 1.1. už rok starý, stejně tak kobyla narozená 31.12.2004 je 1.1.2007 už tříletá. U lidí to tak jednoduché není.
2.2.2007 12:08:25
finc:
To máš pravdu, ale otázkou je, co budu považovat za nejpřesnější jednotku, jestli rok, měsíc, den, hodinu nebo minutu.
Holt někdo by byl rád mladý do poslední vteřiny :)
2.2.2007 12:56:59
Na planetě Zemi se obvykle ve věku počítá s přesností na dny. V hospodě ti nalejou, i když slavíš osmnáctiny a neptají se, jestli ses narodil ráno nebo večer ;-).
2.2.2007 13:10:10
Slash:
Nenalejou, až den po narozeninách, teda aspon by neměli. Tohle je jedna z mála věcí co si pamatuju ze základky :)
Ale jinak super funkce, díky za tip vždycky jsem přemýšlel jak to elegantně vypočítat.
3.2.2007 11:03:07
Ať tak nebo tak, na čas narození se tě neptají.
5.2.2007 12:39:16
pavel:
nevím kam jsi chodil do školy, ale dle zákona jsi plnoletý od 0:00 v den svých 18. narozenin. Takže ti nejspíš nechtěli nalejt protože už jsi byl namol =D
8.6.2013 18:38:15
Ehm?:
Jasně, proč nepoužít rovnou DATE_SUB(date,INTERVAL expr unit), když to jde dělat složitě
2.2.2007 10:05:10
Zkus se na chvíli zamyslet nad tím, co napíšeš do toho "expr unit". Nepotřebuješ od data něco odečíst (to by se dalo použít, pokud bys chtěl zjistit, jestli je uživatel starší než X let), potřebuješ rozdíl dvou dat.
2.2.2007 11:55:40
Orion:
SELECT DATE_FORMAT(FROM_DAYS(TO_DAYS(NOW())-TO_DAYS(dn)), '%Y')+0 AS age FROM people
dn = datum narozeni
2.2.2007 10:36:49
Problém je opět s přestupnými roky: Když pominu, že DATE_FORMAT(FROM_DAYS(365), '%Y') vrací 0, takže ve vzorci by mělo být ještě +1, tak dostaneme špatné výsledky třeba 9.10.2004 pro lidi narozené 10.10.2003 (vyjde, že už jim je rok, i když zatím není).
2.2.2007 12:05:53
Pavel Zbytovský:
S tímhle mi vyrůstá ještě další problém, jak vypsat uživatele, kteří budou mít nejblíže narozeniny. Napadlo mě seřadit si všechny narozené dnes a později (roky ignorujeme), jenže to tenhle dotaz nám nikdy nevrátí ty narozené dřív než dnes.
Napadlo mě jedině spojit dva dotazy UNIONem, ale nevím, jestli je to nejelegantnější.
2.2.2007 18:42:50
Nevím jestli jsem správně pochopil dotaz, ale mělo by stačit(tobd je počet dní zbývajících do narozenin a bd je samotné datum narození):
Oba dotazy jsou dost neefektivní, ale první je asi o 5 procent rychlejší:
SELECT IF((DATE_FORMAT(bd, "%j") - DATE_FORMAT(CURDATE(), "%j")) < 0, DATE_FORMAT(bd, "%j") - DATE_FORMAT(CURDATE(), "%j") + DATE_FORMAT(CONCAT(YEAR(CURDATE()), "-12-31"), "%j"), DATE_FORMAT(bd, "%j") - DATE_FORMAT(CURDATE(), "%j")) as tobd, bd FROM birthdays ORDER BY tobd ASC;
SELECT IF((DATE_FORMAT(bd, "%j") - DATE_FORMAT(CURDATE(), "%j")) < 0, DATE_FORMAT(bd, "%j") - DATE_FORMAT(CURDATE(), "%j") + IF((!(YEAR(CURDATE()) % 4) && (YEAR(CURDATE()) % 100)) || (!(YEAR(CURDATE()) % 400)), 366, 365), DATE_FORMAT(bd, "%j") - DATE_FORMAT(CURDATE(), "%j")) as tobd, bd FROM birthdays ORDER BY tobd ASC;
Testováno na MySQL 5.x
4.2.2007 16:03:47
Btw. možná existuje MySQL fce co vrátí počet dní přímo a mohla by být i ryhclejěí a přehlednější.
4.2.2007 16:07:44
Jedná se o funkci DAYOFYEAR.
5.2.2007 12:27:44
Problém je zase s přestupnými roky:
DAYOFYEAR('2003-03-01') = 60;
DAYOFYEAR('2004-03-01') = 61;
Kvůli tomu se můžou data seřadit špatně, navíc se u někoho může zdát, že už narozeniny měl, i když je má právě dnes.
Ze stejného důvodu je špatně i přičtení 365 nebo 366 - používá se vždy aktuální rok, ale od 1.3. by se měl použít už následující.
Proto bych použil raději něco takovéhoto:
SELECT DATEDIFF(IFNULL(CONVERT(CONCAT(YEAR(CURDATE()) + IF(RIGHT(bd, 5) >= RIGHT(CURDATE(), 5), 0, 1), RIGHT(bd, 6)), DATE), CONCAT(YEAR(CURDATE()) + IF(RIGHT(bd, 5) >= RIGHT(CURDATE(), 5), 0, 1), '-03-01')), CURDATE()) tobd FROM birthdays;
Popis:
Pokud už narozeniny letos člověk měl, tak se použije přiští rok (IF(RIGHT(bd, 5) >= RIGHT(CURDATE(), 5), 0, 1)).
CONVERT('nepřestupný_rok-02-29', DATE) vrátí NULL, v tom případě se použije '-03-01'.
Pokud ve sloupci mohou být hodnoty NULL, musí se výraz upravit třeba takhle: IF(bd IS NULL, NULL, výraz).
5.2.2007 13:23:32
Děkuji moc za ozřejmění a opravení, ale byl bych moc rád, kdyby jste prosím vysvětlil:
Citace:
Ze stejného důvodu je špatně i přičtení 365 nebo 366 - používá se vždy aktuální rok, ale od 1.3. by se měl použít už následující.
Píšete "ze stejného důvodu", ale já žádnou analogii s předchozím bohužel nevidím, přestože ten přechozí text chápu. Ještě jednou děkuji za Vaši přinosnou činnost a případně i za vševysvětlující příspěvek. :)
6.2.2007 16:52:38
Řekněme, že je 31.12.2003. Chci vědět, za jak dlouho má narozeniny člověk narozený 1.3.2003. Podle vašeho vzorce by to bylo:
+ DATE_FORMAT(bd, "%j") = 60
- DATE_FORMAT(CURDATE(), "%j") = 365
+ DATE_FORMAT(CONCAT(YEAR(CURDATE()), "-12-31"), "%j") = 365
Celkem 60, správně je ale 61 (rok 2004 je přestupný), protože v poslední části vzorce se musí použít už příští rok.
6.2.2007 17:06:21
Děkuji mnohokrát, už je mi vše naprosto jasné, ale přeci si jen mírně rýpnu. Co když určitá část národa narozená 2.29. slaví, když není přestupný rok, 2.28.? :P To by chtělo nějakou spešl tabulku pro ony jedince, kde by bylo id jedince a booleovská hodnota, kdy že to vlastně slaví. ;)
6.2.2007 19:29:23
No, moje babička mi taky vždycky volá den předem a to ani nejsem narozen 29.2. :-). 28.2. těm lidem o rok víc zkrátka ještě není, kdy to slaví, je podružné.
7.2.2007 10:41:58
SELECT jmeno, (YEAR(CURDATE())-YEAR(narozeni)) as vek, date_format(narozeni,'%e.%c.') as datum FROM lide WHERE right(narozeni,5) BETWEEN right(CURDATE(),5) AND right(DATE_ADD( CURDATE( ) , INTERVAL 7 DAY ),5) AND umrti = '0000-00-00' AND narozeni != '0000-00-00' ORDER BY right(narozeni,5) ASC
Vypisuje narozeniny na 7 dní dopředu, třeba ve formátu 5.7. Jméno (věk). Odzkoušeno na MySQL 4.x.
5.2.2007 11:34:05
Už jsem to sice viděl(díky moc za článek v češtině), ale stále nepřestávám obdivovat jak je to jednoduché a přitom funkční! I když možná by se do článku hodil nějaký nástin proč to tak vlastně funguje, chvilku mi trvalo než mi to docvaklo.
4.2.2007 05:16:32
mike:
no taky by sem prijal nejaky nastin... Mam tet stoho akorat tak chaos co pouzit
21.2.2007 21:22:54
mike:
Pri tomhle to:
<?php
echo floor((date("Ymd") - date("Ymd", $narozeni)) / 10000);
?>
by se seslo napsat co ma byt v promenne $narozeni... Jestli time() tak to nevim presne na sekundy kdy se clovek narodil, jenom rok, mesic, den
21.2.2007 21:28:09
Ze dne, měsíce a roku se dá timestamp vyrobit funkcí mktime().
21.2.2007 23:27:13
mike:
nemnelo to byt nahodou takhle:
<?php
echo floor((date("Ymd") - $narozeni) / 10000);
?>
?
protoze tet mi to uz funguje.
Tohle hazelo nesmysli:
<?php
echo floor((date("Ymd") - date("Ymd", $narozeni)) / 10000);
?>
21.2.2007 21:41:17
To záleží na tom, co je uloženo v proměnné $narozeni. Pokud UNIX timestamp, tak je správně druhá varianta, pokud Ymd, tak první.
21.2.2007 23:26:39
jarks:
Pěkné. Ocenil bych nějakou funkci, co by převedla na datum narození rodné číslo. Myslel jsem, že udělat takovou věc je velmi jednoduché, ale není.
2.3.2007 13:34:24
<?php
/** Zjištění data narození z rodného čísla
* @param string $rodne_cislo rodné číslo ve formátu rrmmdd/xxxx
* @return string datum ve formátu rrrr-mm-dd
* @copyright Jakub Vrána, http://php.vrana.cz
*/
function datum_rc($rodne_cislo) {
if (preg_match('~^([0-9]{2})([0-9]{2})([0-9]{2})/([0-9]{3,4})$~', $rodne_cislo, $match)) {
return (strlen($match[4]) < 4 || $match[1] >= 54 ? "19" : "20") . "$match[1]-" . sprintf("%02d", $match[2] % 50) . "-$match[3]";
}
return false;
}
?>
2.3.2007 14:09:39
Fanda Šidák:
Pěkná a užitečná funkce. Díky. :) Je rok 1954 nějak systémově závazný (např. nějakými pravidly pro rodná čísla)?
10.3.2007 20:10:33
Misha:
A co kdyz potrbuji jen odecist od dnesniho data jeden mesic?
4.3.2007 19:02:51
K tomu je nejlepší v MySQL použít:
CURDATE() - INTERVAL 1 MONTH
5.3.2007 11:10:37
Libor Skala:
Potřeboval bych něco podobného. Situace je následující. Do repertoáru kapely přidam novou skladbu a ta se zvýrazní jako novinka. Chtěl bych pro formátování použít CSS a když by datum přidíní splňovalo stáří měsíce, použilo by se odlišné formátování. pokud by to šlapalo pod PHP4.3.4 Díky
9.3.2007 18:59:10
stealth:
dik moc, prave taketo nieco som uz hodnu chvilu hladal ;-)
28.6.2009 16:21:23
Petr:
Ahoj, vymýšlím jak z data narození vypočítávat, jestli bude někdo mít narozeniny během 5 dnů. Je to teda makačka na bednu. Nevíte někdo jak na to? Jen podotýkám, že nemám data v MySQL a tudíž nemám k dispozici zmíněné funkce (data jsou v Paradox přes ODBC). Snažím se to tedy vyřešit pomocí php. Díky moc za tipy.
11.3.2007 12:53:04
To je jednoduché, vytvoříš si v PHP timestamp pomocí strtotime ('-5 days') a potom kontroluješ u všech záznamů, zda 'time'>=strtotime('-5 days'). Kdyžtak se pro jistotu koukni do manuálu PHP.
27.9.2008 10:05:47
Jrx:
Nějak mi to nefunguje, jak by mělo...
při použití:
echo ((date("Ymd") - $narozeni) / 10000)*365;
by měl koeficient přepočítat na dny, mám pravdu? je to dost nepřesné. Zřetelné to je, když $narozeni naplníte jen pár dní starým datem. Existuje nějaká funkce, která mi to v PHP spočítá na den přesně?
10.9.2007 05:06:28
Zdravím,
napsal jsem si svou vlastní funkci, měla by být (snad) přesná. Používám ji na svých stránkách. Testoval jsem různá data a ošetřil chyby (a že těch pokusů, i cílených, bylo dost). Můj PHP skript si můžete stáhnout na mých stránkách v sekci "Download" (
http://liborse.mechenice.net/download.php) či si ho vyzkoušet zde:
http://liborse.mechenice.net/vek/index.php. Je tam i kód, který stačí vložit právě do stránky a pak jen dál použít proměnnou "vek". Je možné, že ten skript není dokonalý či obsahuje chyby, takže pokud je najdete, budu rád, když mě kontaktujete (mail mám na stránkách). Doufám, že to někomu pomůže.
13.7.2008 14:01:33
nemas osetreni vkladani pismen a nul, mozna by se toho naslo vic
15.8.2008 09:10:49
Ahoj, a co když se ten člověk narodil třeba v roce 1945? To už je mimo rozsah čísel v PHP, ne?
27.9.2008 10:03:13
Ahoj,
rád bych do své stránky chtěl vložit informaci o věku pejska.
Představoval bych si to takhle:
Místo "Dnes je mi 74 dní" by se zobrazilo "Dnes mi je 2 měsíce a 13 dní.
Prosím Vás o napsání příkladu.
Všem dopředu MOC děkuji :)
Pája
10.10.2008 04:30:32
Jack:
DObrý den mám dotaz, nějak to nechápu...
Mám v databázi tabulku type dat tzn: 1990-15-02 takto datum
a potřeboval bych získat věk s jedním desetinným místem tzn např 19.2 let a vůbec nevím jak pořád mi to píše 39 let nebo chybu numeric nebo co :-( prosím prosím.. děkuji
16.9.2009 16:55:30
Eldik:
Ahoj, čistě teoreticky, zajímalo by mě, jestli někdy failne tohle, případně jaké to má nevýhody (opomenu-li vyšší spotřebu proměnných, na druhou stranu získám hezky upravitelný kód pro jakékoli lidsky vkládané datum):
<?php
$zaznam = "1998-02-15"; // ukazka vstupu
$vek = 0;
if (!is_null($zaznam) and $zaznam != "") {
$poleNarozeni = explode("-",$zaznam);
$vek = date("Y") - $poleNarozeni[0];
if ($poleNarozeni[1] > date("m")) { // porovname mesice
$vek -= 1;
} elseif (($poleNarozeni[1] = date("m")) and ($poleNarozeni[2] >= date("d"))) { // porovname dny v mesici
$vek -= 1;
}
}
echo $vek;
?>
24.2.2011 01:46:33
ELDIK - toto je zatial co pozeram super, uz som vyskusal asi 5 kodov a ziadny nebol na 100%, divne je ze ani oficialna funkcia v php - DetermineAgeFromDOB nefunguje
dik
14.6.2011 11:26:19