ISO8601DateFormatter: ключові поняття та форматування ISO 8601

Автор: IT Sectr Опубліковано: 2026-07-13 Час читання: 9 хв

ISO8601DateFormatter — це клас Foundation в iOS і macOS, призначений для форматування та парсингу дат у міжнародному стандарті ISO 8601. За даними Apple Developer Documentation, 2024, ISO8601DateFormatter автоматично обробляє формати з мілісекундами, часовими поясами та частками секунд без необхідності задавати DateFormat вручну. На відміну від DateFormatter, цей клас не залежить від Locale та TimeZone — він працює строго за специфікацією ISO 8601, що робить його ідеальним для обміну датами між сервером і клієнтом. Клас доступний починаючи з iOS 10 та macOS 10.12.

Головне

  • ISO8601DateFormatter — клас Foundation для форматування дат за стандартом ISO 8601
  • Не потребує DateFormat — формат визначається автоматично за налаштуваннями опцій
  • Локально-незалежний — працює однаково на всіх пристроях без налаштування Locale
  • Підтримка мілісекунд — обробляє частки секунд будь-якої точності (три, шість і більше знаків)
  • Опції форматування — withFullDate, withTime, withMilliseconds, withTimeZone та інші керують компонентами виведення

Що таке ISO8601DateFormatter?

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: відсутність залежності від локалі (парсинг працює однаково на будь-якому пристрої), вбудована підтримка часток секунд (з будь-якою кількістю знаків після коми) та автоматичне визначення формату за переданими опціями. Клас також коректно обробляє 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 у Foundation?

Принцип роботи 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Повний формат (дата + час + пояс)2024-07-21T14:30:00+00:00

Комбінування опцій: .withInternetDateTime еквівалентний об'єднанню .withFullDate, .withTime та .withTimeZone. Для парсингу рядків з мілісекундами додайте .withFractionalSeconds. Важливо пам'ятати, що .withMilliseconds обмежує частки секунд трьома знаками, а .withFractionalSeconds підтримує будь-яку точність — від однієї до дев'яти цифр після коми.

Налаштування формату ISO 8601

Опції формату ISO8601DateFormatter поділяються на три групи: компоненти дати (withFullDate, withYear, withMonth, withDay, withWeekOfYear), компоненти часу (withTime, withHours, withMinutes, withSeconds) та додаткові налаштування (withMilliseconds, withFractionalSeconds, withTimeZone, withColonSeparatorInTimeZone, withDashSeparatorInDate, withFullTime). Комбінуючи їх, можна отримати практично будь-який підформат ISO 8601.

Основні комбінації опцій

  • .withFullDate — тільки дата: 2024-07-21. Для парсингу рядків YYYY-MM-DD
  • .withFullDate + .withTime — дата і час без часового поясу: 2024-07-21T14:30:00
  • .withInternetDateTime — повний формат: 2024-07-21T14:30:00Z або 2024-07-21T14:30:00+03:00
  • .withInternetDateTime + .withFractionalSeconds — з частками секунд: 2024-07-21T14:30:00.123456+00:00
  • .withFullDate + .withTime + .withTimeZone — повний формат без двокрапки в tz: 2024-07-21T14:30:00+0300

Важливий нюанс: .withFractionalSeconds та .withMilliseconds взаємовиключні — якщо встановлені обидві, застосовується .withFractionalSeconds. Для парсингу мілісекунд з серверних даних рекомендується .withFractionalSeconds, оскільки багато серверів відправляють частки секунд з трьома, шістьма або дев'ятьма знаками, і .withFractionalSeconds обробляє будь-яку довжину.

swift
import Foundation

// Configure ISO8601DateFormatter
let formatter = ISO8601DateFormatter()
formatter.timeZone = TimeZone(secondsFromGMT: 0)

// Different format option combinations
formatter.formatOptions = [.withFullDate]
let dateOnly = formatter.string(from: Date())
print("Date: \(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: \(full)")

// Parse string with milliseconds
let serverString = "2024-07-21T14:30:00.123456+03:00"
if let parsed = formatter.date(from: serverString) {
    print("Parsed: \(parsed)")
}

ISO8601DateFormatter у Swift: приклади коду

Базове використання ISO8601DateFormatter зводиться до створення екземпляра, налаштування timeZone (рекомендується UTC для серверних даних) та formatOptions, після чого можна викликати string(from:) для форматування та date(from:) для парсингу. На відміну від DateFormatter, не потрібно турбуватися про Locale — клас ігнорує регіональні налаштування.

swift
import Foundation

let formatter = ISO8601DateFormatter()

// Parse different ISO 8601 formats
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("Parsed '\(str)': \(autoParsed)")
    } else {
        // Use withFullDate for date-only strings
        formatter.formatOptions = [.withFullDate]
        if let fallback = formatter.date(from: str) {
            print("Fallback parsed '\(str)': \(fallback)")
        }
        formatter.formatOptions = [.withInternetDateTime]
    }
}

// Serialize to 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" — тільки тризначні мілісекунди.

swift
import Foundation

let variantFormatter = ISO8601DateFormatter()
variantFormatter.formatOptions = [
    .withInternetDateTime,
    .withFractionalSeconds
]

// Different fractional second precision
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)")
    }
}

// Use withMilliseconds (3 digits only)
variantFormatter.formatOptions = [
    .withInternetDateTime,
    .withMilliseconds
]
let milliParsed = variantFormatter.string(from: Date())
print("With milliseconds: \(milliParsed)")

Тестування парсингу всіх варіантів: наведений код демонструє, що ISO8601DateFormatter з .withFractionalSeconds успішно обробляє частки секунд будь-якої довжини від 1 до 9 знаків. Це важливо для сумісності з різними серверними платформами: .NET часто генерує 7 знаків (100-наносекундні тіки), Python — 6, Java — 3 або 9 залежно від версії.

Порівняння з DateFormatter для ISO 8601

DateFormatter також може парсити ISO 8601, але потребує ручного налаштування dateFormat, locale та timeZone. Основна проблема — DateFormatter залежить від Locale, і якщо не встановити en_US_POSIX, парсинг може зламатися у користувачів з регіонів з нестандартними форматами дат. ISO8601DateFormatter вирішує цю проблему на рівні архітектури: він не використовує Locale.

ПараметрISO8601DateFormatterDateFormatter
Налаштування LocaleНе потрібне (ігнорує)en_US_POSIX обов'язково
DateFormatАвтоматичний (через опції)Ручний рядок формату
Дробові секундиБудь-яка точність (.withFractionalSeconds)Фіксована SSS
Z-суфіксКоректно обробляєЧерез dateFormat
ПродуктивністьВища (спеціалізований)Нижча (загальний)
СтандартТільки ISO 8601Будь-який формат
Версія iOSiOS 10+iOS 2+

Коли використовувати DateFormatter: якщо потрібно відформатувати дату в не-ISO 8601 форматі (наприклад, «21 липня 2024 р.» для UI) або якщо потрібна підтримка iOS 9 і старше. Для всіх завдань обміну датами з сервером використовуйте ISO8601DateFormatter — він безпечніший, продуктивніший і потребує менше коду. DateFormatter для ISO 8601 — це джерело потенційних багів, пов'язаних з локалью та регіональними налаштуваннями.

Міграція з DateFormatter на ISO8601DateFormatter: замініть створення DateFormatter + налаштування dateFormat + locale + timeZone на створення ISO8601DateFormatter + налаштування formatOptions + timeZone. Парсинг рядка залишається незмінним через date(from:). Для зворотної сумісності можна використовувати #available(iOS 10, *) з fallback на DateFormatter.

Типові помилки при парсингу ISO 8601

Забуте налаштування 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?

ISO8601DateFormatter — спеціалізований клас Foundation для форматування та парсингу дат у форматі ISO 8601, доступний з iOS 10. Автоматично обробляє стандартні формати без ручного завдання dateFormat.

Чим ISO8601DateFormatter відрізняється від DateFormatter?

ISO8601DateFormatter не залежить від Locale, використовує опції замість dateFormat і коректно обробляє частки секунд будь-якої довжини. DateFormatter універсальний, але потребує ручного налаштування та схильний до багів, пов'язаних з регіональними налаштуваннями.

Як обробляти частки секунд змінної довжини?

Використовуйте опцію .withFractionalSeconds — вона підтримує від 1 до 9 знаків після коми. Не використовуйте .withMilliseconds, якщо точність може варіюватися. .withFractionalSeconds доступний з iOS 13.

Який часовий пояс використовує ISO8601DateFormatter?

За замовчуванням UTC. Для зміни встановіть властивість timeZone. Якщо timeZone = nil, використовується локальний час пристрою. При парсингу рядка з явним часовим поясом у форматі +HH:MM formatter враховує його автоматично.

Чому парсинг дати без часу повертає nil?

Тому що formatOptions за замовчуванням = .withInternetDateTime, який очікує дату + час + часовий пояс. Для парсингу тільки дати встановіть formatOptions = [.withFullDate]. Для підтримки обох варіантів використовуйте fallback з різними опціями.

Підсумки

  • ISO8601DateFormatter — спеціалізований клас для ISO 8601, безпечніший і простіший за DateFormatter
  • Опції формату замінюють ручний dateFormat — комбінуйте .withFullDate, .withTime, .withTimeZone
  • Не залежить від Locale — парсинг працює однаково на всіх пристроях без налаштування локалі
  • .withFractionalSeconds обробляє частки секунд будь-якої точності (1–9 знаків)
  • DateFormatter програє в продуктивності, безпеці та простоті для задач ISO 8601
  • Плутанина опцій — withMilliseconds та withFractionalSeconds невзаємозамінні
  • Формат без секунд (2024-07-21T14:30+03:00) не підтримується — потрібен DateFormatter

Ми розробимо мобільний застосунок під ключ

IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.

Обговорити проект

Читайте також