Blog webdeveloperski Patryk yarpo Jar

Obsluga daty i czasu w PHP: klasa `\DateTime`

Autor wiadomości Listopad 14, 2014

Niedawno zaczalem serie wpisow o dzialaniach na dacie i czasie w PHP. Po omowieniu podstawowej funkcji `date` przyszedl czas na cos bardziej wyrachowanego - klase `\DateTime`. Chcialbym powiedziec, ze rozwiazano wszystkie problemy. Niestety tak dobrze nie bedzie. Planuje pokazac nie tylko jak uzywac tej klasy, ale rowniez, jak uzywac jej bezpiecznie. Tradycyjnie przeciez cos bedzie dzialac nie do konca tak, jak bysmy oczekiwali.

Klasa \DateTime

Drugim (lepszym, choc nadal posiadajacym wady) sposobem jest tworzenie daty i czasu z wykorzystaniem klasy `\DateTime`. Uuu, jak milo - mamy "obiektowe programowanie". Super. No niby fajnie, szkoda jednak, ze troche skiepszczono i to. Ale o tym juz za chwile 🙂

Proste wykorzystanie:

echo (new \DateTime())->format('d M y H:i:s');

Zwroci date i czas w takim samym formacie jak poprzednio.

Uwaga! obiekt klasy `\DateTime` mozna takze otrzymac za pomoca statycznej metody `DateTime::createFromFormat`:

public static DateTime DateTime::createFromFormat(string $format , string $time [, DateTimeZone $timezone])

Odpowiedniki `\DateTime::now()` - jak tworzyc obiekt "teraz"

W funkcji `date`, podanie wartosci `null` jako drugi parametrzwraca "1 sty 1970". Zakladam, ze wynika to z rzutowania `null` do inta (null == 0). Tutaj tez mozemy wywolac z nullem. Co otrzymamy?:

echo (new \DateTime())->format('d M y H:i:s');
echo (new \DateTime(null))->format('d M y H:i:s');
echo (new \DateTime('NOW'))->format('d M y H:i:s');
echo (new \DateTime('now'))->format('d M y H:i:s');
echo (new \DateTime(date('d M y H:i:s')))->format('d M y H:i:s');
echo \DateTime::createFromFormat('d M y H:i:s', date('d M y H:i:s'))->format('d M y H:i:s');

Wszystkie powyzsze wywolania dadza ten sam wynik - aktualna date czasem. Wolalbym chyba jedna opcje, np. statyczne metode `::now()`.

Obsluga oczywistych bledow

Co powinno sie zdarzyc w konstruktorze obiektu `\DateTime`, gdy sprobujemy stworzyc date, ktora nie ma prawa istniec. Chocby "31 lutego 2010" (taki dzien nie istnial nigdy):

echo (new \DateTime('31-02-2010'))->format('d M y H:i:s');

Oczywiscie, trudno wrecz nie przewidziec, co sie stanie:

03 Mar 10 00:00:00

Tak, nie pomylilem sie. Mozecie sprawdzic na swoich serwerach. Bzdura kompletna.

- Ale to nie mam co liczyc na wyjatek?
- Nie
- To jak mam wykryc, ze jest blad?

Ano, jest na to sposob. Specjalna, statyczne metoda `\DateTime::getLastErrors`.

echo (new \DateTime('31-02-2010'))->format('d M y H:i:s');
print_r(\DateTime::getLastErrors());

W wyniku takiego kodu otrzymamy:

Array
(
    [warning_count] => 1
    [warnings] => Array
        (
            [11] => The parsed date was invalid
        )

    [error_count] => 0
    [errors] => Array
        (
        )
)

Jak widac, nie mozna dostac od razu informacji o tym, ze wystapil problem. Az sie prosi miec tu jakis wyjatek. Ale nie ma. Trzeba za kazdym razem sprawdzic, czy na pewno wszystko sie powiodlo. Mozna jednak troche swoj kod zabezpieczyc za pomoca funkcji `checkdate`.

Funkcja `checkdate`

Skoro tworzenie obiektu `\DateTime` przypomina losowo wybuchajacy granat, co nalezy zrobic, aby chocby prowizorycznie sie zabezpieczyc? Mozna dzialac po fakcie - odczytujac zawartosc `\DateTime::getLastErrors()` lub nim jeszcze stworzymy obiekt sprawdzic, czy nasze dane (np. pobrane z formularza) tworza prawidlowa date. Mozna to zrobic za pomoca wbudowanej funkcji `checkdate`:

bool checkdate( int $month, int $day, int $year )

Zwraca ona wartosc logiczna (`true` / `false`) dla sprawdzanej daty. Sprawdzmy, czy zadzialalaby na poprzednim przykladzie

if (true === checkdate(2, 31, 2010)) {
    echo "31 lutego 2010 roku jest poprawna data.";
} else {
    echo "31 lutego 2010 roku nie jest poprawna data.";
}

W wyniku takiego kodu otrzymamy na ekranie:

31 lutego 2010 roku nie jest poprawna data.

UWAGA - Hameryka!

Stereotypowy AmerykaninAmerykanie maja wiele zboczen. Jednym z nich jest kolejnosc przekazanych wyzej parametrow. Miesiac, dzien, rok. Tak - miesiac jest pierwszym parametrem. Nastepnie dzien i rok. Co zrobic, w przypadku jezyka takiego jak PHP, ktory byl rozwijany bez glebszego planu takie kwiatki mnie juz nie dziwia ;).

Podsumowanie

PHP i daty jakos sie nie lubia. A moze inaczej - bardzo wspolnie nie lubia programistow. Sa oczywiscie sposoby, aby jakos z ta data radzic. To nie wszystkie problemy, na jakie mozna napotkac, gdy uzywamy dat i czasu w PHP. Jednak nie chcialbym rozwlekac tego wpisu nadmiernie. Sadze, ze jeszcze kiedys wroce do tematu. A czy Wy macie jakies wlasne mniej badz bardziej przyjemne doswiadczenia z tym zagadnieniem?

Komentarze (2) Trackbacks (1)
  1. Daty to wgl bardzo nieprzyjemny temat – nie tylko w PHP. W JS mamy do czynienia z jeszcze większymi dziwactwami. Dla wyświetlania daty w różnych językach (w tym po polsku) stworzono… odrębny standard – ECMAScript Internationalization API. Do ładnego formatowania daty zatem służy odrębny obiekt. Z jednej strony to bardzo fajne rozwiązanie, z drugiej – IMO ciut przekombinowane.
    Trochu smutne, że w sumie MySQL wciąż udostępnia lepsze mechanizmy do obsługi dat niźli PHP i JS razem wzięte 😉

  2. No, daty sa wielkim bolem. Szczegolnie, jesli problemy z ich obsluga nie zostaly za wczasu zauwazone i trzeba na plynnie rozwijanym projekcie cos zmienic :/

    Tu jeszcze warto wspomniec, ze konwercja obiektu \DateTime do JSON daje taki wynik:
    {“date”:”2014-11-14 21:10:40.000000″,”timezone_type”:3,”timezone”:”UTC”}

    Do tego dodajmy, ze mozna natknac sie na problem roznych stref czasowych, dodatkowo z weba moze przychodzic (nieprawidlowo) data w postaci 3 pol (dzien, miesiac, rok) itp. itd. Ogolnie polecam dobrze sie przygotowac do pracy z datami i solidnie to przemyslec oraz zaprojektowac 😉


Leave a comment