Převod data z českého formátu
Školení, která pořádám
V databázi se datum ukládá obvykle ve tvaru yyyy-mm-dd
, v češtině se ale používá formát d.m.yyyy
. O tom, jak toto datum správně zobrazit, už jsem psal, tento článek ukazuje, jak datum převést zpátky na tvar použitelný v databázi.
Databáze MySQL si u kalendářního data poradí i s formátem yyyy-m-d
, který se z českého formátu dá získat jednoduchým převodem. Pokud se však datum převede na řetězec (třeba po aplikaci funkce GREATEST()
), tak se při porovnávání bez plného formátu neobejdeme. Jak ho získat?
<?php
// převod na formát yyyy-m-d
$datum_iso = preg_replace('~^([0-9]+)\\.([0-9]+)\\.([0-9]+)~', '\\3-\\2-\\1', $datum);
// převod na formát yyyy-mm-dd
if (preg_match('~^([0-9]+)\\.([0-9]+)\\.([0-9]+)$~', $datum, $match)) {
$datum_iso = sprintf("%d-%02d-%02d", $match[3], $match[2], $match[1]);
}
// trik
$datum_iso = vsprintf('%3$d-%2$02d-%1$02d', explode('.', $datum));
?>
Poslední způsob využívá méně známou funkci vsprintf, která se od sprintf liší v tom, že místo seznamu parametrů přijímá pole. To získáme funkcí explode. Pro otočení pořadí parametrů by se dala použít funkce array_reverse, příklad ale používá formátovací řetězec 1$
.
Ve většině případů bych nicméně spíše doporučoval obdobu druhého příkladu, která formát zároveň i zkontroluje.
Diskuse
Kcko:
<OT> Muzu vedet, proc v danem REGEXPU pouzivate dvojita zpetna lomitka pred teckou, kdyz staci jedno? ( \. => znak tecky, . => zastupny znak jakehokoliv znaku krome tusim whitespaces )
Diky za vysvetleni
BlackSUN:
Právě proto, aby se výraz kontroloval na český formát data s tečkami. Pokud by tam byla tečka jakožto "jeden libovolný znak", výraz by akceptoval i datum ve formátu d-m-yyyy, ale také třeba ddmmyyyy, což by pak nepřelouskala navazující funkce, protože pole by mělo jeden prvek a ne tři.
Prostě se na začátku definoval formát s tečkou, tak ji použijeme ve výrazu. Lze použít cokoliv nečíselného.
Kcko:
To samozrejme chapu.
Kdyz potrebuju vyjadrit napr znak dolaru v REGEXPU tak ho vyjadrim takto -> \$
Kdyz potrebuju znak tecky tak -> \.
Nechapu proc tam jsou 2x zpetna lomitka
Protože lomítka mají escape funkci v řetězci (v řetězci uzavřeném do apostrofů ' lze jen escapovat apostrof '\'') a je správné je zdvojovat i když někdy to není nutné. Např.: 'L\\A' je L\A
Kcko:
Aha nejvsiml jsem si apostrofu, ja je totiz v REGEXPECH nepouzivam, tak jsem byl lehce udiven. Zkusim priste vice premyslet. Diky !
Jen jedna maličkost na kterou se na webu často zapomíná: český formát datumu není dd.mm.rrrr, ale dd. mm. rrrr (jde o typografickou věc a nemá s PHP nic společného, ale třeba to někomu připomene základy typografie ;-) ).
joe:
nj, to by pak mohlo byt treba ...
'~(0[1-9]|[12][0-9]|3[01])[- /.]\s?(0[1-9]|1[012])[-
/.]\s?((?:19|20)\d\d)~'
ale treba seznameni s knihovnou zend_date by asi bylo zajimavejsi, nez objevovat znovu kolo.
Pokud se dobře pamatuji, jedná se o českou normu zápisu data, takže v české typografii se jen uplatňuje (jen pro přesnost).
a nestačilo by použít tohle?
$datum_iso = date("Y-m-d", strtotime("31.4.2005"));
m:
Nicmene v PHP nikdy nejde o rychlost.
Jakub Vrána :
Já bych se strotime() trochu bál s formátem d.m.yyyy používat, protože to není žádný mezinárodně uznávaný formát a nemám jistotu, že na nějakém systému se nevyloží třeba jako m.d.yyyy. Nicméně novější verze PHP mají vlastní knihovnu pro zpracování data, takže tam jakás takás jistota už je.
Bohumír Bednařík (BoboCop):
Nic proti typografii, ale kdo přiznejme si, že BFU českou typografii nedodržují. Rovněž vím, že by se za tečkami v datu měly dělat mezery, ale když píšu nějaké datum (zvláště v prostředí počítače), tak píšu ve formátu d.m.yyyy (případně dd.mm.yyyy - podle toho co je za den a měsíc). Takže výše uvedený příklad sice možná není typograficky správně, ale z uživatelského hlediska správný je, protože většina uživatelů to datum zapíše bez mezer.
dgx:
Většina asi ano, ale ne všichni. Já píšu s mezerama.
Tomáš:
Nezlobte se, ale to jako když bude většina lidí na vašem webu psát "ponďelý" a "vijymka", tak to prohlásíte za správné jenom proto, že vaši návštěvníci nedokončili ani čtvrtou třídu pomocné školy? To mi připadá jako poněkud podivná logika...
Andrew:
Nicméně když už chceme být přesní, tak není mezera jako mezera a v tomhle případě se používá poloviční. Klasická mezera z mezerníku je tedy stejně špatná jako psaní bez mezery a navíc blbě vypadá.
Nevermind:
"V databázi se datum ukládá obvykle ve tvaru yyyy-mm-dd"
Skoro určitě se datum takto v databázi neukládá :) Snad jedině, že by to byla nějaká zvláštní textová/xml databáze.
Kasparek:
Nedavno jsem resil podobny problem a to jak obecne prevadet datum z/do ruznych formatu a vysledkem je tato funkce. Lze jednoduse rozsirit a na prevod DATETIME. V 99% pripadu ji ale stejne pouzivam na prevod z YYYY-mm-dd do dd.mm.YYYY :)
<?php
function convert_date($datum, $inf, $outf)
{
$inf = str_replace('d', '(\d{2})', $inf);
$inf = str_replace('m', '(\d{2})', $inf);
$inf = str_replace('Y', '(\d{4})', $inf);
if(preg_match("|$inf|", $datum, $match))
{
$result = $outf;
$result = str_replace('Y', $match[1], $result);
$result = str_replace('m', $match[2], $result);
$result = str_replace('d', $match[3], $result);
return $result;
}
return false;
}
?>
Kasparek:
Jeste bych chtel podotknout ze $datum je retezec, datum naformatovane dle $inf (input format) "1900-01-01" pro $inf = "YYYY-mm-dd" a $outf (output format) muze byt napriklad vyse uvedene "dd.mm.YYYY"
kovar:
zdravim, zkošim tvojí funkci a furt mi neslape...:) a to to používám jak říkáš:)) nesedá mi tam proměná $match....he to napsané správně?
finc:
S timhle je stale velky problem. Nechapu jednu vec. Proc sakra v PHP neexistuje nejaka samostatna funkce na prevod datumu.
Navic, pokud nekdo ulozi do MySQL neco jako '0000-00-00' je to spatne. Dost casto se zapomina na to, ze pokud datum neni, ma byt "NULL" nikoli zmrseny retezec, ktery neni typu date.
Napsal jsem si na to dost silenou tridu, ktera mi datum parsuje. Problem totiz nastava jeste ve chvili, kdy potrebuji vytvorit pivotni tabulku, kde sloupce budou (mesice, tydny, dny, atd.). Pak je treba pouzit v GROUP BY DATE_FORMAT, ktery ma odlisnou masku od te v PHP. Tohle samotne je dost silene.
Nikdy se totiz datum presne validovat neda, uz jen z toho duvodu, ze uzivatele pokazde napisi datum uplne jinak :). Jedine co se mi trosku osvedcilo je, ze jsem jim datum nechal vybirat pres kalendar, kde mam jakz takz jistotu, ze z toho nevyleze kravina. Nikdy by ovsem nemelo vyjit '0000-00-00', coz je stejna chyba jako miti 32 dni ci 13 mesicu.
Ronnie:
"Navic, pokud nekdo ulozi do MySQL neco jako '0000-00-00' je to spatne."
Někdy to může být i užitečné. Když víš třeba rok a měsíc, ale dnem si nejsi jistý, tak jak to uděláš? Vložíš třeba 2006-12-00 a později doplníš za nuly konkrétní datum.
Jinak pokud nechceš ukládat nuly v datu, mělo by stačit zapnout mod NO_ZERO_IN_DATE a MySQL a nuly se ukládat nebudou.
finc:
Jinak funkci strtotime povazuji za stastnejsi reseni, nez regulari vyrazy. Uz jen z toho duvodu, ze clovek musi resit pocet dni v mesici, atd.
PHP + MySQL pro Date je stejne silene, jak PHP tak MySQL se k nejakemu Date chova naprosto svevolne a dovoluje ukladat snad jakoukoli kravinu.
Jakub Vrána :
Funkce pro práci s datem v PHP používají Unix timestamp, který práci s neplatnými daty neumožňuje z principu. V MySQL se neplatná data dají zakázat proměnnou ALLOW_INVALID_DATES. Tvůj nářek není tedy v tomto případě opodstatněný.
Ronnie:
Velmi hezky řeší práci s datem třída Zend_Date
<?php
Zend_Date::setOptions(array('format_type' => 'php'));
$date = new Zend_Date('1. 1. 2007');
//alternativně i
$date = new Zend_Date('1.1.2007');
echo $date->toString('Y-m-d'); //2007-01-01
?>
Pochopí mnoho různých formátů.
JFK:
Jestlize datum pouze vkladam do databaze, pouzivam na prevod vzdy SQL.
SET date = DATE_FORMAT('"17. 04. 2007"', '%d. %m. %y')
Jakub Vrána :
Funkce DATE_FORMAT ale přeci slouží k formátování data ve správném formátu a ne k jeho rozebrání do toho formátu.
JFK:
Po preceteni vaseho clanku, jsem me pocit ze se snazite pouze prevest datum, tedy formatovat pred ulozenim. Viz nadpis ... Převod data z českého formátu ... a pokracujete dale textem ... tento článek ukazuje, jak datum převést zpátky na tvar použitelný v databázi... . Proto jsem zminil funkci, ktera se snazi o totez.
Jakub Vrána :
Problém je v tom, že funkce se nesnaží o totéž, ale o něco zcela jiného - mě osobně tento konkrétní případ vrátí NULL.
JFK:
Mate pravdu ... nevsiml jsem si.
Je pravda, ze nektere jeji parametry nefunguji stejne pri SELECTu jako pri UPDATE.
funkcni priklad
UPDATE tb_xxx SET date = DATE_FORMAT('23.04.07', '%d.%m.%y')
pro INSERT neni funkcni vubec.
Pro INSERT pouzivam STR_TO_DATE
INSERT INTO tb_xxx (date) VALUES (STR_TO_DATE('23. 04. 2007','%d. %m. %Y')).
Pravdepodobne je lepsi pouzivat pouze STR_TO_DATE ...
aha:
a tak to je docela k nicemu ne?
proc neukazes priklad kamo?
url:
pise chybu:
Warning: vsprintf() [function.vsprintf]: Too few arguments in_
Jakub:
A nestačilo by třebas nahrát do databáze oba formáty datumu - jeden pro výstup, druhý pro porovnávání datumů v databázi.
$datum=date("d.m.Y H:i:s");
$table = Mysql_Query("insert into tabulka (datum_mysql, datum_ceske)
values (now(),'$datum')");
Jakub Vrána :
To bych nedoporučoval, protože jednak jde o duplicitu dat a jednak může dojít k nekonzistenci.
Petr:
Uprava pro pouziti s formatem DATETIME
preg_replace('~^([0-9]+)-0?([0-9]+)-0?([0-9]+) ([0-9]+)\\:([0-9]+)\\:([0-9]+)~', '\\3.\\2.\\1 \\4:\\5', $datum);
Vypise datum ve formatu "28.1.2012 13:32"
Pavel:
Zdravím. Mám formulář, kde se uvádí datum narození ("DatumNarozeni1") ve formátu dd/mm/yyyy. V jiné části formuláře se musí uvádět ale formát mm/dd/yyyy. Jak zajistit, aby se datum přepsal z ("DatumNarozeni1") ale v požadovaném formátu.
Díky
Diskuse je zrušena z důvodu spamu.