DateComponents es una estructura de Foundation que almacena los componentes de una fecha calendárica como campos separados: año, mes, día, hora, minuto, segundo y otros. A diferencia de Date, que representa un momento absoluto en el tiempo, DateComponents contiene valores legibles por humanos que dependen del calendario y la zona horaria. Según la documentación de Apple Developer (2025), DateComponents se utiliza como un vínculo intermedio entre Date y Calendar — a través de ella se extraen y construyen fechas calendáricas, se realizan cálculos y desplazamientos de fechas sin aritmética manual.
Puntos clave
DateComponents es un tipo de valor de Foundation diseñado para almacenar componentes temporales del calendario. Cada componente se representa mediante un campo Int opcional: year, month, day, hour, minute, second, nanosecond, weekday, weekOfMonth, weekOfYear, quarter, yearForWeekOfYear y otros.
La principal diferencia con Date es el vínculo al calendario. Date almacena el tiempo absoluto (cantidad de segundos desde la fecha de referencia), mientras que DateComponents es una representación legible que solo tiene sentido en el contexto de un Calendar específico. Un mismo Date puede representarse con diferentes DateComponents en distintos calendarios y zonas horarias.
DateComponents no es un tipo de tiempo independiente, sino un contenedor de datos. Para interpretar DateComponents como una fecha se necesita un Calendar que entienda cómo se relacionan los componentes con el sistema calendárico. Calendar.dateComponents(from: Date) realiza la extracción de componentes, Calendar.date(from: DateComponents) realiza el ensamblaje inverso.
Cada campo de DateComponents es opcional (Int?), lo cual es fundamental para trabajar con fechas parciales. Si solo se especifican el año y el mes, Calendar completa los campos faltantes con valores predeterminados: día = 1, hora = 0, minuto = 0. Esto es útil para crear fechas de inicio de período — solo hay que especificar los componentes relevantes.
Al comparar DateComponents con el operador ==, solo se comparan los campos especificados (no nil). Dos estructuras DateComponents con el año 2026 pero diferentes meses se consideran distintas. isEqual de NSObjectProtocol no se aplica a DateComponents — DateComponents no hereda de NSObject.
Campos principales de DateComponents incluyen year, month, day, hour, minute, second, nanosecond. Cada campo almacena un valor numérico en la unidad correspondiente: año — 2026, mes — 1..12, día — 1..31, hora — 0..23, minuto — 0..59, segundo — 0..59. Los nanosegundos pueden tomar valores de 0 a 999999999.
Campos de semana — weekday (1..7, donde 1 = domingo en el calendario gregoriano), weekOfMonth, weekOfYear. Estos campos dependen de Calendar y no tienen sentido fuera de su contexto. weekday depende de la configuración firstWeekday del calendario: en la configuración regional rusa la semana comienza en lunes (weekday = 2 en el sistema gregoriano), mientras que en la estadounidense comienza en domingo (weekday = 1).
Campos especializados — quarter (1..4), yearForWeekOfYear (el año al que pertenece la semana), isLeapMonth (un indicador booleano para meses bisiestos en los calendarios hebreo o chino). Los campos calendar y timeZone almacenan referencias a los objetos correspondientes con los que se creó la estructura.
| Categoría | Campos | Rango |
|---|---|---|
| Calendario | year, month, day | 1..∞, 1..12, 1..31 |
| Tiempo | hour, minute, second, nanosecond | 0..23, 0..59, 0..59, 0..999999999 |
| Semana | weekday, weekOfMonth, weekOfYear | 1..7, 1..5, 1..53 |
| Especiales | quarter, yearForWeekOfYear | 1..4, dependiente |
Al extraer componentes mediante Calendar.dateComponents, es importante solicitar solo los campos necesarios por rendimiento. Calendar extrae todos los campos solicitados en una sola pasada — esto es significativamente más rápido que llamar a Calendar.component para cada campo individualmente.
Inicializar DateComponents — la forma más simple: crear una estructura vacía y llenar los campos necesarios. Todos los campos no especificados obtienen automáticamente nil. Una fecha creada a partir de componentes parciales no se valida en la inicialización — un error solo puede ocurrir al convertir a Date mediante Calendar.
El inicializador DateComponents(calendar:timeZone:era:year:month:day:hour:minute:second:nanosecond:weekday:…) permite establecer todos los campos en una sola llamada. Este inicializador es conveniente para crear una fecha completa a partir de valores ya preparados, pero rara vez se usa con más de 5-6 argumentos debido a la legibilidad.
Calendar.dateComponents(_:from:) — la forma principal de obtener DateComponents a partir de un Date existente. El segundo argumento es el conjunto de componentes a extraer. Calendar realiza los cálculos calendáricos teniendo en cuenta la zona horaria y devuelve una estructura solo con los campos solicitados; los campos restantes permanecen como nil.
import Foundation
// Creación mediante inicializador de campos
var components = DateComponents()
components.year = 2026
components.month = 7
components.day = 21
// Extracción de Date
let now = Date()
let extracted = Calendar.current.dateComponents(
[.year, .month, .day],
from: now
)
print("Today: \(extracted.day!).\(extracted.month!).\(extracted.year!)")
// Creación mediante inicializador extendido
let birthday = DateComponents(
calendar: Calendar.current,
year: 1990, month: 5, day: 15
)
Al crear DateComponents estableciendo los campos manualmente, verifica siempre el Calendar antes de convertir a Date. Al convertir date(from:), Calendar puede devolver nil si los componentes forman una fecha inexistente — por ejemplo, 31 de febrero o 30 de febrero en un año no bisiesto. La validación de la fecha es responsabilidad de Calendar, no de DateComponents.
Calendar.date(from:) — el método principal para convertir DateComponents a Date. Calendar interpreta los componentes según su propio calendario y zona horaria. Si algunos campos no están establecidos (nil), Calendar usa valores predeterminados: día = 1, hora = 0, minuto = 0, segundo = 0.
El método devuelve un Date opcional — nil ocurre si los componentes se contradicen entre sí o forman una fecha no válida. Causas típicas de nil: fecha inexistente (32 de enero, 29 de febrero de 2023), campos contradictorios (weekday=1, day=5 en el mismo conjunto), año imposible para el calendario dado (año 0 en el calendario gregoriano).
DateComponents con timeZone — si DateComponents contiene una timeZone, Calendar la usa durante la conversión. Si no se especifica timeZone, Calendar usa su propia timeZone actual. Si Calendar.timeZone no coincide con la zona horaria esperada de la fecha, el resultado puede diferir en varias horas — asegúrate de que timeZone esté explícitamente establecida en uno de los objetos.
let calendar = Calendar(identifier: .gregorian)
// Creación de Date desde 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)")
}
// Creación especificando 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 para diferencia de fechas — otro caso de uso de DateComponents. Calendar.dateComponents([.year, .month, .day], from: Date(), to: futureDate) devuelve la diferencia en años, meses y días entre dos fechas. Esta es la forma correcta de calcular la edad en lugar de dividir TimeInterval por la cantidad de segundos en un año, ya que Calendar tiene en cuenta los años bisiestos.
Calendar — la clase central que trabaja con DateComponents. Todas las operaciones de extracción, ensamblaje y comparación de fechas pasan por Calendar. Sin Calendar, DateComponents es solo un conjunto de números sin significado temporal. Calendar da a los componentes su interpretación: determina que el mes 2 es febrero y que weekday 2 es lunes.
Calendar.nextDate y Calendar.enumerateDates — dos métodos basados en DateComponents. nextDate(after: Date(), matching: DateComponents) encuentra la siguiente fecha que coincida con los componentes especificados — por ejemplo, el próximo lunes después de hoy. enumerateDates(startingAfter:matching:matchingPolicy:using:) itera sobre todas las fechas que coinciden con el patrón hasta el límite especificado.
Calendar.dateInterval — un método que devuelve un DateInterval para el componente especificado. dateInterval(of: .month, for: Date()) devuelve el inicio y el final del mes actual. Internamente, este método usa DateComponents para encontrar los límites del período: crea DateComponents con el primer y último día del mes y los convierte a Date mediante Calendar.
let calendar = Calendar.current
// Próximo lunes
let nextMonday = calendar.nextDate(
after: Date(),
matching: DateComponents(weekday: 2),
matchingPolicy: .nextTime
)!
// Diferencia entre fechas en días
let diff = calendar.dateComponents(
[.day], from: Date(), to: nextMonday
)
// Rango del mes
let monthInterval = calendar.dateInterval(
of: .month, for: Date()
)!
let startOfMonth = monthInterval.start
let endOfMonth = monthInterval.end
MatchingPolicy — un parámetro importante de los métodos de Calendar al trabajar con DateComponents. strictPolicy requiere una coincidencia exacta de todos los componentes, nextTimePolicy selecciona la siguiente coincidencia en el tiempo, nextTimePreservingSmallerComponents conserva los componentes más pequeños (minutos, segundos) de la fecha de origen. La elección de la política afecta el resultado de la búsqueda de fechas, especialmente al desplazarse a través de cambios de horario de verano.
Exploremos casos prácticos de uso de DateComponents en una aplicación. Cada ejemplo demuestra una tarea típica que enfrenta un desarrollador de iOS al trabajar con fechas calendáricas.
Calendar.nextDate con DateComponents(day: 1) encuentra el primer día del mes siguiente. Calendar determina automáticamente la cantidad de días en el mes actual y pasa al siguiente. Para notificaciones recurrentes, usa enumerateDates o Combine.Timer con una clave de 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
)!
}
// Cálculo de edad en años
func ageInYears(from birthDate: Date) -> Int {
let calendar = Calendar.current
let ageComponents = calendar.dateComponents(
[.year], from: birthDate, to: Date()
)
return ageComponents.year ?? 0
}
// Agrupación de eventos por año y mes
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!)"
}
}
Cálculo de edad mediante Calendar.dateComponents([.year], from:to:) — la única forma correcta que tiene en cuenta los años bisiestos. El cálculo basado en TimeInterval (segundos / 31536000) da error para personas nacidas el 29 de febrero. Calendar determina correctamente si el cumpleaños ocurrió en el año actual y devuelve la edad exacta.
Agrupación por año y mes — una tarea común para pantallas de historial o calendario. DateComponents sirve como clave de agrupación: extrae el año y el mes de la fecha del evento, forma una clave de cadena y agrupa mediante Dictionary(grouping:). Para la visualización, usa DateFormatter con la plantilla "LLLL yyyy" para un nombre de mes localizado.
| Tarea | Método de Calendar | Rol de DateComponents |
|---|---|---|
| Primer día del mes | nextDate(after:matching:) | day: 1 |
| Cálculo de edad | dateComponents(from:to:) | [.year] de la diferencia |
| Agrupación de fechas | dateComponents(_:from:) | clave año + mes |
| Búsqueda de día de semana | nextDate(after:matching:) | weekday: N |
Preguntas frecuentes
Causas: fecha inexistente (31 de abril), campos contradictorios (weekday=1 con day=5), combinación no válida de campos para el calendario seleccionado. Calendar intenta interpretar los componentes en su sistema — si la combinación es imposible, el resultado es nil. Usa siempre guard let o if let al convertir.
Sí, mediante el operador ==. DateComponents implementa Equatable, comparando todos los campos. Dos estructuras son iguales si todos sus campos son iguales (nil == nil se considera verdadero). Para comparar solo un subconjunto de campos — extrae el mismo conjunto mediante Calendar.dateComponents.
Date es un momento absoluto en el tiempo sin vínculo al calendario. DateComponents es un conjunto de números legibles (año, mes, día) que solo tienen sentido en el contexto de un Calendar. Date se puede comparar, restar, serializar a ISO 8601. DateComponents es una representación intermedia para interactuar con el calendario.
Establece solo los campos year y month, dejando el resto como nil. Al convertir a Date mediante Calendar.date(from:), Calendar establecerá automáticamente día = 1, hora = 0, minuto = 0. El resultado es un Date correspondiente al primer día del mes especificado a medianoche.
DateComponents no almacena información de zona horaria en sus campos — los valores de los campos (año, mes, día) dependen de la timeZone en la que se extrajeron. Los componentes "21 de julio de 2026 14:00 MSK" y "21 de julio de 2026 10:00 UTC" representan el mismo Date, pero los campos de DateComponents son diferentes.
Resumen
Desarrollaremos una aplicación móvil llave en mano
IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.
Lea también