DateFormatter — класа Foundation-а дизајнирана за двосмерну конверзију између Date објеката и њиховог стринг приказа. Класа узима у обзир локал, временску зону и календар корисника, обезбеђујући исправно приказивање датума у било ком региону света. Према Apple Developer Documentation (2025), DateFormatter подržava четири унапред дефинисана стила датума и времена, као и потпуно прилагођене формате кроз стринг шаблона. Без DateFormatter-а немогуће је коректно приказати датум кориснику у интернационализованој апликацији.
Главно
DateFormatter — класа из Framework-а Foundation која имплементира двосмерну конверзију између Date и стринга. Појавила се у OpenStep-у као NSDateFormatter и од тада остаје главни алат за форматирање датума на свим Apple платформама. Класа наслеђује од Formatter-а и пружа згодан API за локализовани приказ датума.
Принцип рада DateFormatter-а се заснива на шаблонима Unicode LDML — истим онима који се користе у ICU (International Components for Unicode). Шаблон се дефинише кроз својство dateFormat, где симболи y, M, d, H, m, s одговарају години, месецу, дану, сатима, минутима и секундама. Понављање симбола одређује формат: „y” — двосцифрена година, „yyyy” — четвороцифрена.
Креирање DateFormatter-а је скупа операција, jer се при иницијализацији учитавају подаци локала и календара. Apple препоручује креирање форматтера једном за сваки тип форматирања и поновну употребу. У SwiftUI-ју и UIKit-у, форматтери се често кеширају у статичким својствима или се креирају lenjivo при првом приступу.
DateFormatter се примењује у многим системским компонентама iOS-а. UIDatePicker користи DateFormatter интерно за приказивање датума у режиму countDownTimer. TextField са форматтером на улазу може аутоматски да валидира датум који уноси корисник. Core Data подржава атрибуте типа Date, али њихов стринг приказ се увек врши кроз DateFormatter.
Thread Safety — DateFormatter није безбедан за нити. Модификација својстава форматтера из различитих нити води ка недефинисаном понашању. За вишенитно коришћење креирајте засебне инстанце форматтера за сваку нит или користите синхронизацију кроз NSLock или serial queue.
dateStyle и timeStyle — najjednostavniji начини за подешавање приказа датума. Сваки стил има четири варијанте: .short, .medium, .long, .full. Комбинација dateStyle и timeStyle омогућава независно подешавање формата датума и времена, а својство .none искључује одговарајући део.
Za амерички локал стил .short форматира датум као „7/21/26”, а за српски — као „21.07.2026”. Стил .long за српски локал приказује „21. jul 2026.”, а .full — „utorak, 21. jul 2026.” са навођењем дана у недељи. Сва четири стила се аутоматски прилагођавају регионалним стандардима, укључујући редослед компоненти и сепараторе.
RelativeDateFormatter у iOS 15+ nudi алтернативни приступ кроз RelativeDateFormatter и DateIntervalFormatter. RelativeDateFormatter приказује „danas”, „juče”, „za 3 дана” за тренутни контекст. DateIntervalFormatter приказује опсеге датума: “21–25. jul 2026.” — za резервације и планирање.
| Стил | Пример (sr_RS) | Пример (en_US) |
|---|---|---|
| .short | 21.07.2026. | 7/21/26 |
| .medium | 21. jul 2026. | Jul 21, 2026 |
| .long | 21. jul 2026. | July 21, 2026 |
| .full | utorak, 21. jul 2026. | Tuesday, July 21, 2026 |
При комбиновању стилова DateFormatter аутоматски бира сепаратор: за .short.date + .short.time резултат може бити „21.07.2026, 14:30”. За .full.date + .full.time — „utorak, 21. jul 2026, 14:30:00 MSK”. Сепаратором управља локал, а не програмер — то garantiuje усклађеност са регионалним очекивањима корисника.
dateFormat омогућава дефинисање произвољног шаблона форматирања коришћењем симбола спецификације Unicode LDML. Ово пружа потпуну контролу над приказом: можете приказати само годину и месец, дан у недељи без броја или време без секунди. Прилагођени формат je незаменљив за специфичне захтеве дизајна.
Основни симболи — yyyy (година: 2026), MM (месец: 07), dd (дан: 21), HH (сати: 14), mm (минути: 30), ss (секунде: 00). За пуни назив месеца користите MMMM (jul), za skraćeni — MMM (jul.). Дан у недељи — EEEE (utorak), skraćeno — E (ut).
При коришћењу dateFormat важно je подесити locale форматтера. Ако locale nije подешен, форматтер користи системски локал, što može biti nepoželjno za фиксни формат у API-ju. Apple препоручује подешавање locale = Locale(identifier: “en_US_POSIX”) za фиксни међурегионални формат, посебно при парсингу датума из серверских одговора.
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "ru_RU")
formatter.dateFormat = "d MMMM yyyy"
let customString = formatter.string(from: Date())
// "21 July 2026"
// Парсирање прилагођеног низа
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
let date = formatter.date(from: "2026-07-21 14:30:00")!
Грешка у dateFormat — jedan од честих узрока пада апликације. Ако се формат ne поклапа са стрингом, метода date(from:) враћа nil. Користите guard let или ?? зa безбедно издвајање опционе вредности. Зa валидацију формата тестирајте на свим подржаним језицима — neki LDML симболи раде различито у различитим локалима.
Locale одређује kako se приказују називи месеци, дани у недељи i koji separatori se koriste. DateFormatter подразумевано користи Locale.current, ali u nekim scenarijima je potrebno navesti konkretan lokal: zа фиксни формат у логовима користите en_US_POSIX, zа серверске датуме — локал идентичан серверу.
Својство TimeZone одређује временску зону зa приказ. Подразумевано se koristi системска временска зона, ali u aplikacijama са међународном публиком često je potrebno приказивати датуме u временској зони korisnika ili u UTC. Промена timeZone утиче samo na prikaz — вредност Date ostaje nepromenjena.
Важна карактеристика: ако se DateFormatter koristi zа парсинг стринга, a stринг садржи ознаку временске зоне (na primer, “2026-07-21T14:30:00Z” са Z zа UTC), својство timeZone se ignoriše — форматтер користи временску зону iz стринга. Ако временска зона u стрингу nedostaje, примењује се timeZone форматтера.
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "ru_RU")
formatter.timeZone = TimeZone(identifier: "Europe/Moscow")
formatter.dateStyle = .long
formatter.timeStyle = .short
let moscowTime = formatter.string(from: Date())
// "21 July 2026, 14:30"
// Парсирање без временске зоне у низу
formatter.timeZone = TimeZone(secondsFromGMT: 0)
formatter.dateFormat = "yyyy-MM-dd HH:mm"
let utcDate = formatter.date(from: "2026-07-21 10:30")!
AutoupdatingCurrentLocale — poseban tip локала koji se automatski ažurira pri promeni sistemskih podešavanja korisnika. DateFormatter ga podržava podrazumevano. Ako aplikacija radi u pozadini i korisnik promeni jezik sistema, форматтер креиран pre promene nastaviće da koristi stari локал — zа ažuriranje je potrebno kreirati novu instancu.
ISO8601DateFormatter — специјализовани форматтер zа rad sa datumima u formatu ISO 8601. Ovaj format je de facto стандард zа REST API, JSON i razmenu podataka. ISO8601DateFormatter radi značajno brže od DateFormatter-а jer ne zavisi od локала i koristi fiksnu gramatiku парсинга.
Основне опције форматтера — .withInternetDateTime (2026-07-21T14:30:00Z), .withFractionalSeconds (dodaje milisekunde), .withTimeZone (uključuje pomeraj временске зоне). Kombinovanjem opcija može se dobiti bilo koja ISO 8601 varijanta: са milisekundama, sa vremenskom zonom, sa datumom bez vremena.
JSONEncoder.DateEncodingStrategy omogućava globalno podešavanje kodiranja datuma zа sve Codable modele. Varijante — .iso8601 (koristi ISO8601DateFormatter), .formatted(DateFormatter), .millisecondsSince1970, .secondsSince1970. Izbor strategije utiče na ceo životni ciklus серијализације i treba da bude jedinstven zа sve API ендпоинте.
// ISO8601DateFormatter
let isoFormatter = ISO8601DateFormatter()
isoFormatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
let isoString = isoFormatter.string(from: Date())
// "2026-07-21T14:30:00.000Z"
// JSONEncoder са ISO8601
let encoder = JSONEncoder()
encoder.dateEncodingStrategy = .iso8601
// Алтернатива: JSONEncoder са прилагођеним форматтером
let customEncoder = JSONEncoder()
customEncoder.dateEncodingStrategy = .formatted(myFormatter)
DateFormatter vs ISO8601DateFormatter — birajte ISO8601DateFormatter zа серијализацију i парсинг datuma u API-ju, jer radi 5-10 puta brže od DateFormatter-а i nije podložan greškama локализације. DateFormatter ostavite zа korisnički interfejs, gde je potreban локализовани prikaz sa nazivima meseci i dana u nedelji na maternjem jeziku korisnika.
Razmotrimo realne scenarije korišćenja DateFormatter-а u iOS aplikaciji: prikaz u listi vesti, unos datuma rođenja i izvoz izveštaja sa datumima u različitim vremenskim zonama.
RelativeDateFormatter je optimalan zа novinske feedove. On prikazuje „upravo sada”, „pre 5 minuta”, „juče” zа sveže vesti i prelazi na pun datum zа starije. Prag prelaska se podešava kroz calendar: zа vesti koristite prag od 24 sata, zа месинџере — nedelju dana.
func formatRelativeDate(_ date: Date) -> String {
let relative = RelativeDateFormatter()
relative.unitsStyle = .full
let formatter = DateFormatter()
formatter.dateStyle = .medium
formatter.timeStyle = .short
let daysDiff = Calendar.current.dateComponents(
[.day], from: date, to: Date()
).day ?? 0
return daysDiff < 1
? relative.localizedString(for: date, relativeTo: Date())
: formatter.string(from: date)
}
Unos datuma rođenja — još jedan čest scenario. DateFormatter se podešava sa konkretnim dateFormat-om „dd.MM.yyyy” i lokalom „sr_RS”. Pri парсингу unetog stringa važno je obraditi moguće greške: форматтер vraća nil za neispravan string. Nakon uspešnog парсинга, datum se proverava da li spada u dozvoljeni opseg — ne pre 1900. godine, ne posle današnjeg dana.
Izvoz izveštaja sa datumima zahteva fiksni format nezavisan od локала korisnika. Koristite dateFormat „yyyy-MM-dd HH:mm:ss” sa lokalom en_US_POSIX i vremenskom zonom UTC. Ovakav pristup garantuje da će se fajl ispravno otvoriti u bilo kojoj zemlji bez obzira na regionalna podešavanja sistema.
| Сценарио | Форматтер | Кључно подешавање |
|---|---|---|
| Новински фид | RelativeDateFormatter | unitsStyle = .full |
| Унос датума | DateFormatter | dateFormat + fallback |
| API серијализација | ISO8601DateFormatter | withInternetDateTime |
| Извоз извештаја | DateFormatter | en_US_POSIX + UTC |
Često postavljana pitanja
Najčešći uzrok — nepodudaranje dateFormat-a sa formatom stringa. Na primer, format „dd.MM.yyyy” neće parsirati string „2026-07-21”. Drugi razlog — nepodudaranje локала: string „July 21, 2026” se neće parsirati sa lokalom sr_RS. Treće — greške u simbolima LDML: koristite yyyy, a ne YYYY (različito značenje).
Ne. DateFormatter je težak objekat, njegova inicijalizacija uključuje učitavanje podataka локала. Kreirajte jednu instancu po tipu форматирања i ponovo je koristite. U višenitnom okruženju koristite Thread-local storage ili pool форматтера sa serial queue zа sinhronizaciju.
DateFormatter prikazuje apsolutni datum (21. jul 2026), a RelativeDateFormatter — relativni (danas, juče, za 3 dana). RelativeDateFormatter se pojavio u iOS 15+ i koristi isti LDML шаблон, ali automatski bira relativni prikaz.
Podesite timeZone форматтера na UTC pre парсинга. Ako server vraća datum u lokalnom vremenu bez oznake zone, proverite specifikaciju API-ja — najverovatnije se podrazumeva UTC. Za ISO 8601 sa Z na kraju, timeZone nije potreban — форматтер parsira pomeraj iz stringa.
Ne koristite jednu instancu iz različitih niti bez sinhronizacije. Kreirajte novu instancu u svakoj niti ili koristite Thread.current.threadDictionary za skladištenje. Alternativa — NSLock sa blokiranjem za vreme string(from:) i date(from:).
Zaključak
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође