Datum a čas v mobilním vývoji — jedno z klíčových témat v mobilním inženýrství. iOS používá DateFormatter a ISO8601DateFormatter, Android — DateTimeFormatter a LocalDate z java.time. Podle Android Developer Docs, správný výběr mobilního API pro formátování data a času přímo ovlivňuje výkon a správnost zobrazení v různých časových pásmech.
Hlavní body
Na iOS se datum a čas tradičně formátuje pomocí DateFormatter z Foundation. Tato třída převádí Date na řetězec a zpět pomocí zadaného vzoru, locale a časového pásma. Hlavní problém DateFormatter — není vláknově bezpečný a pomalu se vytváří, proto se v mobilním kódu instance povinně kešují. Datum v mobilním vývoji na iOS vyžaduje správný formátovač.
Vytvoření DateFormatter trvá asi 1 milisekundu kvůli parsování vzoru dateFormat, načítání locale a určení časového pásma. Při formátování seznamu stovek dat vede opakované vytváření k znatelným lagům v UI. Řešení — vytvořit formátovač jednou pro celou obrazovku a znovu používat. Pro vícevláknový přístup použijte synchronizaci přes DispatchQueue.
import Foundation
// Kešovaný DateFormatter — vytváří se jednou
private let dateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "dd.MM.yyyy HH:mm"
formatter.locale = Locale(identifier: "ru_RU")
formatter.timeZone = TimeZone(identifier: "Europe/Moscow")
return formatter
}()
// ISO8601DateFormatter — lehká alternativa
private let isoFormatter: ISO8601DateFormatter = {
let formatter = ISO8601DateFormatter()
formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
return formatter
}()
let now = Date()
let formattedDate = dateFormatter.string(from: now)
// "01.07.2026 14:30"
let isoDate = isoFormatter.string(from: now)
// "2026-07-01T14:30:00.000Z"
Pro formát ISO 8601 použijte ISO8601DateFormatter — nevyžaduje uvedení vzoru a pracuje rychleji než DateFormatter. Na iOS 10+ je tato třída preferovaná pro práci se serverovými daty. Pro staré formáty používejte DateFormatter, ale povinně kešujte instanci jako statickou vlastnost nebo singleton.
Na Android se datum a čas zpracovává přes balíček java.time, dostupný od API 26. Hlavní třídy: LocalDate (pouze datum), LocalTime (pouze čas), LocalDateTime (datum a čas bez časového pásma) a ZonedDateTime (s časovým pásmem). DateTimeFormatter — vláknově bezpečný formátovač pro mobilní platformu Android. Datum v mobilních aplikacích se formátuje přes DateTimeFormatter a LocalDate bez ztráty výkonu.
LocalDate ukládá datum bez času — ideální pro narozeniny. ZonedDateTime ukládá úplnou informaci s časovým pásmem. DateTimeFormatter podporuje předdefinované vzory (ISO_LOCAL_DATE, ISO_DATE_TIME) a vlastní přes ofPattern. Všechny třídy java.time jsou neměnné a vláknově bezpečné, což eliminuje race conditions při práci ve více vláknech.
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter
import java.time.ZoneId
// Vytvoření a formátování aktuálního data
val today = LocalDate.now()
val formatter = DateTimeFormatter.ofPattern("dd.MM.yyyy")
val formatted = today.format(formatter)
// Práce s časovými pásmy
val utcTime = ZonedDateTime.now(ZoneId.of("UTC"))
val moscowTime = utcTime.withZoneSameInstant(ZoneId.of("Europe/Moscow"))
DateTimeFormatter je výrazně výkonnější než SimpleDateFormat, protože neparsuje vzor při každém volání. Doporučuje se vytvářet konstanty formátovačů na úrovni třídy nebo objektu společníka. Pro konverzi Unix Timestamp použijte Instant.ofEpochSecond, poté převeďte na LocalDateTime nebo ZonedDateTime přes časové pásmo.
Správné datum a čas na obrazovce uživatele závisí na správném zpracování časových pásem. Datum a čas v mobilním vývoji pro Android vyžaduje zohlednění časového pásma zařízení. Zlaté pravidlo: ukládejte čas v UTC, konvertujte do místního časového pásma pouze pro zobrazení. iOS poskytuje TimeZone z Foundation, Android — ZoneId z java.time. Obě API pro mobilní platformy podporují automatické určení aktuálního časového pásma zařízení. Datum v mobilní aplikaci závisí na ZoneId a TimeZone.
Letní čas vytváří nejednoznačnost: jeden okamžik v čase může být reprezentován dvěma různými místními hodinami. java.time a Foundation zohledňují DST automaticky. Používejte identifikátory IANA (Europe/Moscow, America/New_York), nikoli zkratky (MSK, EST) — zkratky jsou nejednoznačné a ignorují přechod na letní čas.
import Foundation
// Konverze UTC na místní čas na iOS
func formatToLocalTime(utcDate: Date, timeZoneId: String) -> String {
let formatter = DateFormatter()
formatter.dateFormat = "dd.MM.yyyy HH:mm"
formatter.timeZone = TimeZone(identifier: timeZoneId)
return formatter.string(from: utcDate)
}
// Určení časového pásma zařízení
let localTimeZone = TimeZone.current
let isDST = localTimeZone.isDaylightSavingTime()
let secondsFromGMT = localTimeZone.secondsFromGMT()
Na Android použijte ZoneId.systemDefault() pro získání časového pásma zařízení. Při konverzi UTC na místní čas vytvářejte ZonedDateTime s ZoneOffset.UTC a aplikujte withZoneSameInstant. Pro stará API bez java.time použijte TimeZone.getDefault() a Calendar — ale raději rovnou připojte ThreeTenABP.
import java.time.Instant
import java.time.ZonedDateTime
import java.time.ZoneId
// Konverze Unix Timestamp na místní čas
fun toLocalDisplay(unixSeconds: Long): String {
val instant = Instant.ofEpochSecond(unixSeconds)
val local = instant.atZone(ZoneId.systemDefault())
return local.format(DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm"))
}
ThreeTenABP — knihovna-adaptér portující java.time na Android API pod 26. Poskytuje stejné třídy: LocalDate, LocalTime, ZonedDateTime, DateTimeFormatter, Instant. Pokud aplikace podporuje Android 5 nebo 6 (API 21-25), ThreeTenABP je jediný způsob, jak používat moderní mobilní API bez přechodu na zastaralé Calendar. Datum a čas v mobilním vývoji pro stará zařízení je implementován přes tuto knihovnu. Pro stará zařízení je datum a čas implementován přes ThreeTenABP.
Knihovna vyžaduje inicializaci v metodě Application.onCreate() voláním AndroidThreeTen.init(). To načte data časových pásem z assets. Po inicializaci je API zcela identické s java.time — kód pro API 26+ a pro API 21+ bude stejný. Velikost knihovny je asi 400 KB, což je přijatelné pro většinu mobilních aplikací.
import org.threeten.bp.LocalDate
import org.threeten.bp.format.DateTimeFormatter
// Inicializace v Application
class App : Application() {
override fun onCreate() {
super.onCreate()
AndroidThreeTen.init(this)
}
}
// API je identické s java.time
val today = LocalDate.now()
val formatted = today.format(DateTimeFormatter.ISO_LOCAL_DATE)
Výkon ThreeTenABP je srovnatelný s originálním java.time na moderních zařízeních. Na starých zařízeních může dojít ke zpoždění při prvním volání kvůli načítání dat časových pásem. Migrujte stávající kód z Calendar na ThreeTenABP postupně: nahrazujte Date a Calendar za Instant a LocalDate v nových modulech, starý kód ponechte beze změn.
Unix Timestamp — počet sekund, které uplynuly od 1. ledna 1970 00:00 UTC. Je to univerzální formát pro ukládání a výměnu času mezi serverem a klientem. Čas na zařízení se však může lišit od reálného kvůli nastavení uživatele. Pro synchronizaci se používá NTP — protokol přesného času přes UDP. Unix Timestamp je formát, ve kterém se ukládá datum a čas na serveru.
Date().timeIntervalSince1970 na iOS vrací sekundy od roku 1970. Na Android System.currentTimeMillis() vrací milisekundy. Pro přesnost použijte NTP: knihovna TrueTime pro iOS a AndroidNtp pro Android odesílají požadavek na NTP server a vypočítávají časový posun. Přesnost NTP je 1-10 ms v lokální síti a 10-100 ms přes internet.
import Foundation
// Získání Unix Timestamp na iOS
let seconds = Date().timeIntervalSince1970
let milliseconds = Int64(seconds * 1000)
// NTP synchronizace s TrueTime
TrueTime.shared.start { result in
switch result {
case .success:
let ntpTime = TrueTime.shared.now()
print("Přesný čas: \(ntpTime)")
case .failure(let error):
print("Chyba NTP: \(error)")
}
}
Bez synchronizace v mobilní aplikaci jsou možné chyby: nesprávné označení událostí, desynchronizace se serverem. Čas v mobilním vývoji se synchronizuje přes NTP jednorázově při spuštění aplikace, poté se používá vypočtený posun. Pro kritické aplikace vyžadující přesnost (finance, logistika) je protokol NTP povinný před odesláním dat na server.
Často kladené otázky
DateFormatter parsuje vzor formátování, načítá locale a určuje časové pásmo při každém vytvoření instance. Kešujte formátovač jako statickou konstantu nebo použijte ISO8601DateFormatter pro ISO 8601 — je lehčí a rychlejší.
DateTimeFormatter z balíčku java.time — vláknově bezpečný a výkonný. Dostupný od API 26. Pro staré verze použijte ThreeTenABP, který poskytuje identické API.
Připojte ThreeTenABP — backport java.time pro Android API 21+. Inicializace přes AndroidThreeTen.init() v Application.onCreate(). API je zcela identické s originálním java.time.
Ano, ukládejte čas v UTC na serveru a v lokální databázi. Konvertujte do místního časového pásma uživatele pouze ve fázi zobrazení. Tím vyloučíte chyby při změně časového pásma nebo při cestování.
Date().timeIntervalSince1970 vrací počet sekund od 1. ledna 1970. Pro milisekundy vynásobte 1000 a převeďte na Int64. Pro přesnou synchronizaci použijte knihovnu TrueTime s NTP serverem.
Souhrn
Vyvineme mobilní aplikaci na klíč
IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.