TimeZone — to klasa Foundation w iOS i macOS, abstrahująca informacje o strefach czasowych w celu poprawnej konwersji czasu między regionami geograficznymi. Według Apple Developer Documentation, 2024, TimeZone udostępnia metody do pracy z identyfikatorami stref czasowych (IANA Time Zone Database), przesunięciami względem UTC oraz regułami zmiany czasu na letni. Klasa jest zintegrowana z DateFormatter i Calendar, zapewniając automatyczne zastosowanie właściwej strefy czasowej podczas formatowania dat. W przeciwieństwie do ręcznego obliczania przesunięcia, TimeZone automatycznie aktualizuje dane przy zmianie strefy czasowej urządzenia.
Najważniejsze
TimeZone — to typ wartościowy w Swift, dostarczający informacji o geograficznej strefie czasowej: przesunięciu względem UTC, nazwie, skrócie i regułach zmiany czasu na letni. W Objective-C klasa nazywa się NSTimeZone. Obie klasy opierają się na IANA Time Zone Database (znanej również jako baza Olsona), która zawiera historię zmian stref czasowych od 1970 roku.
Każda instancja TimeZone przechowuje identyfikator strefy czasowej (np. Europe/Moscow), bieżące przesunięcie w sekundach od UTC, flagę isDaylightSavingTime oraz datę następnej zmiany. Identyfikator jest kluczem podstawowym: podczas inicjalizacji TimeZone(identifier:) system ładuje odpowiedni wpis z bazy danych stref czasowych urządzenia.
Według danych IANA (2024), baza danych zawiera ponad 600 unikalnych identyfikatorów stref czasowych. Apple dostarcza wycinek tej bazy w każdej wersji iOS i macOS, co gwarantuje jednolitość obliczeń na wszystkich urządzeniach bez konieczności zapytań sieciowych.
Architektura TimeZone w Foundation opiera się na dwupoziomowym systemie: identyfikatorze strefy czasowej (nazwa czytelna dla człowieka) i jej reprezentacji numerycznej (przesunięcie od UTC). System automatycznie wybiera bieżącą strefę czasową z ustawień urządzenia, ale programista może ją nadpisać dla konkretnych operacji formatowania.
TimeZone jest ściśle powiązany z Calendar i DateFormatter. Podczas formatowania daty DateFormatter używa właściwości timeZone instancji TimeZone do przekształcenia absolutnego momentu w czasie (Date) na reprezentację tekstową w odpowiedniej strefie czasowej. Jeśli timeZone nie jest ustawiony, używana jest domyślna strefa czasowa systemu — TimeZone.current.
| Typ | Inicjalizacja | Cechy |
|---|---|---|
| Bieżący | TimeZone.current | Automatycznie aktualizuje się przy zmianie regionu w ustawieniach, śledzi zmianę czasu na letni |
| Stały | TimeZone(identifier:) | Nie zależy od regionu urządzenia. Stale stosuje wybrany identyfikator |
| UTC | TimeZone(secondsFromGMT: 0) | Strefa czasowa bez korekty. Identyfikator: GMT |
| Z dowolnym przesunięciem | TimeZone(secondsFromGMT: 10800) | Stałe przesunięcie w sekundach. Nie uwzględnia czasu letniego |
Ważny niuans: TimeZone(identifier:) zwraca nil dla nieznanych identyfikatorów. To częsta przyczyna awarii aplikacji — programiści zapominają obsłużyć opcjonalną wartość, przekazując nieprawidłowy identyfikator z danych wprowadzonych przez użytkownika. Dla identyfikatorów IANA wielkość liter ma znaczenie: Europe/Moscow — poprawnie, europe/moscow — nil.
IANA Time Zone Database używa formatu „Region/Miasto" (Continent/City), gdzie region to jeden z kontynentów (Africa, America, Asia, Atlantic, Australia, Europe, Indian, Pacific) lub ocean, a miasto to największa miejscowość w zasięgu strefy czasowej. Ten format gwarantuje unikalność i czytelność identyfikatora.
Oprócz podstawowego formatu, TimeZone obsługuje trzy dodatkowe sposoby identyfikacji: skróty (MSK, EST, PST), trzyliterowe kody stref czasowych (GMT, UTC) oraz numeryczne przesunięcia (+0300, -0500). Jednak skróty są niejednoznaczne: EST może oznaczać zarówno Eastern Standard Time (GMT-5), jak i Eastern Summer Time (GMT+10) w Australii. Apple zaleca używanie wyłącznie identyfikatorów IANA.
import Foundation
// Pobierz wszystkie znane identyfikatory stref czasowych
let allIdentifiers: [String] = TimeZone.knownTimeZoneIdentifiers
print("Łączna liczba stref czasowych: \(allIdentifiers.count)")
// Filtruj według regionu
let europeZones = allIdentifiers.filter { $0.hasPrefix("Europe/") }
print("Strefy europejskie: \(europeZones)")
// Skróty (niezalecane w produkcji)
if let moscowTimeZone = TimeZone(abbreviation: "MSK") {
print("Sekund MSK od GMT: \(moscowTimeZone.secondsFromGMT())")
}
// Znajdź identyfikator według przesunięcia
let utcPlus3 = TimeZone(secondsFromGMT: 10800)
print("Identyfikator: \(utcPlus3.identifier)")
Skróty w TimeZone.abbreviationDictionary zawierają skróty dla wszystkich znanych stref czasowych, ale ten słownik nie gwarantuje unikalności: klucz PST może odpowiadać zarówno America/Los_Angeles, jak i Pacific/Pago_Pago. W kodzie produkcyjnym zawsze używaj identyfikatorów IANA.
TimeZone automatycznie uwzględnia zmianę czasu na letni i zimowy (DST — Daylight Saving Time) dla wszystkich regionów, w których jest to praktykowane. System używa danych historycznych z IANA Time Zone Database, które zawierają dokładne daty zmian dla każdej strefy czasowej. Właściwość isDaylightSavingTime zwraca true, jeśli strefa czasowa obecnie znajduje się w czasie letnim.
Metoda nextDaylightSavingTimeTransition pozwala sprawdzić datę następnej zmiany, co jest przydatne do planowania przyszłych wydarzeń. Ta funkcjonalność jest szczególnie ważna dla regionów z częstymi zmianami zasad DST, takich jak Brazylia czy Maroko — do 2024 roku Brazylia corocznie zmieniała daty przejścia, a ręczne obliczanie prowadziło do błędów w aplikacjach.
Według danych Apple WWDC 2023, biblioteka ICU (International Components for Unicode), leżąca u podstaw Foundation, aktualizuje dane DST przy każdej aktualizacji iOS. Aplikacje nie powinny przechowywać danych o czasie letnim w pamięci podręcznej dłużej niż jeden dzień po aktualizacji systemu — baza IANA może ulec zmianie nawet bez aktualizacji wersji systemu operacyjnego poprzez korekty stref czasowych.
import Foundation
// Sprawdź DST dla Europy/Moskwy
let moscow = TimeZone(identifier: "Europe/Moscow")!
let now = Date()
let isMoscowDST = moscow.isDaylightSavingTime(for: now)
print("Moskwa obecnie w DST: \(isMoscowDST)")
// Pobierz datę następnej zmiany DST
if let nextTransition = moscow.nextDaylightSavingTimeTransition(
after: now
) {
let dstOffset = moscow.daylightSavingTimeOffset(
for: nextTransition
)
print("Następna zmiana: \(nextTransition), przesunięcie DST: \(dstOffset)s")
}
// Bezpieczna konwersja z uwzględnieniem DST
let newYork = TimeZone(identifier: "America/New_York")!
let offsetNY = newYork.secondsFromGMT(for: now)
print("Bieżące przesunięcie NY: \(offsetNY / 3600)h")
Krytyczny niuans: secondsFromGMT(for:) uwzględnia DST dla określonej daty, natomiast secondsFromGMT() — tylko dla bieżącego czasu. Podczas formatowania dat historycznych zawsze używaj wersji z parametrem Date: secondsFromGMT(for: someHistoricalDate). Różnica może wynosić 1–2 godziny, co jest krytyczne dla logów lub danych historycznych.
Formatowanie daty z konkretną strefą czasową — to najczęstsze zadanie podczas pracy z TimeZone. DateFormatter używa właściwości timeZone do przekształcenia Date na tekst. Jeśli timeZone nie zostanie jawnie ustawiony, formatatter używa TimeZone.current — strefy czasowej ustawionej na urządzeniu użytkownika, co może prowadzić do nieoczekiwanych wyników dla danych serwerowych.
import Foundation
// Formatuj datę w określonej strefie czasowej
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
let tokyo = TimeZone(identifier: "Asia/Tokyo")!
formatter.timeZone = tokyo
let tokyoTime = formatter.string(from: Date())
print("Czas w Tokio: \(tokyoTime)")
// Dostępne identyfikatory do wyboru przez użytkownika
let displayNames: [(String, String)] = TimeZone.knownTimeZoneIdentifiers
.prefix(20)
.map { ($0, TimeZone(identifier: $0)!.localizedName(
for: .generic, locale: .current
)) }
// Porównaj dwie strefy czasowe
let london = TimeZone(identifier: "Europe/London")!
let difference = tokyo.secondsFromGMT(for: Date())
- london.secondsFromGMT(for: Date())
print("Różnica Tokio-Londyn: \(difference / 3600)h")
// Pracuj ze słownikiem skrótów
let knownAbbrevs = TimeZone.abbreviationDictionary
for (abbr, ident) in knownAbbrevs.sorted(by: { $0.key < $1.key }).prefix(5) {
print("\(abbr) -> \(ident)")
}
Zlokalizowana nazwa strefy czasowej poprzez localizedName(for:locale:) zwraca czytelną dla człowieka nazwę w określonym języku. Na przykład dla Europe/Moscow z lokalizacją rosyjską metoda zwróci „Moskwa", a z angielską — „Moscow Time". Dostępne są style: .standard (nazwa standardowa), .daylightSaving (czas letni) i .shortGeneric (krótka).
import Foundation
let paris = TimeZone(identifier: "Europe/Paris")!
let nameRU = paris.localizedName(
for: .standard,
locale: Locale(identifier: "ru_RU")
)
print("Nazwa rosyjska: \(nameRU)")
// Sprawdź, czy region jest tego samego dnia
let isSameDay = Calendar.current.isDate(
Date(),
equalTo: Date(),
toGranularity: .day
)
print("Ten sam dzień w różnych strefach: \(isSameDay)")
Serializacja identyfikatora strefy czasowej — najlepsza praktyka przechowywania TimeZone w bazach danych lub UserDefaults. Zachowaj identyfikator (ciąg typu Europe/Moscow), a nie przesunięcie w sekundach ani skrót. Przesunięcie może się zmieniać przy zmianie DST, a skrót jest niejednoznaczny. Odtworzenie: TimeZone(identifier: savedString).
Używanie stałego przesunięcia zamiast identyfikatora strefy czasowej — to najczęstszy błąd. TimeZone(secondsFromGMT: 10800) nie uwzględnia DST, więc dla Europe/Moscow latem ta konstrukcja daje błędne przesunięcie o 1 godzinę. Zawsze używaj identyfikatora IANA dla regionów z czasem letnim.
Brak obsługi nil podczas inicjalizacji TimeZone(identifier:) — drugi najczęstszy błąd. Jeśli użytkownik wprowadził identyfikator z błędem (np. „moscow" zamiast „Europe/Moscow"), konstruktor zwraca nil. Bez obsługi wartości opcjonalnej aplikacja ulega awarii z runtime error. Użyj guard let lub TimeZone(identifier:) ze znanym fallbackiem.
Ignorowanie DST przy pracy z przyszłymi datami. TimeZone.secondsFromGMT(for:) — jedyny poprawny sposób uzyskania przesunięcia dla konkretnej daty. Użycie secondsFromGMT() bez parametru dla dat historycznych lub przyszłych daje przesunięcie dla bieżącego momentu, co może nie odpowiadać rzeczywistemu przesunięciu w danej dacie, szczególnie dla regionów z odwołaniem lub wprowadzeniem DST.
Według danych Stack Overflow (2024), około 15% pytań dotyczących DateFormatter wiąże się z nieprawidłowym ustawieniem timeZone. Typowy scenariusz: serwer wysyła datę w UTC, programista formatuje ją bez ustawienia timeZone formatatora, a data wyświetla się w strefie czasowej urządzenia, powodując zamieszanie u użytkowników z różnych regionów. Zasada: zawsze jawnie ustawiaj timeZone formatatora dla danych serwerowych.
Często zadawane pytania
TimeZone — klasa Foundation do pracy ze strefami czasowymi w iOS i macOS. Zapewnia informacje o przesunięciu względem UTC, regułach zmiany czasu na letni i identyfikatorach stref czasowych na podstawie IANA Time Zone Database.
Trzy formaty: identyfikatory IANA (Europe/Moscow), skróty (MSK, EST) i numeryczne przesunięcia (+0300). Apple zaleca używanie identyfikatorów IANA jako jedynego jednoznacznego formatu w kodzie produkcyjnym.
Automatycznie poprzez metody secondsFromGMT(for:) i isDaylightSavingTime(for:). TimeZone używa danych historycznych IANA, aktualizowanych przy każdej wersji iOS, co gwarantuje poprawne przejścia DST dla dowolnej daty.
TimeZone.current zwraca strefę czasową wybraną przez użytkownika w ustawieniach (może różnić się od geograficznej). TimeZone.system zwraca strefę czasową urządzenia, która jest automatycznie określana na podstawie geopozycji i nie może być nadpisana przez użytkownika.
TimeZone.current zwraca bieżącą strefę czasową urządzenia. Aby uzyskać identyfikator, użyj właściwości identifier: TimeZone.current.identifier. Aby uzyskać zlokalizowaną nazwę, wywołaj localizedName(for:locale:).
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również