ISO8601DateFormatter — este o clasă Foundation în iOS și macOS, destinată formatării și parsării datelor în standardul internațional ISO 8601. Conform Apple Developer Documentation, 2024, ISO8601DateFormatter procesează automat formatele cu milisecunde, fusuri orare și fracțiuni de secundă fără a fi nevoie să setezi manual DateFormat. Spre deosebire de DateFormatter, această clasă nu depinde de Locale și TimeZone — funcționează strict conform specificației ISO 8601, ceea ce o face ideală pentru schimbul de date între server și client. Clasa este disponibilă începând cu iOS 10 și macOS 10.12.
Principalele puncte
ISO8601DateFormatter — este o subclasă specializată a Formatter în Foundation, care implementează conversia bidirecțională între Date și un șir în formatul ISO 8601. Standardul ISO 8601 (International Standard for the Representation of Dates and Times) definește formatul internațional pentru schimbul de date și timp: 2024-07-21T14:30:00+00:00. Spre deosebire de DateFormatter, această clasă nu necesită specificarea dateFormat și determină automat structura șirului pe baza opțiunilor date.
Principalele avantaje ale ISO8601DateFormatter față de DateFormatter: absența dependenței de locale (parsarea funcționează la fel pe orice dispozitiv), suportul încorporat pentru fracțiuni de secundă (cu orice număr de zecimale) și determinarea automată a formatului pe baza opțiunilor transmise. Clasa gestionează corect și sufixul Z (desemnarea UTC), fusurile orare în formatul +HH:mm și precizia redusă (doar data fără timp).
Conform ISO Specification (ISO 8601-1:2019), standardul suportă patru niveluri de precizie: an (2024), an-lună (2024-07), dată completă (2024-07-21) și dată-timp cu fus orar (2024-07-21T14:30:00+00:00). ISO8601DateFormatter acoperă toate aceste niveluri prin combinarea opțiunilor de format, eliberând dezvoltatorul de construirea manuală a șirului dateFormat.
Principiul de funcționare al ISO8601DateFormatter se bazează pe combinarea opțiunilor binare (formatOptions), fiecare activând o anumită componentă de dată sau timp în rezultat. De exemplu, opțiunea .withFullDate activează anul, luna și ziua; .withTime — orele, minutele și secundele. Combinând opțiunile, dezvoltatorul obține nivelul de precizie dorit fără a scrie un șir dateFormat.
Intern, ISO8601DateFormatter folosește biblioteca ICU pentru parsare, dar cu reguli fixe ISO 8601. Aceasta înseamnă că ignoră setările Locale și TimeZone instalate pe dispozitiv — rezultatul este întotdeauna previzibil. Pentru setarea fusului orar se folosește proprietatea timeZone, care implicit este egală cu UTC. Dacă timeZone este setat la nil, se folosește timpul local al dispozitivului.
| Opțiune | Descriere | Exemplu rezultat |
|---|---|---|
| .withFullDate | An, lună, zi | 2024-07-21 |
| .withTime | Ore, minute, secunde | 14:30:00 |
| .withMilliseconds | Fracțiuni de secundă (până la 3 caractere) | .123 |
| .withFractionalSeconds | Fracțiuni de secundă (orice precizie) | .123456 |
| .withTimeZone | Fus orar | +03:00 |
| .withColonSeparatorInTimeZone | Separator : în fusul orar | +03:00 (în loc de +0300) |
| .withInternetDateTime | Format complet (date + time + tz) | 2024-07-21T14:30:00+00:00 |
Combinarea opțiunilor: .withInternetDateTime este echivalent cu combinarea .withFullDate, .withTime și .withTimeZone. Pentru parsarea șirurilor cu milisecunde, adăugați .withFractionalSeconds. Este important să rețineți că .withMilliseconds limitează fracțiunile de secundă la trei caractere, în timp ce .withFractionalSeconds suportă orice precizie — de la una până la nouă cifre zecimale.
Opțiunile de format ISO8601DateFormatter se împart în trei grupuri: componente de dată (withFullDate, withYear, withMonth, withDay, withWeekOfYear), componente de timp (withTime, withHours, withMinutes, withSeconds) și setări suplimentare (withMilliseconds, withFractionalSeconds, withTimeZone, withColonSeparatorInTimeZone, withDashSeparatorInDate, withFullTime). Combinându-le, se poate obține practic orice subformat ISO 8601.
Nuanță importantă: .withFractionalSeconds și .withMilliseconds se exclud reciproc — dacă ambele sunt setate, se aplică .withFractionalSeconds. Pentru parsarea milisecundelor din datele serverului, se recomandă .withFractionalSeconds, deoarece multe servere trimit fracțiuni de secundă cu trei, șase sau nouă caractere, iar .withFractionalSeconds gestionează orice lungime.
import Foundation
// Configurează ISO8601DateFormatter
let formatter = ISO8601DateFormatter()
formatter.timeZone = TimeZone(secondsFromGMT: 0)
// Diferite combinații de opțiuni de format
formatter.formatOptions = [.withFullDate]
let dateOnly = formatter.string(from: Date())
print("Data: \(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("Complet: \(full)")
// Parsează șirul cu milisecunde
let serverString = "2024-07-21T14:30:00.123456+03:00"
if let parsed = formatter.date(from: serverString) {
print("Parsat: \(parsed)")
}
Utilizarea de bază a ISO8601DateFormatter se reduce la crearea unei instanțe, setarea timeZone (se recomandă UTC pentru datele serverului) și formatOptions, după care se poate apela string(from:) pentru formatare și date(from:) pentru parsare. Spre deosebire de DateFormatter, nu trebuie să vă faceți griji despre Locale — clasa ignoră setările regionale.
import Foundation
let formatter = ISO8601DateFormatter()
// Parsează diferite formate 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("Parsat '\(str)': \(autoParsed)")
} else {
// Folosește withFullDate pentru șiruri doar cu dată
formatter.formatOptions = [.withFullDate]
if let fallback = formatter.date(from: str) {
print("Fallback parsat '\(str)': \(fallback)")
}
formatter.formatOptions = [.withInternetDateTime]
}
}
// Serializare la RFC 3339 (API GitHub)
formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
let rfc3339 = formatter.string(from: Date())
print("RFC 3339: \(rfc3339)")
Parsarea datelor cu secunde fracționare de lungime variabilă — caracteristica multor API-uri moderne. Serverul poate trimite atât 2024-07-21T14:30:00.123Z (3 caractere), cât și 2024-07-21T14:30:00.123456Z (6 caractere). ISO8601DateFormatter cu opțiunea .withFractionalSeconds va gestiona corect ambele variante, în timp ce DateFormatter cu dateFormat = „yyyy-MM-dd’T’HH:mm:ss.SSSZ” va procesa doar milisecunde cu trei cifre.
import Foundation
let variantFormatter = ISO8601DateFormatter()
variantFormatter.formatOptions = [
.withInternetDateTime,
.withFractionalSeconds
]
// Diferite precizii de secunde fracționare
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)")
}
}
// Folosește withMilliseconds (doar 3 cifre)
variantFormatter.formatOptions = [
.withInternetDateTime,
.withMilliseconds
]
let milliParsed = variantFormatter.string(from: Date())
print("Cu milisecunde: \(milliParsed)")
Testarea parsării tuturor variantelor: codul de mai sus demonstrează că ISO8601DateFormatter cu .withFractionalSeconds procesează cu succes fracțiuni de secundă de orice lungime de la 1 la 9 caractere. Acest lucru este important pentru compatibilitatea cu diferite platforme server: .NET generează adesea 7 caractere (ticuri de 100 de nanosecunde), Python — 6, Java — 3 sau 9 în funcție de versiune.
DateFormatter poate de asemenea să parseze ISO 8601, dar necesită setarea manuală a dateFormat, locale și timeZone. Problema principală este că DateFormatter depinde de Locale, iar dacă nu se setează en_US_POSIX, parsarea se poate strica la utilizatorii din regiuni cu formate de dată non-standard. ISO8601DateFormatter rezolvă această problemă la nivel de arhitectură: nu folosește Locale.
| Parametru | ISO8601DateFormatter | DateFormatter |
|---|---|---|
| Configurare Locale | Nu este necesară (ignoră) | en_US_POSIX obligatoriu |
| DateFormat | Automat (prin opțiuni) | Șir format manual |
| Secunde fracționare | Orice precizie (.withFractionalSeconds) | SSS fix |
| Sufix Z | Gestionează corect | Prin dateFormat |
| Performanță | Mai mare (specializat) | Mai mică (general) |
| Standard | Doar ISO 8601 | Orice format |
| Versiune iOS | iOS 10+ | iOS 2+ |
Când să folosiți DateFormatter: dacă trebuie să formatați o dată într-un format non-ISO 8601 (de exemplu, „21 iulie 2024” pentru UI) sau dacă este necesară suportarea iOS 9 și mai vechi. Pentru toate sarcinile de schimb de date cu serverul, folosiți ISO8601DateFormatter — este mai sigur, mai performant și necesită mai puțin cod. DateFormatter pentru ISO 8601 este o sursă de potențiale bug-uri legate de locale și setări regionale.
Migrarea de la DateFormatter la ISO8601DateFormatter: înlocuiți crearea DateFormatter + setarea dateFormat + locale + timeZone cu crearea ISO8601DateFormatter + setarea formatOptions + timeZone. Parsarea șirului rămâne neschimbată prin date(from:). Pentru compatibilitate inversă, puteți folosi #available(iOS 10, *) cu fallback pe DateFormatter.
Setarea uitată a formatOptions face ca formatter-ul să folosească valoarea implicită — .withInternetDateTime. Dacă serverul trimite doar data fără timp (2024-07-21), parsarea va returna nil. Verificați întotdeauna că formatOptions acoperă toate formatele posibile care pot veni de la server. Pentru API-uri cu formate variabile, folosiți încercări de fallback cu diferite combinații de opțiuni.
Confuzia între withMilliseconds și withFractionalSeconds — o eroare frecventă la parsarea datelor cu fracțiuni de secundă. withMilliseconds așteaptă exact 3 cifre zecimale. Dacă serverul trimite 6 cifre (microsecunde), parsarea cu withMilliseconds va eșua. Folosiți .withFractionalSeconds pentru compatibilitate cu orice număr de caractere. .withFractionalSeconds a apărut în iOS 13; pentru versiunile mai vechi, folosiți DateFormatter cu dateFormat.
Ignorarea fusului orar — o altă problemă frecventă. Dacă serverul trimite data cu un fus orar (+03:00), iar formatter-ul este setat la UTC, parsarea nu va eșua, dar rezultatul va fi în UTC. Dezvoltatorii se așteaptă adesea ca Date să păstreze fusul orar, dar Date este un moment absolut în timp și nu stochează informații despre fusul orar. Pentru afișarea corectă, salvați fusul orar separat sau folosiți ISO8601DateFormatter cu timeZone corect.
Conform Apple Forum (2024), aproximativ 20% din întrebările referitoare la ISO8601DateFormatter sunt legate de formatul în care secundele sunt opționale. Standardul ISO 8601 permite formatul fără secunde: 2024-07-21T14:30+03:00. ISO8601DateFormatter cu .withInternetDateTime nu suportă acest format — pentru parsarea lui este necesar DateFormatter cu dateFormat = „yyyy-MM-dd’T’HH:mmZ”. Această limitare trebuie luată în considerare atunci când lucrați cu API-uri care folosesc formatul scurtat de timp.
Întrebări frecvente
ISO8601DateFormatter — o clasă Foundation specializată pentru formatarea și parsarea datelor în format ISO 8601, disponibilă de la iOS 10. Procesează automat formatele standard fără setarea manuală a dateFormat.
ISO8601DateFormatter nu depinde de Locale, folosește opțiuni în loc de dateFormat și gestionează corect fracțiuni de secundă de orice lungime. DateFormatter este universal, dar necesită configurare manuală și este susceptibil la bug-uri legate de setările regionale.
Folosiți opțiunea .withFractionalSeconds — suportă de la 1 la 9 caractere zecimale. Nu folosiți .withMilliseconds dacă precizia poate varia. .withFractionalSeconds este disponibil din iOS 13.
Implicit UTC. Pentru a-l schimba, setați proprietatea timeZone. Dacă timeZone = nil, se folosește timpul local al dispozitivului. La parsarea unui șir cu un fus orar explicit în format +HH:MM, formatter-ul îl ia în considerare automat.
Pentru că formatOptions implicit = .withInternetDateTime, care așteaptă dată + timp + fus orar. Pentru parsarea doar a datei, setați formatOptions = [.withFullDate]. Pentru a suporta ambele variante, folosiți fallback cu opțiuni diferite.
Rezumat
Vom dezvolta o aplicație mobilă la cheie
IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.
Citiți și