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.

Jakub Vrána, Dobře míněné rady, 16.4.2007, diskuse: 36 (nové: 0)

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

ikona MiSHAK:

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 !

Jakub Šulák:

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.

ikona MiSHAK:

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).

ikona kukulich:

a nestačilo by použít tohle?

$datum_iso = date("Y-m-d", strtotime("31.4.2005"));

ikona MiSHAK:

Ten trik bude rychlejší :)

m:

Nicmene v PHP nikdy nejde o rychlost.

ikona Jakub Vrána OpenID:

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.

ikona 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.

ikona 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.

ikona 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;
}
?>

ikona 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ě?

ikona 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.

ikona 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.

ikona Jakub Vrána OpenID:

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')

ikona Jakub Vrána OpenID:

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.

ikona Jakub Vrána OpenID:

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')");

ikona Jakub Vrána OpenID:

To bych nedoporučoval, protože jednak jde o duplicitu dat a jednak může dojít k nekonzistenci.

ikona 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"

Kith:

Uff

Vložit komentář

Používejte diakritiku. Vstup se chápe jako čistý text, ale URL budou převedeny na odkazy a PHP kód uzavřený do <?php ?> bude zvýrazněn. Pokud máte dotaz, který nesouvisí s článkem, zkuste raději diskusi o PHP, zde se odpovědi pravděpodobně nedočkáte.

Jméno: URL:

avatar © 2005-2018 Jakub Vrána. Publikované texty můžete přetiskovat pouze se svolením autora. Ukázky kódu smíte používat s uvedením autora a URL tohoto webu bez dalších omezení Creative Commons. Můžeme si tykat. Skripty předpokládají nastavení: magic_quotes_gpc=Off, magic_quotes_runtime=Off, error_reporting=E_ALL & ~E_NOTICE a očekávají předchozí zavolání mysql_set_charset. Skripty by měly být funkční v PHP >= 4.3 a PHP >= 5.0.