Trabajo con Fechas y Hora

Tutorial Swift – Trabajo con Fechas y Hora

In Swift Avanzado, Tutoriales by Josué V. Herrera0 CommentsLast Updated: 05 Ago 2017

En este Tutorial Swift aprenderemos acerca del trabajo con fechas y hora, veremos esas necesidades que usualmente requerimos cuando ejecutamos operaciones sobre unidades de tiempo.

El trabajo con fechas y hora en Swift se centra principalmente en las siguientes estructuras:

  • Date: Nos permite representar un punto en el tiempo, independientemente del sistema de calendario o de la franja horaria.
  • DateFormatter: Nos permite crear cadenas de texto en representación de una fecha y hora determinado, cadenas que luego podemos convertir a un objeto Date.
  • Calendar: Encapsula los distintos sistemas de control de tiempo (Gregoriano, Persa, Budista…) en los cuales se definen datos como el inicio, la duración y las divisiones del año.
  • DateComponentsFormatter: Toma una cantidad determinada de tiempo y la representa como una cadena de texto legible.
  • DateIntervalFormatter: Toma dos fechas y crea a partir de estas una cadena de texto legible.

Quisiera agregar que la estructura Date también nos permite comparar fechas, calcular el intervalo de tiempo, la creación de una nueva fecha producto de un intervalo de tiempo, entre otras posibilidades.

Obtener la Fecha y Hora actual

Comencemos por lo más básico y usual, veamos como obtener la fecha y hora actual:

…la salida en pantalla sería:

Como podemos ver el código es bien simple, una línea de hecho. Ahora, con respecto a la salida en pantalla creo que es evidentemente que será distinta para cada cual que ejecute este código. Lo que si tenemos que percatarnos es que la hora la estamos obteniendo en UTC / GMT y esto lo podemos comprobar teniendo en cuenta dos indicios: el primero sería que la hora no es la correcta, en mi caso está adelantada 5 horas y al final de la línea nos muestra el texto “+0000” lo cual ya nos confirma que la fecha y hora nos la está dando en Tiempo Medio de Greenwich (GMT) o como también se le conoce el Tiempo Universal Coordinado (UTC).

Yo ahora me encuentro en Quito / Ecuador y la zona horaria aquí es de -5 o para seguir el formato que nos devuelve Date sería de -0500, esto quiere decir que estoy 5 horas atrás con respecto al UTC / GMT. Es evidente que tenemos un problema con la localización lo cual puede deberse a una mala configuración de alguno de los parámetros que conforman el Locale en sistemas Unix / Linux.

Dando Formato a una Fecha y Hora

En mi caso el problema no reside en el Locale del sistema, la estructura Date por defecto nos devuelve la información teniendo en cuenta UTC / GMT. Para lograr resultados que cumplan con nuestra zona horaria, que nos muestren el horario en formato de 12 horas, el año primero seguido por el mes y por último el día, todas estás variaciones y otras, las podemos conseguir haciendo uso de DateFormatter. Veamos el ejemplo anterior en una versión adaptada a nuestras necesidades:

…la salida en pantalla:

En esta salida vemos la diferencia tan grande que existe entre estas dos líneas. La primera línea de la salida es la correspondiente al código anterior seguida de otra mucho más amigable y sobre todo con información correcta. Lo único que hemos hecho es crear una instancia de DateFormatter en la línea 5 para luego establecer que estilo de fecha queremos que nos muestre. Aquí vemos los distintos estilos con los que contamos y su respectiva salida:

EstiloSalida en Pantalla
.fullMonday, November 14, 2016 at 2:43:02 PM Ecuador Time
.longNovember 14, 2016 at 2:43:44 PM GMT-5
.mediumNov 14, 2016, 2:44:29 PM
.short11/14/16, 2:44 PM
.noneNo muestra nada

…esta salida en pantalla se logra estableciendo los valores de fecha y hora al mismo estilo.

En la columna de la salida también nos podemos percatar de que los resultados están en Inglés ya que trabajo con mi sistema en este idioma. Si cuando estamos desarrollando una aplicación deseamos que el usuario, sin importar donde esté, obtenga la información en pantalla acorde a su región, a su zona horaria, su moneda, etc… ¿Pudiéramos lograr esto? La respuesta es sí, a continuación una variante del código anterior.

…la nueva salida sería:

En este ejemplo hemos solamente añadido la línea 7 con la cual establecemos el Locale de la instancia de DateFormatter a es_EC, es decir Español de Ecuador, ya que es_ES que sería Español de España no serviría pues aunque se comparte el mismo idioma la moneda en Europa es el Euro y en Ecuador el dólar estadounidense.

Cambiando el Formato por Defecto

Quizás el formato mostrado por defecto sea muy largo y solamente con poner la fecha y la hora sería más que suficiente para cubrir nuestras necesidades:

En esta línea hemos establecido un template por el cual la instancia de DateFormatter se guiará para mostrarnos la fecha y hora. Importante aclarar, aunque lo creo muy evidente, que el template que hemos definido no es texto al azar, para que este sea válido tiene cumplir con el formato de fecha y tiempo establecido en la ISO8601. La salida en pantalla producida es:

Comparar dos Fechas

Como ya he comentado en Swift las fechas se manejan con la estructura Date y esta representa un instante de tiempo, lo cual incluye tanto una fecha como una hora. Así que a la hora de comparar dos fechas tenemos que tener también en cuenta el tiempo, aunque en dependencia de la necesidad, este puede ser desestimado. En algunas situaciones el 18 de Noviembre de 2016 es solamente lo que nos interesa, manejamos este dato como un todo sin importarnos de unidades menores como las 24 horas que lo componen. Pero cuando queremos comparar dos fechas no es lo mismo “18/12/2016 12:15:37” a  “18/12/2016 12:15:38”, ambos son momentos distintos en el tiempo, con la diferencia de un segundo pero finalmente distintos aun cuando la fecha es la misma.

Antes de comparar dos fechas necesitamos tenerlas como información, como dato manejable, en nuestro caso como instancias de Date con las cuales podamos trabajar, así que, de paso, veamos como definir un momento en el tiempo distinto al actual:

…la salida en pantalla de nuestra primera fecha sería:

Para crear una fecha, para definir un instante en el tiempo, nos hemos apoyado en DateComponents, en cuya instancia encapsulamos los componentes que conforman la primera fecha que deseamos representar. Luego para obtener una instancia de Date hacemos uso de Calendar, ejecutamos su inicializador al cual le pasamos como parámetro el sistema de calendario que usaremos, al mismo tiempo que ejecutamos el método date de la instancia de Calendar que nos devuelve su inicializador. Este método y específicamente esta sobrecarga nos devuelve una instancia de Date a partir de una instancia de DateComponents.

Dicho esto continuemos con nuestra comparación, añadamos otra fecha con un mes de diferencia con respecto a la primera:

…la salida:

En esta última versión del ejemplo hemos usado el método compare de nuestra instancia Date el cual nos permite comparar dos fechas devolviendo un valor ComparisionResult que nos indica el orden temporal de la fecha establecida en la instancia que ejecuta el método y la fecha pasada como parámetro. Los valores posibles del enum ComparisionResult son:

ComparisionResultDescripción
.orderedAscendingLa fecha representada por la instancia Date es anterior a la pasada como parámetro.
.orderedDescendingLa fecha representada por la instancia Date es posterior a la pasada como parámetro.
.orderedSame La fecha representada por la instancia Date es igual a la pasada como parámetro.

La ejecución de estos chequeos se realizan de la línea 22 a la 34 en una estructura if / else-if. Con respecto a este segmento del código no creo que esta sea la opción que brinde más legibilidad, pero se usó para mostrar el uso del método compare.

A mi modo de ver, la versión más óptima de este segmento sería la siguiente:

Claramente es un código mucho más limpio gracias a la sobrecarga de operadores y que a simple vista comprendemos la intención que percibe, mientras que en la versión anterior tenemos que comenzar por verificar que valor de retorno tiene el método compare para luego consultar los valores que comprenden al enum ComparisionResult.

Objeto Date a partir de un String

Una caso de estudio que no puede faltar en este artículo es aquel donde necesitamos hacer un parse sobre una cadena de texto que representa una fecha y con esta información crear un objeto Date. Esto es muy sencillo:

…la salida:

Lo primero que debemos establecer es la fecha en modo texto para luego crear una instancia de DateFormatter a la cual le especificamos el formato de la fecha para que este sepa como hacer el parse correctamente. Continuamos pasando como parámetro al método date nuestra fecha en modo texto y de tipo String, esto nos devuelve un objeto Date. En la última línea imprimimos el contenido de la instancia de Date llamada dateFromString a través de DateFormatter para que la salida corresponda al formato especificado en la línea 5.

Formateando Componentes de Fecha y Hora

Como ya comentamos al inicio con DateComponentsFormatter podemos ir un poco más allá de lo que logramos con DateFormatter ya que podemos obtener una representación más literal de una cantidad de tiempo determinada. Veamos:

…la salida en pantalla sería:

Lo primero que hemos hecho aquí es algo que ya hemos visto. Creamos una fecha en el futuro (23/04/2017), luego una instancia de DateComponentsFormatter en la línea 9, de la línea 11 a la 14 configuramos el estilo, los segmentos de texto informativo que deseamos sean incluidos y las unidades de tiempo que manejaremos. En la línea 16 convertimos nuestra instancia DateComponent a una de tipo Date en pos de ser usada en la siguiente en la cual es pasada como parámetro a la instancia de DateComponentsFormatter que nos devuelve una cadena de caracteres en representación del segmento de tiempo comprendido entre el momento actual y la fecha que hemos establecido en el futuro. Esta cadena de texto es interpolada en la línea 24.

Formateando Intervalos de Tiempo

En ocaciones es necesario, dada una cantidad de tiempo, determinar una fecha futura. Por ejemplo si un cliente aplica por una subscripción mensual conocer la próxima fecha de pago es bastante sencillo, pero si por alguna razón el lapso de tiempo es variable ya la cosa cambia. Por ejemplo si ahora nos dicen que el tiempo es de 48 días a partir de ahora y luego de 64 días, pues ya no es tan sencillo determinar la fecha en cuestión sin vernos enfrascados en código extra. Para esto nos puede ayudar Date, uno de sus inicializadores y por último DateIntervalFormatter para mostrarnos este segmento que comienza en el momento actual hasta una fecha final que quizás no sepamos cual es:

…la salida:

La anterior explicación más la salida en pantalla, creo que son más que claras en explicar lo que deseábamos lograr en este ejemplo. La línea a destacar sería la 8 donde creamos una fecha final pasando como parámetro la cantidad de segundos en el futuro donde caducaría la actual suscripción de nuestro usuario. Luego en la línea 10 hacemos uso de una instancia de DateIntervalFormatter en pos de lograr el formato que podemos observar en la salida en pantalla.

Eso es todo amigos, está de más enfatizar la importancia del trabajo con fechas y hora. Es un apartado que siempre debemos de dominar o tener a la mano una referencia como el artículo actual donde hallar respuestas rápidas a las necesidades más comunes.

El código de ejemplo lo pueden encontrar en un proyecto Playground alojado en la cuenta personal de Josué V. Herrera en GitHub, específicamente en el repositorio asociado a este artículo.

Espero que todo cuanto se ha dicho aquí, de una forma u otra le haya servido de aprendizaje, de referencia, que haya valido su preciado tiempo.

Este artículo, al igual que el resto, será revisado con cierta frecuencia en pos de mantener un contenido de calidad y actualizado.

Cualquier sugerencia, ya sea errores a corregir, información o ejemplos a añadir será, más que bienvenida, necesaria!

Acerca de Josué V. Herrera

Ciudadano del mundo. Me gustan mucho los animales y la naturaleza, la literatura, la música y el cine, aficionado a la aviación y a todo lo que es tecnología y ciencia. Desde el 2005 me dedico de manera profesional a la informática, trabajando principalmente como administrador de redes y sistemas aunque también he fungido como programador, especializándome en Linux y en C++ / Qt. Soy Experto en Administración y Seguridad de Redes por la Universidad Tecnológica Nacional FRVM de Argentina. Desde el 2014 me dedico al estudio de Swift y el entorno que lo rodea, ya sea desde el lado de Apple como del Open Source.

Ver Todos