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: одсуство зависности од локалитета (парсирање ради исто на било ком уређају), уграђена подршка за разломке секунди (са било којим бројем децимала) и аутоматско одређивање формата на основу прослеђених опција. Класа такође коректно обрађује 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 | Аутоматски (кроз опције) | Ручни низ format |
| Разломци секунди | Било која прецизност (.withFractionalSeconds) | Фиксни SSS |
| Z-суфикс | Коректно обрађује | Кроз dateFormat |
| Перформансе | Више (специјализован) | Мање (општи) |
| Стандард | Само ISO 8601 | Било који формат |
| iOS верзија | iOS 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.
Заборављено подешавање 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. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође