Datum a čas
***********
.[perex]
Nette nabízí dvě třídy pro práci s datem a časem: [api:Nette\Utils\DateTimeImmutable] (immutable, doporučená) a [api:Nette\Utils\DateTime] (mutable). Obě rozšiřují nativní PHP třídy, takže všechny nativní metody zůstávají k dispozici, a přidávají stejná dvě vylepšení.
Zaprvé jsou **striktní**. Zatímco PHP tiše akceptuje nesmyslná data jako `0000-00-00` (převede na `-0001-11-30`) nebo `2024-02-31` (převede na `2024-03-02`), tyto třídy místo toho vyhodí výjimku.
Zadruhé **opravují chování** při přechodu na letní/zimní čas, kdy v nativním PHP může přičtení relativního času (např. `+100 minutes`) "vést k dřívějšímu výslednému času":https://phpfashion.com/cs/100-minut-je-mene-nez-50-paradoxy-php-pri-zmene-casu než přičtení kratšího úseku (např. `+50 minutes`). Tyto třídy zajišťují, že aritmetika funguje intuitivně a `+100 minutes` je vždy více než `+50 minutes`.
Instalace:
```shell
composer require nette/utils
```
Immutable, nebo mutable?
========================
Třída `DateTimeImmutable` je k dispozici od verze 4.1.5 a je doporučenou volbou. Každá upravující metoda vrací **novou instanci** místo změny původní, takže objekt, který máte uložený nebo předaný do funkce, se nikdy nezmění nečekaně:
```php
use Nette\Utils\DateTimeImmutable;
$date = new DateTimeImmutable('2024-02-26');
$next = $date->modify('+1 day');
echo $date; // 2024-02-26 00:00:00 (beze změny)
echo $next; // 2024-02-27 00:00:00 (nový objekt)
```
`DateTime` je mutable: totéž volání změní objekt na místě. Není zavržená (deprecated), ale pro nový kód je vhodnější immutable varianta.
```php
use Nette\Utils\DateTime;
$date = new DateTime('2024-02-26');
$date->modify('+1 day');
echo $date; // 2024-02-27 00:00:00 (původní objekt se změnil)
```
Protože obě třídy rozšiřují nativní, dál používáte metody, které už znáte: `format()`, `getTimestamp()`, `add()`, `sub()`, `diff()`, `setTimezone()`, operátory porovnání a další. U `DateTimeImmutable` vracejí všechny upravující metody novou instanci. Zbytek této stránky popisuje jen to, co Nette přidává navíc; není-li uvedeno jinak, vše funguje na obou třídách stejně.
Vytváření objektů
=================
static from(string|int|\DateTimeInterface|null $time): static .[method]
-----------------------------------------------------------------------
Vytvoří objekt z řetězce, UNIX timestampu nebo jiného objektu [php:DateTimeInterface]. `null` znamená aktuální čas. Vyhodí výjimku, pokud datum a čas nejsou platné.
```php
DateTimeImmutable::from(1_138_013_640); // z UNIX timestampu, s výchozí časovou zónou
DateTimeImmutable::from('1994-02-26 04:15:32'); // z řetězce
DateTimeImmutable::from('1994-02-26'); // z data, čas bude 00:00:00
DateTimeImmutable::from(null); // aktuální datum a čas
```
static fromParts(int $year, int $month, int $day, int $hour=0, int $minute=0, float $second=0.0): static .[method]
------------------------------------------------------------------------------------------------------------------
Vytvoří objekt z jednotlivých částí, nebo vyhodí výjimku, pokud datum a čas nejsou platné.
```php
DateTimeImmutable::fromParts(1994, 2, 26, 4, 15, 32);
```
static createFromFormat(string $format, string $datetime, string|\DateTimeZone|null $timezone=null): static|false .[method]
---------------------------------------------------------------------------------------------------------------------------
Rozšiřuje nativní [php:DateTime::createFromFormat] o možnost zadat časovou zónu jako řetězec.
```php
DateTimeImmutable::createFromFormat('d.m.Y', '26.02.1994', 'Europe/London');
```
Striktní validace
=================
Neplatné datum ani čas se nikdy tiše neupraví; vždy vyhodí výjimku. Platí to pro všechny způsoby, jak objekt vytvořit nebo změnit: konstruktor, `from()`, `fromParts()` i metody `setDate()` a `setTime()`.
```php
new DateTimeImmutable('2024-02-31'); // vyhodí výjimku (únor nemá 31. den)
DateTimeImmutable::fromParts(2024, 2, 31); // vyhodí výjimku
$date->setDate(2024, 2, 31); // vyhodí výjimku
$date->setTime(25, 0); // vyhodí výjimku (25. hodina neexistuje)
```
Výstup do řetězce a JSON
========================
`__toString()` vrací datum a čas ve formátu `Y-m-d H:i:s`, takže objekt lze přímo vypsat nebo zřetězit:
```php
echo $date; // '2017-02-03 04:15:32'
```
Obě třídy implementují `JsonSerializable` a serializují se do formátu ISO 8601, který se běžně používá v JavaScriptu:
```php
echo json_encode($date); // '"2017-02-03T04:15:32+01:00"'
```
Metody navíc ve třídě DateTime
==============================
Mutable `DateTime` nese několik prvků navíc, které dávají smysl jen u mutable objektu, a proto **nejsou** součástí `DateTimeImmutable`.
Její metoda `from()` navíc bere malé číslo jako posun v sekundách od aktuálního času. Immutable varianta tuto zkratku záměrně vynechává; tam je číslo vždy doslovný timestamp.
```php
DateTime::from(42); // aktuální čas plus 42 sekund
```
`modifyClone(string $modify=''): static` vrátí upravenou kopii a původní objekt nechá beze změny. U mutable objektu poskytuje to, co `modify()` u immutable dává zadarmo:
```php
$original = DateTime::from('2017-02-03');
$clone = $original->modifyClone('+1 day');
$original->format('Y-m-d'); // '2017-02-03' (beze změny)
$clone->format('Y-m-d'); // '2017-02-04'
```
`DateTime::relativeToSeconds(string $relativeTime): int` převede relativní časový údaj na sekundy: .{data-version:4.0.7}
```php
DateTime::relativeToSeconds('1 minute'); // 60
DateTime::relativeToSeconds('-1 hour'); // -3600
```
Nakonec `DateTime` definuje konstanty `MINUTE`, `HOUR`, `DAY`, `WEEK`, `MONTH` a `YEAR` vyjadřující délku v sekundách; `MONTH` a `YEAR` jsou průměry, používejte je proto jen pro hrubé odhady.
{{maintitle: Datum a čas v PHP: DateTime a DateTimeImmutable v Nette}}
{{description: Třídy pro datum a čas v Nette rozšiřují ty nativní z PHP a přidávají striktní validaci, snazší vytváření objektů a čistý výstup do řetězce a JSON.}}
Datum a čas
Nette nabízí dvě třídy pro práci s datem a časem: Nette\Utils\DateTimeImmutable (immutable,
doporučená) a Nette\Utils\DateTime (mutable). Obě
rozšiřují nativní PHP třídy, takže všechny nativní metody zůstávají k dispozici, a přidávají stejná dvě
vylepšení.
Zaprvé jsou striktní. Zatímco PHP tiše akceptuje nesmyslná data jako 0000-00-00 (převede na
-0001-11-30) nebo 2024-02-31 (převede na 2024-03-02), tyto třídy místo toho vyhodí
výjimku.
Zadruhé opravují chování při přechodu na letní/zimní čas, kdy v nativním PHP může přičtení relativního
času (např. +100 minutes) vést k dřívějšímu výslednému
času než přičtení kratšího úseku (např. +50 minutes). Tyto třídy zajišťují, že aritmetika funguje
intuitivně a +100 minutes je vždy více než +50 minutes.
Instalace:
composer require nette/utils
Immutable, nebo mutable?
Třída DateTimeImmutable je k dispozici od verze 4.1.5 a je doporučenou volbou. Každá upravující metoda
vrací novou instanci místo změny původní, takže objekt, který máte uložený nebo předaný do funkce, se nikdy
nezmění nečekaně:
use Nette\Utils\DateTimeImmutable;
$date = new DateTimeImmutable('2024-02-26');
$next = $date->modify('+1 day');
echo $date; // 2024-02-26 00:00:00 (beze změny)
echo $next; // 2024-02-27 00:00:00 (nový objekt)
DateTime je mutable: totéž volání změní objekt na místě. Není zavržená (deprecated), ale pro nový kód
je vhodnější immutable varianta.
use Nette\Utils\DateTime;
$date = new DateTime('2024-02-26');
$date->modify('+1 day');
echo $date; // 2024-02-27 00:00:00 (původní objekt se změnil)
Protože obě třídy rozšiřují nativní, dál používáte metody, které už znáte: format(),
getTimestamp(), add(), sub(), diff(), setTimezone(), operátory
porovnání a další. U DateTimeImmutable vracejí všechny upravující metody novou instanci. Zbytek této
stránky popisuje jen to, co Nette přidává navíc; není-li uvedeno jinak, vše funguje na obou třídách stejně.
Vytváření objektů
static from (string|int|\DateTimeInterface|null $time): static
Vytvoří objekt z řetězce, UNIX timestampu nebo jiného objektu DateTimeInterface. null znamená aktuální čas. Vyhodí výjimku,
pokud datum a čas nejsou platné.
DateTimeImmutable::from(1_138_013_640); // z UNIX timestampu, s výchozí časovou zónou
DateTimeImmutable::from('1994-02-26 04:15:32'); // z řetězce
DateTimeImmutable::from('1994-02-26'); // z data, čas bude 00:00:00
DateTimeImmutable::from(null); // aktuální datum a čas
static fromParts (int $year,
int $month, int $day, int $hour=0, int $minute=0, float $second=0.0): static
Vytvoří objekt z jednotlivých částí, nebo vyhodí výjimku, pokud datum a čas nejsou platné.
DateTimeImmutable::fromParts(1994, 2, 26, 4, 15, 32);
Rozšiřuje nativní DateTime::createFromFormat o možnost zadat
časovou zónu jako řetězec.
DateTimeImmutable::createFromFormat('d.m.Y', '26.02.1994', 'Europe/London');
Striktní validace
Neplatné datum ani čas se nikdy tiše neupraví; vždy vyhodí výjimku. Platí to pro všechny způsoby, jak objekt
vytvořit nebo změnit: konstruktor, from(), fromParts() i metody setDate() a
setTime().
new DateTimeImmutable('2024-02-31'); // vyhodí výjimku (únor nemá 31. den)
DateTimeImmutable::fromParts(2024, 2, 31); // vyhodí výjimku
$date->setDate(2024, 2, 31); // vyhodí výjimku
$date->setTime(25, 0); // vyhodí výjimku (25. hodina neexistuje)
Výstup do řetězce a JSON
__toString() vrací datum a čas ve formátu Y-m-d H:i:s, takže objekt lze přímo vypsat nebo
zřetězit:
echo $date; // '2017-02-03 04:15:32'
Obě třídy implementují JsonSerializable a serializují se do formátu ISO 8601, který se běžně používá
v JavaScriptu:
echo json_encode($date); // '"2017-02-03T04:15:32+01:00"'
Metody navíc ve třídě DateTime
Mutable DateTime nese několik prvků navíc, které dávají smysl jen u mutable objektu, a proto nejsou
součástí DateTimeImmutable.
Její metoda from() navíc bere malé číslo jako posun v sekundách od aktuálního času. Immutable varianta
tuto zkratku záměrně vynechává; tam je číslo vždy doslovný timestamp.
DateTime::from(42); // aktuální čas plus 42 sekund
modifyClone(string $modify=''): static vrátí upravenou kopii a původní objekt nechá beze změny. U mutable
objektu poskytuje to, co modify() u immutable dává zadarmo:
$original = DateTime::from('2017-02-03');
$clone = $original->modifyClone('+1 day');
$original->format('Y-m-d'); // '2017-02-03' (beze změny)
$clone->format('Y-m-d'); // '2017-02-04'
DateTime::relativeToSeconds(string $relativeTime): int převede relativní časový údaj na
sekundy:
DateTime::relativeToSeconds('1 minute'); // 60
DateTime::relativeToSeconds('-1 hour'); // -3600
Nakonec DateTime definuje konstanty MINUTE, HOUR, DAY, WEEK,
MONTH a YEAR vyjadřující délku v sekundách; MONTH a YEAR jsou průměry,
používejte je proto jen pro hrubé odhady.