Časová razítka, časová pásma a hodina, která se stane dvakrát
Časové razítko odpovídá na jednu ze dvou otázek a většina chyb s daty pochází z jejich zaměnění. Pojmenovává buď okamžik, tedy bod na časové ose, na kterém se shodne každý pozorovatel, nebo údaj na nástěnných hodinách, který nic neznamená, dokud nevíte, čí ty hodiny jsou. Platba proběhla v okamžiku; termín u zubaře je údaj na hodinách. Uložte jedno jako druhé a později se to rozbije, přesně o jednu hodinu, v neděli ráno.
Unixový čas počítá, neříká, kolik je hodin
Unixový čas je počet sekund, které uplynuly od 1970-01-01 00:00:00 UTC. To UTC říká, kde počítání začalo; sama hodnota žádné časové pásmo nenese. Týž okamžik je stejné číslo v Amsterdamu, Aucklandu i Limě.
Problémem je jednotka, takže si spočítejte číslice.
| Číslic | Příklad | Jednotka |
|---|---|---|
| 10 | 1756080000 | sekundy |
| 13 | 1756080000000 | milisekundy |
Každé dnešní datum má v sekundách 10 číslic a v milisekundách 13 a bude tomu tak až do roku 2286. Obě špatná čtení jsou nápadná: milisekundy čtené jako sekundy skončí v roce 57626, sekundy čtené jako milisekundy v lednu 1970. Převodník časových razítek, který zobrazí interpretované datum, to udělá zjevným.
Druhým limitem je nádoba. Počet sekund ve 32 bitech se znaménkem končí na 2147483647, což je 19. ledna 2038 v 03:14:07 UTC, pak přeteče do záporných hodnot a čte se jako prosinec 1901. Cokoli, co ukládá třicetiletou expiraci, už dnes počítá za tuto hranici. Bez znaménka dosáhne 32 bitů do roku 2106, ale ztratí každé datum před rokem 1970; 64 bitů nemá praktický strop.
ISO 8601 a RFC 3339
Tvar, který stojí za zapamatování, je 2026-08-25T14:30:00Z: datum, T, čas a pak označení pásma. RFC 3339 je přísnější profil ISO 8601 pro internetové protokoly a toto označení vyžaduje. Samotné ISO 8601 povoluje také týdenní data, tvar bez oddělovačů jako 20260825T143000Z a časová razítka bez posunu, takže API, které slibuje „ISO 8601“, obvykle myslí podmnožinu RFC 3339.
Z znamená nulový posun. +02:00 znamená, že místní hodiny jsou o dvě hodiny napřed oproti UTC, takže UTC získáte odečtením; obrácení tohoto směru je běžná chyba o dvě hodiny.
| Řetězec | Co pojmenovává |
|---|---|
2026-08-25T14:30:00Z | Okamžik, jednoznačně |
2026-08-25T16:30:00+02:00 | Týž okamžik na hodinách o dvě hodiny napřed |
2026-08-25T14:30:00 | Údaj na hodinách, žádný okamžik |
2026-08-25 | Datum dlouhé 23, 24 nebo 25 hodin |
Řetězec bez Z i bez posunu je nejednoznačný a parsery se neshodnou: některé předpokládají UTC, jiné pásmo systému. Právě proto se hodnota obsahující jen datum, jako je datum narození, západně od Greenwiche často vykreslí o den dřív.
Posun není časové pásmo
+02:00 je fakt o jednom okamžiku na jednom místě. Europe/Amsterdam je sada pravidel: +01:00 v zimě, +02:00 v létě a v dřívějších dekádách zase jinak. Uložení posunu zachová odpověď pro okamžik, kdy se měřil, a zahodí možnost spočítat jakoukoli jinou.
Posuny navíc nejsou jedinečné. V letním odpoledni pokrývá +02:00 Amsterdam, Lagos i Johannesburg, takže ze samotného posunu nevede cesta zpět k pravidlům a není jak zjistit, co ty hodiny ukážou příští prosinec. Zkratky jsou ještě horší, protože CST pojmenovává tři různá pásma. Ukládejte identifikátor IANA ve tvaru Area/Location.
Hodina, která neexistuje, a hodina, která se stane dvakrát
V celé EU se hodiny posouvají dopředu poslední neděli v březnu a zpátky poslední neděli v říjnu. V Amsterdamu se na jaře z 02:00 stane 03:00, takže každý místní čas od 02:00:00 do 02:59:59 ten den nenastane. Na podzim se z 03:00 stane 02:00, takže 02:30 nastane dvakrát, jednou při +02:00 a znovu o hodinu později při +01:00.
- Opakované budíky. Úloha nastavená na 02:30 místního času nemá o jarní neděli žádný platný čas. Plánovače se liší: přeskočí ji, posunou ji na 03:00, nebo na 01:30. O podzimní neděli se táž úloha může spustit jednou, nebo dvakrát, takže si nejbližší spuštění ověřte v parseru cronu.
- Logy. Hodinu místních časových razítek bez posunu nelze seřadit a dvě události hodinu od sebe nesou totožný text. Logujte v UTC, nebo posun uvádějte na každém řádku.
- Délky trvání. Od 20:00 dne 24. října 2026 do 20:00 následujícího dne je v amsterdamském místním čase 25 hodin. „Přidej jeden den“ a „přidej 24 hodin“ jsou různé operace a rozdíl dat v kalendářních dnech odpovídá na jinou otázku než převodník délek trvání pracující v hodinách.
Pravidla se mění, takže je uložení musí přežít
Databáze časových pásem IANA, tzdata, tato pravidla drží a vydává se několikrát ročně, protože ta pravidla jsou legislativa: vlády letní čas ruší, zavádějí nebo posouvají data přechodu, někdy s několikatýdenním předstihem. Zafixovaný obraz kontejneru, prohlížeč a běhové prostředí jazyka si každý nese vlastní kopii a na jednom stroji se mohou neshodnout. Převod budoucího místního času na okamžik je předpověď, která se potvrdí, teprve až ten čas nastane.
| Druh hodnoty | Ukládejte | Důvod |
|---|---|---|
| Něco, co se stalo | Okamžik v UTC | Minulost se nedá znovu odhlasovat |
| Budoucí termín podle hodin | Místní datum a čas plus id pásma IANA | Záměrem je údaj na hodinách |
| Budoucí okamžik pevný napříč pásmy | UTC | Záměrem je okamžik |
| Datum bez času | Prosté datum, bez pásma, bez půlnoci | Připojení pásma způsobí, že bude ujíždět |
Právě proto je „prostě ukládej UTC“ pro schůzku příští rok špatně. Převeďte dnes 09:00 dne 30. března 2027 v Europe/Amsterdam do UTC a odpovědí je 07:00Z. Pokud se datum přechodu do té doby posune, schůzka je pořád v 09:00 místního času, ale 07:00Z se teď vykreslí jako 08:00: uložená hodnota zmrazila předpověď a zahodila záměr. Jako zdroj pravdy si nechte místní čas plus pásmo; jakýkoli sloupec s UTC vedle něj je cache.
Zobrazování dat lidem
03/04 se ze samotného řetězce jednoznačně určit nedá. Pro většinu světa je to 3. dubna a ve Spojených státech 4. března; 03/04/05 přidává třetí neznámou. Pro lidský výstup měsíc vypište slovem (4 Apr 2026) a pro výměnu mezi stroji použijte ISO. Značka časového razítka v Discordu to v chatu obchází tím, že se vykreslí v pásmu a lokalizaci toho, kdo ji čte: uložte jednoznačnou hodnotu a formátujte až na okraji.
Čísla týdnů mají vlastní pravidlo. Týdny podle ISO začínají v pondělí a týden 1 je ten, který obsahuje první lednový čtvrtek, což je totéž jako týden obsahující 4. leden. Takže 1. leden může padnout do týdne 52 nebo 53 předchozího roku podle ISO a 29. prosinec do týdne 1 toho následujícího. Běžná severoamerická konvence místo toho čísluje od nedělí začínajícího týdne, který obsahuje 1. leden, takže totéž datum dostane dvě čísla, a ISO týden z kalkulačky dnů v týdnu se vyplatí porovnat s tabulkovým procesorem.
Délky trvání, měsíce a přestupné roky
„O měsíc později“ není pevný počet dní; je to 28, 29, 30 nebo 31. Přičtení měsíce k 31. lednu nemá správnou odpověď a knihovny výsledek obvykle přiříznou na konec února, čímž se operace stane nevratnou: přičtěte měsíc, zase ho odečtěte, a z 31. ledna je teď 28. leden. Zaokrouhlování je táž past v malém: oříznout čas na nejbližší čtvrthodinu a zaokrouhlit na ni se liší až o celý interval.
Pravidlo přestupného roku celé: rok je přestupný, pokud je dělitelný 4, s výjimkou roků dělitelných 100, které přestupné nejsou, ledaže jsou zároveň dělitelné 400. Rok 1900 tedy přestupný nebyl, 2000 byl a 2100 nebude, což dává průměrný rok o 365,2425 dnech. Kód, který testuje jen dělitelnost čtyřmi, je správný pro každý rok v žijící paměti, a proto přežije až do roku 2100. Související otrava je výročí 29. února: následující rok nabízí 28. února a 1. března a mezi nimi nerozhoduje žádné pravidlo, takže si jedno vyberte záměrně.