Calendar — qué es, calendario Date y cálculos en Swift

Autor: IT Sectr Publicado: 2026-07-12 Tiempo de lectura: 7 min

Calendar es una clase de Foundation que define un sistema calendárico y proporciona métodos para cálculos con calendarios: extracción de componentes de fecha, cálculo de diferencias entre fechas, búsqueda de límites de períodos y desplazamiento de fechas. El calendario vincula el tiempo absoluto (Date) con componentes legibles por humanos y tiene en cuenta las particularidades regionales: inicio de la semana, zona horaria y horario de verano. Según la Documentación para Desarrolladores de Apple (2025), Foundation admite 17 sistemas calendáricos — desde el gregoriano hasta el budista y el japonés — lo que hace de Calendar una herramienta universal para aplicaciones internacionalizadas.

Puntos clave

  • Calendar — una clase de Foundation para cálculos calendáricos: extracción de componentes, comparación y desplazamiento de fechas.
  • Calendar.current — el calendario del sistema del usuario, que incorpora automáticamente la configuración regional.
  • 17 sistemas calendáricos — Foundation admite gregoriano, budista, japonés, hebreo, islámico y otros.
  • Calendar.dateComponents extrae componentes (año, mes, día) de Date teniendo en cuenta la zona horaria.
  • Calendar.dateInterval devuelve la fecha de inicio y fin de un período especificado (día, semana, mes).

¿Qué es Calendar en Foundation?

Calendar es una clase de Foundation que implementa cálculos calendáricos basados en ICU (International Components for Unicode). El calendario define cómo el tiempo absoluto (Date) se asigna a componentes calendáricos: año, mes, día, hora, minuto, segundo. Sin Calendar es imposible saber qué año, mes y día es — Date por sí mismo no contiene esta información.

El calendario tiene en cuenta tres grupos de parámetros: el sistema calendárico (gregoriano, budista, japonés), la zona horaria y la configuración regional. Calendar.current combina los tres a partir de la configuración del sistema del usuario. Calendar.autoupdatingCurrent es una versión especial que se actualiza automáticamente al cambiar la configuración sin reiniciar la aplicación mediante NotificationCenter.

Calendar es un tipo por valor (value type) en Foundation. Calendar(identifier:) crea una nueva instancia con parámetros fijos. Calendar se puede copiar, comparar con == y usar como clave en un diccionario. Esto permite crear calendarios con configuraciones específicas de timeZone y locale para pruebas.

Calendar y NSCalendar

Calendar es la versión Swift de NSCalendar de Objective-C, con puente mediante as Calendar / as NSCalendar. En Swift moderno se usa Calendar en todas partes. NSCalendar se mantiene para compatibilidad inversa con APIs de Objective-C. Calendar tiene un conjunto completo de métodos sin el prefijo NS, con argumentos type-safe y opcionales de Swift.

Seguridad para hilos — Calendar es seguro para lectura desde múltiples hilos. Una instancia creada se puede leer de forma segura desde varios hilos. La modificación de propiedades (timeZone, locale) no es segura para hilos — cree instancias separadas de Calendar para diferentes configuraciones.

Tipos de calendarios en Foundation

Foundation admite 17 sistemas calendáricos mediante la enumeración Calendar.Identifier. Cada sistema tiene sus propias reglas para años bisiestos, número de meses e inicio de la era. La elección del calendario afecta todos los cálculos: dateComponents, dateInterval, nextDate.

Principales sistemas calendáricos:

  • .gregorian — estándar internacional, 12 meses, 365/366 días, era desde el nacimiento de Cristo.
  • .buddhist — calendario budista, usado en Tailandia, Camboya, Laos, la era supera en 543 años a la gregoriana.
  • .japanese — calendario japonés por eras de reinado imperial, 12 meses como el gregoriano.
  • .hebrew — calendario hebreo, 12/13 meses, mes bisiesto Adar II.
  • .islamic — calendario islámico, 12 meses lunares, 354/355 días.
  • .indian — calendario nacional indio (Saka), 12 meses.

Calendar(identifier: .gregorian) — el más utilizado. Cumple con el estándar internacional ISO 8601 y es el calendario predeterminado en la mayoría de países. Para aplicaciones con audiencia internacional, use Calendar.current — se ajusta automáticamente al calendario del sistema del usuario.

IdentificadorTipoRegión de uso
.gregorianSolarInternacional
.buddhistSolarTailandia, Camboya
.japaneseSolarJapón
.hebrewLunisolarIsrael
.islamicLunarPaíses islámicos
.chineseLunisolarChina

Calendar y DateComponents

DateComponents y Calendar son un par inseparable. Calendar.dateComponents(_:from:) extrae componentes de Date respetando la zona horaria del calendario. Calendar.date(from:) ensambla un Date a partir de DateComponents, completando los campos faltantes con valores predeterminados: día = 1, hora = 0, minuto = 0, segundo = 0.

El método Calendar.component extrae un único componente, conveniente para comprobaciones rápidas. Calendar.dateComponents extrae múltiples componentes en una sola llamada — esto es más eficiente ya que Calendar realiza los cálculos calendáricos una vez en lugar de hacerlo para cada componente por separado. Para una lista de 3+ componentes, use siempre dateComponents.

Calendar.compare compara dos Dates con una precisión determinada. El parámetro toGranularity define la precisión del componente: .year compara solo el año, .month — año y mes, .day — año, mes, día. Es útil para comprobar si dos fechas caen en el mismo día, ignorando la hora.

swift
let calendar = Calendar.current
let now = Date()

// Extraer un solo componente
let year = calendar.component(.year, from: now)

// Extraer un conjunto de componentes
let comps = calendar.dateComponents(
    [.year, .month, .day], from: now
)

// Comparar con granularidad de día
let isSameDay = calendar.compare(date1, to: date2,
                                  toGranularity: .day) == .orderedSame

// Verificar si la fecha es hoy
let isToday = calendar.isDateInToday(someDate)

Calendar.isDateInToday, isDateInTomorrow, isDateInYesterday — métodos para comprobaciones relativas. Calendar.isDate(_:inSameDayAs:) comprueba si dos fechas caen en el mismo día calendario considerando la zona horaria del calendario. Estos métodos usan Calendar.compare internamente y están optimizados para llamadas frecuentes.

Cálculos con Calendar

Calendar.dateInterval es uno de los métodos más útiles para analítica e interfaz de usuario. Devuelve un DateInterval para el componente especificado: inicio y fin de un día, semana, mes, año. DateInterval contiene start (Date) y end (Date) — los límites del período. Por ejemplo, dateInterval(of: .weekOfYear, for: Date()) devuelve el inicio del lunes y el fin del domingo de la semana actual.

Calendar.date con byAdding — un método para desplazar fechas. Calendar.date(byAdding: .day, value: 7, to: Date()) devuelve la fecha una semana después. Calendar.date(byAdding: DateComponents) es una versión más flexible que permite desplazar múltiples componentes a la vez: +1 mes +3 días. Calendar tiene en cuenta automáticamente las distintas longitudes de los meses y los años bisiestos.

Calendar.nextDate busca la siguiente fecha que coincida con los DateComponents especificados. El parámetro matchingPolicy define el comportamiento en caso de no coincidencia: .nextTime — la siguiente coincidencia horaria, .nextTimePreservingSmallerComponents — conserva los minutos y segundos de la fecha original, .strict — requiere una coincidencia exacta.

swift
let calendar = Calendar.current
let today = Date()

// Inicio y fin de la semana
let weekInterval = calendar.dateInterval(
    of: .weekOfYear, for: today
)!

// Desplazar 1 mes
let nextMonth = calendar.date(
    byAdding: .month, value: 1, to: today
)!

// Desplazar mediante DateComponents
var delta = DateComponents()
delta.month = 1
delta.day = 3
let shifted = calendar.date(byAdding: delta, to: today)!

// Siguiente viernes 13
let friday13Components = DateComponents(
    weekday: 6, day: 13
)
let nextFriday13 = calendar.nextDate(
    after: today, matching: friday13Components,
    matchingPolicy: .nextTime
)

EnumerateDates — un método potente para iterar fechas por patrón. Calendar.enumerateDates(startingAfter:matching:matchingPolicy:using:) llama a un bloque para cada coincidencia hasta que el bloque devuelve stop = true. Se usa para generar eventos recurrentes en calendarios y horarios. Este método es más eficiente que un bucle manual con nextDate, ya que está optimizado por ICU.

TimeZone y Locale

TimeZone es una parte integral de Calendar. La zona horaria determina a qué hora calendario corresponde un Date absoluto. El mismo Date en UTC y en Moscú produce componentes diferentes: Date() en UTC puede mostrar las 10:00, mientras que en MSK — las 13:00. Calendar.timeZone por defecto es TimeZone.current.

Locale afecta al primer día de la semana, al número mínimo de días en la primera semana del año (minDaysInFirstWeek) y a los nombres de los meses y días de la semana (al convertir mediante DateFormatter). Calendar.locale por defecto es Locale.current. En la configuración regional rusa la semana comienza el lunes, en la estadounidense — el domingo.

Calendar.availableIdentifiers devuelve una lista de todos los identificadores de calendario admitidos. La propiedad estática Calendar.availableCalendarIdentifiers es un array de cadenas con los mismos identificadores. Se usa para construir una interfaz de selección de calendario y para verificar la disponibilidad de un sistema calendárico específico en el dispositivo.

swift
// Calendar con zona horaria específica
var utcCalendar = Calendar(identifier: .gregorian)
utcCalendar.timeZone = TimeZone(identifier: "UTC")!

// Calendar con configuración regional rusa
var russianCalendar = Calendar(identifier: .gregorian)
russianCalendar.locale = Locale(identifier: "ru_RU")

// El primer día laborable depende de la configuración regional
let firstWeekday = russianCalendar.firstWeekday
// 2 = lunes (en ru_RU)

// Lista de calendarios disponibles
for identifier in Calendar.availableIdentifiers {
    print(identifier)
}

firstWeekday — una propiedad de Calendar que determina qué día de la semana se considera el primero. En la configuración regional rusa Sunday = 2 (el lunes es el primero). En la configuración regional estadounidense Sunday = 1. Esto afecta a weekOfMonth y weekOfYear: la misma fecha puede pertenecer a diferentes números de semana en distintas configuraciones regionales. Para aplicaciones que trabajan con fechas, use Calendar.current o establezca firstWeekday explícitamente.

Ejemplos de trabajo con Calendar

Consideremos escenarios prácticos que demuestran las capacidades de Calendar. Cada ejemplo resuelve una tarea específica de desarrollo iOS y muestra la forma correcta de usar los cálculos calendáricos.

Verificar: ¿la fecha está en este mes?

Calendar.dateInterval(of: .month, for:) devuelve los límites del mes actual. Verificar si un Date cae dentro de este intervalo es la forma más rápida de determinar si una fecha pertenece al mes actual. Una alternativa es Calendar.compare con granularidad .month: si el resultado es .orderedSame, el mes coincide.

swift
func isInCurrentMonth(_ date: Date) -> Bool {
    let calendar = Calendar.current
    let monthInterval = calendar.dateInterval(
        of: .month, for: Date()
    )!
    return monthInterval.contains(date)
}

// Número de días en un mes
func daysInMonth(for date: Date) -> Int {
    let calendar = Calendar.current
    return calendar.range(
        of: .day, in: .month, for: date
    )?.count ?? 0
}

// Adición de meses con ajuste correcto
func addMonths(_ months: Int, to date: Date) -> Date {
    let calendar = Calendar.current
    return calendar.date(
        byAdding: .month, value: months, to: date
    )!
}

Calendar.range(of:in:for:) devuelve el rango de valores válidos para un componente especificado en el contexto de otro componente. Por ejemplo, range(of: .day, in: .month, for: date) devuelve 1..<32 para meses con 31 días o 1..<29 para febrero de un año no bisiesto. Esta es la forma correcta de obtener el número de días de un mes, sin usar valores fijos.

Adición de meses mediante Calendar.date(byAdding:value:to:) maneja correctamente las fechas límite. Si se añade 1 mes al 31 de enero, Calendar devuelve el 28 de febrero (o 29 en año bisiesto), en lugar del 3 de marzo, que resultaría de simplemente añadir 30 días mediante TimeInterval. Esta es otra razón para no usar TimeInterval en cálculos calendáricos.

Método de CalendarPropósitoEjemplo
dateIntervalLímites de períodoInicio y fin de un mes
range(of:in:for:)Rango de componenteDías del mes actual
date(byAdding:)Desplazamiento de fecha+1 mes desde hoy
isDateInTodayComprobación de hoy¿La fecha pertenece a hoy?
compare(toGranularity:)Comparación con precisiónMismo día ignorando la hora

Preguntas frecuentes

¿Cuál es la diferencia entre Calendar.current y Calendar(identifier: .gregorian)?

Calendar.current devuelve el calendario de la configuración del sistema del usuario — puede no ser gregoriano (por ejemplo, budista en Tailandia). Calendar(identifier: .gregorian) siempre crea un calendario gregoriano independientemente de la configuración. Use Calendar.current para mostrar fechas y un identificador explícitamente elegido para la lógica de negocio.

¿Por qué Calendar.date(byAdding: .month, value: 1) a veces devuelve la misma fecha?

Esto se debe a las diferentes longitudes de los meses. Si la fecha actual es el 31 de enero, añadir 1 mes da como resultado el 28 de febrero, ya que febrero no tiene 31 días. Calendar ajusta automáticamente la fecha al último día válido del mes. Para un control preciso, use DateComponents con day: 1 para pasar al primer día del mes.

¿Qué calendario usa DateFormatter por defecto?

DateFormatter usa Calendar.current — el calendario del sistema del usuario. Si una aplicación debe mostrar siempre las fechas en el calendario gregoriano independientemente de la configuración, establezca formatter.calendar = Calendar(identifier: .gregorian). Esto garantiza una visualización uniforme para todos los usuarios.

¿Cómo comprobar si un año es bisiesto mediante Calendar?

Calendar.range(of: .day, in: .year, for: date) devuelve 365 o 366 días. Más simple: Calendar.date(from: DateComponents(year: year, month: 2, day: 29)) != nil — si existe el 29 de febrero, el año es bisiesto. Calendar maneja automáticamente las reglas del sistema calendárico específico.

¿Se puede cambiar firstWeekday después de crear un Calendar?

, la propiedad firstWeekday se puede modificar. El cambio afecta a weekOfMonth, weekOfYear y todos los cálculos relacionados con números de semana. Al establecer locale = Locale(identifier: “ru_RU”), firstWeekday se convierte automáticamente en 2 (lunes). La asignación manual anula el valor de la configuración regional.

Resumen

  • Calendar — una clase de Foundation para cálculos calendáricos, que vincula Date con componentes legibles por humanos mediante DateComponents.
  • 17 sistemas calendáricos son admitidos por Foundation — desde el gregoriano hasta el budista y el japonés, con manejo automático de reglas regionales.
  • Calendar.current coincide con el calendario del sistema del usuario — úselo para la interfaz de usuario y DateFormatter.
  • Calendar.dateInterval y Calendar.range son métodos clave para trabajar con límites de períodos y rangos de componentes.
  • Calendar.date(byAdding:) maneja correctamente las diferentes longitudes de meses y años bisiestos — no lo reemplace con aritmética de TimeInterval.
  • TimeZone y Locale como propiedades de Calendar afectan todos los cálculos calendáricos — establézcalos explícitamente para un comportamiento predecible.
  • firstWeekday se determina por la configuración regional y afecta los números de semana — para aplicaciones globales, establézcalo explícitamente o use Calendar.current.

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.

Discutir el proyecto

Lea también