Lekce 3 - Dokončení knihovny DateUtils v PHP
V minulé lekci, Knihovna DateUtils pro český datum a čas v PHP, jsme rozpracovali jednoduchou utility třídu pro práci s datem a časem.
Dnes do knihovny DateUtils naprogramujeme formátování
"hezkého času" a také se dostaneme k parsovací metodě.
Hezké datum a čas
Nevím, jestli má tato podoba data a času nějaký název, ale já jsem si
metody implementoval jako prettyDateTime() a
prettyDate(). Pokud je datum dnes, včera nebo zítra, vypíše se
slovy, tedy např. Dnes. Pokud je někdy tento rok, vypíše
se měsíc slovy, např. 2.ledna. Pokud je rok jiný než
současný, vypíše se celé datum čísly, např. 2.1.2013.
Případně se za hodnotu doplní ještě čas. Jistě uznáte, že toto je pro
uživatele mnohem příjemnější, než hromada čísel. A s knihovnou to máte
během okamžiku použité.
Nejprve si vytvořme privátní metodu, která nám převede nastavenou
instanci DateTime na hezké datum:
private static function getPrettyDate(DateTime $dateTime): string { $now = new DateTime(); if ($dateTime->format('Y') != $now->format('Y')) return $dateTime->format('j.n.Y'); $dayMonth = $dateTime->format('d-m'); if ($dayMonth == $now->format('d-m')) return "Dnes"; $now->modify('-1 DAY'); if ($dayMonth == $now->format('d-m')) return "Včera"; $now->modify('+2 DAYS'); if ($dayMonth == $now->format('d-m')) return "Zítra"; return $dateTime->format('j.') . self::$months[$dateTime->format('n') - 1]; }
V metodě si uložíme současné datum a pokud se roky současného a
zadaného data různí, vrátíme číselně zapsané datum i s rokem. Pokud
jsou den a měsíc stejné, vrátíme textový řetězec
Dnes. Následně měníme současné datum a kontrolujeme,
zda není včera nebo zítra. Jakmile vyčerpáme všechny možnosti, vrátíme
den a měsíc slovy.
Konečně přidáme 2 veřejné metody, které nám datum naformátují do požadované podoby:
public static function prettyDate(string $date): string { return self::getPrettyDate(self::getDateTime($date)); } public static function prettyDateTime(string $date): string { $dateTime = self::getDateTime($date); return self::getPrettyDate($dateTime) . $dateTime->format(' H:i:s'); }
Vše můžeme vyzkoušet převedením různých dat v různých formátech:
require_once('Utility/DateUtils.php'); echo(DateUtils::prettyDate(1376906152) . '<br />'); echo(DateUtils::prettyDateTime('2014-02-23 10:50') . '<br />'); echo(DateUtils::prettyDateTime('2014-02-22 10:50') . '<br />'); echo(DateUtils::prettyDate('24.2.2014') . '<br />'); echo(DateUtils::prettyDate('2014-02-01') . '<br />'); echo(DateUtils::prettyDate('2013/02/20') . '<br />');
Výsledek za předpokladu, že je dnes 23.2.2014:
Opět vidíme, jak naše metoda naformátuje naprosto cokoli na lidsky příjemně čitelnou hodnotu.
Parsování
Přejděme k parsovací metodě. Na vstupu ji dáme české datum (např. 15.1.2014) a formát, v jakém má být datum/čas zapsané. Metoda buď vyhodí výjimku nebo vrátí datum v DB formátu:
public static function parseDateTime(string $date, string $format = self::DATETIME_FORMAT): string { if (mb_substr_count($date, ':') == 1) $date .= ':00'; // Smaže mezery před nebo za separátory $a = array('/([\.\:\/])\s+/', '/\s+([\.\:\/])/', '/\s{2,}/'); $b = array('\1', '\1', ' '); $date = trim(preg_replace($a, $b, $date)); // Smaže nuly před čísly $a = array('/^0(\d+)/', '/([\.\/])0(\d+)/'); $b = array('\1', '\1\2'); $date = preg_replace($a, $b, $date); // Vytvoří instanci DateTime, která zkontroluje zda zadané datum existuje $dateTime = DateTime::createFromFormat($format, $date); $errors = DateTime::getLastErrors(); // Vyvolání chyby if ($errors['warning_count'] + $errors['error_count'] > 0) { if (in_array($format, self::$errorMessages)) throw new InvalidArgumentException(self::$errorMessages[$format]); else throw new InvalidArgumentException('Neplatná hodnota'); } // Návrat data v MySQL formátu return $dateTime->format(self::$formatDictionary[$format]); }
V metodě nejprve k řetězci s datem přidáme 00 sekund v
případě, že je v něm jedna dvojtečka (uživatel zadává čas a nezadal
vteřiny). Dále pomocí regulárních výrazů odstraníme bílé znaky před
separátory, kterými jsou : a .. Díky tomu projde
např. i datum 15.1. 2014 12:00, které se přeloží na
15.1.2014 12:00:00. Výsledný formát data a času je tedy vždy
bez mezer a se vteřinami.
Tento formát můžeme předat třídě DateTime, validaci data
provede za nás. Na internetu pod validací data naleznete zoufalá řešení,
která na datum volají funkci explode() a podobně, která jsou
vždy neúplná, zrovna kontrola rozsahů a zadání všech součástí data a
času je poměrně náročná. Nikdy neprogramujte sami něco, co je součástí
standardních knihoven jazyka! Nikdy to neuděláte tak kvalitně a to už jen
proto, že PHP je v céčku. A i kdyby ano, je to vyhozený
čas.
Instanci DateTime sdělíme formát a předáme ji naše datum.
Ona se ho pokusí naparsovat. Jakékoli chyby nám vrátí v poli pomocí metody
getLastErrors(). Pokud se zde nějaké objeví, vyhodíme výjimku
s hláškou podle formátu, v opačném případě vrátíme datum v
příslušném databázovém formátu. Výjimku můžete poté chytat někde
při zpracovávání údajů a zobrazit ji uživateli. Za tímto účelem by
bylo ideální vytvořit si nějakou svou.
Funkci si hned můžeme vyzkoušet:
$date = DateUtils::parseDateTime('24.2. 2014', DateUtils::DATE_FORMAT); echo($date . '<br />'); $dateTime = DateUtils::parseDateTime('24.2. 2014 10:30', DateUtils::DATETIME_FORMAT); echo($dateTime . '<br />'); $time = DateUtils::parseDateTime('10:30', DateUtils::TIME_FORMAT); echo($time . '<br />');
Výsledek:
Validace data
Pro úplnost si dodejme i metodu validDate(), která o daném
datu zjistí, zda je validní:
public static function validDate(string $date, string $format = self::DATETIME_FORMAT): bool { try { self::parseDateTime($date, $format); return true; } catch (InvalidArgumentException $e) {} return false; }
Metodu vyzkoušejme zadáním několika dat:
var_dump(DateUtils::validDate('24.2. 2014', DateUtils::DATETIME_FORMAT)); var_dump(DateUtils::validDate('24.2 14', DateUtils::DATE_FORMAT)); var_dump(DateUtils::validDate('29.2. 2014', DateUtils::DATE_FORMAT)); var_dump(DateUtils::validDate('29. 2. 2012', DateUtils::DATE_FORMAT)); var_dump(DateUtils::validDate('14:56', DateUtils::TIME_FORMAT)); var_dump(DateUtils::validDate('14:62', DateUtils::TIME_FORMAT));
Výsledek:
Vidíme, že prošla opravu jen validní data.
Databázová funkce NOW()
Jelikož MySQL neumí pří vkládání nového řádku u formátu
DateTime dosadit aktuální datum/čas, používám často ještě
následující metodu, která vrátí současné datum a čas v databázovém
formátu:
public static function dbNow() { $dateTime = new DateTime(); return $dateTime->format(self::DB_DATETIME_FORMAT); }
Můžete si o mě myslet, že jsem barbar, protože nepoužívám nativní
funkci NOW(), ale tuto pihu na kráse mi několikanásobně
vyváží výhody vložení řádku do databáze přímo z PHP pole, které mi
mimochodem přijde přímo z formuláře, dotaz se sám generuje a ušetří to
spoustu práce. Pokud jste pracovali s místním databázovým wrapperem nad
PDO, tak víte o čem mluvím. Pokud ne, dostaneme se k tomu u motivace k
formulářové knihovně.
Opět vyzkoušíme:
echo(DateUtils::dbNow());
Výsledek:
Okomentovanou knihovnu máte v příloze ke stažení, ať vám dělá radost

V následujícím cvičení, Řešené úlohy k 1.-3. lekci knihovny v PHP, si procvičíme nabyté zkušenosti z předchozích lekcí.
Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.
Stáhnout
Stažením následujícího souboru souhlasíš s licenčními podmínkami
Staženo 765x (3.26 kB)
Aplikace je včetně zdrojových kódů v jazyce PHP


David se informační technologie naučil na