TimeZone — це клас Foundation в iOS та macOS, який абстрагує інформацію про часові пояси для коректного перетворення часу між географічними регіонами. За даними Apple Developer Documentation, 2024, TimeZone надає методи для роботи з ідентифікаторами часових поясів (IANA Time Zone Database), зміщеннями відносно UTC та правилами переходу на літній час. Клас інтегрований з DateFormatter та Calendar, забезпечуючи автоматичне застосування правильного часового поясу при форматуванні дат. На відміну від ручного розрахунку зміщення, TimeZone автоматично оновлює дані при зміні часового поясу пристрою.
Головне
TimeZone — це value-тип в Swift, який надає інформацію про географічний часовий пояс: зміщення відносно UTC, назву, абревіатуру та правила переходу на літній час. В Objective-C клас називається NSTimeZone. Обидва класи спираються на IANA Time Zone Database (також відому як база даних Olson), яка містить історію змін часових поясів з 1970 року.
Кожен екземпляр TimeZone зберігає ідентифікатор часового поясу (наприклад, Europe/Moscow), поточне зміщення в секундах від UTC, прапорець isDaylightSavingTime та дату наступного переходу. Ідентифікатор є первинним ключем: при ініціалізації TimeZone(identifier:) система завантажує відповідний запис з бази даних часових поясів пристрою.
За даними IANA (2024), база даних містить понад 600 унікальних ідентифікаторів часових поясів. Apple постачає знімок цієї бази даних в складі кожної версії iOS та macOS, що гарантує єдність розрахунків на всіх пристроях без необхідності мережевих запитів.
Архітектура TimeZone в Foundation побудована на дворівневій системі: ідентифікатор часового поясу (людиночитана назва) та його числове представлення (зміщення від UTC). Система автоматично вибирає поточний часовий пояс з налаштувань пристрою, але розробник може перевизначити його для конкретних операцій форматування.
TimeZone тісно пов’язаний з Calendar та DateFormatter. При форматуванні дати DateFormatter використовує властивість timeZone екземпляра TimeZone для перетворення абсолютного моменту часу (Date) у рядкове представлення в потрібному часовому поясі. Якщо timeZone не задано, використовується системний часовий пояс за замовчуванням — TimeZone.current.
| Тип | Ініціалізація | Особливості |
|---|---|---|
| Поточний | TimeZone.current | Автоматично оновлюється при зміні регіону в налаштуваннях, відстежує перехід на літній час |
| Фіксований | TimeZone(identifier:) | Не залежить від регіону пристрою. Постійно застосовує вибраний ідентифікатор |
| UTC | TimeZone(secondsFromGMT: 0) | Часовий пояс без корекції. Ідентифікатор: GMT |
| З довільним зміщенням | TimeZone(secondsFromGMT: 10800) | Фіксоване зміщення в секундах. Не враховує літній час |
Важливий нюанс: TimeZone(identifier:) повертає nil для невідомих ідентифікаторів. Це часта причина падіння додатку — розробники забувають обробити опціональне значення, передаючи невірний ідентифікатор з користувацького вводу. Для ідентифікаторів IANA регістр має значення: Europe/Moscow — коректно, europe/moscow — nil.
IANA Time Zone Database використовує формат «Регіон/Місто» (Continent/City), де регіон — один з континентів (Africa, America, Asia, Atlantic, Australia, Europe, Indian, Pacific) або океан, а місто — найбільший населений пункт в зоні дії часового поясу. Цей формат гарантує унікальність та читабельність ідентифікатора.
Крім основного формату, TimeZone підтримує три додаткових способи ідентифікації: абревіатури (MSK, EST, PST), трибуквені коди часових зон (GMT, UTC) та числові зміщення (+0300, -0500). Однак абревіатури неоднозначні: EST може означати як Eastern Standard Time (GMT-5), так і Eastern Summer Time (GMT+10) в Австралії. Apple рекомендує використовувати виключно IANA-ідентифікатори.
import Foundation
// Отримати всі відомі ідентифікатори часових поясів
let allIdentifiers: [String] = TimeZone.knownTimeZoneIdentifiers
print("Всього часових поясів: \(allIdentifiers.count)")
// Фільтрувати за регіоном
let europeZones = allIdentifiers.filter { $0.hasPrefix("Europe/") }
print("Європейські часові пояси: \(europeZones)")
// Абревіатури (не рекомендовано для продакшну)
if let moscowTimeZone = TimeZone(abbreviation: "MSK") {
print("Секунд MSK від GMT: \(moscowTimeZone.secondsFromGMT())")
}
// Знайти ідентифікатор за зміщенням
let utcPlus3 = TimeZone(secondsFromGMT: 10800)
print("Ідентифікатор: \(utcPlus3.identifier)")
Абревіатури в TimeZone.abbreviationDictionary містять скорочення для всіх відомих часових поясів, але цей словник не гарантує унікальність: ключ PST може відповідати як America/Los_Angeles, так і Pacific/Pago_Pago. Для production-коду завжди використовуйте ідентифікатори IANA.
TimeZone автоматично враховує перехід на літній та зимовий час (DST — Daylight Saving Time) для всіх регіонів, де це прийнято. Система використовує історичні дані з IANA Time Zone Database, які включають точні дати переходів для кожного часового поясу. Властивість isDaylightSavingTime повертає true, якщо часовий пояс в даний момент знаходиться в літньому часі.
Метод nextDaylightSavingTimeTransition дозволяє дізнатися дату наступного переходу, що корисно для планування подій в майбутньому. Особливо важлива ця функціональність для регіонів з частими змінами правил DST, таких як Бразилія чи Марокко — до 2024 року Бразилія щорічно змінювала дати переходу, і ручний розрахунок призводив до помилок в додатках.
За даними Apple WWDC 2023, бібліотека ICU (International Components for Unicode), що лежить в основі Foundation, оновлює дані DST при кожному оновленні iOS. Додатки не повинні кешувати дані про літній час довше, ніж на один день після оновлення системи — база IANA може змінитися навіть без оновлення версії ОС через коригування часових поясів.
import Foundation
// Перевірити DST для Europe/Moscow
let moscow = TimeZone(identifier: "Europe/Moscow")!
let now = Date()
let isMoscowDST = moscow.isDaylightSavingTime(for: now)
print("Москва зараз у DST: \(isMoscowDST)")
// Отримати дату наступного переходу DST
if let nextTransition = moscow.nextDaylightSavingTimeTransition(
after: now
) {
let dstOffset = moscow.daylightSavingTimeOffset(
for: nextTransition
)
print("Наступний перехід: \(nextTransition), зміщення DST: \(dstOffset)s")
}
// Безпечне перетворення з урахуванням DST
let newYork = TimeZone(identifier: "America/New_York")!
let offsetNY = newYork.secondsFromGMT(for: now)
print("Поточне зміщення NY: \(offsetNY / 3600)h")
Критичний нюанс: secondsFromGMT(for:) враховує DST для вказаної дати, а secondsFromGMT() — тільки для поточного часу. При форматуванні історичних дат завжди використовуйте версію з параметром Date: secondsFromGMT(for: someHistoricalDate). Різниця може досягати 1–2 годин, що критично для логів або історичних даних.
Форматування дати з конкретним часовим поясом — найбільш часта задача при роботі з TimeZone. DateFormatter використовує властивість timeZone для перетворення Date в рядок. Якщо не задати timeZone явно, форматер використовує TimeZone.current — часовий пояс, встановлений на пристрої користувача, що може призвести до неочікуваних результатів для серверних даних.
import Foundation
// Форматувати дату в конкретному часовому поясі
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("Час у Токіо: \(tokyoTime)")
// Доступні ідентифікатори для вибору користувача
let displayNames: [(String, String)] = TimeZone.knownTimeZoneIdentifiers
.prefix(20)
.map { ($0, TimeZone(identifier: $0)!.localizedName(
for: .generic, locale: .current
)) }
// Порівняти два часові пояси
let london = TimeZone(identifier: "Europe/London")!
let difference = tokyo.secondsFromGMT(for: Date())
- london.secondsFromGMT(for: Date())
print("Різниця Токіо-Лондон: \(difference / 3600)h")
// Працювати зі словником абревіатур
let knownAbbrevs = TimeZone.abbreviationDictionary
for (abbr, ident) in knownAbbrevs.sorted(by: { $0.key < $1.key }).prefix(5) {
print("\(abbr) -> \(ident)")
}
Локалізована назва часового поясу через localizedName(for:locale:) повертає людиночитану назву вказаною мовою. Наприклад, для Europe/Moscow з російською локаллю метод поверне російську назву «Moskva», а з англійською — «Moscow Time». Доступні стилі: .standard (стандартна назва), .daylightSaving (літній час) та .shortGeneric (коротка).
import Foundation
let paris = TimeZone(identifier: "Europe/Paris")!
let nameRU = paris.localizedName(
for: .standard,
locale: Locale(identifier: "ru_RU")
)
print("Назва російською: \(nameRU)")
// Перевірити, чи регіон в тому самому дні
let isSameDay = Calendar.current.isDate(
Date(),
equalTo: Date(),
toGranularity: .day
)
print("Той самий день у різних часових поясах: \(isSameDay)")
Серіалізація ідентифікатора часового поясу — найкраща практика для зберігання TimeZone в базах даних або UserDefaults. Зберігайте ідентифікатор (рядок типу Europe/Moscow), а не зміщення в секундах або абревіатуру. Зміщення може змінюватися при зміні DST, а абревіатура неоднозначна. Відновлення: TimeZone(identifier: savedString).
Використання фіксованого зміщення замість ідентифікатора часового поясу — найпоширеніша помилка. TimeZone(secondsFromGMT: 10800) не враховує DST, тому для Europe/Moscow влітку ця конструкція дає невірне зміщення на 1 годину. Завжди використовуйте ідентифікатор IANA для регіонів з літнім часом.
Забута обробка nil при ініціалізації TimeZone(identifier:) — друга за частотою помилка. Якщо користувач ввів ідентифікатор з помилкою (наприклад, «moscow» замість «Europe/Moscow»), конструктор повертає nil. Без обробки опціонального значення додаток падає з runtime error. Використовуйте guard let або TimeZone(identifier:) з відомим fallback.
Ігнорування DST при роботі з майбутніми датами. TimeZone.secondsFromGMT(for:) — єдиний коректний спосіб отримати зміщення для конкретної дати. Використання secondsFromGMT() без параметра для історичних або майбутніх дат дає зміщення для поточного моменту, що може не відповідати реальному зміщенню у вказану дату, особливо для регіонів зі скасуванням або введенням DST.
За даними Stack Overflow (2024), близько 15% питань по DateFormatter пов’язані з неправильним налаштуванням timeZone. Типовий сценарій: сервер надсилає дату в UTC, розробник форматує її без встановлення timeZone форматера, і дата відображається в часовому поясі пристрою, створюючи плутанину у користувачів з різних регіонів. Правило: завжди явно задавати timeZone форматера для серверних даних.
Часті запитання
TimeZone — клас Foundation для роботи з часовими поясами в iOS та macOS. Він надає інформацію про зміщення відносно UTC, правила переходу на літній час та ідентифікатори часових поясів на основі IANA Time Zone Database.
Три формати: IANA ідентифікатори (Europe/Moscow), абревіатури (MSK, EST) та числові зміщення (+0300). Apple рекомендує використовувати IANA-ідентифікатори як єдиний однозначний формат для production-коду.
Автоматично через методи secondsFromGMT(for:) та isDaylightSavingTime(for:). TimeZone використовує історичні дані IANA, що оновлюються при кожному релізі iOS, що гарантує коректні переходи на DST для будь-якої дати.
TimeZone.current повертає часовий пояс, вибраний користувачем в налаштуваннях (може відрізнятися від географічного). TimeZone.system повертає часовий пояс пристрою, який автоматично визначається за геопозицією і не може бути перевизначений користувачем.
TimeZone.current повертає поточний часовий пояс пристрою. Для отримання ідентифікатора використовуйте властивість identifier: TimeZone.current.identifier. Для локалізованої назви викличте localizedName(for:locale:).
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також