Časová pásma ve webové aplikaci: změna času, termíny a reporty

Špatně zpracovaná časová pásma ve webové aplikaci se projeví hlavně při změně času: připomínka přijde o hodinu dřív, pravidelný termín se po přechodu na letní čas posune a denní report za den změny nesedí s účetnictvím. Řešením není jedno nastavení serveru, ale jasné pravidlo pro každý druh časového údaje. Okamžiky ukládejte v UTC, budoucí termíny dohodnuté na místní čas jako datum, čas a název pásma, například Europe/Bratislava, data bez času jako data a opakování jako pravidlo s pásmem. Potom cíleně otestujte dny, kdy se mění čas.

Netýká se to jen firem se zákazníky v zahraničí. Stačí rezervační systém, plánování servisních výjezdů nebo noční export do účetnictví. I na Slovensku má jeden den v roce 23 hodin a jeden 25 hodin.

Proč se při změně času hodina ztratí nebo zdvojí

V pásmu Europe/Bratislava se na jaře hodiny posouvají z 2:00 na 3:00, takže místní čas 2:30 v ten den neexistuje. Na podzim se z 3:00 vracejí na 2:00 a čas 2:30 nastane dvakrát: nejprve s posunem vůči UTC +02:00, potom s posunem +01:00. Podle aktuálních pravidel připadne další jarní změna na 28. března 2027 a podzimní na 31. října 2027.

Tato pravidla nevymýšlí programátor. Udržuje je databáze časových pásem IANA, známá také jako tz nebo zoneinfo. Podle IANA se průběžně aktualizuje podle rozhodnutí státních orgánů o hranicích pásem, posunech vůči UTC a letním čase a koncoví uživatelé ji obvykle dostávají prostřednictvím aktualizací softwaru a operačního systému. Změna může přijít těsně před termínem: vydání 2026f z 27. října 2026 zachytilo, že Kuba 1. listopadu 2026 nepřejde na zimní čas.

Pro rozhodování je klíčové rozlišit dva druhy času. Okamžik je bod na časové ose, například přijetí platby nebo odeslání e-mailu, a dá se jednoznačně zapsat v UTC. Místní termín je dohoda lidí, například servis v úterý v 9:00 v Košicích. Jeho vztah k UTC určují pravidla pásma platná v den termínu, ne v den, kdy ho někdo zapsal.

Příklad: rezervace servisní firmy

Ilustrativní příklad, nikoli případ konkrétního klienta. Servisní firma plánuje ve webové aplikaci výjezdy techniků, a to i o víkendu. Zákazník si v lednu objedná pravidelnou kontrolu každé úterý v 9:00. Aplikace posílá připomínku 24 hodin před termínem, v noci exportuje dokončené zakázky do účetnictví a ráno pošle vedoucímu přehled tržeb za předchozí den.

Vývojář při ukládání přepočítal termín na UTC: 9:00 v lednu je 8:00 UTC. Po přechodu na letní čas aplikace stále plánuje 8:00 UTC, což je ale 10:00 místního času. Technik přijede o hodinu později, než zákazník čeká.

Další chyby jsou tišší. Připomínka „24 hodin předem“ k termínu v neděli 28. března 2027 v 9:00 odejde v sobotu v 8:00, protože mezi stejnými hodinami těchto dvou dnů uplyne jen 23 hodin. Noční export naplánovaný na 2:30 se v jarní noci nemusí spustit a v podzimní se může spustit dvakrát, podle toho, jak se chová plánovač. A pokud report „za včerejšek“ počítá den od půlnoci do půlnoci UTC, v zimě začíná v 1:00 a v létě ve 2:00 slovenského času.

Náprava neznamená přepisovat celou aplikaci. Pravidelný termín se uloží jako pravidlo „každé úterý v 9:00, Europe/Bratislava“, firma rozhodne, zda připomínka znamená přesně 24 hodin, nebo den předem ve stejný čas, export se přesune mimo noční hodinu změny a report se bude počítat podle slovenského kalendářního dne.

Jak ukládat jednotlivé druhy časových údajů

Tabulka slouží jako zadání pro vývojáře i jako kontrola existující aplikace. Každý sloupec v databázi a každé pole v API by mělo patřit do jednoho řádku.

Druh údaje Jak ho uložit Příklad
Okamžik události Bod v čase v UTC Přijetí platby, přihlášení, záznam v auditní stopě
Budoucí místní termín Místní datum a čas plus název pásma; UTC jen jako odvozená hodnota Servis 6. dubna 2027 v 9:00, Europe/Bratislava
Opakovaný termín Pravidlo opakování, místní čas začátku a pásmo Každé úterý v 9:00
Datum bez času Pouze datum, bez času a bez pásma Splatnost faktury, den dovolené
Čas vázaný na člověka, ne na místo Místní čas bez pásma, výslovně označený Denní upozornění v 8:00 tam, kde se uživatel právě nachází
Obchodní den v reportech Datum a pásmo firmy; hranice dne se přepočítají na UTC Tržby za 25. října 2026, den s 25 hodinami

Pozor na to, co dělá databáze. Podle dokumentace PostgreSQL se hodnota typu timestamp with time zone ukládá interně v UTC a původně zadané pásmo se neuchovává. Pokud vstup pásmo neobsahuje, databáze předpokládá pásmo z nastavení TimeZone a při výstupu čas převede do aktuálně nastaveného pásma. Pro okamžiky je to správně, pro budoucí místní termíny však potřebujete název pásma v samostatném sloupci.

Pro čas vázaný na člověka zná kalendářová norma iCalendar (RFC 5545) takzvaný plovoucí čas. Představuje stejnou hodinu bez ohledu na to, v jakém pásmu se člověk právě nachází. Používejte ho jen tehdy, když je to záměr, ne jako náhradu za chybějící pásmo.

Postup zavedení v existující aplikaci

1. Sepište všechny časové údaje

Projděte sloupce v databázi, pole v API, importy, exporty, plánované úlohy a reporty. Ke každému údaji zapište druh z tabulky a odkud pochází. Nejasné případy, například termín bez uloženého pásma, označte k doplnění.

2. Ukládejte název pásma, ne pevný posun

U termínů, poboček a uživatelů ukládejte název pásma IANA, například Europe/Bratislava. Pevný posun +01:00 platí jen část roku. Norma RFC 9557 používání pevných posunů místo pásma důrazně nedoporučuje: posun převzatý z lednové hodnoty by po přičtení šesti měsíců vedl k výsledku posunutému o hodinu.

3. Posílejte čas v API jednoznačně

V API používejte formát RFC 3339 s posunem vůči UTC, například 2027-04-06T09:00:00+02:00. RFC 9557 tento formát rozšiřuje o příponu s názvem pásma, například 2027-04-06T09:00:00+02:00[Europe/Bratislava]. U místních termínů tak druhá strana dostane okamžik i pásmo, podle kterého má čas dále přepočítávat. Pokud příponě nerozumí, pošlete název pásma v samostatném poli. Podle téže normy mohou odesílatel a příjemce používat různé verze databáze pásem, proto musí integrace zvládnout i neznámý název pásma.

4. Zobrazujte čas ve správném pásmu

Pokud aplikace formátuje čas v prohlížeči pomocí Intl.DateTimeFormat, podle dokumentace MDN přijímá volba timeZone název pásma IANA a bez ní se použije pásmo prostředí, ve kterém kód běží. Zákazník, který má v telefonu nastavené jiné pásmo, například na dovolené, by tak viděl termín slovenského servisu posunutý. Rozhodněte, zda se termín zobrazuje v pásmu místa konání, nebo uživatele, a pásmo u času uveďte.

5. Určete pravidla pro neexistující a zdvojený čas

Norma iCalendar na to má jasná pravidla: pokud místní čas nastane dvakrát, platí jeho první výskyt, a pokud neexistuje, přepočítá se s posunem platným před změnou. Termín 28. března 2027 ve 2:30 by tak připadl na 3:30 letního času. Norma zároveň upozorňuje, že pravidlo opakování může vygenerovat neplatné datum, například 30. února, nebo neexistující místní čas. Zvolte jedno chování, zapište ho do zadání a v rezervačním formuláři takové časy raději vůbec nenabízejte.

6. Upravte plánované úlohy a reporty

Technické úlohy, jako zálohy či exporty, plánujte v UTC nebo mimo noční hodinu změny a navrhněte je tak, aby opakované spuštění nevytvořilo duplicitu. Úlohy vázané na místní čas, například ranní rozesílání, plánujte podle pásma. Report za den počítejte od místní půlnoci po další místní půlnoc převedené na UTC a průměr za hodinu nedělte pevným číslem 24.

7. Aktualizujte databázi pásem a testujte dny změny

Údaje o pásmech přicházejí přes operační systém, runtime jazyka, knihovny a databázi, proto jejich aktualizaci zařaďte do pravidelné údržby. Po změně pravidel přepočítejte odvozené hodnoty UTC budoucích termínů. Automatické testy spouštějte s pevnými daty, například 28. března a 31. října 2027, a s různým pásmem serveru i prohlížeče.

Výhody a nevýhody jednotlivých přístupů

  • Vše v UTC: jednoduché porovnávání a řazení okamžiků, ale pravidelné termíny se při změně času posunou a budoucí termíny i při změně pravidel pásma.
  • Vše v místním čase serveru: pohodlné pro firmu v jedné zemi, ale nejednoznačné v podzimní noci a citlivé na přesun serveru do jiného pásma.
  • Místní čas, pásmo a odvozené UTC: správné pro termíny, ale vyžaduje více sloupců, jasná pravidla a přepočet po aktualizaci databáze pásem.
  • Zobrazení v pásmu uživatele: přirozené u online schůzek, matoucí u služeb na konkrétním místě, kde dává smysl pásmo místa konání.

Pro většinu firemních aplikací doporučujeme kombinaci: okamžiky v UTC, termíny v místním čase s pásmem a zobrazení v pásmu místa konání s jasně uvedeným pásmem.

Časté chyby

  • Pravidelný termín uložený jako jeden čas v UTC.
  • Pevný posun +01:00 nebo pásmo serveru místo názvu pásma u termínu.
  • Předpoklad, že den má vždy 24 hodin, u připomínek, lhůt a reportů.
  • Formátování času v prohlížeči bez určeného pásma.
  • Noční úlohy naplánované mezi 2:00 a 3:00 místního času.
  • Zastaralá databáze pásem na serveru, v kontejneru nebo v aplikaci.
  • Testy, které běží jen v jednom pásmu a nikdy ne v den změny času.

Kontrolní seznam pro zadání a převzetí

  • Každý časový údaj má určený druh: okamžik, místní termín, opakování, datum nebo obchodní den.
  • Termíny a pobočky mají uložený název pásma IANA.
  • API posílá čas s posunem vůči UTC a u termínů i název pásma.
  • Rozhraní ukazuje, v jakém pásmu je čas uveden.
  • Chování pro neexistující a zdvojený čas i pro opakování je zapsané a otestované.
  • Reporty počítají obchodní den v pásmu firmy a zvládnou den s 23 i 25 hodinami.
  • Aktualizace databáze pásem je součástí údržby a po ní se přepočítají budoucí termíny.

Časté dotazy

Stačí ukládat všechny časy v UTC?

Pro okamžiky ano, pro budoucí termíny dohodnuté na místní čas ne. Podle RFC 9557 se pravidla pojmenovaného pásma mohou měnit a platí ta, která jsou aktuální v době zpracování údaje. Ukládejte proto místní čas s pásmem a UTC si odvoďte.

Musíme to řešit, když máme jen tuzemské zákazníky?

Ano. Změna času se týká i jednoho pásma a server, databáze či telefon zákazníka mohou být nastaveny na jiné pásmo než firma. Chyby se proto mohou ukázat až v noci změny času nebo v týdnech po ní.

Co s termínem v hodině, která neexistuje?

Rezervační formulář by ji neměl nabídnout. Pokud takový termín vznikne z importu nebo z opakování, použijte zapsané pravidlo, například posun na 3:30 podle iCalendar, a dotčené rezervace ukažte dispečerovi ke kontrole.

Jak často se databáze časových pásem mění?

Průběžně, když státy změní hranice pásem, posun vůči UTC nebo pravidla letního času. Změna může vyjít jen pár dní před účinností, jak ukázalo vydání 2026f. Aktualizujte ji proto spolu s běžnými aktualizacemi systému, ne až při problému.

Potřebujete prověřit časy ve vaší aplikaci?

Pokud vaše aplikace pracuje s rezervacemi, plánováním, připomínkami nebo denními reporty, pomůžeme s inventurou časových údajů, úpravou datového modelu, testy dnů změny času i opravou už uložených termínů. Více o naší práci najdete na stránce webové aplikace na míru.

Pošlete nám krátký popis, které termíny nebo reporty se vám posouvají a kdy se to stalo. Další články o vývoji a provozu aplikací najdete na blogu.

Zdroje

Vyberte si, které cookies povolíte. Volbu můžete kdykoli změnit přes odkaz v patičce nebo tlačítko cookies.

Ochrana soukromí a podrobnosti o cookies