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 761x (3.26 kB)
Aplikace je včetně zdrojových kódů v jazyce PHP