CodeGym /Blog Java /Random-ES /Cómo no perderse en el tiempo: DateTime y Calendar
Autor
Milan Vucic
Programming Tutor at Codementor.io

Cómo no perderse en el tiempo: DateTime y Calendar

Publicado en el grupo Random-ES
¡Hola! Hoy comenzaremos a trabajar con un nuevo tipo de datos que no hemos encontrado antes, a saber, las fechas. Cómo no perderse en el tiempo: DateTime y Calendar - 1No creo que sea necesario explicar qué es una cita. :) En principio, podríamos almacenar la fecha y la hora actuales en una cadena ordinaria de Java.

public class Main {
   public static void main(String[] args) {

       String date = "June 11, 2018";
       System.out.println(date);
   }
}
Pero este enfoque tiene muchos inconvenientes. La Stringclase está diseñada para trabajar con texto y sus métodos son apropiados para esta tarea. Si necesitamos manipular una fecha de alguna manera (agregar 2 horas, por ejemplo), Stringno funciona tan bien. O si queremos mostrar la fecha y hora actual cuando se compila el programa. Stringtampoco ayuda aquí: para cuando escriba el código y lo ejecute, la hora habrá cambiado y la consola mostrará la información incorrecta. Es por eso que los creadores de Java proporcionaron varias clases para trabajar con fechas y horas. El primero de estos esjava.util.Date

Clase de fecha

Especificamos su nombre completo, porque otro paquete de Java tiene la java.sql.Dateclase. ¡No los mezcles! Lo primero que debe saber al respecto es que almacena la fecha como la cantidad de milisegundos que han pasado desde el 1 de enero de 1970. Este sistema de tiempo incluso tiene su propio nombre: " Unix-time ". Un enfoque bastante interesante, ¿verdad? no estas de acuerdo? :) La segunda cosa que vale la pena recordar es esto: si crea un Dateobjeto utilizando el constructor predeterminado, el resultado representa la fecha y hora actual en el momento en que se creó el objeto . ¿Recuerdas que dijimos que una fecha representada como Stringlucharía con tal tarea? La Dateclase lo maneja con facilidad.

public class Main {
   public static void main(String[] args) {

       Date date = new Date();
       System.out.println(date);
   }
}
Ejecute este código varias veces y verá que la hora cambia repetidamente. :) Esto es posible porque el tiempo se almacena en milisegundos: son unidades de tiempo extremadamente pequeñas, por lo que los resultados son muy precisos. La Dateclase otro constructor: puede pasar el número exacto de milisegundos desde las 00:00 del 1 de enero de 1970 hasta la fecha requerida, y se creará un objeto de fecha correspondiente:

public class Main {
   public static void main(String[] args) {

       Date date = new Date(1212121212121L);
       System.out.println(date);
   }
}
Salida de la consola: Vie 30 de mayo 04:20:12 GMT 2008 Obtenemos el 30 de mayo de 2008. "Fri" indica el día de la semana (viernes, duh) y GMT es la zona horaria (hora del meridiano de Greenwich). Los milisegundos se pasan como longs, porque la cantidad de milisegundos no suele caber en un int. Entonces, ¿qué operaciones con fechas podríamos necesitar realizar? Bueno, la más obvia, por supuesto, es la comparación . Para determinar si una fecha viene antes o después de otra. Esto se puede hacer de varias maneras. Por ejemplo, puede llamar al Date.getTime()método, que devuelve la cantidad de milisegundos que han transcurrido desde la medianoche del 1 de enero de 1970. Simplemente llámelo en dos objetos Date y compare los resultados:

public class Main {
   public static void main(String[] args) {

       Date date1 = new Date();

       Date date2 = new Date();

       System.out.println((date1.getTime() > date2.getTime())?
               "date1 is later than date2" : "date1 is earlier than date2");
   }
}
Salida: fecha1 es anterior a fecha2 Pero también hay una forma más conveniente, es decir, mediante el uso de métodos especiales proporcionados por la clase Fecha: before(), after()y equals(). Todos ellos devuelven un valor booleano. El before()método comprueba si nuestra fecha es anterior a la fecha pasada como argumento:

public class Main {
   public static void main(String[] args) throws InterruptedException {

       Date date1 = new Date();

       Thread.sleep(2000);// Suspend the program for 2 seconds
       Date date2 = new Date();

       System.out.println(date1.before(date2));
   }
}
Salida de la consola: verdadero De manera similar, el after()método verifica si nuestra fecha es posterior a la fecha pasada como argumento:

public class Main {
   public static void main(String[] args) throws InterruptedException {

       Date date1 = new Date();

       Thread.sleep(2000);// Suspend the program for 2 seconds
       Date date2 = new Date();

       System.out.println(date1.after(date2));
   }
}
Salida de la consola: falso En nuestros ejemplos, "ponemos el programa en suspensión" durante 2 segundos, de modo que se garantiza que las dos fechas sean diferentes. En equipos rápidos, el tiempo entre la creación de date1y date2puede ser inferior a un milisegundo, lo que provoca que ambos before()y after()devuelvan falso. ¡Pero en este caso, el equals()método devolverá verdadero! Después de todo, compara el número de milisegundos desde las 00:00 del 1 de enero de 1970 para cada fecha. Los objetos se consideran iguales solo si coinciden con el milisegundo :

public static void main(String[] args) {

   Date date1 = new Date();
   Date date2 = new Date();

   System.out.println(date1.getTime());
   System.out.println(date2.getTime());

   System.out.println(date1.equals(date2));
}
Aquí hay otra cosa a la que debes prestar atención. Si abre la documentación de la Dateclase en el sitio web de Oracle , verá que muchos de sus métodos y constructores se han marcado como obsoletos (es decir, no se recomienda su uso). Esto es lo que los creadores de Java tienen que decir sobre las partes de las clases que han quedado en desuso:
"Un elemento de programa anotado como @Deprecated es algo que no se recomienda que usen los programadores, generalmente porque es peligroso o porque hay una alternativa mejor".
Esto no significa que estos métodos no se puedan usar en absoluto. Si intenta ejecutar código utilizando métodos en desuso en un IDE, lo más probable es que funcione. Por ejemplo, considere el método en desuso Date.getHours(), que devuelve la cantidad de horas asociadas con un Dateobjeto.

public static void main(String[] args) {

   Date date1 = new Date();

   System.out.println(date1.getHours());
}
Si inicia el código a las 14:21 (2:21 p. m.), mostrará el número 14. Como puede ver, el método obsoleto está tachado, pero aún funciona. Estos métodos no se eliminan para no romper el enorme cuerpo de código existente que los usa. En otras palabras, estos métodos no están ni "rotos" ni "eliminados". Simplemente no se recomienda su uso porque existe una alternativa más conveniente. Por cierto, la documentación menciona específicamente esta alternativa:
Cómo no perderse en el tiempo: DateTime y Calendar - 2
La mayoría de los Datemétodos de la clase se han movido a la Calendarclase mejorada y extendida. Nos familiarizaremos con esa clase a continuación. :)

Clase de calendario

JDK 1.1 introdujo una nueva clase: Calendar. Hizo que trabajar con fechas en Java fuera un poco más fácil que antes. La única implementación de la clase Calendarcon la que trabajaremos es la GregorianCalendarclase. Implementa el calendario gregoriano, que es observado por la mayoría de los países del mundo. Su principal ventaja es que puede trabajar con fechas en un formato más conveniente. Por ejemplo, puede:
  • Agregar un mes o un día a la fecha actual
  • Compruebe si el año es un año bisiesto;
  • Devolver componentes individuales de la fecha (por ejemplo, extraer el número de mes de una fecha completa)
  • También contiene un sistema de constantes muy conveniente (muchas de las cuales veremos a continuación).
Otra mejora importante de la Calendarclase es su constante Calendar.ERA : puede indicar una fecha anterior a la era común (BC - antes de Cristo) o en la era común (AD - Anno Domini). Veamos todo esto con ejemplos. Vamos a crear un calendarobjeto con la fecha del 25 de enero de 2017:

public static void main(String[] args) {

  Calendar calendar = new GregorianCalendar(2017, 0 , 25);
}
En la Calendarclase (así como en la Dateclase), los meses comienzan desde cero , por lo que pasamos el número 0 como segundo argumento. Al trabajar con la Calendarclase, es importante comprender que esto es solo eso, un calendario , no una fecha individual. Cómo no perderse en el tiempo: DateTime y Calendar - 3 Una fecha son solo unos pocos números que indican un intervalo de tiempo específico. Un calendario es un sistema completo que te permite hacer muchas cosas con las fechas. :) Esto es muy evidente si intenta mostrar el Calendarobjeto: Salida: java.util.GregorianCalendar[time=?,areFieldsSet=false,areAllFieldsSet=false,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Europa/Londres",offset=0,dstSavings=0,useDaylight= false,transitions=79,lastRule=null],firstDayOfWeek=2,minimalDaysInFirstWeek=1,ERA=?,YEAR=2017,MONTH=0,WEEK_OF_YEAR=?,WEEK_OF_MES=?,DAY_OF_MES=25,DAY_OF_YEAR=?,DAY_OF_WEEK=? ,DAY_OF_WEEK_IN_MONTH=?,AM_PM=0,HOUR=0,HOUR_OF_DAY=0,MINUTE=0,SECOND=0,MILLISECOND=?,ZONE_OFFSET=?,DST_OFFSET=?] ¡Vea cuánta información obtiene ! Un calendario tiene un montón de propiedades que una fecha normal no tiene, y todas ellas se muestran (así es comotoString()funciona el método en laCalendarclase). Si solo necesita obtener una fecha simple del calendario, es decir, unDateobjeto, use elCalendar.getTime()método (el nombre no es el más lógico, pero ¿qué puedes hacer?):

public static void main(String[] args) {

   Calendar calendar = new GregorianCalendar(2017, 0 , 25);
   Date date = calendar.getTime();
   System.out.println(date);
}
Salida: miércoles 25 de enero a las 00:00:00 GMT de 2017 Ahora hemos tomado el calendario y lo hemos "reducido" a una fecha normal. Vayamos más lejos. Además de designar meses por su número, puede usar los valores de campo constantesCalendar de la clase . Estas constantes son campos estáticos de la clase con un valor predeterminado que no se puede cambiar. En realidad, esta es una opción aún mejor, porque su uso mejora la legibilidad de su código. Calendar

public static void main(String[] args) {
   GregorianCalendar calendar = new GregorianCalendar(2017, Calendar.JANUARY , 25);
}
Calendario.ENERO es una de las constantes que representan los meses del año. Usando estas constantes con nombre, nadie olvidará, por ejemplo, que el número 3 significa abril, y no el tercer mes, que nos gusta llamar marzo. Solo escribe Calendar.APRIL y listo. :) Todos los campos del calendario (número, mes, minutos, segundos, etc.) se pueden especificar por separado utilizando elset()método. Este método es muy conveniente porque laCalendarclase tiene una constante para cada campo y el código resultante es muy fácil de leer. En el último ejemplo, creamos una fecha, pero no establecimos una hora. Pongamos la hora 19:42:12

public static void main(String[] args) {
   Calendar calendar = new GregorianCalendar();
   calendar.set(Calendar.YEAR, 2017);
   calendar.set(Calendar.MONTH, 0);
   calendar.set(Calendar.DAY_OF_MONTH, 25);
   calendar.set(Calendar.HOUR_OF_DAY, 19);
   calendar.set(Calendar.MINUTE, 42);
   calendar.set(Calendar.SECOND, 12);

   System.out.println(calendar.getTime());
}
Salida: miércoles 25 de enero 19:42:12 GMT 2017 Llamamos al set()método, pasando una constante (dependiendo del campo que queramos cambiar) y el nuevo valor para el campo. Resulta que este set()método es una especie de "superestablecedor" que sabe cómo establecer el valor no solo para un campo, sino para muchos campos. :) La Calendarclase usa el add()método para sumar y restar valores. Pasa el campo que desea cambiar y un número (exactamente cuánto desea agregar/restar del valor actual). Por ejemplo, obtengamos una fecha que sea 2 meses antes de la fecha que creamos:

public static void main(String[] args) {
   Calendar calendar = new GregorianCalendar(2017, Calendar.JANUARY , 25);
   calendar.set(Calendar.HOUR, 19);
   calendar.set(Calendar.MINUTE, 42);
   calendar.set(Calendar.SECOND, 12);

   calendar.add(Calendar.MONTH, -2); // To subtract, pass a negative number
   System.out.println(calendar.getTime());
}
Salida: Vie 25 Nov 19:42:12 GMT 2016 ¡Muy bien! Tenemos la fecha hace 2 meses. Esto no solo hizo que cambiara el mes: el año también cambió de 2017 a 2016. Por supuesto, al convertir fechas, el año actual se calcula automáticamente sin necesidad de que lo rastree manualmente. Pero si por alguna razón necesita deshabilitar este comportamiento, puede hacerlo. El roll()método puede sumar y restar valores sin afectar los valores restantes . Por ejemplo, así:

public static void main(String[] args) {
   Calendar calendar = new GregorianCalendar(2017, Calendar.JANUARY , 25);
   calendar.set(Calendar.HOUR, 10);
   calendar.set(Calendar.MINUTE, 42);
   calendar.set(Calendar.SECOND, 12);

   calendar.roll(Calendar.MONTH, -2);
   System.out.println(calendar.getTime());
}
Hicimos exactamente lo mismo que en el ejemplo anterior: tomamos 2 meses desde la fecha actual. Pero ahora el código hace algo diferente: el mes ha cambiado de enero a noviembre, pero el año permanece sin cambios: ¡2017! Salida: sábado 25 de noviembre a las 10:42:12 GMT de 2017 Avanzando. Como dijimos anteriormente, podemos obtener todos los Calendarcampos por separado. Esto lo hacemos con el get()método:

public static void main(String[] args) {
   GregorianCalendar calendar = new GregorianCalendar(2017, Calendar.JANUARY , 25);
   calendar.set(Calendar.HOUR, 10);
   calendar.set(Calendar.MINUTE, 42);
   calendar.set(Calendar.SECOND, 12);

   System.out.println("Year: " + calendar.get(Calendar.YEAR));
   System.out.println("Month: " + calendar.get(Calendar.MONTH));
   System.out.println("Week in the month: " + calendar.get(Calendar.WEEK_OF_MONTH));// Week in this month?

   System.out.println("Day: " + calendar.get(Calendar.DAY_OF_MONTH));

   System.out.println("Hours: " + calendar.get(Calendar.HOUR));
   System.out.println("Minutes: " + calendar.get(Calendar.MINUTE));
   System.out.println("Seconds: " + calendar.get(Calendar.SECOND));
   System.out.println("Milliseconds: " + calendar.get(Calendar.MILLISECOND));

}
Salida: Año: 2017 Mes: 0 Semana del mes: 5 Día: 25 Horas: 10 Minutos: 42 Segundos: 12 Milisegundos: 0 Entonces, además del Calendar"super-setter" de la clase, también hay un "super-getter" ". :) Por supuesto, otro aspecto interesante de esta clase es trabajar con eras. Para crear una fecha "BC", deberá usar el campo Calendar.ERA. Por ejemplo, creemos una fecha para la Batalla de Cannas, donde Aníbal derrotó al ejército romano. Esto sucedió el 2 de agosto de 216 a.C.:

public static void main(String[] args) {
   GregorianCalendar cannae = new GregorianCalendar(216, Calendar.AUGUST, 2);
   cannae.set(Calendar.ERA, GregorianCalendar.BC);

   DateFormat df = new SimpleDateFormat("MMM dd, yyy GG");
   System.out.println(df.format(cannae.getTime()));
}
Aquí usamos la SimpleDateFormatclase para imprimir la fecha en un formato que es más fácil de entender para nosotros (las letras "GG" indican que queremos que se muestre la era). Salida: 02 de Agosto de 216 a.C. La Calendarclase tiene muchos más métodos y constantes. Puede leer sobre ellos en la documentación . Si no le gusta este formato de fecha Sáb 25 de noviembre 10:42:12 GMT 2017 , puede usarlo SimpleDateFormatpara convertirlo fácilmente en lo que desea que sea.

public static void main(String[] args) {

   SimpleDateFormat dateFormat = new SimpleDateFormat("EEEE, MMMM d, yyyy");
   Calendar calendar = new GregorianCalendar(2017, Calendar.JANUARY , 25);
   calendar.set(Calendar.HOUR, 10);
   calendar.set(Calendar.MINUTE, 42);
   calendar.set(Calendar.SECOND, 12);

   calendar.roll(Calendar.MONTH, -2);
   System.out.println(dateFormat.format(calendar.getTime()));
}
Salida: sábado, 25 de noviembre de 2017 Eso es mucho mejor, ¿no? :)
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION