PHP posiada wiele funkcji, dzięki którym można przetwarzać zapis daty.

Podstawowym sposobem zapisu daty w systemie Unix i pochodnych jest tzw. timestamp. Jest to ilość sekund jaka upłynęła od 1.1.1970 (moment ten nazywany jest Epoch). Zapis taki daje najwięcej możliwości – łatwo go przetwarzać na inne zapisy, dodawać, odjemować odpowiednie przedziały czasu i zapisywać w bazach danych.

Sprawdzanie poprawności

Jedną z podstawowych spraw przy odbieraniu danych od użytkownika to sprawdzenie ich poprawności. Funkcja checkdate() przyjmuje 3 argumenty – odpowiedno miesiąc, dzień i rok (kolejność może się wydawać nielogiczna, lecz w takiej kolejności Amerykanie zapisuję datę), których poprawność ma być sprawdzony. Funkcja zwraca logiczną wartość TRUE jeśli data jest poprawna.

Funkcja ta sprawdza, czy rok ma wartości pomiędzy 1 a 32767, miesiąc między 1 a 12 a dzień od jeden do liczby zależnej od danego miesiąca. Uwzględniane są lata przestępne. W poniższym przykładzie sprawdzane są dane przekazane metodą GET. Dla wywołania test.php?dzien=12&miesiac=10&rok=1992 skrypt wyświetli „Data jest poprawna” a dla test.php?dzien=2&miesiac=14&rok=1980 – „Data jest niepoprawna”.

Przykład 12.1. Przykład skryptu sprawdzającego poprawność daty


<?php

if (checkdate( $_GET['miesiac'], $_GET['dzien'], $_GET['rok'])) {
    echo 'Data jest poprawna';
} else {
    echo 'Data jest niepoprawna';
}

?>

            

Pobieranie aktualnej daty

Aktualną datę można uzyskać tylko w formacie timestamp. Datę taką zwraca funkcja time(). Dokładniejszy czas można uzyskać dzięki funkcji microtime() – dane zwracane przez tą funkcję zapisane są nieco dziwnie, ponieważ zwracany jest string zawierający oddzielone spacją odpowiednio część mikrosekundową i sekundową, czyli „msec sec”.

Przykład 12.2. Funkcja zwracająca czas z mikrosekundami


<?php
function getmicrotime()
{
   list($usec, $sec) = explode(" ",microtime()); 
   return ((float)$usec + (float)$sec); 
} 
?>

            

Informacja ta może zostać użyta do sprawdzenia czasu wykonania danego skryptu.

Przykład 12.3. Liczenie czasu generowania strony


<?php

$start = getmicrotime();

for ($i = 0; $i < 10000; $i++) {
   $str = 'test';
}

$end = getmicrotime();

echo '<!-- strona wygenerowana w '.($end - $start).' sekund -->';

?>

            

W ten sposób można też sprawdzać wydajność różnych rozwiązań. Skrypty wykonywane są zazwyczaj tak szybko, że mikrosekundy to za duża jednostka. Ograniczenie to można obejść przez wykonywanie konkretnych instrukcji w pętli, 100 czy nawet 1000 razy, podobnie jak w powyższym przykładzie.

Funkcję konwertujące datę do podanego formatu jako parametr przyjmują timestamp daty do przekonwertowania. Parametr ten można jednak pominąć i zamiast określonej daty użyta zostanie aktualna data i czas. Pozwala to na uzyskanie aktualnego czasu w formacie innym niż timestamp.

Konwersja daty do formatu timestamp

Timestamp inny niż aktualny można uzyskać na różne sposoby, które trzeba wykorzystać odpowiednio do zastosowania.

Jeśli dostępna jest 'rozbita’ data, czyli w osobnych zmiennych dzień, miesiąc, rok itp., lub można do takiej sytuacji doprowadzić przez odpowiednie użycie funkcji explode() lub substr(), można użyć funkcji mktime(). Funkcja ta jako argumenty przyjmuje odpowiednio godzinę, minutę, sekundę, miesiąc, dzień, rok i opcjonalnie informację o „daylight saving”, czyli czasie letnim (wartość 1 oznacza czas letni, 0 w przeciwnym przypadku, lub -1, wartość domyślną, jeśli PHP ma samo zgadywać).

Przykład 12.4. Generowanie timestampu


<?php

$dzien   = 10;
$miesiac = 4;
$rok     = 2002;
$godzina = 12;
$minuta  = 32;
$sekunda = 0;

$ts = mktime($godzina, $minuta, $sekunda, $miesiac, $dzien, $rok);

?>

            

Jeśli data jest zapisana jako string, lecz w jednym z formatów rozpoznawanych przez PHP, string ten można przekonwertować do timestamp używając funkcji strtotime(). Data składa się z kilku elementów, oddzielonych „białymi znakami” (spacja, znak tabulacji). Białe znaki można pominąć, jeśli nie powoduje to żadnych wieloznaczności. Elementy daty to część kalendarzowa, część zegarowa, strefa czasowa, część wyłącznie liczbowa. Kolejność tych elementów nie ma znaczenia. Dwa inne elementy, dzień tygodnia i przesunięcie czasowe, zostaną opisane w dalszej części rozdziału.

Część kalendarzowa

Część ta określa konkretny dzień roku. Może być podana na jeden z wielu sposobów.


1970-09-17           # Zapis zgodny z ISO 8601.
70-9-17              # Domyślnie przyjmuje się bieżący wiek
70-09-17             # Wszystkie początkowe zera są ignorowane
9/17/72
24 September 1972
24 Sept 72           # Specjalny skrót słowa September
24 Sep 72            # Wszędzie dopuszczalne są trzyliterowe skróty
Sep 24, 1972
24-sep-72
24sep72

            

Dopuszczalne są angielskie nazwy miesięcy: `January’, `February’, `March’, `April’, `May’, `June’, `July’, `August’, `September’, `October’, `November’ `December’ oraz ich trzyliterowe skróty.

Część zegarowa

Dopuszczalne formaty:


<pre>
20:02:0
20:02
8:02pm
20:02-0500           # z podaniem strefy czasowej

            

Mówiąc ogólnie, podstawowy format to gg:mm:ss, przy czym część sekundową można pominąć. Jeśli na końcu dodany zostanie wskażnik am (rano – inna forma to a.m.) lub pm (po południu – p.m.), to część godzinowa ograniczona jest do zakresu 1-12. W takim zapisie północ to 12am, południe – 12pm; część minutowa może być pominęta.

Opcjonalnie, od razu po czasie może zostać podana strefa czasowa w formacie „zggmm”, gdzie „z” to odpowiedni znak + lub -, zależnie od strefy, gg to ilość godzin a mm ilość minut przesunięcia.

Strefa czasowa

Strefa czasowa może zostać określona tak samo jak w przypadku informacji „doklejonej” do części zegarowej.

Część liczbowa

Liczby w postaci rrrrmmdd, jeśli wcześniej nie było części kalendarzowej, traktowane są jako te części. Podobnie liczby w postaci ggmm jest traktowana jako część zegarowa, jeśli nie było takiej wcześniej.

Dokładny opis formatów rozpoznawanych przez funkcję strtotime() można znaleźć pod adresem http://www.gnu.org/software/tar/manual/html_chapter/tar_7.html

Formatowanie daty

PHP udostępnia kilka funkcji służących do zapisywania daty w preferowanym formacie. Podstawowa to date. Pierwszym jej argumentem jest ciąg tekstowy, który służy jako opis formatu. Każda litera wstawiona do tego ciągu zostanie zamieniona na pewien element daty. Na przykład litera 'Y’ zostanie zamieniona na rok w postaci czterocyfrowej. Pełną listę elementów formatu można znaleźć w opisie funkcji date().

Przykład 12.5. Przykład użycia funkcji date


<?php

// Zakładając, że dzisiaj jest 7 luty 2005, 22:31:44

$today = date("F j, Y, g:i a");                 // February 7, 2005, 22:31 pm
$today = date("m.d.y");                         // 07.02.05
$today = date("j, n, Y");                       // 7, 2, 2005
$today = date("Ymd");                           // 20050207
$today = date('h-i-s, j-m-y, it is w Day z ');  // 22-31-44, 7-02-05, 3128 3144 1 Monpm05 37
$today = date('\i\t \i\s \t\h\e jS \d\a\y.');   // It is the 7th day.
$today = date("D M j G:i:s T Y");               // Mon Feb 7 22:31:44 CET 2005
$today = date('H:m:s \m \t\o\ \m\i\e\s\i\a\c'); // 22:02:44 m to miesiac
$today = date("H:i:s");                         // 22:02:44
?>

            

Jak widać, większość liter ma swoje znaczenie, więc nie ma możliwości wstawienia zwykłego tekstu, oprócz zamieniania ich na sekwencje escape.

Domyślnie wyświetlana jest odpowiednio sformatowana data i czas bieżący. Można to zmienić podając opcjonalny, drugi parametr – czas w formacie timestamp. Wtedy format będzie dotyczył właśnie tego czasu.

Funkcja date wszelkie „tekstowe” wartości zwraca w języku angielskim. Istnieje jednak inna funkcja, strftime, która, podobnie jak funkcja date, służy do formatowania daty, jednak różni się trochę zachowaniem. Po pierwsze, elementy formatu (inne niż w funkcji date) są nie pojedyńczymi literami, ale zawsze poprzedzone są znakiem procenta, przez co można wstawiać do formatu zwykły tekst. Druga ważna różnica to uwzględnianie ustwień regionalnych systemu locale. Wystarczy ustawić odpowiednie locale (dla języka polskiego – pl_PL i strftime zacznie mówić po naszemu).

Przykład 12.6. Użycie funkcji strftime


<?php

setlocale(LC_TIME, "pl_PL");
echo strftime("%A");

?>

            

Powyższy program wyświetli:


poniedziałek

            

Tworzenie daty względnej do aktualnej (np. 'za 2 dni’)

W PHP niejako standardowym sposobem przechowywania informacji o dacie jest wspomniany wcześniej format timestamp. Modyfikacje takiego zapisu przeprowadza się przez dodanie lub odjęcie odpowiedniej liczby sekund. Na przykład, aby stworzyć datę o godzinę w przód od aktualnej, wystarczy dodać do niej 3600 sekund, czyli tyle, ile jest w sekund w godzinie.

Przykład 12.7. Tworzenie daty o godzinę w przód


<?php

$czas_akt = time();
$za_godz = $czas_akt + 3600;
echo date("r", $za_godz); // Wyświetli datę 'za godzinę'

?>

            

Podobnie sprawa wygląda z dodawaniem dni czy tygodni. Gorzej jest w przypadku miesięcy – przecież nie wiadomo z góry ile sekund ma miesiąc. Problem ten można rozwiązać na kilka sposobów. Można rozbić datę na poszczególne części (dzień, miesiąc, rok), dodać jedynkę do miesiąca, sprawdzić, czy przypadkiem nie wyszedł nam miesiąc trzynasty (co się stanie jeśli dodamy miesiąc do grudnia) i w takim przypadku odpowiednio zareagować.

Przykład 12.8. Data „za miesiąc”


<?php

$day   = date('d'); // dzień
$month = date('m'); // miesiąc
$year  = date('Y'); // rok

// dodanie jedynki do miesiąca
$month++;

// sprawdzenie czy licznik się nie przekręcił
if ($month == 13) {
   $month = 1;
   $year++;
}

?>

            

Jest jednak prostrza metoda tworzenia daty względnej. Można do tego użyć funkcji strtotime(). Jako jej argument można podać względny czas.

Przykład 12.9. Tworzenie względnej daty za pomocą funkcji strtotime


<?php

echo strtotime("now"), "\n";           // teraz
echo strtotime("+1 day"), "\n";        // jutro
echo strtotime("+1 week"), "\n";       // za tydzień
echo strtotime("+1 week 2 days 4 hours 2 seconds"), "\n"; // za tydzień, dwa dni,
                                                          // 4 godziny i 2 sekundy
echo strtotime("next Thursday"), "\n"; // następny czwartek
echo strtotime("last Monday"), "\n";   // poprzedni poniedziałek

?>

            

Porównywanie dat

Daty mogą być porównywane tylko i wyłącznie jeśli są przechowywane w tym samym formacie. Jako że w PHP nie ma zmiennej typu „data”, musi być ona przechowywana jako timestamp lub tekst. Z kolei postać tekstowa może być skrajnie różna zależnie od standardów narodowych czy też fantazji programisty – tu widać przewagę formatu timestamp.

Jeśli daty są już w tej samej postaci, można je porównać po prostu przez operator porównania. Dodatkowo, jeśli data jest w formacie timestamp, można użyć operatorów matematycznych (< czy >) dla ustalenia która data jest „nowsza”.

Przykład 12.10. Porównywanie daty w formacie timestamp


<?php

$time1 = mktime(19, 30, 0);
$time2 = mktime(20, 0, 0);

if ($time1 > $time2) {
   echo "Czas w zmiennej time1 jest późniejszy";
} else {
   echo "Czas w zmiennej time2 jest późniejszy";
}

?>

            

Porównywanie dat w formacie timestamp jest łatwe, jeśli potrzebne jest porównanie z dokładnością „co do sekundy”. Sprawa się komplikuje, jeśli potrzebne jest stwierdzenie, na przykład czy dwie daty są z tego samego dnia. Aby to stwierdzić, trzeba rozbić datę i porównywać składowe. Kod taki można przenieść do osobnej funkcji, aby wygodnie porównywać daty.

Przykład 12.11. Sprawdzanie czy daty są z tego samego dnia


<?php

function sameDay($ts1, $ts2)
{
   if (date("Y", $ts1) != date("Y", $ts2)) {
      return False;
   }
   if (date("m", $ts1) != date("m", $ts2)) {
      return False;
   }
   if (date("d", $ts1) != date("d", $ts2)) {
      return False;
   }
   return True;
}

$date1 = mktime(19, 30, 0, 12, 10, 2001);   
$date2 = mktime(10, 12, 0, 12, 10, 2001);   
$date3 = mktime(19, 30, 0, 12, 11, 2001);   

if (sameDay($date1, $date2)) {
   echo "Daty 1 i 2 są z tego samego dnia";
}

if (sameDay($date1, $date3)) {
   echo "Daty 1 i 3 są z tego samego dnia";
}

?>