DateComponents è una struttura di Foundation che memorizza i componenti di una data calendario come campi separati: anno, mese, giorno, ora, minuto, secondo e altri. A differenza di Date, che rappresenta un momento assoluto nel tempo, DateComponents contiene valori leggibili dall'uomo che dipendono dal calendario e dal fuso orario. Secondo la documentazione Apple Developer (2025), DateComponents viene utilizzata come collegamento intermedio tra Date e Calendar — attraverso di essa, le date calendario vengono estratte e costruite, i calcoli e gli spostamenti di data vengono eseguiti senza aritmetica manuale.
Punti chiave
DateComponents è un tipo valore di Foundation progettato per memorizzare i componenti temporali del calendario. Ogni componente è rappresentato da un campo Int opzionale: year, month, day, hour, minute, second, nanosecond, weekday, weekOfMonth, weekOfYear, quarter, yearForWeekOfYear e altri.
La differenza principale da Date è il vincolo al calendario. Date memorizza il tempo assoluto (numero di secondi dalla data di riferimento), mentre DateComponents è una rappresentazione leggibile che ha senso solo nel contesto di un Calendar specifico. La stessa Date può essere rappresentata da diversi DateComponents in diversi calendari e fusi orari.
DateComponents non è un tipo di tempo indipendente, ma un contenitore di dati. Per interpretare DateComponents come una data, è necessario un Calendar che capisca come i componenti si relazionano al sistema calendario. Calendar.dateComponents(from: Date) esegue l'estrazione dei componenti, Calendar.date(from: DateComponents) esegue l'assemblaggio inverso.
Ogni campo di DateComponents è opzionale (Int?), il che è fondamentale per lavorare con date parziali. Se si specificano solo anno e mese, Calendar riempie i campi mancanti con valori predefiniti: giorno = 1, ora = 0, minuto = 0. Questo è comodo per creare date di inizio periodo — basta specificare i componenti rilevanti.
Quando si confrontano DateComponents con l'operatore ==, vengono confrontati solo i campi specificati (non nil). Due strutture DateComponents con lo stesso anno 2026 ma mesi diversi sono considerate distinte. isEqual di NSObjectProtocol non si applica a DateComponents — DateComponents non eredita da NSObject.
Campi principali di DateComponents includono year, month, day, hour, minute, second, nanosecond. Ogni campo memorizza un valore numerico nell'unità corrispondente: anno — 2026, mese — 1..12, giorno — 1..31, ora — 0..23, minuto — 0..59, secondo — 0..59. I nanosecondi possono variare da 0 a 999999999.
Campi della settimana — weekday (1..7, dove 1 = domenica nel calendario gregoriano), weekOfMonth, weekOfYear. Questi campi dipendono da Calendar e non hanno significato al di fuori del suo contesto. weekday dipende dall'impostazione firstWeekday del calendario: nelle impostazioni regionali russe la settimana inizia di lunedì (weekday = 2 nel sistema gregoriano), mentre in quelle americane inizia di domenica (weekday = 1).
Campi specializzati — quarter (1..4), yearForWeekOfYear (l'anno a cui appartiene la settimana), isLeapMonth (un indicatore booleano per i mesi intercalari nei calendari ebraico o cinese). I campi calendar e timeZone memorizzano riferimenti agli oggetti corrispondenti con cui la struttura è stata creata.
| Categoria | Campi | Intervallo |
|---|---|---|
| Calendario | year, month, day | 1..∞, 1..12, 1..31 |
| Tempo | hour, minute, second, nanosecond | 0..23, 0..59, 0..59, 0..999999999 |
| Settimana | weekday, weekOfMonth, weekOfYear | 1..7, 1..5, 1..53 |
| Speciali | quarter, yearForWeekOfYear | 1..4, dipendente |
Quando si estraggono componenti tramite Calendar.dateComponents, è importante richiedere solo i campi necessari per le prestazioni. Calendar estrae tutti i campi richiesti in un unico passaggio — questo è significativamente più veloce che chiamare Calendar.component per ogni singolo campo.
Inizializzare DateComponents — il modo più semplice: creare una struttura vuota e riempire i campi necessari. Tutti i campi non specificati ricevono automaticamente nil. Una data creata da componenti parziali non viene convalidata all'inizializzazione — un errore può verificarsi solo quando si converte in Date tramite Calendar.
L'inizializzatore DateComponents(calendar:timeZone:era:year:month:day:hour:minute:second:nanosecond:weekday:…) consente di impostare tutti i campi in una singola chiamata. Questo inizializzatore è comodo per creare una data completa da valori già pronti, ma viene usato raramente con più di 5-6 argomenti a causa della leggibilità.
Calendar.dateComponents(_:from:) — il modo principale per ottenere DateComponents da un Date esistente. Il secondo argomento è l'insieme dei componenti da estrarre. Calendar esegue i calcoli calendari tenendo conto del fuso orario e restituisce una struttura solo con i campi richiesti; i campi rimanenti rimangono nil.
import Foundation
// Creazione tramite inizializzatore di campi
var components = DateComponents()
components.year = 2026
components.month = 7
components.day = 21
// Estrazione da Date
let now = Date()
let extracted = Calendar.current.dateComponents(
[.year, .month, .day],
from: now
)
print("Today: \(extracted.day!).\(extracted.month!).\(extracted.year!)")
// Creazione tramite inizializzatore esteso
let birthday = DateComponents(
calendar: Calendar.current,
year: 1990, month: 5, day: 15
)
Quando si creano DateComponents impostando manualmente i campi, controlla sempre Calendar prima di convertire in Date. Durante la conversione date(from:), Calendar può restituire nil se i componenti formano una data inesistente — ad esempio, 31 febbraio o 30 febbraio in un anno non bisestile. La convalida della data è responsabilità di Calendar, non di DateComponents.
Calendar.date(from:) — il metodo principale per convertire DateComponents in Date. Calendar interpreta i componenti secondo il proprio calendario e fuso orario. Se alcuni campi non sono impostati (nil), Calendar utilizza valori predefiniti: giorno = 1, ora = 0, minuto = 0, secondo = 0.
Il metodo restituisce una Date opzionale — nil si verifica se i componenti si contraddicono a vicenda o formano una data non valida. Cause tipiche di nil: data inesistente (32 gennaio, 29 febbraio 2023), campi contraddittori (weekday=1, day=5 nello stesso insieme), anno impossibile per il calendario dato (anno 0 nel calendario gregoriano).
DateComponents con timeZone — se DateComponents contiene un timeZone, Calendar lo usa durante la conversione. Se timeZone non è specificato, Calendar usa il proprio timeZone corrente. Se Calendar.timeZone non corrisponde al fuso orario previsto della data, il risultato può differire di alcune ore — assicurati che timeZone sia impostato esplicitamente in uno degli oggetti.
let calendar = Calendar(identifier: .gregorian)
// Creazione di Date da DateComponents
var comps = DateComponents()
comps.year = 2026
comps.month = 12
comps.day = 25
comps.hour = 10
if let date = calendar.date(from: comps) {
print("Christmas: \(date)")
}
// Creazione con specifica di timeZone
calendar.timeZone = TimeZone(identifier: "UTC")!
let utcComps = DateComponents(
calendar: calendar, year: 2026, month: 7, day: 21,
hour: 12
)
let utcDate = calendar.date(from: utcComps)!
Calendar.dateComponents per la differenza di date — un altro caso d'uso di DateComponents. Calendar.dateComponents([.year, .month, .day], from: Date(), to: futureDate) restituisce la differenza in anni, mesi e giorni tra due date. Questo è il modo corretto per calcolare l'età invece di dividere TimeInterval per il numero di secondi in un anno, poiché Calendar tiene conto degli anni bisestili.
Calendar — la classe centrale che lavora con DateComponents. Tutte le operazioni di estrazione, assemblaggio e confronto delle date passano attraverso Calendar. Senza Calendar, DateComponents è solo un insieme di numeri senza significato temporale. Calendar dà ai componenti la loro interpretazione: determina che il mese 2 è febbraio e che weekday 2 è lunedì.
Calendar.nextDate e Calendar.enumerateDates — due metodi basati su DateComponents. nextDate(after: Date(), matching: DateComponents) trova la prossima data corrispondente ai componenti specificati — ad esempio, il prossimo lunedì dopo oggi. enumerateDates(startingAfter:matching:matchingPolicy:using:) scorre tutte le date che corrispondono al modello fino al limite specificato.
Calendar.dateInterval — un metodo che restituisce un DateInterval per il componente specificato. dateInterval(of: .month, for: Date()) restituisce l'inizio e la fine del mese corrente. Internamente, questo metodo utilizza DateComponents per trovare i limiti del periodo: crea DateComponents con il primo e l'ultimo giorno del mese e li converte in Date tramite Calendar.
let calendar = Calendar.current
// Prossimo lunedì
let nextMonday = calendar.nextDate(
after: Date(),
matching: DateComponents(weekday: 2),
matchingPolicy: .nextTime
)!
// Differenza tra date in giorni
let diff = calendar.dateComponents(
[.day], from: Date(), to: nextMonday
)
// Intervallo del mese
let monthInterval = calendar.dateInterval(
of: .month, for: Date()
)!
let startOfMonth = monthInterval.start
let endOfMonth = monthInterval.end
MatchingPolicy — un parametro importante dei metodi di Calendar quando si lavora con DateComponents. strictPolicy richiede una corrispondenza esatta di tutti i componenti, nextTimePolicy seleziona la corrispondenza temporale successiva, nextTimePreservingSmallerComponents preserva i componenti più piccoli (minuti, secondi) dalla data di origine. La scelta della politica influenza il risultato della ricerca di date, specialmente quando ci si sposta attraverso i cambi dell'ora legale.
Esploriamo i casi d'uso pratici di DateComponents in un'applicazione. Ogni esempio dimostra un'attività tipica che uno sviluppatore iOS affronta quando lavora con le date del calendario.
Calendar.nextDate con DateComponents(day: 1) trova il primo giorno del mese successivo. Calendar determina automaticamente il numero di giorni nel mese corrente e passa a quello successivo. Per notifiche ricorrenti, utilizza enumerateDates o Combine.Timer con una chiave Calendar.
func firstDayOfNextMonth(from date: Date) -> Date {
let calendar = Calendar.current
let comps = DateComponents(day: 1)
return calendar.nextDate(
after: date,
matching: comps,
matchingPolicy: .nextTime
)!
}
// Calcolo dell'età in anni
func ageInYears(from birthDate: Date) -> Int {
let calendar = Calendar.current
let ageComponents = calendar.dateComponents(
[.year], from: birthDate, to: Date()
)
return ageComponents.year ?? 0
}
// Raggruppamento di eventi per anno e mese
func groupEventsByMonth(_ events: [Event]) -> [String: [Event]] {
let calendar = Calendar.current
return Dictionary(grouping: events) { event in
let comps = calendar.dateComponents(
[.year, .month], from: event.date
)
return "\(comps.year!)-\(comps.month!)"
}
}
Calcolo dell'età tramite Calendar.dateComponents([.year], from:to:) — l'unico modo corretto che tiene conto degli anni bisestili. Il calcolo basato su TimeInterval (secondi / 31536000) dà errore per le persone nate il 29 febbraio. Calendar determina correttamente se il compleanno è avvenuto nell'anno corrente e restituisce l'età esatta.
Raggruppamento per anno e mese — un'attività comune per schermate di cronologia o calendario. DateComponents funge da chiave di raggruppamento: estrai anno e mese dalla data dell'evento, forma una chiave stringa e raggruppa tramite Dictionary(grouping:). Per la visualizzazione, utilizza DateFormatter con il modello "LLLL yyyy" per un nome di mese localizzato.
| Attività | Metodo Calendar | Ruolo DateComponents |
|---|---|---|
| Primo giorno del mese | nextDate(after:matching:) | day: 1 |
| Calcolo età | dateComponents(from:to:) | [.year] dalla differenza |
| Raggruppamento date | dateComponents(_:from:) | chiave year + month |
| Ricerca giorno settimana | nextDate(after:matching:) | weekday: N |
Domande frequenti
Cause: data inesistente (31 aprile), campi contraddittori (weekday=1 con day=5), combinazione non valida di campi per il calendario selezionato. Calendar tenta di interpretare i componenti nel proprio sistema — se la combinazione è impossibile, il risultato è nil. Utilizza sempre guard let o if let durante la conversione.
Sì, tramite l'operatore ==. DateComponents implementa Equatable, confrontando tutti i campi. Due strutture sono uguali se tutti i loro campi sono uguali (nil == nil è considerato vero). Per confrontare solo un sottoinsieme di campi — estrai lo stesso insieme tramite Calendar.dateComponents.
Date è un momento assoluto nel tempo senza vincolo al calendario. DateComponents è un insieme di numeri leggibili (anno, mese, giorno) che hanno senso solo nel contesto di un Calendar. Date può essere confrontata, sottratta, serializzata in ISO 8601. DateComponents è una rappresentazione intermedia per interagire con il calendario.
Imposta solo i campi year e month, lasciando gli altri come nil. Durante la conversione in Date tramite Calendar.date(from:), Calendar imposterà automaticamente giorno = 1, ora = 0, minuto = 0. Il risultato è un Date corrispondente al primo giorno del mese specificato a mezzanotte.
DateComponents non memorizza informazioni sul fuso orario nei suoi campi — i valori dei campi (anno, mese, giorno) dipendono essi stessi dal timeZone in cui sono stati estratti. I componenti "21 luglio 2026 14:00 MSK" e "21 luglio 2026 10:00 UTC" rappresentano la stessa Date, ma i campi DateComponents sono diversi.
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