DateFormatter è una classe Foundation progettata per la conversione bidirezionale tra oggetti Date e la loro rappresentazione in stringa. La classe tiene conto delle impostazioni regionali, del fuso orario e del calendario dell'utente, garantendo una visualizzazione corretta delle date in qualsiasi regione del mondo. Secondo la Documentazione per Sviluppatori Apple (2025), DateFormatter supporta quattro stili predefiniti di data e ora, oltre a formati completamente personalizzati tramite una stringa di modello. Senza DateFormatter è impossibile visualizzare correttamente una data all'utente in un'applicazione internazionalizzata.
Punti chiave
DateFormatter è una classe del framework Foundation che implementa la conversione bidirezionale tra Date e stringa. Apparve per la prima volta in OpenStep come NSDateFormatter e da allora è rimasto lo strumento principale per la formattazione delle date su tutte le piattaforme Apple. La classe eredita da Formatter e fornisce un'API comoda per la visualizzazione localizzata delle date.
DateFormatter funziona sulla base di modelli Unicode LDML — gli stessi utilizzati in ICU (International Components for Unicode). Il modello viene impostato tramite la proprietà dateFormat, dove i simboli y, M, d, H, m, s corrispondono a anno, mese, giorno, ore, minuti, secondi. La ripetizione di un simbolo determina il formato: “y” — anno a due cifre, “yyyy” — anno a quattro cifre.
Creare un DateFormatter è un'operazione costosa, poiché durante l'inizializzazione vengono caricati i dati delle impostazioni regionali e del calendario. Apple consiglia di creare un formattatore una volta per ogni tipo di formattazione e riutilizzarlo. In SwiftUI e UIKit, i formattatori sono spesso memorizzati nella cache in proprietà statiche o creati pigramente al primo accesso.
DateFormatter viene utilizzato in molti componenti di sistema iOS. UIDatePicker utilizza DateFormatter internamente per visualizzare le date in modalità countDownTimer. Un TextField con un formattatore può convalidare automaticamente le date inserite dall'utente. Core Data supporta attributi di tipo Date, ma la loro rappresentazione in stringa viene sempre gestita tramite DateFormatter.
Sicurezza dei thread — DateFormatter non è thread-safe. La modifica delle proprietà del formattatore da thread diversi porta a un comportamento indefinito. Per l'uso multi-thread, creare istanze separate del formattatore per ogni thread o utilizzare la sincronizzazione tramite NSLock o una coda seriale.
dateStyle e timeStyle sono i modi più semplici per configurare la visualizzazione delle date. Ogni stile ha quattro varianti: .short, .medium, .long, .full. La combinazione di dateStyle e timeStyle consente una configurazione indipendente del formato di data e ora, e la proprietà .none disabilita la parte corrispondente.
Per le impostazioni regionali statunitensi, .short formatta la data come “7/21/26”, e per quelle russe come “21.07.2026”. Lo stile .long per le impostazioni regionali russe restituisce “21 luglio 2026”, e .full restituisce “martedì, 21 luglio 2026” con il giorno della settimana. Tutti e quattro gli stili si adattano automaticamente agli standard regionali, inclusi l'ordine dei componenti e i separatori.
SFDateFormatter in iOS 15+ offre un approccio alternativo tramite RelativeDateFormatter e DateIntervalFormatter. RelativeDateFormatter restituisce “oggi”, “ieri”, “tra 3 giorni” per un contesto immediato. DateIntervalFormatter visualizza intervalli di date: “21–25 luglio 2026” — per prenotazioni e pianificazione.
| Stile | Esempio (ru_RU) | Esempio (en_US) |
|---|---|---|
| .short | 21.07.2026 | 7/21/26 |
| .medium | 21 lug 2026 | Jul 21, 2026 |
| .long | 21 luglio 2026 | July 21, 2026 |
| .full | martedì, 21 luglio 2026 | Tuesday, July 21, 2026 |
Quando si combinano gli stili, DateFormatter seleziona automaticamente il separatore: per .short.date + .short.time il risultato potrebbe essere “21.07.2026, 14:30”. Per .full.date + .full.time — “martedì, 21 luglio 2026, 14:30:00 MSK”. Il separatore è gestito dalle impostazioni regionali, non dallo sviluppatore — questo garantisce la conformità con le aspettative regionali dell'utente.
dateFormat consente di impostare un modello di formattazione arbitrario utilizzando simboli di specifica Unicode LDML. Questo dà il controllo completo sulla visualizzazione: è possibile mostrare solo anno e mese, o il giorno della settimana senza la data, o l'ora senza secondi. Il formato personalizzato è indispensabile per requisiti di progettazione specifici.
Simboli principali — yyyy (anno: 2026), MM (mese: 07), dd (giorno: 21), HH (ore: 14), mm (minuti: 30), ss (secondi: 00). Per il nome completo del mese utilizzare MMMM (luglio), per l'abbreviato — MMM (lug). Giorno della settimana — EEEE (martedì), abbreviato — E (mar).
Quando si utilizza dateFormat, è importante impostare le impostazioni regionali del formattatore. Se le impostazioni regionali non sono impostate, il formattatore utilizza le impostazioni regionali di sistema, il che potrebbe essere indesiderabile per un formato fisso in un'API. Apple consiglia di impostare locale = Locale(identifier: “en_US_POSIX”) per un formato fisso interregionale, specialmente quando si analizzano le date dalle risposte del server.
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "ru_RU")
formatter.dateFormat = "d MMMM yyyy"
let customString = formatter.string(from: Date())
// "21 July 2026"
// Analisi di una stringa personalizzata
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
let date = formatter.date(from: "2026-07-21 14:30:00")!
Un errore in dateFormat è una delle cause comuni di arresto anomalo dell'app. Se il formato non corrisponde alla stringa, il metodo date(from:) restituisce nil. Utilizzare guard let o ?? per l'unwrapping sicuro degli optional. Per convalidare il formato, testarlo in tutte le lingue supportate — alcuni simboli LDML funzionano diversamente in diverse impostazioni regionali.
Locale determina come vengono visualizzati i nomi dei mesi, i giorni della settimana e i separatori. DateFormatter utilizza Locale.current per impostazione predefinita, ma in alcuni scenari è necessario specificare impostazioni regionali specifiche: per un formato fisso nei registri utilizzare en_US_POSIX, per le date del server — le impostazioni regionali corrispondenti al server.
La proprietà TimeZone determina il fuso orario per la visualizzazione. Per impostazione predefinita viene utilizzato il fuso orario di sistema, ma per le applicazioni con pubblico internazionale è spesso necessario visualizzare le date nel fuso orario dell'utente o in UTC. La modifica di timeZone influisce solo sulla visualizzazione — il valore Date rimane invariato.
Una caratteristica importante: se DateFormatter viene utilizzato per analizzare una stringa e la stringa contiene un'indicazione del fuso orario (ad esempio, “2026-07-21T14:30:00Z” con Z per UTC), la proprietà timeZone viene ignorata — il formattatore utilizza il fuso orario della stringa. Se il fuso orario è assente nella stringa, viene applicato il timeZone del formattatore.
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"
// Analisi senza fuso orario nella stringa
formatter.timeZone = TimeZone(secondsFromGMT: 0)
formatter.dateFormat = "yyyy-MM-dd HH:mm"
let utcDate = formatter.date(from: "2026-07-21 10:30")!
AutoupdatingCurrentLocale — un tipo speciale di impostazioni regionali che si aggiorna automaticamente quando le impostazioni di sistema dell'utente cambiano. DateFormatter lo supporta per impostazione predefinita. Se l'applicazione è in esecuzione in background e l'utente cambia la lingua di sistema, un formattatore creato prima della modifica continuerà a utilizzare le vecchie impostazioni regionali — per aggiornarlo è necessario creare una nuova istanza.
ISO8601DateFormatter è un formattatore specializzato per lavorare con date in formato ISO 8601. Questo formato è lo standard de facto per API REST, JSON e scambio di dati. ISO8601DateFormatter funziona significativamente più velocemente di DateFormatter perché non dipende dalle impostazioni regionali e utilizza una grammatica di analisi fissa.
Opzioni principali del formattatore — .withInternetDateTime (2026-07-21T14:30:00Z), .withFractionalSeconds (aggiunge millisecondi), .withTimeZone (include l'offset del fuso orario). Combinando le opzioni, è possibile ottenere qualsiasi variante ISO 8601: con millisecondi, con fuso orario, solo con data.
JSONEncoder.DateEncodingStrategy consente di configurare globalmente la codifica delle date per tutti i modelli Codable. Opzioni — .iso8601 (utilizza ISO8601DateFormatter), .formatted(DateFormatter), .millisecondsSince1970, .secondsSince1970. La scelta della strategia influisce sull'intero ciclo di vita della serializzazione e deve essere coerente su tutti gli endpoint dell'API.
// ISO8601DateFormatter
let isoFormatter = ISO8601DateFormatter()
isoFormatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
let isoString = isoFormatter.string(from: Date())
// "2026-07-21T14:30:00.000Z"
// JSONEncoder con ISO8601
let encoder = JSONEncoder()
encoder.dateEncodingStrategy = .iso8601
// Alternativa: JSONEncoder con formattatore personalizzato
let customEncoder = JSONEncoder()
customEncoder.dateEncodingStrategy = .formatted(myFormatter)
DateFormatter vs ISO8601DateFormatter — scegliere ISO8601DateFormatter per serializzare e analizzare le date nelle API, poiché è 5-10 volte più veloce di DateFormatter e non è soggetto a errori di localizzazione. Utilizzare DateFormatter per l'interfaccia utente dove è richiesta una visualizzazione localizzata con nomi di mesi e giorni nella lingua madre dell'utente.
Vediamo scenari reali di utilizzo di DateFormatter in un'app iOS: visualizzazione in un feed di notizie, inserimento di una data di nascita ed esportazione di un report con date in diversi fusi orari.
RelativeDateFormatter è ottimale per i feed di notizie. Mostra “proprio ora”, “5 minuti fa”, “ieri” per le notizie recenti e passa alla data completa per quelle vecchie. La soglia di commutazione viene configurata tramite il calendario: per le notizie utilizzare una soglia di 24 ore, per i messenger — una settimana.
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)
}
Inserimento di una data di nascita — un altro scenario comune. DateFormatter viene configurato con un dateFormat specifico “dd.MM.yyyy” e locale “ru_RU”. Quando si analizza la stringa inserita, è importante gestire i possibili errori: il formattatore restituisce nil per una stringa non valida. Dopo l'analisi riuscita, la data viene verificata per essere entro un intervallo accettabile — non prima del 1900, non dopo oggi.
Esportazione di un report con date richiede un formato fisso indipendente dalle impostazioni regionali dell'utente. Utilizzare dateFormat “yyyy-MM-dd HH:mm:ss” con locale en_US_POSIX e fuso orario UTC. Questo approccio garantisce che il file si apra correttamente in qualsiasi paese indipendentemente dalle impostazioni regionali del sistema.
| Scenario | Formattatore | Impostazione chiave |
|---|---|---|
| Feed di notizie | RelativeDateFormatter | unitsStyle = .full |
| Inserimento data | DateFormatter | dateFormat + fallback |
| Serializzazione API | ISO8601DateFormatter | withInternetDateTime |
| Esportazione report | DateFormatter | en_US_POSIX + UTC |
Domande frequenti
Il motivo più comune — una mancata corrispondenza tra dateFormat e il formato della stringa. Ad esempio, il formato “dd.MM.yyyy” non analizzerà la stringa “2026-07-21”. Il secondo motivo — mancata corrispondenza delle impostazioni regionali: la stringa “July 21, 2026” non verrà analizzata con le impostazioni regionali ru_RU. Il terzo — errori di battitura nei simboli LDML: utilizzare yyyy, non YYYY (significato diverso).
No. DateFormatter è un oggetto pesante, la sua inizializzazione include il caricamento dei dati delle impostazioni regionali. Creare un'istanza per tipo di formattazione e riutilizzarla. In un ambiente multi-thread, utilizzare l'archiviazione locale del thread o un pool di formattatori con una coda seriale per la sincronizzazione.
DateFormatter mostra una data assoluta (21 luglio 2026), mentre RelativeDateFormatter mostra una data relativa (oggi, ieri, tra 3 giorni). RelativeDateFormatter è stato introdotto in iOS 15+ e utilizza lo stesso modello LDML ma seleziona automaticamente la visualizzazione relativa.
Impostare il timeZone del formattatore su UTC prima dell'analisi. Se il server restituisce una data in ora locale senza indicazione del fuso orario, controllare la specifica dell'API — molto probabilmente si intende UTC. Per ISO 8601 con Z alla fine, non è necessario timeZone — il formattatore analizza l'offset dalla stringa.
Non utilizzare una singola istanza da thread diversi senza sincronizzazione. Creare una nuova istanza in ogni thread o utilizzare Thread.current.threadDictionary per l'archiviazione. Un'alternativa è NSLock con blocco per la durata di string(from:) e date(from:).
Riepilogo
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.
Leggi anche