DateFormatter es una clase de Foundation diseñada para la conversión bidireccional entre objetos Date y su representación en cadena. La clase tiene en cuenta la configuración regional, la zona horaria y el calendario del usuario, garantizando una visualización correcta de las fechas en cualquier región del mundo. Según la documentación para desarrolladores de Apple (2025), DateFormatter admite cuatro estilos predefinidos de fecha y hora, así como formatos completamente personalizados mediante una cadena de plantilla. Sin DateFormatter es imposible mostrar correctamente una fecha al usuario en una aplicación internacionalizada.
Puntos clave
DateFormatter es una clase del framework Foundation que implementa la conversión bidireccional entre Date y cadena. Apareció por primera vez en OpenStep como NSDateFormatter y desde entonces sigue siendo la herramienta principal para el formateo de fechas en todas las plataformas Apple. La clase hereda de Formatter y proporciona una API conveniente para la visualización localizada de fechas.
DateFormatter funciona basándose en patrones Unicode LDML — los mismos utilizados en ICU (International Components for Unicode). El patrón se establece mediante la propiedad dateFormat, donde los símbolos y, M, d, H, m, s corresponden a año, mes, día, horas, minutos, segundos. La repetición de un símbolo determina el formato: "y" — año de dos dígitos, "yyyy" — año de cuatro dígitos.
Crear un DateFormatter es una operación costosa, ya que durante la inicialización se cargan los datos de configuración regional y calendario. Apple recomienda crear un formateador una vez para cada tipo de formato y reutilizarlo. En SwiftUI y UIKit, los formateadores a menudo se almacenan en caché en propiedades estáticas o se crean de forma diferida en el primer acceso.
DateFormatter se utiliza en muchos componentes del sistema iOS. UIDatePicker usa DateFormatter internamente para mostrar fechas en modo countDownTimer. Un TextField con un formateador puede validar automáticamente las fechas introducidas por el usuario. Core Data admite atributos de tipo Date, pero su representación en cadena siempre se maneja a través de DateFormatter.
Seguridad en hilos — DateFormatter no es seguro para hilos. Modificar las propiedades del formateador desde diferentes hilos provoca un comportamiento indefinido. Para uso multi-hilo, cree instancias separadas del formateador para cada hilo o use sincronización mediante NSLock o una cola serial.
dateStyle y timeStyle son las formas más sencillas de configurar la visualización de fechas. Cada estilo tiene cuatro variantes: .short, .medium, .long, .full. La combinación de dateStyle y timeStyle permite configurar de forma independiente el formato de fecha y hora, y la propiedad .none desactiva la parte correspondiente.
Para la configuración regional de EE. UU., .short formatea la fecha como "7/21/26", y para la rusa como "21.07.2026". El estilo .long para la configuración regional rusa muestra "21 de julio de 2026", y .full muestra "martes, 21 de julio de 2026" con el día de la semana. Los cuatro estilos se adaptan automáticamente a los estándares regionales, incluido el orden de los componentes y los separadores.
SFDateFormatter en iOS 15+ proporciona un enfoque alternativo mediante RelativeDateFormatter y DateIntervalFormatter. RelativeDateFormatter muestra "hoy", "ayer", "en 3 días" para contexto inmediato. DateIntervalFormatter muestra rangos de fechas: "21–25 de julio de 2026" — para reservas y planificación.
| Estilo | Ejemplo (ru_RU) | Ejemplo (en_US) |
|---|---|---|
| .short | 21.07.2026 | 7/21/26 |
| .medium | 21 jul 2026 | Jul 21, 2026 |
| .long | 21 de julio de 2026 | July 21, 2026 |
| .full | martes, 21 de julio de 2026 | Tuesday, July 21, 2026 |
Al combinar estilos, DateFormatter selecciona automáticamente el separador: para .short.date + .short.time el resultado podría ser "21.07.2026, 14:30". Para .full.date + .full.time — "martes, 21 de julio de 2026, 14:30:00 MSK". El separador lo gestiona la configuración regional, no el desarrollador — esto garantiza el cumplimiento de las expectativas regionales del usuario.
dateFormat permite establecer un patrón de formato arbitrario utilizando símbolos de especificación Unicode LDML. Esto da control total sobre la visualización: se puede mostrar solo el año y el mes, o el día de la semana sin la fecha, o la hora sin segundos. El formato personalizado es indispensable para requisitos de diseño específicos.
Símbolos principales — yyyy (año: 2026), MM (mes: 07), dd (día: 21), HH (horas: 14), mm (minutos: 30), ss (segundos: 00). Para el nombre completo del mes use MMMM (julio), para abreviado — MMM (jul). Día de la semana — EEEE (martes), abreviado — E (mar).
Al usar dateFormat es importante establecer la configuración regional del formateador. Si no se establece locale, el formateador usa la configuración regional del sistema, lo que puede ser indeseable para un formato fijo en una API. Apple recomienda establecer locale = Locale(identifier: "en_US_POSIX") para un formato fijo entre regiones, especialmente al analizar fechas de respuestas del servidor.
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "ru_RU")
formatter.dateFormat = "d MMMM yyyy"
let customString = formatter.string(from: Date())
// "21 July 2026"
// Analizando una cadena personalizada
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
let date = formatter.date(from: "2026-07-21 14:30:00")!
Un error en dateFormat es una de las causas comunes de fallos de la aplicación. Si el formato no coincide con la cadena, el método date(from:) devuelve nil. Use guard let o ?? para desempaquetado seguro de opcionales. Para validar el formato, pruébelo en todos los idiomas compatibles — algunos símbolos LDML funcionan de manera diferente en distintas configuraciones regionales.
Locale determina cómo se muestran los nombres de meses, días de la semana y qué separadores se utilizan. DateFormatter usa Locale.current por defecto, pero en algunos escenarios es necesario especificar una configuración regional concreta: para un formato fijo en registros use en_US_POSIX, para fechas del servidor — la configuración regional que coincida con el servidor.
La propiedad TimeZone determina la zona horaria para la visualización. Por defecto se usa la zona horaria del sistema, pero para aplicaciones con audiencia internacional a menudo es necesario mostrar las fechas en la zona horaria del usuario o en UTC. Cambiar timeZone solo afecta a la visualización — el valor Date permanece sin cambios.
Una característica importante: si DateFormatter se usa para analizar una cadena y la cadena contiene una indicación de zona horaria (por ejemplo, "2026-07-21T14:30:00Z" con Z para UTC), la propiedad timeZone se ignora — el formateador usa la zona horaria de la cadena. Si la zona horaria está ausente en la cadena, se aplica el timeZone del formateador.
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"
// Analizando sin zona horaria en la cadena
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 especial de configuración regional que se actualiza automáticamente cuando cambian los ajustes del sistema del usuario. DateFormatter lo admite por defecto. Si la aplicación se ejecuta en segundo plano y el usuario cambia el idioma del sistema, un formateador creado antes del cambio seguirá usando la configuración regional anterior — para actualizarlo es necesario crear una nueva instancia.
ISO8601DateFormatter es un formateador especializado para trabajar con fechas en formato ISO 8601. Este formato es el estándar de facto para API REST, JSON e intercambio de datos. ISO8601DateFormatter funciona significativamente más rápido que DateFormatter porque no depende de la configuración regional y utiliza una gramática de análisis fija.
Opciones principales del formateador — .withInternetDateTime (2026-07-21T14:30:00Z), .withFractionalSeconds (añade milisegundos), .withTimeZone (incluye el desplazamiento de zona horaria). Combinando opciones se puede obtener cualquier variante ISO 8601: con milisegundos, con zona horaria, con solo fecha.
JSONEncoder.DateEncodingStrategy permite configurar globalmente la codificación de fechas para todos los modelos Codable. Opciones — .iso8601 (usa ISO8601DateFormatter), .formatted(DateFormatter), .millisecondsSince1970, .secondsSince1970. La elección de la estrategia afecta a todo el ciclo de vida de serialización y debe ser coherente en todos los endpoints de la 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 formateador personalizado
let customEncoder = JSONEncoder()
customEncoder.dateEncodingStrategy = .formatted(myFormatter)
DateFormatter vs ISO8601DateFormatter — elija ISO8601DateFormatter para serializar y analizar fechas en API, ya que es 5-10 veces más rápido que DateFormatter y no es propenso a errores de localización. Use DateFormatter para la interfaz de usuario donde se requiere visualización localizada con nombres de meses y días en el idioma nativo del usuario.
Veamos escenarios reales de uso de DateFormatter en una aplicación iOS: visualización en un feed de noticias, introducción de fecha de nacimiento y exportación de un informe con fechas en diferentes zonas horarias.
RelativeDateFormatter es óptimo para feeds de noticias. Muestra "justo ahora", "hace 5 minutos", "ayer" para noticias recientes y cambia a la fecha completa para las antiguas. El umbral de cambio se configura mediante calendar: para noticias use un umbral de 24 horas, para mensajeros — una semana.
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)
}
Introducción de fecha de nacimiento — otro escenario común. DateFormatter se configura con un dateFormat específico "dd.MM.yyyy" y locale "ru_RU". Al analizar la cadena introducida es importante manejar posibles errores: el formateador devuelve nil para una cadena no válida. Después del análisis exitoso, la fecha se verifica para que esté dentro de un rango aceptable — no antes de 1900, no después de hoy.
Exportación de un informe con fechas requiere un formato fijo independiente de la configuración regional del usuario. Use dateFormat "yyyy-MM-dd HH:mm:ss" con locale en_US_POSIX y zona horaria UTC. Este enfoque garantiza que el archivo se abra correctamente en cualquier país independientemente de los ajustes regionales del sistema.
| Escenario | Formateador | Configuración clave |
|---|---|---|
| Feed de noticias | RelativeDateFormatter | unitsStyle = .full |
| Entrada de fecha | DateFormatter | dateFormat + fallback |
| Serialización API | ISO8601DateFormatter | withInternetDateTime |
| Exportación de informe | DateFormatter | en_US_POSIX + UTC |
Preguntas frecuentes
La razón más común — una discrepancia entre dateFormat y el formato de la cadena. Por ejemplo, el formato "dd.MM.yyyy" no analizará la cadena "2026-07-21". La segunda razón — discrepancia de configuración regional: la cadena "July 21, 2026" no se analizará con la configuración regional ru_RU. La tercera — errores tipográficos en símbolos LDML: use yyyy, no YYYY (significado diferente).
No. DateFormatter es un objeto pesado, su inicialización incluye la carga de datos de configuración regional. Cree una instancia por tipo de formato y reutilícela. En un entorno multi-hilo use almacenamiento local de hilo o un pool de formateadores con una cola serial para sincronización.
DateFormatter muestra una fecha absoluta (21 de julio de 2026), mientras que RelativeDateFormatter muestra una relativa (hoy, ayer, en 3 días). RelativeDateFormatter se introdujo en iOS 15+ y usa la misma plantilla LDML pero selecciona automáticamente la visualización relativa.
Establezca el timeZone del formateador a UTC antes de analizar. Si el servidor devuelve una fecha en hora local sin indicación de zona horaria, consulte la especificación de la API — lo más probable es que se refiera a UTC. Para ISO 8601 con Z al final, timeZone no es necesario — el formateador analiza el desplazamiento de la cadena.
No use una sola instancia desde diferentes hilos sin sincronización. Cree una nueva instancia en cada hilo o use Thread.current.threadDictionary para almacenamiento. Una alternativa es NSLock con bloqueo durante la duración de string(from:) y date(from:).
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