fbpx
 

Como trabajar con Fechas [Parte 2]

Como trabajar con Fechas [Parte 2]

1. Introducción

Si estás aquí, probablemente hayas echado un vistazo a la primera parte de nuestro tutorial Como trabajar con Fechas, donde explicábamos el funcionamiento de la clase NSDate.

Si no es así, te recomiendo que comiences por ese tutorial. Eso te permitirá aprovechar mucho mejor esta segunda parte.

Puedes acceder a la primera parte del tutorial desde aquí.

2. ¿Qué vamos a ver en este Tutorial?

En esta segunda parte del Tutorial veremos las tareas más comunes que suelen hacerse con las fechas:

  • Dividir una fecha completa en partes
  • Modificar la zona horaria
  • Comparar fechas y horas entre sí
  • Realizar cálculos de fechas pasadas y futuras
  • Calcular cuanto tiempo ha pasado entre dos fechas

3. Sobre nuestra Aplicación Demo

En este tutorial no vamos a crear una app demo como hemos hecho en otros tutoriales de Efecto/Apple.

Vamos a trabajar directamente en un playground de Xcode.

Lo mejor es que crees tu propio playground y vayas realizando cada uno de los ejemplos que vamos a ir viendo aquí.

De esta forma podrás realizar modificaciones en los ejemplos y entender perfectamente como funciona todo.

El fichero playground que vamos a crear se llama PlayingWithDates2. Puedes llamarlo igual o utilizar el nombre que prefieras.

Lo primero que tienes que hacer una vez que hayas creado el fichero playground es añadir la siguiente linea a la parte superior del mismo:

Esto nos permitirá importar el framework UIKit, que utilizaremos para trabajar a lo largo de todo el tutorial.

4. Conceptos Básicos

Antes de comenzar, comprueba si estás familiarizado con las siguientes clases:

  • NSDate
  • NSDateFormatter
  • NSLocale

Si tienes dudas con algunas de ellas, no te preocupes, tienes a tu disposición la primera parte de este tutorial, donde podrás ver como utilizarlas.

Si por el contrario, tienes dominados estos conceptos, perfecto, no tendrás ningún problema con lo que viene a continuación.

Antes de comenzar es importante tratar algunos conceptos fundamentales.

Además de las 3 clases anteriores, otra clase fundamental para trabajar con fechas es NSDateComponents. Podríamos decir que se trata de una clase hermana de NSDate y proporciona a los desarrolladores algunas características de gran utilidad. Lo más importante de esta clase es que puede representar cada componente de una fecha u hora, como una propiedad individual, por lo que puede ser accedida directamente y utilizada por ejemplo en cálculos entre fechas.

Para que lo entiendas mejor, aquí tienes un ejemplo sencillo. El día y el mes en una instancia de NSDateComponents están representados por las propiedades día y mes, como se muestra a continuación:

let day = dateComponents.day let month = dateComponents.month

Tan simple como esto.

Por supuesto, acceder a partes de una fecha y setear los valores de un objeto NSDateComponents requiere realizar algunas conversiones, pero esto lo veremos más adelante.

Además de esto, la clase NSDateComponents también es bastante útil cuando necesitamos calcular fechas en el futuro o en el pasado. Todo lo que tienes que hacer cuando quieres encontrar una fecha anterior o posterior a partir de una fecha dada, es añadir o eliminar los componentes adecuados, para finalmente convertirlo a una nueva fecha. NSDateComponents también es adecuado para buscar diferencias entre fechas. No te preocupes por esto ahora, veremos cada uno de los casos con ejemplos más adelante.

A pesar de que no vamos a hacer mucho uso de ella y que solo la utilizaremos para realizar conversiones de NSDate a NSDateComponents y viceversa, también es importante que conozcas la clase NSCalendar. No entraremos en mucho detalle sobre esta clase, en este tutorial, simplemente quédate con que para convertir fechas de NSDate a NSDateComponents y viceversa necesitaremos utilizar la clase NSCalendar.

Esto se debe a que iOS necesita saber que calendario utilizar antes de realizar cualquier conversión, para poder obtener los resultados adecuados. No olvides que hay diferentes calendarios en el mundo, por lo que los días, meses y horas convertidas pueden variar entre unos países y otros. No es nada complicado, solo adelantarte que utilizaremos el método de clase currentCalendar() para obtener el calendario que el usuario tenga especificado en los Ajustes de su dispositivo.

La última clase de la que vamos a hablar es NSDateComponentsFormatter, y puede ser utilizada para convertir fechas y horas en strings legibles por los usuarios. Dispone de varios métodos para realizar este trabajo. Veremos un par de ellos en la última parte del tutorial.

Una vez comentada toda la teoría, vamos a pasar a ver todo esto con algunos ejemplos. Si algo de lo que hayamos hablado aquí, no te ha quedado claro del todo, no te preocupes, vamos a verlo paso a paso a continuación.

5. Trabajando con NSDateComponents

En muchas ocasiones, en tus proyectos, es probable que necesites dividir una fecha completa en partes, obteniendo los valores individuales de cada parte. Por ejemplo, puede que tengas que obtener el número del mes de una fecha o los minutos de una hora específica. En este caso, la clase que tendrás que utilizar será NSDateComponents.

La clase NSDateComponents se suele usar junto con la clase NSCalendar. Para ser más específicos, los métodos de NSCalendar permiten la conversión de NSDate to NSDateComponents y como veremos más adelante, también nos permiten la conversión inversa. Sabiendo esto, lo primero que haremos será obtener el calendario actual y asignarlo a una constante para tener un acceso fácil al mismo.

Ahora, veremos un ejemplo típico de como un objeto NSDate puede ser convertido en un objeto NSDateComponents y luego explicaremos el proceso que hemos seguido:

NSDate post10_imagen1

Lo primero que hacemos es crear una constante donde almacenaremos la fecha actual, para poder trabajar con ella.

Después dividimos esa fecha en componentes. Para ello utilizamos el método components(_:fromDate:) de la clase NSCalendar.

Este método acepta dos parámetros: El segundo parámetro es la fuente de donde obtendremos nuestros componentes. El primer parámetro debe ser un array de propiedades NSCalendarUnit que describirá los componentes que queremos extraer de la variable currentDate.

NSCalendarUnit es un struct y puedes ver todas sus propiedades aquí:

En nuestro último ejemplo, en la imagen que va justo antes del código puedes observar como especificamos que queremos recuperar los siguientes dateComponents:

  • Día
  • Month
  • Year
  • Week of year
  • Hour
  • Minute
  • Second
  • Nanosecond

Ten en cuenta, que las NSCalendarUnit que no se especifiquen en el primer parámetro no estarán disponibles para ser utilizadas más tarde. Por ejemplo, no hemos incluido NSCalendarUnit.TimeZone en la lista, por lo que no podremos acceder a la zona horaria como hemos hecho con el resto de componentes. Es decir, si en nuestro playground añadiéramos en la última linea:

Se produciría un error en tiempo de ejecución. Siempre debes especificar los componentes que vas a utilizar, utilizando el método components(_:fromDate:), antes de poder trabajar con ellos.

Realizar el proceso inverso, es decir, pasar de NSDateComponents a objetos NSDate es muy simple también. Unicamente tendrás que utilizar el método dateFromComponents() de la clase NSCalendar . Lo vemos en el siguiente ejemplo:

NSDate calendar

Como puedes ver, creamos un objeto de tipo NSDateComponents, al que le vamos asignando los valores que queramos en cada una de sus propiedades, para al final convertir ese objeto en un NSDate utilizando el método dateFromComponents().

En uno de los ejemplos que vimos en la parte 1 de este tutorial, hablamos de como influye la zona horaria a un objeto NSDate. Vamos a ver ahora el ejemplo de como modificando la zona horaria (representada con NSTimeZone) se modifica automáticamente el valor de un objeto NSDate:

NSDate post10_imagen3

Como puedes ver, obtenemos valores distintos, dependiendo de la zona horaria que establezcamos.

Las abreviaturas de las zonas horarias que hemos utilizado son estas:

  • GMT = Greenwich Mean Time
  • CST = China Standard Time
  • CET = Central European Time

Puedes acceder a una lista completa de todas las abreviaturas de las zonas horarias desde aquí:

Ahora que ya sabes como trabajar con objetos NSDateComponents también, pasemos a otra de las tareas más típicas cuando trabajamos con objetos NSDate.

6. Comparando Fechas y Horas

Uno de los escenarios más comunes cuando trabajamos con fechas es el de tener que comparar dos objetos NSDate. Eso es lo que vamos a ver en este apartado.

Antes de explicarte los métodos de comparación de fechas, tendremos que crear un objeto NSDateFormatter y dos objetos NSDate con los que poder trabajar. Estos objetos serán dateFormatterfirstDate y secondDate. A ambos les aplicaremos el mismo dateFormatter. Añade las siguientes lineas a tu Playground:

Una vez que hemos creado estos 3 objetos, veremos la forma de comparar las fechas.

La clase NSDate, nos provee de tres métodos con los que trabajar:

  • earlierDate()
  • laterDate()
  • isEqualToDate()

La sintaxis para llamar al método earlierDate() es la siguiente:

Y aquí tienes explicado su funcionamiento:

  • Si firstDate es anterior a secondDate, el método devolverá el valor de firstDate.
  • Si secondDate es anterior a firstDate, el método devolverá el valor de secondDate.
  • Si ambas fechas son iguales, el método devolverá el valor de firstDate.

La sintaxis para llamar al método laterDate() es la siguiente:

Y aquí tienes explicado su funcionamiento:

  • Si firstDate es posterior a secondDate, el método devolverá el valor de firstDate.
  • Si secondDate es posterior a firstDate, el método devolverá el valor de secondDate.
  • Si ambas fechas son iguales, el método devolverá el valor de firstDate.

Este es el ejemplo de utilización de ambos métodos con nuestros dos objetos NSDate:

NSDate post10_imagen4

Tal vez te extrañen los signos de exclamación en el código. Estos signos se utilizan para tratar con Opcionales. Los opcionales son uno de los conceptos fundamentales de Swift. Si quieres ampliar más información sobre opcionales y otros conceptos fundamentales puedes descargar de forma totalmente gratuita este ebook de más 50 de páginas donde se explican estos conceptos: Descarga el ebook.

Esta forma de comparar fechas también puede utilizarse para comparar horas. A continuación vamos a ver un ejemplo, donde los objetos firstHour y secondHour almacenarán horas. En este ejemplo, además verás en funcionamiento al tercer método que hemos mencionado: isEqualToDate(), que nos dirá si dos objetos NSDate son iguales.

Como puedes ver, aquí tienes un ejemplo completo utilizando los 3 métodos que hemos visto de comparación de NSDates. El código es bastante sencillo, por lo que no creo que haga falta comentar nada.

Si ejecutas este código en tu playground tal vez te preguntes por que en la salida por pantalla aparece varias veces la fecha “2000-01-01”.

Simplemente se trata de una fecha por defecto que se añade cuando creas un NSDate y únicamente especificas la hora y no la fecha. Cada vez que crees un NSDate especificando únicamente la hora y no la fecha, ese objeto NSDate cogerá como fecha por defecto “2000-01-01”.

Con lo que acabamos de ver ya conoces la forma de comparar objetos NSDate entre si.

7. Calculando fechas pasadas y futuras

Otro aspecto interesante que debes manejar cuando trabajes con objetos NSDate es a realizar cálculos de fechas pasadas o futuras. Como por ejemplo, incrementar una fecha en 6 meses y 12 días o reducirla en 1 mes y 1 día.

Para realizar estos cálculos, utilizaremos una clase que ya conoces, NSDateComponents.

Supongamos que queremos añadir 4 meses y 10 días a nuestra fecha actual.

Lo primero que tendremos que hacer será crear una variable donde almacenaremos nuestra fecha actual. La tenemos creada de antes, pero si no la has creado, escribe la siguiente linea de código:

Además crearemos dos constantes donde almacenaremos los meses y los días que queremos añadir:

El código que tendrías que utilizar después sería este (La explicación del mismo viene justo después):

Lo que hemos hecho es crear un objeto de tipo NSDateComponents y a sus componentes mes y día le añadimos nuestras variables con los 4 meses y los 10 días que queremos añadir a la fecha actual.

Después llamaremos al método dateByAddingComponents:toDate:options: de la clase NSCalendar, pasándole newDateComponents y nuestra variable currentDate y obtendremos una nueva fecha con los 4 meses y 10 días añadidos.

En la imagen puedes comprobar como nuestra fecha 19 de Septiembre de 2016 se ha convertido en 29 de Enero de 2017:

NSDate post10_imagen5

Te habrás dado cuenta que en la llamada al método dateByAddingComponents:toDate:options: hemos utilizado un último parámetro:

rawValue:0

Esta es la forma de especificar que no queremos establecer ninguna opción más en la llamada al método. Puedes consultar más información sobre la clase NSCalendar aquí.

En el ejemplo que acabamos de ver, lo que hemos hecho ha sido añadir tiempo a nuestra fecha actual. Lo que haremos en el siguiente ejemplo, será descontar tiempo a nuestra fecha actual, que es algo que también puede serte muy útil.

Concretamente vamos a descontar 90 días a nuestra fecha actual, obteniendo de esta forma una fecha pasada:

Tienes la explicación de este ejemplo en los propios comentarios del código. Aun así, aquí tienes un pequeño resumen de lo que hemos hecho:

  • Hemos declarado una constante con nuestros 90 días en negativo
  • Utilizando el método dateByAddingComponents:toDate:options: le sumamos esos -90 días
  • Una vez que hemos obtenido la fecha deseada (calculatedDate), la convertimos a NSString utilizando el formateador adecuado.

En la siguiente imagen puedes ver como restando esos 90 días, hemos convertido la fecha 19 de Septiembre en 21 de Junio:

NSDate post10_imagen6

Estos 2 ejemplos que hemos visto, te permitirán calcular nuevas fechas añadiendo unidades positivas o negativas a una fecha base. Puedes realizar todas las pruebas que quieras utilizando estos ejemplos que acabamos de ver.

8. Calculando el intervalo de tiempo entre dos fechas

Puede que en algún momento, en algún proyecto, necesites calcular la diferencia entre dos fechas. En esta parte final del tutorial veremos como hacer esto.

Para poder realizar los ejemplos, declararemos dos objetos NSDate y un dateFormatter:

También necesitaremos la clase NSDateComponentsFormatter, que dispone de varios métodos que realizan el cálculo y devuelven un string con el formato que queramos.

La propiedad unitsStyle especifica el estilo de las unidades de tiempo que vamos a mostrar por pantalla. En nuestro caso hemos elegido Full, por lo que el nombre del día, del mes, etc se mostrará de forma completa. Si en lugar de Full hubiéramos utilizado el estilo Abbreviated por ejemplo, estos nombres aparecerían abreviados.

Puedes ver una lista completa de los posibles estilos aquí.

Como último paso, utilizaremos el método stringFromDate:toDate: de la clase NSDateComponentsFormatter. Sin embargo, antes de poder usarlo, tenemos que cumplir una condición. Al menos una unidad de tiempo debe especificarse dentro de la propiedad allowedUnits, si no el método devolver un valor nil. En nuestro ejemplo, en esta propiedad especificaremos un array de unidades de tiempo y después realizaremos la llamada al método stringFromDate:

En la siguiente imagen puedes ver como se muestra por pantalla la diferencia en años, meses, días, horas, minutos y segundos entre las dos fechas:

NSDate post10_imagen7

8. Resumen Final

A lo largo de esta segunda parte del tutorial hemos podido ver como dividir una fecha completa en partes, como modificar la zona horaria en tu aplicación, hemos realizado comparaciones entre fechas y horas y calculado la diferencia entre fechas pasadas y futuras y el tiempo que ha pasado entre dos fechas determinadas. Todo con ejemplos, con los que puedes practicar de aquí en adelante.

9. Conclusiones

Trabajar con fechas no suele ser una de las partes favoritas de la mayoría de los programadores, sin embargo, en muchos proyectos, es inevitable tener que hacerlo.

A través de los ejemplos de este tutorial has podido ver como realizar la mayoría de las tareas a las que tendrás que enfrentarte cuando trabajes con objetos de tipo NSDate.

Puede que este artículo te sea muy útil en el futuro, es probable que tarde o temprano en algún proyecto necesites trabajar con fechas y puedas necesitarlo.

Si quieres descargar el fichero playground utilizado para realizar los ejemplos de esta segunda parte del tutorial puedes bajarlo desde aquí.

Como siempre, espero que te haya servido para mejorar tus capacidades como Desarrollador iOS y un poco más abajo tienes a tu disposición los comentarios del artículo.

Si compartes esta entrada en tus redes sociales ayudas a que EfectoApple llegue cada vez a mas lectores.

Etiquetas:
7 Comentarios
  • Mini
    Publicado a las 09:37h, 27 septiembre Responder

    Hola Luis!
    He estado echando un vistazo a tu blog, y haces un contenido buenísimo, me parece super completo y del que se puede aprender muchísimo.
    Sigue así!!
    Yo de momento me quedo para seguir viendo tus posts.

    Muchas gracias por compartir. Un saludo

    • Luis R
      Publicado a las 19:16h, 27 septiembre Responder

      Muchas gracias por el comentario Mini.

      El lunes de cada semana publico un tutorial nuevo.

      Y hay de camino, temas muy interesantes…

      Saludos!

  • Aitor Uranga
    Publicado a las 19:51h, 27 septiembre Responder

    Hola Luis!
    Segundo post que leo y segundo post del que he aprendido mucho.

    Solo una pregunta, comentario… no habría que gestionar de otra manera las opcionales en vez de desempaquetarlas implícitamente para evitar posibles errores? aunque para los ejemplos no hace falta, ya que sabemos que seguro tenemos un valor, siempre esta bien hacerlo de manera mas segura para acostumbrarnos a ello no?

    Gracias y seguire de cerca tus publicaciones.

    • Aitor Uranga
      Publicado a las 20:44h, 27 septiembre Responder

      La manera mas segura de hacerlo por si alguno de los date es nil, según el manejo de los opcionales, sería así?

      if let fecha1 = date1, let fecha2 = date2 {
      let autoFormattedDifference = dateComponentsFormatter.stringFromDate(date1!, toDate: date2!)
      }

      Gracias y un saludo.

      • Luis R
        Publicado a las 11:27h, 29 septiembre Responder

        Hola Aitor.

        El tema de los opcionales en Swift es muy interesante.

        Tal y como comentas, para desempaquetar un opcional de forma segura debemos utilizar la ASIGNACIÓN OPCIONAL.

        La asignación opcional simplemente consiste en comprobar que nuestros opcionales efectivamente contienen algún dato en su interior y no almacenan nil.

        Por ejemplo, si tenemos 2 opcionales “cadenaOpcional1” y “cadenaOpcional2”, deberíamos de actuar así:

        if let string1 = cadenaOpcional1, string2 = cadenaOpcional2 {
        print(“La cadena 1 es \(string1), la cadena 2 es \(string2)”)
        }

        De esta forma, nos aseguramos que tanto cadenaOpcional1 como cadenaOpcional2 no contienen nil.

        Muy buena pregunta Aitor.

  • Rodrigo
    Publicado a las 10:51h, 22 octubre Responder

    Hola! Muy buen post! Tengo una pequeña duda! estoy intentando recoger una fecha desde un Datepickerview y quiero que a apartir de esa fecha pueda añadirle 4 meses o 3 meses… el problema es que cuando lo hago, si pongo como mes, el mes en el que estamos… me sale bien y si le pongo 1 mes mas o menos me sale mal el conteo del mes…

    Te dejo el código:

    el codigo es el siguiente:
    let date = Calendar.current.date(byAdding: .month, value: 4, to: data.dateFirstRecipt)

    let today = NSDate()
    let todayString = dateformatter.string(from: today as Date)
    let date2 = dateformatter.date(from: todayString)!

    let dateComponentsFormatter = DateComponentsFormatter()
    dateComponentsFormatter.unitsStyle = DateComponentsFormatter.UnitsStyle.full

    dateComponentsFormatter.allowedUnits = [NSCalendar.Unit.year, NSCalendar.Unit.month, NSCalendar.Unit.day]

    let autoFormattedDifference = dateComponentsFormatter.string(from: date2, to: date!)

    Gracias!

    • Luis R
      Publicado a las 11:30h, 27 octubre Responder

      Hola Rodrigo.

      A simple vista no veo ningún error en el código.

      Para revisarlo correctamente tendría que ver el proyecto completo, ya que desde la porción de código que has pegado se hace referencia a variables que no aparecen en el propio código.

      Si no has conseguido solucionarlo todavía y te interesa mucho, puedes enviarme el proyecto a behappy@efectoapple.com. Así podría revisar qué está fallando.

      Saludos!

Escribe un comentario

10AppsIpad

Descarga las 10 Aplicaciones

Introduce tu Nombre y tu Email para recibir las Apps en tu Correo

Acabo de enviarte un email. Sigue las instrucciones para descargar las Aplicaciones. Puede que tengas que revisar tu buzón de correo no deseado. Gracias.