Lekcja 18: Praca z Datą i Czasem w PHP
Witaj w osiemnastej lekcji kursu PHP! Manipulowanie datami i czasem jest fundamentalnym aspektem wielu aplikacji internetowych. Od wyświetlania aktualnej daty, przez obliczanie różnic czasowych, formatowanie dat dla użytkownika, aż po planowanie zadań (cron jobs) czy zarządzanie datami ważności – PHP dostarcza bogaty zestaw narzędzi do pracy z tymi danymi. W tej lekcji zgłębimy zarówno tradycyjne funkcje związane z datą i czasem, jak i potężny, obiektowy interfejs dostarczany przez klasę DateTime
i jej pochodne.
Podstawowe Funkcje Daty i Czasu
PHP od dawna oferuje zestaw prostych funkcji do pracy z datą i czasem, które są nadal użyteczne w wielu sytuacjach, szczególnie do szybkich operacji lub w starszym kodzie.
Funkcja time()
Funkcja time()
jest jedną z najprostszych i najczęściej używanych. Zwraca ona aktualny uniksowy znacznik czasu (Unix timestamp), czyli liczbę sekund, które upłynęły od początku epoki Uniksa (Unix Epoch), czyli od 1 stycznia 1970 roku, godziny 00:00:00 czasu uniwersalnego koordynowanego (UTC).
<?php
$aktualny_timestamp = time();
echo "Aktualny uniksowy znacznik czasu: $aktualny_timestamp sekund.<br>";
// Przykład: 1678886400 (co odpowiada jakiejś dacie w marcu 2023)
?>
Timestamp jest bardzo użyteczny do przechowywania momentów w czasie w bazach danych (jako liczba całkowita) oraz do wykonywania obliczeń na datach (np. dodawanie lub odejmowanie sekund).
Funkcja date()
Funkcja date()
służy do formatowania daty i czasu na podstawie podanego formatu i opcjonalnie znacznika czasu. Jeśli znacznik czasu nie zostanie podany, używany jest aktualny czas (wynik time()
).
Składnia: string date(string $format, ?int $timestamp = null)
Argument $format
to string określający, jak data i czas mają być sformatowane. Używa się do tego specjalnych znaków formatujących. Oto niektóre z najczęściej używanych:
- Dzień:
d
- Dzień miesiąca, 2 cyfry z wiodącym zerem (np. "01" do "31")j
- Dzień miesiąca bez wiodącego zera (np. "1" do "31")D
- Tekstowa reprezentacja dnia tygodnia, 3 litery (np. "Mon" do "Sun")l
(małe L) - Pełna tekstowa reprezentacja dnia tygodnia (np. "Monday" do "Sunday")N
- Numeryczna reprezentacja dnia tygodnia wg ISO-8601 (1 dla poniedziałku do 7 dla niedzieli)w
- Numeryczna reprezentacja dnia tygodnia (0 dla niedzieli do 6 dla soboty)z
- Dzień roku (licząc od 0, np. "0" do "365")
- Tydzień:
W
- Numer tygodnia w roku wg ISO-8601, tygodnie zaczynają się w poniedziałek (np. "42")
- Miesiąc:
m
- Numeryczna reprezentacja miesiąca, z wiodącym zerem (np. "01" do "12")n
- Numeryczna reprezentacja miesiąca, bez wiodącego zera (np. "1" do "12")M
- Krótka tekstowa reprezentacja miesiąca, 3 litery (np. "Jan" do "Dec")F
- Pełna tekstowa reprezentacja miesiąca (np. "January" do "December")t
- Liczba dni w danym miesiącu (np. "28" do "31")
- Rok:
Y
- Pełna numeryczna reprezentacja roku, 4 cyfry (np. "1999" lub "2003")y
- Dwucyfrowa reprezentacja roku (np. "99" lub "03")L
- Czy rok jest przestępny (1 jeśli tak, 0 jeśli nie)
- Czas:
a
- Małe "am" lub "pm"A
- Duże "AM" lub "PM"g
- Godzina w formacie 12-godzinnym, bez wiodącego zera (np. "1" do "12")h
- Godzina w formacie 12-godzinnym, z wiodącym zerem (np. "01" do "12")G
- Godzina w formacie 24-godzinnym, bez wiodącego zera (np. "0" do "23")H
- Godzina w formacie 24-godzinnym, z wiodącym zerem (np. "00" do "23")i
- Minuty, z wiodącym zerem (np. "00" do "59")s
- Sekundy, z wiodącym zerem (np. "00" do "59")u
- Mikrosekundy (od PHP 5.2.2). Zauważ, żedate()
samo w sobie nie ma precyzji mikrosekundowej dla timestampu; użyjDateTime::format()
dla pełnej precyzji.
- Strefa czasowa:
e
- Identyfikator strefy czasowej (np. "UTC", "Europe/Warsaw")O
- Różnica do czasu Greenwich (GMT) w godzinach (np. "+0200")P
- Różnica do czasu Greenwich (GMT) z dwukropkiem między godzinami a minutami (np. "+02:00")T
- Skrót strefy czasowej (np. "EST", "MDT", "CEST")Z
- Przesunięcie strefy czasowej w sekundach. Przesunięcie na zachód od UTC jest zawsze ujemne, a na wschód od UTC zawsze dodatnie.
- Pełna data/czas:
c
- Data i czas w formacie ISO 8601 (np. "2004-02-12T15:19:21+00:00")r
- Data i czas w formacie RFC 2822 (np. "Thu, 21 Dec 2000 16:01:07 +0200")U
- Sekundy od początku epoki Uniksa (to samo cotime()
)
<?php
// Ustawienie domyślnej strefy czasowej (ważne dla spójności)
// Najlepiej ustawić w php.ini: date.timezone = "Europe/Warsaw"
// Lub na początku skryptu:
date_default_timezone_set("Europe/Warsaw");
$teraz = time();
echo "Dziś jest: " . date("Y-m-d") . "<br>"; // np. 2023-03-15
echo "Pełna data i czas: " . date("l, j F Y, H:i:s") . "<br>"; // np. Wednesday, 15 March 2023, 14:30:00
echo "Format polski: " . date("d.m.Y H:i") . "<br>"; // np. 15.03.2023 14:30
echo "Numer tygodnia: " . date("W") . "<br>"; // np. 11
echo "Czas z AM/PM: " . date("h:i:s a") . "<br>"; // np. 02:30:00 pm
// Formatowanie konkretnego timestampu
$timestamp_urodzin = 946684800; // 1 stycznia 2000, 00:00:00 UTC
echo "Data urodzin (z timestampu): " . date("d F Y", $timestamp_urodzin) . "<br>"; // np. 01 January 2000 (zależne od strefy czasowej serwera!)
// Aby uzyskać datę z timestampu UTC niezależnie od strefy serwera, użyj gmdate()
// echo "Data urodzin (UTC): " . gmdate("d F Y", $timestamp_urodzin) . "<br>";
?>
Funkcja gmdate()
Funkcja gmdate()
działa identycznie jak date()
, ale zawsze zwraca datę sformatowaną według czasu uniwersalnego koordynowanego (UTC), znanego również jako Greenwich Mean Time (GMT), ignorując domyślną strefę czasową serwera.
<?php
date_default_timezone_set("Europe/Warsaw"); // Strefa +01:00 lub +02:00 (DST)
$teraz = time();
echo "Czas lokalny (Warszawa): " . date("Y-m-d H:i:sP", $teraz) . "<br>";
echo "Czas UTC (gmdate): " . gmdate("Y-m-d H:i:sP", $teraz) . "<br>";
?>
Funkcja strtotime()
Funkcja strtotime()
jest niezwykle potężna i elastyczna. Próbuje ona przekształcić tekstowy opis daty/czasu w języku angielskim na uniksowy znacznik czasu. Akceptuje wiele formatów, takich jak "now", "+1 day", "next Thursday", "last Monday", "10 September 2000", "2023-03-15 14:30:00".
Składnia: int|false strtotime(string $datetime, ?int $baseTimestamp = null)
Drugi, opcjonalny argument $baseTimestamp
pozwala określić bazowy znacznik czasu, względem którego obliczany jest relatywny czas (np. "+1 day" od podanego $baseTimestamp
).
<?php
date_default_timezone_set("Europe/Warsaw");
echo "Timestamp dla \"now\": " . strtotime("now") . " ( " . date("Y-m-d H:i:s", strtotime("now")) . " )<br>";
echo "Timestamp dla \"+1 day\": " . strtotime("+1 day") . " ( " . date("Y-m-d H:i:s", strtotime("+1 day")) . " )<br>";
echo "Timestamp dla \"next Monday\": " . strtotime("next Monday") . " ( " . date("Y-m-d H:i:s", strtotime("next Monday")) . " )<br>";
echo "Timestamp dla \"15 July 2024\": " . strtotime("15 July 2024") . " ( " . date("Y-m-d H:i:s", strtotime("15 July 2024")) . " )<br>";
$dzisiaj = strtotime("2023-10-26");
echo "Timestamp dla \"+1 week\" od 2023-10-26: " . strtotime("+1 week", $dzisiaj) . " ( " . date("Y-m-d", strtotime("+1 week", $dzisiaj)) . " )<br>";
// Jeśli strtotime() nie może zinterpretować stringa, zwraca false
$niepoprawny_string = "to nie jest data";
if (strtotime($niepoprawny_string) === false) {
echo "Nie udało się przekształcić stringa: '" . htmlspecialchars($niepoprawny_string) . "' na timestamp.<br>";
}
?>
Chociaż strtotime()
jest bardzo wygodne, należy używać go ostrożnie, ponieważ jego zachowanie może być niejednoznaczne dla niektórych formatów lub ustawień regionalnych. Dla bardziej precyzyjnej i kontrolowanej pracy z datami, zaleca się używanie obiektowego API (klasa DateTime
).
Inne Przydatne Funkcje
mktime()
/gmmktime()
: Tworzą uniksowy znacznik czasu na podstawie podanych argumentów (godzina, minuta, sekunda, miesiąc, dzień, rok).mktime()
używa lokalnej strefy czasowej,gmmktime()
używa UTC.
<?php
// Timestamp dla 15 marca 2023, godzina 10:30:00 (czas lokalny)
$ts_mktime = mktime(10, 30, 0, 3, 15, 2023);
echo "mktime: " . date("Y-m-d H:i:s", $ts_mktime) . "<br>";
?>
getdate()
: Zwraca tablicę asocjacyjną z informacjami o dacie dla podanego (lub bieżącego) znacznika czasu. Klucze to np. "seconds", "minutes", "hours", "mday" (dzień miesiąca), "mon" (miesiąc), "year", "weekday" (nazwa dnia tygodnia), itp.checkdate()
: Sprawdza, czy podana data (miesiąc, dzień, rok) jest poprawną datą kalendarzową (np. czy luty ma 29 dni w roku przestępnym).date_default_timezone_set()
/ date_default_timezone_get()
: Ustawia / pobiera domyślną strefę czasową używaną przez wszystkie funkcje daty/czasu w skrypcie. Bardzo ważne jest, aby zawsze ustawiać domyślną strefę czasową, aby uniknąć niejednoznaczności i błędów związanych z różnicami czasowymi. Najlepiej ustawić ją w php.ini
(date.timezone = "Europe/Warsaw"
) lub na początku każdego skryptu.Obiektowe Podejście: Klasa DateTime
, DateTimeImmutable
i DateInterval
Od PHP 5.2 wprowadzono obiektowy interfejs do pracy z datą i czasem, który jest znacznie bardziej elastyczny, precyzyjny i mniej podatny na błędy niż tradycyjne funkcje. Główne klasy to:
DateTime
: Reprezentuje datę i czas. Obiekty tej klasy są modyfikowalne (mutowalne), co oznacza, że metody takie jakadd()
czymodify()
zmieniają stan samego obiektu.DateTimeImmutable
(od PHP 5.5): Reprezentuje datę i czas, ale obiekty tej klasy są niemodyfikowalne (niemutowalne). Każda operacja modyfikująca (np.add()
,modify()
) zwraca nowy obiektDateTimeImmutable
z wprowadzonymi zmianami, pozostawiając oryginalny obiekt nietkniętym. Jest to często preferowane podejście ze względu na większe bezpieczeństwo i przewidywalność kodu.DateTimeZone
: Reprezentuje strefę czasową.DateInterval
: Reprezentuje przedział czasu (np. 2 dni, 3 miesiące, 10 godzin).DatePeriod
: Reprezentuje okres czasu, pozwalając na iterację po datach w określonych odstępach.
Tworzenie Obiektów DateTime
i DateTimeImmutable
Obiekty te można tworzyć na kilka sposobów:
<?php
date_default_timezone_set("Europe/Warsaw");
// 1. Bez argumentów - tworzy obiekt z aktualną datą i czasem
$teraz_dt = new DateTime();
$teraz_dti = new DateTimeImmutable();
echo "DateTime (teraz): " . $teraz_dt->format("Y-m-d H:i:sP") . "<br>";
echo "DateTimeImmutable (teraz): " . $teraz_dti->format("Y-m-d H:i:sP") . "<br>";
// 2. Z argumentem stringa z datą/czasem (podobnie jak strtotime())
$data_string = "2024-07-15 10:30:00";
$konkretna_data_dt = new DateTime($data_string);
echo "DateTime ($data_string): " . $konkretna_data_dt->format("Y-m-d H:i:s") . "<br>";
// 3. Z argumentem stringa i obiektem DateTimeZone
$strefa_nowy_jork = new DateTimeZone("America/New_York");
$data_w_ny_dt = new DateTime("now", $strefa_nowy_jork);
echo "DateTime (teraz w Nowym Jorku): " . $data_w_ny_dt->format("Y-m-d H:i:sP") . "<br>";
// 4. Metoda statyczna createFromFormat() - bardziej precyzyjne tworzenie z określonego formatu
$format_wejsciowy = "d.m.Y H:i";
$data_string_polski = "20.05.2024 15:45";
$data_z_formatu_dti = DateTimeImmutable::createFromFormat($format_wejsciowy, $data_string_polski);
if ($data_z_formatu_dti) {
echo "DateTimeImmutable (z formatu '$format_wejsciowy'): " . $data_z_formatu_dti->format("Y-m-d H:i:s") . "<br>";
} else {
echo "Nie udało się utworzyć daty z formatu: " . htmlspecialchars($data_string_polski) . "<br>";
// Można sprawdzić błędy: print_r(DateTime::getLastErrors());
}
// 5. Metoda statyczna createFromMutable() (od PHP 7.3) do konwersji DateTime na DateTimeImmutable
// $immutable_z_mutable = DateTimeImmutable::createFromMutable($teraz_dt);
// 6. Metoda statyczna createFromInterface() (od PHP 7.0) do konwersji z dowolnego obiektu implementującego DateTimeInterface
?>
DateTimeInterface
jest interfejsem implementowanym zarówno przez DateTime
jak i DateTimeImmutable
.
Formatowanie Daty i Czasu - Metoda format()
Metoda format()
obiektów DateTime
i DateTimeImmutable
działa podobnie do funkcji date()
, przyjmując ten sam zestaw znaków formatujących.
<?php
$data_obiekt = new DateTimeImmutable("2023-12-25 08:00:00");
echo "ISO 8601: " . $data_obiekt->format("c") . "<br>";
echo "Format przyjazny: " . $data_obiekt->format("l, j F Y, g:i A") . "<br>";
?>
Modyfikowanie Daty i Czasu
Dla DateTime
(mutowalny):
modify(string $modifier)
: Modyfikuje obiekt daty/czasu na podstawie stringa podobnego do tego zstrtotime()
(np. "+1 day", "-2 months").add(DateInterval $interval)
: Dodaje określony interwał czasu (obiektDateInterval
).sub(DateInterval $interval)
: Odejmuje określony interwał czasu.setDate(int $year, int $month, int $day)
: Ustawia datę.setTime(int $hour, int $minute, int $second = 0, int $microsecond = 0)
: Ustawia czas.setTimestamp(int $timestamp)
: Ustawia datę/czas na podstawie uniksowego znacznika czasu.setTimezone(DateTimeZone $timezone)
: Zmienia strefę czasową obiektu.
Dla DateTimeImmutable
(niemutowalny):
Metody te mają takie same nazwy (modify
, add
, sub
, setDate
, setTime
, setTimestamp
, setTimezone
), ale zwracają nowy obiekt DateTimeImmutable
z wprowadzonymi zmianami, pozostawiając oryginalny obiekt nietkniętym.
<?php
// Przykład z DateTime (mutowalny)
$data_mutowalna = new DateTime("2023-01-01");
echo "Oryginalna (mutowalna): " . $data_mutowalna->format("Y-m-d") . "<br>";
$data_mutowalna->modify("+1 month");
echo "Po modify (+1 month): " . $data_mutowalna->format("Y-m-d") . "<br>"; // Oryginalny obiekt zmieniony
// Przykład z DateTimeImmutable (niemutowalny)
$data_niemutowalna_oryg = new DateTimeImmutable("2023-01-01");
echo "Oryginalna (niemutowalna): " . $data_niemutowalna_oryg->format("Y-m-d") . "<br>";
$data_niemutowalna_nowa = $data_niemutowalna_oryg->modify("+1 month");
echo "Oryginalna po modify (niemutowalna): " . $data_niemutowalna_oryg->format("Y-m-d") . "<br>"; // Pozostaje bez zmian
echo "Nowy obiekt po modify (niemutowalna): " . $data_niemutowalna_nowa->format("Y-m-d") . "<br>";
?>
Klasa DateInterval
DateInterval
reprezentuje okres czasu. Można go utworzyć na podstawie stringa w formacie interwału ISO 8601 (np. "P2Y4DT6H8M" - 2 lata, 4 dni, 6 godzin, 8 minut) lub za pomocą metody diff()
obiektów DateTime
/DateTimeImmutable
.
Format stringa dla konstruktora DateInterval
: P[n]Y[n]M[n]DT[n]H[n]M[n]S
lub P[n]W
(dla tygodni).
P
- oznaczenie początku specyfikacji okresu (Period)Y
- lataM
- miesiąceD
- dniW
- tygodnie (nie można łączyć z Y, M, D)T
- oznaczenie początku specyfikacji czasu (Time)H
- godzinyM
- minuty (w sekcji czasu)S
- sekundy
<?php
$interwal_1 = new DateInterval("P2D"); // Interwał 2 dni
$interwal_2 = new DateInterval("PT3H30M"); // Interwał 3 godziny 30 minut
$interwal_3 = DateInterval::createFromDateString("3 weeks and 2 days"); // Od PHP 5.3
$data_start = new DateTimeImmutable("2023-03-10");
$data_plus_2_dni = $data_start->add($interwal_1);
echo "Data + 2 dni: " . $data_plus_2_dni->format("Y-m-d") . "<br>";
$data_plus_3w2d = $data_start->add($interwal_3);
echo "Data + 3 tygodnie i 2 dni: " . $data_plus_3w2d->format("Y-m-d") . "<br>";
?>
Porównywanie Dat i Obliczanie Różnic - Metoda diff()
Metoda diff()
obiektów DateTime
/DateTimeImmutable
zwraca obiekt DateInterval
reprezentujący różnicę między dwiema datami.
<?php
$data1_str = "2023-01-15 10:00:00";
$data2_str = "2023-03-20 14:30:00";
$data1 = new DateTimeImmutable($data1_str);
$data2 = new DateTimeImmutable($data2_str);
$roznica_interwal = $data1->diff($data2);
// Formatowanie obiektu DateInterval
echo "Różnica między $data1_str a $data2_str:<br>";
echo $roznica_interwal->format("%R%a dni ogółem") . "<br>"; // %R to znak (+/-), %a to całkowita liczba dni
echo $roznica_interwal->format("%y lat, %m miesięcy, %d dni, %h godzin, %i minut, %s sekund") . "<br>";
if ($roznica_interwal->invert) { // invert = 1 jeśli $data1 > $data2 (różnica ujemna)
echo "Data pierwsza jest późniejsza.<br>";
} else {
echo "Data pierwsza jest wcześniejsza lub taka sama.<br>";
}
// Dostęp do właściwości obiektu DateInterval:
// $roznica_interwal->y, $roznica_interwal->m, $roznica_interwal->d, ...
// $roznica_interwal->days (całkowita liczba dni, jeśli obliczona, lub false)
// Porównywanie obiektów DateTime/DateTimeImmutable
if ($data1 < $data2) {
echo "$data1_str jest wcześniejsza niż $data2_str<br>";
} elseif ($data1 > $data2) {
echo "$data1_str jest późniejsza niż $data2_str<br>";
} else {
echo "Daty są identyczne.<br>";
}
?>
Znaki formatujące dla DateInterval::format()
:
%Y
- Lata, numerycznie, co najmniej 2 cyfry z wiodącym zerem%y
- Lata, numerycznie%M
- Miesiące, numerycznie, co najmniej 2 cyfry z wiodącym zerem%m
- Miesiące, numerycznie%D
- Dni, numerycznie, co najmniej 2 cyfry z wiodącym zerem%d
- Dni, numerycznie%a
- Całkowita liczba dni w wynikuDateTime::diff()
%H
- Godziny, numerycznie, co najmniej 2 cyfry z wiodącym zerem%h
- Godziny, numerycznie%I
- Minuty, numerycznie, co najmniej 2 cyfry z wiodącym zerem%i
- Minuty, numerycznie%S
- Sekundy, numerycznie, co najmniej 2 cyfry z wiodącym zerem%s
- Sekundy, numerycznie%F
- Mikrosekundy, numerycznie, co najmniej 6 cyfr z wiodącym zerem%f
- Mikrosekundy, numerycznie%R
- Znak "+" lub "-" (jeśli interwał jest ujemny)%r
- Pusty string lub "-" (jeśli interwał jest ujemny)
Klasa DateTimeZone
Reprezentuje strefę czasową. Można ją użyć do tworzenia obiektów DateTime
w konkretnej strefie lub do zmiany strefy istniejącego obiektu.
<?php
$strefa_warszawa = new DateTimeZone("Europe/Warsaw");
$strefa_londyn = new DateTimeZone("Europe/London");
$teraz_warszawa = new DateTime("now", $strefa_warszawa);
echo "Teraz w Warszawie: " . $teraz_warszawa->format("Y-m-d H:i:s P") . "<br>";
// Konwersja do strefy Londynu
$teraz_londyn = $teraz_warszawa->setTimezone($strefa_londyn);
echo "Ten sam moment w Londynie: " . $teraz_londyn->format("Y-m-d H:i:s P") . "<br>";
// Lista identyfikatorów stref czasowych
// $lista_stref = DateTimeZone::listIdentifiers();
// print_r($lista_stref);
?>
Klasa DatePeriod
Pozwala na iterowanie po zestawie dat i czasów w określonym okresie, z zadanym interwałem.
Konstruktor: DatePeriod(DateTimeInterface $start, DateInterval $interval, DateTimeInterface $end [, int $options = 0])
lub DatePeriod(DateTimeInterface $start, DateInterval $interval, int $recurrences [, int $options = 0])
lub DatePeriod(string $isostr [, int $options = 0])
(dla ISO 8601 repeating interval string).
<?php
$poczatek = new DateTimeImmutable("2023-04-01");
$koniec = new DateTimeImmutable("2023-04-10");
$interwal_dzienny = new DateInterval("P1D"); // Co 1 dzień
$okres = new DatePeriod($poczatek, $interwal_dzienny, $koniec);
echo "Daty od " . $poczatek->format("Y-m-d") . " do " . $koniec->format("Y-m-d") . " (bez końca):<br>";
foreach ($okres as $data_w_okresie) {
echo $data_w_okresie->format("Y-m-d l") . "<br>";
}
// Opcja DatePeriod::EXCLUDE_START_DATE aby pominąć datę początkową
$okres_z_liczba_powtorzen = new DatePeriod($poczatek, $interwal_dzienny, 5); // 5 powtórzeń (łącznie 6 dat, wliczając startową)
echo "<br>Pierwsze 6 dni od " . $poczatek->format("Y-m-d") . ":<br>";
foreach ($okres_z_liczba_powtorzen as $data_w_okresie) {
echo $data_w_okresie->format("Y-m-d l") . "<br>";
}
?>
Praca z Lokalizacją (Formatowanie Zależne od Języka)
Standardowe funkcje date()
i metody format()
klasy DateTime
formatują nazwy dni tygodnia i miesięcy w języku angielskim. Aby uzyskać formatowanie zależne od ustawień regionalnych (np. polskie nazwy), można użyć rozszerzenia Intl
(Internationalization), a konkretnie klasy IntlDateFormatter
.
<?php
// Upewnij się, że rozszerzenie Intl jest włączone w php.ini (extension=intl)
if (class_exists("IntlDateFormatter")) {
$data_obiekt = new DateTimeImmutable();
// Formatowanie z użyciem IntlDateFormatter
// Locale: "pl_PL" dla Polski
// Date type: IntlDateFormatter::FULL, ::LONG, ::MEDIUM, ::SHORT
// Time type: IntlDateFormatter::FULL, ::LONG, ::MEDIUM, ::SHORT
// Timezone: można podać, lub użyje domyślnej
// Calendar: IntlDateFormatter::GREGORIAN
// Pattern: można podać własny wzorzec (np. "EEEE, d MMMM yyyy, HH:mm")
$formatter_pelny = new IntlDateFormatter(
"pl_PL",
IntlDateFormatter::FULL,
IntlDateFormatter::FULL,
date_default_timezone_get(), // lub konkretna strefa
IntlDateFormatter::GREGORIAN
);
echo "Intl (pełny): " . $formatter_pelny->format($data_obiekt) . "<br>";
// np. środa, 15 marca 2023 14:30:00 Czas środkowoeuropejski standardowy
$formatter_niestandardowy = new IntlDateFormatter(
"pl_PL",
IntlDateFormatter::NONE, // Ignoruj predefiniowany format daty
IntlDateFormatter::NONE, // Ignoruj predefiniowany format czasu
date_default_timezone_get(),
IntlDateFormatter::GREGORIAN,
"EEEE, d MMMM yyyy HH:mm:ss"
);
echo "Intl (niestandardowy): " . $formatter_niestandardowy->format($data_obiekt) . "<br>";
// np. środa, 15 marca 2023 14:30:00
} else {
echo "Rozszerzenie Intl nie jest dostępne. Używam formatowania angielskiego.<br>";
$data_obiekt = new DateTimeImmutable();
echo "Standardowo: " . $data_obiekt->format("l, j F Y, H:i:s") . "<br>";
}
?>
Wzorce dla IntlDateFormatter
są nieco inne niż dla date()
/DateTime::format()
i są zgodne ze standardem ICU. Przykłady:
yyyy
- rok (np. 2023)MMMM
- pełna nazwa miesiąca (np. marzec)MMM
- skrócona nazwa miesiąca (np. mar)MM
- miesiąc numerycznie, 2 cyfry (np. 03)dd
- dzień miesiąca, 2 cyfry (np. 15)EEEE
- pełna nazwa dnia tygodnia (np. środa)HH
- godzina (0-23)mm
- minutyss
- sekundy
Podsumowanie Lekcji
W tej obszernej lekcji nauczyliśmy się, jak efektywnie pracować z datami i czasem w PHP. Poznaliśmy tradycyjne funkcje takie jak time()
, date()
, strtotime()
, oraz ich ograniczenia. Następnie zagłębiliśmy się w potężny, obiektowy interfejs dostarczany przez klasy DateTime
, DateTimeImmutable
, DateInterval
, DateTimeZone
i DatePeriod
, które oferują znacznie większą precyzję, elastyczność i czytelność kodu. Omówiliśmy tworzenie obiektów daty, formatowanie, modyfikowanie, porównywanie, pracę ze strefami czasowymi oraz iterowanie po okresach. Na koniec dotknęliśmy tematu lokalizacji dat za pomocą rozszerzenia Intl
.
Umiejętność manipulowania datami i czasem jest niezbędna w niemal każdej aplikacji webowej. Wybór między tradycyjnymi funkcjami a podejściem obiektowym zależy od konkretnego zadania, ale dla nowych projektów i bardziej złożonych operacji zdecydowanie zalecane jest korzystanie z klas DateTimeImmutable
i powiązanych.
W następnej lekcji zajmiemy się pracą z danymi w popularnym formacie JSON.
Zadanie praktyczne
Napisz skrypt, który wykonuje następujące czynności:
- Pobiera aktualną datę i czas używając
DateTimeImmutable
. - Wyświetla aktualną datę w formacie "RRRR-MM-DD".
- Wyświetla aktualny czas w formacie "GG:MM:SS AM/PM".
- Oblicza datę, która będzie za 7 dni od teraz, i wyświetla ją.
- Oblicza datę, która była 1 miesiąc i 3 dni temu, i wyświetla ją.
- Tworzy obiekt daty dla "2025-01-01 00:00:00". Oblicza, ile dni, godzin i minut pozostało do tej daty od teraz. Wyświetl wynik w czytelnej formie (np. "Do Nowego Roku 2025 pozostało: X dni, Y godzin, Z minut.").
- Wyświetl aktualną datę i czas dla strefy czasowej "America/New_York".
Pokaż przykładowe rozwiązanie
<?php
date_default_timezone_set("Europe/Warsaw");
// 1. Aktualna data i czas
$teraz = new DateTimeImmutable();
// 2. Aktualna data RRRR-MM-DD
echo "Aktualna data: " . $teraz->format("Y-m-d") . "<br>";
// 3. Aktualny czas GG:MM:SS AM/PM
echo "Aktualny czas: " . $teraz->format("h:i:s A") . "<br>";
// 4. Data za 7 dni
$za_7_dni_interwal = new DateInterval("P7D");
$data_za_7_dni = $teraz->add($za_7_dni_interwal);
echo "Data za 7 dni: " . $data_za_7_dni->format("Y-m-d l") . "<br>";
// Alternatywnie: $data_za_7_dni = $teraz->modify("+7 days");
// 5. Data 1 miesiąc i 3 dni temu
$miesiac_3_dni_temu_interwal = new DateInterval("P1M3D");
$data_miesiac_3_dni_temu = $teraz->sub($miesiac_3_dni_temu_interwal);
echo "Data 1 miesiąc i 3 dni temu: " . $data_miesiac_3_dni_temu->format("Y-m-d l") . "<br>";
// Alternatywnie: $data_miesiac_3_dni_temu = $teraz->modify("-1 month -3 days");
// 6. Czas do Nowego Roku 2025
$nowy_rok_2025 = new DateTimeImmutable("2025-01-01 00:00:00");
if ($teraz < $nowy_rok_2025) {
$roznica_do_nr = $teraz->diff($nowy_rok_2025);
echo "Do Nowego Roku 2025 pozostało: " .
$roznica_do_nr->days . " dni ogółem, lub dokładniej: " .
$roznica_do_nr->format("%m miesięcy, %d dni, %h godzin, %i minut, %s sekund") .
".<br>";
} else {
echo "Nowy Rok 2025 już minął!<br>";
}
// 7. Aktualna data i czas w Nowym Jorku
$strefa_ny = new DateTimeZone("America/New_York");
$teraz_w_ny = $teraz->setTimezone($strefa_ny);
echo "Aktualnie w Nowym Jorku: " . $teraz_w_ny->format("Y-m-d H:i:s P (e)") . "<br>";
?>
Zadanie do samodzielnego wykonania
Napisz skrypt, który:
- Przyjmuje od użytkownika datę urodzenia w formacie "RRRR-MM-DD" (np. z formularza lub jako stała w kodzie).
- Używa
DateTimeImmutable::createFromFormat()
do stworzenia obiektu daty urodzenia. Sprawdź poprawność formatu. - Oblicza wiek użytkownika w latach, miesiącach i dniach.
- Wyświetla obliczony wiek.
- Sprawdza, czy użytkownik ma dziś urodziny (porównuje dzień i miesiąc daty urodzenia z aktualną datą). Jeśli tak, wyświetla specjalne życzenia.
FAQ - Praca z Datą i Czasem w PHP
Jaka jest różnica między DateTime
a DateTimeImmutable
?
DateTime
jest mutowalny, co oznacza, że metody modyfikujące (np. add()
, modify()
) zmieniają sam obiekt. DateTimeImmutable
jest niemutowalny – te same metody zwracają nowy obiekt z wprowadzonymi zmianami, pozostawiając oryginalny nietknięty. DateTimeImmutable
jest generalnie bezpieczniejsze i prowadzi do bardziej przewidywalnego kodu.
Dlaczego tak ważne jest ustawienie date_default_timezone_set()
?
PHP używa domyślnej strefy czasowej do interpretacji i wyświetlania dat. Jeśli nie jest ona jawnie ustawiona, PHP może próbować zgadnąć strefę systemową lub użyć UTC, co może prowadzić do niespójności i błędów, zwłaszcza gdy aplikacja działa na serwerach w różnych lokalizacjach lub obsługuje użytkowników z różnych stref czasowych. Ustawienie jej na początku skryptu lub w php.ini
zapewnia spójne zachowanie.
Jak najlepiej przechowywać daty w bazie danych?
Często zaleca się przechowywanie dat w bazie danych w formacie UTC (np. jako timestamp uniksowy lub typ DATETIME/TIMESTAMP bazy danych ustawiony na UTC). Następnie, przy wyświetlaniu daty użytkownikowi, konwertuje się ją na jego lokalną strefę czasową. To upraszcza logikę i zapobiega problemom z DST (czas letni/zimowy).
Czy strtotime()
jest bezpieczne do użycia z danymi od użytkownika?
Należy być ostrożnym. Chociaż strtotime()
jest elastyczne, może niepoprawnie zinterpretować niektóre niejednoznaczne formaty. Jeśli dane pochodzą od użytkownika, lepiej użyć DateTime::createFromFormat()
, które wymaga jawnego podania oczekiwanego formatu, co jest bezpieczniejsze i bardziej przewidywalne.
Jak obsłużyć czas letni (DST)?
Klasy DateTime
, DateTimeImmutable
i DateTimeZone
automatycznie obsługują zmiany czasu letniego/zimowego dla danej strefy czasowej. Jeśli pracujesz z timestampami i funkcją date()
, musisz upewnić się, że domyślna strefa czasowa jest poprawnie ustawiona. Przechowywanie dat w UTC i konwersja do lokalnej strefy przy wyświetlaniu to dobra praktyka minimalizująca problemy z DST.
Czy mogę formatować daty na polskie nazwy miesięcy/dni bez rozszerzenia Intl?
Bez rozszerzenia Intl jest to trudniejsze. Można by stworzyć własne tablice z polskimi nazwami i używać ich do zastępowania angielskich nazw uzyskanych z date()
lub DateTime::format()
, ale jest to mniej eleganckie i bardziej podatne na błędy. Rozszerzenie Intl jest standardowym i zalecanym sposobem na internacjonalizację.
Jak uzyskać timestamp z obiektu DateTime
?
Można użyć metody getTimestamp()
. Na przykład: $timestamp = $dateTimeObject->getTimestamp();
. Zwraca ona uniksowy znacznik czasu.