ISO8601DateFormatter — е клас Foundation в iOS и macOS, предназначен за форматиране и парсиране на дати в международния стандарт ISO 8601. Според Apple Developer Documentation, 2024, ISO8601DateFormatter автоматично обработва формати с милисекунди, часови зони и дробни части от секундата без необходимост от ръчно задаване на DateFormat. За разлика от DateFormatter, този клас не зависи от Locale и TimeZone — той работи строго по спецификацията ISO 8601, което го прави идеален за обмен на дати между сървър и клиент. Класът е достъпен от iOS 10 и macOS 10.12.
Основни точки
ISO8601DateFormatter — е специализиран подклас на Formatter във Foundation, който имплементира двупосочна конверсия между Date и низ във формат ISO 8601. Стандартът ISO 8601 (International Standard for the Representation of Dates and Times) определя международния формат за обмен на дати и часове: 2024-07-21T14:30:00+00:00. За разлика от DateFormatter, този клас не изисква задаване на dateFormat и автоматично определя структурата на низа въз основа на зададените опции.
Основните предимства на ISO8601DateFormatter пред DateFormatter: липса на зависимост от locale (парсирането работи еднакво на всяко устройство), вградена поддръжка на дробни части от секундата (с произволен брой десетични знаци) и автоматично определяне на формата въз основа на предадените опции. Класът също така коректно обработва Z-суфикса (обозначение на UTC), часови зони във формат +HH:mm и намалена точност (само дата без час).
Според ISO Specification (ISO 8601-1:2019), стандартът поддържа четири нива на точност: година (2024), година-месец (2024-07), пълна дата (2024-07-21) и дата-час с часова зона (2024-07-21T14:30:00+00:00). ISO8601DateFormatter покрива всички тези нива чрез комбинация от опции за формат, освобождавайки разработчика от ръчно конструиране на dateFormat низ.
Принцип на работа на ISO8601DateFormatter се основава на комбинация от битови опции (formatOptions), всяка от които активира определен компонент на дата или час в изхода. Например опцията .withFullDate активира година, месец и ден; .withTime — часове, минути и секунди. Комбинирайки опции, разработчикът постига желаното ниво на точност без да пише dateFormat низ.
Вътрешно ISO8601DateFormatter използва ICU библиотека за парсиране, но с фиксирани ISO 8601 правила. Това означава, че игнорира настройките на Locale и TimeZone на устройството — резултатът винаги е предвидим. За настройка на часова зона се използва свойството timeZone, което по подразбиране е равно на UTC. Ако timeZone е зададен на nil, се използва локалното време на устройството.
| Опция | Описание | Примерен изход |
|---|---|---|
| .withFullDate | Година, месец, ден | 2024-07-21 |
| .withTime | Часове, минути, секунди | 14:30:00 |
| .withMilliseconds | Дробни части от секундата (до 3 знака) | .123 |
| .withFractionalSeconds | Дробни части от секундата (произволна точност) | .123456 |
| .withTimeZone | Часова зона | +03:00 |
| .withColonSeparatorInTimeZone | Разделител : в часовата зона | +03:00 (вместо +0300) |
| .withInternetDateTime | Пълен формат (date + time + tz) | 2024-07-21T14:30:00+00:00 |
Комбиниране на опции: .withInternetDateTime е еквивалентен на комбинацията от .withFullDate, .withTime и .withTimeZone. За парсиране на низове с милисекунди добавете .withFractionalSeconds. Важно е да запомните, че .withMilliseconds ограничава дробните части от секундата до три знака, докато .withFractionalSeconds поддържа произволна точност — от една до девет цифри след десетичната запетая.
Опции за формат на ISO8601DateFormatter се делят на три групи: компоненти на дата (withFullDate, withYear, withMonth, withDay, withWeekOfYear), компоненти на час (withTime, withHours, withMinutes, withSeconds) и допълнителни настройки (withMilliseconds, withFractionalSeconds, withTimeZone, withColonSeparatorInTimeZone, withDashSeparatorInDate, withFullTime). Комбинирайки ги, може да се получи практически всеки подформат на ISO 8601.
Важен нюанс: .withFractionalSeconds и .withMilliseconds се изключват взаимно — ако и двете са зададени, се прилага .withFractionalSeconds. За парсиране на милисекунди от сървърни данни се препоръчва .withFractionalSeconds, тъй като много сървъри изпращат дробни части от секундата с три, шест или девет знака, а .withFractionalSeconds обработва произволна дължина.
import Foundation
// Конфигурирай ISO8601DateFormatter
let formatter = ISO8601DateFormatter()
formatter.timeZone = TimeZone(secondsFromGMT: 0)
// Различни комбинации от опции за формат
formatter.formatOptions = [.withFullDate]
let dateOnly = formatter.string(from: Date())
print("Дата: \(dateOnly)")
formatter.formatOptions = [.withFullDate, .withTime]
let dateTime = formatter.string(from: Date())
print("DateTime: \(dateTime)")
formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
let full = formatter.string(from: Date())
print("Пълен: \(full)")
// Парсирай низ с милисекунди
let serverString = "2024-07-21T14:30:00.123456+03:00"
if let parsed = formatter.date(from: serverString) {
print("Парсирано: \(parsed)")
}
Основна употреба на ISO8601DateFormatter се свежда до създаване на инстанция, задаване на timeZone (препоръчва се UTC за сървърни данни) и formatOptions, след което може да се извика string(from:) за форматиране и date(from:) за парсиране. За разлика от DateFormatter, не е нужно да се тревожите за Locale — класът игнорира регионалните настройки.
import Foundation
let formatter = ISO8601DateFormatter()
// Парсирай различни ISO 8601 формати
let strings: [String] = [
"2024-07-21T14:30:00Z",
"2024-07-21T14:30:00+03:00",
"2024-07-21T14:30:00.123Z",
"2024-07-21"
]
for str in strings {
if let autoParsed = formatter.date(from: str) {
print("Парсирано '\(str)': \(autoParsed)")
} else {
// Използвай withFullDate за низове само с дата
formatter.formatOptions = [.withFullDate]
if let fallback = formatter.date(from: str) {
print("Fallback парсирано '\(str)': \(fallback)")
}
formatter.formatOptions = [.withInternetDateTime]
}
}
// Сериализирай към RFC 3339 (GitHub API)
formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
let rfc3339 = formatter.string(from: Date())
print("RFC 3339: \(rfc3339)")
Парсиране на дати с дробни секунди с променлива дължина — характеристика на много съвременни API. Сървърът може да изпрати както 2024-07-21T14:30:00.123Z (3 знака), така и 2024-07-21T14:30:00.123456Z (6 знака). ISO8601DateFormatter с опция .withFractionalSeconds ще обработи правилно и двата варианта, докато DateFormatter с dateFormat = „yyyy-MM-dd’T’HH:mm:ss.SSSZ” — само трицифрени милисекунди.
import Foundation
let variantFormatter = ISO8601DateFormatter()
variantFormatter.formatOptions = [
.withInternetDateTime,
.withFractionalSeconds
]
// Различна точност на дробни секунди
let variants: [String] = [
"2024-07-21T14:30:00.1Z",
"2024-07-21T14:30:00.12Z",
"2024-07-21T14:30:00.123Z",
"2024-07-21T14:30:00.123456Z",
"2024-07-21T14:30:00.123456789Z"
]
for variant in variants {
if let parsed = variantFormatter.date(from: variant) {
print("OK: \(variant) -> \(parsed)")
} else {
print("FAIL: \(variant)")
}
}
// Използвай withMilliseconds (само 3 цифри)
variantFormatter.formatOptions = [
.withInternetDateTime,
.withMilliseconds
]
let milliParsed = variantFormatter.string(from: Date())
print("С милисекунди: \(milliParsed)")
Тестване на парсиране на всички варианти: горният код демонстрира, че ISO8601DateFormatter с .withFractionalSeconds успешно обработва дробни части от секундата с произволна дължина от 1 до 9 знака. Това е важно за съвместимост с различни сървърни платформи: .NET често генерира 7 знака (100-наносекундни тикове), Python — 6, Java — 3 или 9 в зависимост от версията.
DateFormatter също може да парсира ISO 8601, но изисква ръчно задаване на dateFormat, locale и timeZone. Основният проблем е, че DateFormatter зависи от Locale и ако не се зададе en_US_POSIX, парсирането може да се счупи при потребители от региони с нестандартни формати на дати. ISO8601DateFormatter решава този проблем на архитектурно ниво: не използва Locale.
| Параметър | ISO8601DateFormatter | DateFormatter |
|---|---|---|
| Настройка на Locale | Не е необходима (игнорира) | en_US_POSIX задължителен |
| DateFormat | Автоматичен (чрез опции) | Ръчен форматиращ низ |
| Дробни секунди | Произволна точност (.withFractionalSeconds) | Фиксиран SSS |
| Z-суфикс | Коректно обработва | Чрез dateFormat |
| Производителност | По-висока (специализиран) | По-ниска (общ) |
| Стандарт | Само ISO 8601 | Произволен формат |
| iOS версия | iOS 10+ | iOS 2+ |
Кога да използвате DateFormatter: ако трябва да форматирате дата в не-ISO 8601 формат (например „21 юли 2024 г.” за UI) или ако се изисква поддръжка на iOS 9 и по-стари. За всички задачи за обмен на дати със сървъра използвайте ISO8601DateFormatter — той е по-безопасен, по-производителен и изисква по-малко код. DateFormatter за ISO 8601 е източник на потенциални грешки, свързани с locale и регионални настройки.
Миграция от DateFormatter към ISO8601DateFormatter: заменете създаването на DateFormatter + настройка на dateFormat + locale + timeZone със създаване на ISO8601DateFormatter + настройка на formatOptions + timeZone. Парсирането на низа остава непроменено чрез date(from:). За обратна съвместимост можете да използвате #available(iOS 10, *) с fallback към DateFormatter.
Забравена настройка на formatOptions води до това, че formatter използва стойността по подразбиране — .withInternetDateTime. Ако сървърът изпраща само дата без час (2024-07-21), парсирането ще върне nil. Винаги проверявайте дали formatOptions покрива всички възможни формати, които могат да дойдат от сървъра. За API с променливи формати използвайте fallback опити с различни комбинации от опции.
Объркване между withMilliseconds и withFractionalSeconds — честа грешка при парсиране на дати с дробни части от секундата. withMilliseconds очаква точно 3 цифри след десетичната запетая. Ако сървърът изпраща 6 цифри (микросекунди), парсирането с withMilliseconds ще се провали. Използвайте .withFractionalSeconds за съвместимост с произволен брой знаци. .withFractionalSeconds се появи в iOS 13; за по-стари версии използвайте DateFormatter с dateFormat.
Игнориране на часовата зона — друг често срещан проблем. Ако сървърът изпраща дата с часова зона (+03:00) и formatter е настроен на UTC, парсирането няма да се провали, но резултатът ще бъде в UTC. Разработчиците често очакват, че Date ще запази часовата зона, но Date е абсолютен момент във времето и не съхранява информация за часова зона. За правилно показване запазете часовата зона отделно или използвайте ISO8601DateFormatter с правилен timeZone.
Според Apple Forum (2024), около 20% от въпросите относно ISO8601DateFormatter са свързани с формат, в който секундите са опционални. Стандартът ISO 8601 позволява формат без секунди: 2024-07-21T14:30+03:00. ISO8601DateFormatter с .withInternetDateTime не поддържа този формат — за парсирането му е необходим DateFormatter с dateFormat = „yyyy-MM-dd’T’HH:mmZ”. Това ограничение трябва да се вземе предвид при работа с API, които използват съкратен формат за време.
Често задавани въпроси
ISO8601DateFormatter — специализиран Foundation клас за форматиране и парсиране на дати във формат ISO 8601, достъпен от iOS 10. Автоматично обработва стандартни формати без ръчно задаване на dateFormat.
ISO8601DateFormatter не зависи от Locale, използва опции вместо dateFormat и коректно обработва дробни части от секундата с произволна дължина. DateFormatter е универсален, но изисква ръчна настройка и е податлив на грешки, свързани с регионални настройки.
Използвайте опцията .withFractionalSeconds — поддържа от 1 до 9 знака след десетичната запетая. Не използвайте .withMilliseconds, ако точността може да варира. .withFractionalSeconds е достъпен от iOS 13.
По подразбиране UTC. За промяна задайте свойството timeZone. Ако timeZone = nil, се използва локалното време на устройството. При парсиране на низ с изрична часова зона във формат +HH:MM, formatter я взема предвид автоматично.
Защото formatOptions по подразбиране = .withInternetDateTime, който очаква дата + час + часова зона. За парсиране само на дата задайте formatOptions = [.withFullDate]. За поддръжка на двата варианта използвайте fallback с различни опции.
Резюме
Ще разработим мобилно приложение под ключ
IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също