JavaRush /Blog Java /Random-ES /Cómo no perderse en el tiempo - DateTime y Calendar

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 habíamos encontrado antes: las fechas. Cómo no perderse en el tiempo - FechaHora y Calendario - 1Creo que no es necesario explicar qué es una fecha :) En principio, es muy posible escribir la fecha y hora actuales en Java en una cadena normal.
public class Main {
   public static void main(String[] args) {

       String date = "11 de junio de 2018";
       System.out.println(date);
   }
}
Pero este enfoque tiene muchas desventajas. La clase Stringfue creada para trabajar con texto y tiene métodos apropiados. Si necesitamos gestionar de alguna manera la fecha (agregarle 2 horas, por ejemplo), Stringno funcionará aquí. O, por ejemplo, mostrar la fecha y hora actuales en el momento en que se compiló el programa en la consola. Aquí tampoco ayudará String: mientras escribes el código y lo ejecutas, la hora cambiará y lo irrelevante se mostrará en la consola. Por eso, en Java, sus creadores proporcionaron varias clases para trabajar con fechas y horas. El primero es clase.java.util.Date

Clase de fecha de Java

Le dimos su nombre completo porque también hay una clase en otro paquete en Java java.sql.Date. ¡No te confundas! Lo primero que debes saber al respecto es que almacena la fecha en milisegundos que han transcurrido desde el 1 de enero de 1970. Incluso hay un nombre distinto para esta fecha: "hora Unix". Es una forma bastante interesante, ¿no te parece? :) Lo segundo que debe recordar: si crea un objeto Datecon un constructor vacío, el resultado será la fecha y hora actuales en el momento en que se creó el objeto . String¿Recuerdas cómo escribimos que tal tarea sería problemática para un formato de fecha ? La clase Datelo resuelve fácilmente.
public class Main {
   public static void main(String[] args) {

       Date date = new Date();
       System.out.println(date);
   }
}
Ejecuta este código varias veces y verás cómo el tiempo cambiará cada vez :) Esto es posible precisamente porque se almacena en milisegundos: son la unidad de tiempo más pequeña, por eso los resultados son tan precisos. Hay otro constructor para Date: puedes especificar el número exacto de milisegundos que han pasado desde las 00:00 del 1 de enero de 1970 hasta la fecha requerida, y se creará:
public class Main {
   public static void main(String[] args) {

       Date date = new Date(1212121212121L);
       System.out.println(date);
   }
}
Salida de consola:

Fri May 30 08:20:12 MSD 2008
Lo recibimos el 30 de mayo de 2008. "Viernes" significa el día de la semana - "viernes" (viernes) y MSD - "horario de verano de Moscú" (horario de verano de Moscú). Los milisegundos se transmiten en formato long, ya que la mayoría de las veces su número no encaja en int. Entonces, ¿qué tipo de operaciones de fecha podríamos necesitar en nuestro trabajo? Bueno, lo más obvio, por supuesto, es la comparación . Determina si una fecha fue posterior o anterior a otra. Esto se puede hacer de diferentes maneras. Por ejemplo, puedes llamar al método Date.getTime(), que devolverá el número de milisegundos que han pasado desde la medianoche del 1 de enero de 1970. Llamémoslo a dos objetos Date y compárelos entre sí:
public class Main {
   public static void main(String[] args) {

       Date date1 = new Date();

       Date date2 = new Date();

       System.out.println((date1.getTime() > date2.getTime())?
               "fecha1 es posterior a fecha2" : "fecha1 es anterior a fecha2");
   }
}
Conclusión:

date1 раньше date2
Pero hay una manera más conveniente, es decir, utilizar métodos especiales de la clase Date: before(), after()y equals(). Todos devuelven el resultado en boolean. El método before()comprueba si nuestra fecha es anterior a la que pasamos como argumento:
public class Main {
   public static void main(String[] args) throws InterruptedException {

       Date date1 = new Date();

       Thread.sleep(2000);// pausa el programa por 2 segundos
       Date date2 = new Date();

       System.out.println(date1.before(date2));
   }
}
Salida de consola:

true
El método funciona de manera similar after(); verifica si nuestra fecha fue posterior a la que pasamos como argumento:
public class Main {
   public static void main(String[] args) throws InterruptedException {

       Date date1 = new Date();

       Thread.sleep(2000);// pausa el programa por 2 segundos
       Date date2 = new Date();

       System.out.println(date1.after(date2));
   }
}
Salida de consola:

false
En nuestros ejemplos, ponemos el programa en suspensión durante 2 segundos para garantizar que las dos fechas sean diferentes. En computadoras rápidas, el tiempo entre la creación date1y date2puede ser inferior a un milisegundo, en cuyo caso tanto y before()como after()regresarán false. ¡ Pero el método equals()en tal situación volverá true! Después de todo, compara exactamente el número de milisegundos que han pasado desde las 00:00 del 1 de enero de 1970 para cada fecha. Los objetos sólo se considerarán iguales si coinciden hasta 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 algo más a lo que debes prestar atención. Si abre la documentación de la clase Dateen el sitio web de Oracle, verá que muchos de sus métodos y constructores han sido designados como Deprecated(“obsoletos”). Aquí, mire: Fecha de clase Esto es lo que los propios creadores de Java dicen sobre aquellas partes de las clases que han quedado obsoletas: “Un elemento de programa anotado con @Deprecated es algo que los programadores no deben usar, generalmente porque es peligroso o porque Hay una alternativa mejor." Esto no significa que estos métodos no puedan utilizarse en absoluto. Además, si intenta ejecutar el código usted mismo usándolos en IDEA, lo más probable es que funcione. Tomemos, por ejemplo, el método obsoleto Date.getHours(), que devuelve el número de horas del objeto Date.
public static void main(String[] args) {

   Date date1 = new Date();

   System.out.println(date1.getHours());

}
Si en el momento en que ejecutas el código, por ejemplo, son las 14:21, mostrará el número 14. Como puedes ver, el método obsoleto está tachado, pero funciona bastante bien. Estos métodos no se eliminaron por completo, para no romper un montón de código ya escrito con ellos. Es decir, estos métodos no están "rotos" ni "eliminados", simplemente no se recomienda su uso debido a la disponibilidad de una alternativa más conveniente. Por cierto, está escrito sobre esto en la documentación: Cómo no perderse en el tiempo - DateTime y Calendar - 2La mayoría de los métodos de la clase Date se han movido a su versión mejorada y extendida: la clase Calendar. Lo conoceremos más :)

Calendario Java

Java 1.1 introdujo una nueva clase: Calendar. Hizo que trabajar con fechas en Java fuera un poco más fácil de lo que parecía antes. La única implementación de la clase Calendarcon la que trabajaremos es la clase GregorianCalendar(implementa el calendario gregoriano, según el cual viven 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 día a la fecha actual
  • Compruebe si el año es bisiesto;
  • Obtener componentes de fecha individuales (por ejemplo, obtener el número de mes de una fecha completa)
  • Y además, en su interior se ha desarrollado un sistema de constantes muy conveniente (veremos muchas de ellas a continuación).
Otra diferencia importante de la clase Calendares que implementa una constante Calendar.Era: puede establecer la fecha en la era antes de Cristo ("Antes de Cristo" - antes del nacimiento de Cristo, es decir, "antes de nuestra era") o AC ("Después de Cristo" - " nuestra era"). Veamos todo esto con ejemplos. Creemos un calendario con la fecha 25 de enero de 2017:
public static void main(String[] args) {

  Calendar calendar = new GregorianCalendar(2017, 0 , 25);
}
Los meses en la clase Calendar(como en Date, por cierto) comienzan desde cero, por lo que pasamos el número 0 como segundo argumento. Lo principal cuando se trabaja con una clase Calendares comprender que se trata de un calendario y no de una fecha separada. Cómo no perderse en el tiempo - DateTime y Calendar - 3Una fecha es simplemente una serie de números que representan un período de tiempo específico. Y un calendario es un dispositivo completo con el que puedes hacer muchas cosas con las fechas :) Esto se puede ver con bastante claridad si intentas enviar el objeto Calendario a la consola: Salida:

java.util.GregorianCalendar[time=?,areFieldsSet=false,areAllFieldsSet=false,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Europe/Moscow",offset=10800000,dstSavings=0,useDaylight=false,transitions=79,lastRule=null],firstDayOfWeek=2,minimalDaysInFirstWeek=1,ERA=?,YEAR=2017,MONTH=0,WEEK_OF_YEAR=?,WEEK_OF_MONTH=?,DAY_OF_MONTH=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=?]
¡Mira cuanta información hay! El calendario tiene un montón de propiedades que una fecha normal no tiene, y todas se envían a la consola (así es como funciona el método toString()en la clase Calendar). Si, cuando trabaja, sólo necesita obtener una fecha simple del calendario, es decir, objeto Date: esto se hace mediante un método Calendar.getTime()(el nombre no es el más lógico, pero no se puede hacer nada):
public static void main(String[] args) {

   Calendar calendar = new GregorianCalendar(2017, 0 , 25);
   Date date = calendar.getTime();
   System.out.println(date);
}
Conclusión:

Wed Jan 25 00:00:00 MSK 2017
Ahora hemos "simplificado" el calendario a una fecha normal. Vamonos. Además de los símbolos numéricos de los meses, Calendaren el aula se pueden utilizar constantes. Las constantes son campos estáticos de una clase Calendarcon un valor ya establecido que no se puede cambiar. Esta opción es realmente mejor, ya que mejora la legibilidad del código.
public static void main(String[] args) {
   GregorianCalendar calendar = new GregorianCalendar(2017, Calendar.JANUARY , 25);
}
Calendar.JANUARY— una de las constantes para indicar el mes. Con esta opción de nombre, nadie olvidará, por ejemplo, que el número "3" significa abril, y no el tercer mes al que estamos acostumbrados: marzo. Simplemente escribe Calendar.APRIL, y listo :) Todos los campos del calendario (día, mes, minutos, segundos, etc.) se pueden configurar individualmente usando el método set(). Esto es muy conveniente, ya que Calendarcada campo tiene su propia constante en la clase y el código final se verá lo más simple posible. Por ejemplo, en el ejemplo anterior, creamos una fecha, pero no configuramos la hora actual. Pongamos el tiempo en 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());
}
Conclusión:

Wed Jan 25 19:42:12 MSK 2017
Llamamos al método set(), le pasamos una constante (dependiendo del campo que queramos cambiar) y un nuevo valor para este campo. Resulta que el método set()es una especie de "superdefinidor" que puede establecer un valor no para un campo, sino para muchos campos :) La suma y resta de valores en una clase Calendarse realiza mediante el método add(). Debe pasarle el campo que desea cambiar y el número: exactamente cuánto desea sumar/restar del valor actual. Por ejemplo, establezcamos la fecha que creamos hace 2 meses:
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);//para restar un valor - se debe pasar un número negativo al método
   System.out.println(calendar.getTime());
}
Conclusión:

Fri Nov 25 19:42:12 MSK 2016
¡Excelente! Hemos fijado la fecha a hace 2 meses. Como resultado, no solo cambió el mes, sino también el año, de 2017 a 2016. El cálculo del año actual al cambiar las fechas, por supuesto, se realiza automáticamente y no es necesario controlarlo manualmente. Pero si por algún motivo necesita desactivar este comportamiento, puede hacerlo. Un método especial roll()puede sumar y restar valores sin afectar otros valores. 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: restamos 2 meses de la fecha actual. Pero ahora el código funcionó de manera diferente: el mes cambió de enero a noviembre, ¡pero el año siguió siendo el mismo que en 2017! Conclusión:

Sat Nov 25 10:42:12 MSK 2017
Más. Como dijimos anteriormente, todos los campos de un objeto Calendarse pueden obtener por separado. El método es responsable de esto get():
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("Año: " + calendar.get(Calendar.YEAR));
   System.out.println("Mes: " + calendar.get(Calendar.MONTH));
   System.out.println("Número de la semana en el mes: " + calendar.get(Calendar.WEEK_OF_MONTH));// número de serie de la semana en el mes

   System.out.println("Número: " + calendar.get(Calendar.DAY_OF_MONTH));

   System.out.println("Mirar: " + calendar.get(Calendar.HOUR));
   System.out.println("Minutos: " + calendar.get(Calendar.MINUTE));
   System.out.println("Segundos: " + calendar.get(Calendar.SECOND));
   System.out.println("Milisegundos: " + calendar.get(Calendar.MILLISECOND));

}
Conclusión:

Год: 2017 
Месяц: 0 
Порядковый номер недели в месяце: 4 
Число: 25 
Часы: 10 
Минуты: 42 
Секунды: 12 
Миллисекунды: 0
Es decir, además del "super-setter" en la clase Calendartambién hay un "super-getter" :) Otro punto interesante es, por supuesto, trabajar con eras. Para crear una fecha "BC", necesitas usar el campo. Calendar.Era Por ejemplo, creemos una fecha que indique la Batalla de Cannas, en la que Aníbal derrotó al ejército de Roma. Esto sucedió el 2 de agosto del 216 a.C. mi.:
public static void main(String[] args) {
   GregorianCalendar cannes = new GregorianCalendar(216, Calendar.AUGUST, 2);
   cannes.set(Calendar.ERA, GregorianCalendar.BC);

   DateFormat df = new SimpleDateFormat("dd MMM yyy GG");
   System.out.println(df.format(cannes.getTime()));
}
Aquí usamos la clase SimpleDateFormatpara mostrar la fecha en un formato que nos resulte más comprensible (las letras “GG” son responsables de mostrar la época). Conclusión:

02 авг 216 до н.э.
CalendarHay muchos más métodos y constantes en la clase , lea sobre ellos en la documentación:

Nueva línea hasta la fecha

Para convertir String en Date, puede utilizar la clase auxiliar de Java: SimpleDateFormat . Esta es la clase que necesita para convertir una fecha a un formato que usted defina. Cómo no perderse en el tiempo - DateTime y Calendar - 5A su vez, es muy similar a DateFormat . La única diferencia notable entre los dos es que SimpleDateFormat se puede usar para formatear (convertir una fecha en una cadena) y analizar la cadena en una fecha compatible con la configuración regional, mientras que DateFormat no admite una configuración regional. Además, DateFormat es una clase abstracta que proporciona soporte básico para formatear y analizar fechas, mientras que SimpleDateFormat es una clase concreta que extiende la clase DateFormat. Así es como se ve un ejemplo de creación de un objeto SimpleDateFormat y formato de una fecha:
SimpleDateFormat formater = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = new Date(1212121212121L);

System.out.println(formatter.format(date));
En el ejemplo anterior utilizamos el patrón "aaaa-MM-dd HH:mm:ss" que significa:
  • 4 dígitos para el año (aaaa);
  • 2 dígitos para el mes (MM);
  • 2 dígitos para el día (dd);
  • 2 dígitos para horas en formato de 24 horas (HH);
  • 2 dígitos para minutos (mm);
  • 2 dígitos para segundos (ss).
Se conservan las marcas de separación y el orden de los símbolos del patrón. Salida de consola:
2008-05-30 08:20:12
Hay bastantes cartas modelo para la clase SimpleDateFormat . Para que no te confundas, los hemos recopilado en una tabla:
Símbolo Descripción Ejemplo
GRAMO era (en localización inglesa - AD y BC) ANUNCIO
y año (número de 4 dígitos) 2020
yy año (últimos 2 dígitos) 20
yyyy año (número de 4 dígitos) 2020
METRO número de mes (sin ceros a la izquierda) 8
MM número de mes (con ceros a la izquierda si el número de mes < 10) 04
MMM abreviatura de mes de tres letras (según la localización) Ene
Mmmmm nombre del mes completo Junio
w semana del año (sin ceros a la izquierda) 4
ww semana del año (con ceros a la izquierda) 04
W. semana en mes (sin ceros a la izquierda) 3
WW semana en mes (con cero a la izquierda) 03
D dia del año 67
d día del mes (sin ceros a la izquierda) 9
dd día del mes (con ceros a la izquierda) 09
F día de la semana del mes (sin ceros a la izquierda) 9
FF día de la semana en el mes (con ceros a la izquierda) 09
mi día de la semana (abreviatura) W.
EEEE día de la semana (completo) Viernes
tu número del día de la semana (sin ceros a la izquierda) 5
uu número del día de la semana (con ceros a la izquierda) 05
a Marcador AM/PM SOY.
h horas en formato de 24 horas sin ceros a la izquierda 6
S.S Reloj en formato de 24 horas con cero a la izquierda. 06
k número de horas en formato de 24 horas 18
k número de horas en formato de 12 horas 6
h hora en formato de 12 horas sin ceros a la izquierda 6
S.S hora en formato de 12 horas con cero a la izquierda 06
metro minutos sin ceros a la izquierda 32
milímetros minutos con cero a la izquierda 32
s segundos sin ceros a la izquierda once
ss segundos con cero a la izquierda once
S milisegundos 297
z Zona horaria EET
z zona horaria en formato RFC 822 300
Ejemplos de combinaciones de caracteres de patrones:
Muestra Ejemplo
dd-MM-aaaa 01-11-2020
aaaa-MM-dd 2019-10-01
HH:mm:ss.SSS 23:59.59.999
aaaa-MM-dd HH:mm:ss 2018-11-30 03:09:02
aaaa-MM-dd HH:mm:ss.SSS 2016-03-01 01:20:47.999
aaaa-MM-dd HH:mm:ss.SSS Z 2013-13-13 23:59:59.999 +0100
Si comete un pequeño error con el formato, puede convertirse en propietario de una java.text.ParseException, y este no es un logro particularmente agradable. Bueno, la breve excursión a SimpleDateFormat ha terminado: volvamos a la traducción de la cadena Java hasta la fecha . SimpleDateFormat nos brinda dichas capacidades y recorreremos este proceso paso a paso.
  1. Cree una línea a partir de la cual necesite establecer la fecha:

    String strDate = "Sat, April 4, 2020";
  2. Creamos un nuevo objeto SimpleDateFormat con una plantilla que coincide con lo que tenemos en la cadena (de lo contrario no podremos analizarlo):

    SimpleDateFormat formatter = new SimpleDateFormat("EEE, MMMM d, yyyy", Locale.ENGLISH);

    Como puede ver, aquí tenemos un argumento local. Si lo omitimos, utilizará la configuración regional predeterminada, que no siempre es el inglés.

    Si la configuración regional no coincide con la cadena de entrada, los datos de cadena vinculados al idioma, como el nuestro Mon o April , no serán reconocidos y generarán una java.text.ParseException, incluso si el patrón coincide.

    Sin embargo, no tenemos que especificar el formato si utilizamos una plantilla que no es específica del idioma. Como ejemplo: aaaa-MM-dd HH:mm:ss

  3. Creamos una fecha usando un formateador, que a su vez la analiza a partir de la cadena de entrada:

    try {
      Date date = formatter.parse(strDate);
      System.out.println(date);
    }
    catch (ParseException e) {
      e.printStackTrace();
    }

    Salida de consola:

    
    Sat Apr 04 00:00:00 EEST 2020

    Hmmm... ¡Pero el formato ya no es el mismo!

    Para hacer el mismo formato, usamos el formateador nuevamente:

    System.out.println(formatter.format(date));

    Salida de consola:

    
    Sat, April 4, 2020

Formato de fecha simple y calendario

SimpleDateFormat le permite formatear todos los objetos de fecha y calendario que cree para su uso posterior. Consideremos un punto tan interesante como trabajar con eras. Para crear una fecha "BC", necesitas usar el campo Calendar.Era. Por ejemplo, creemos una fecha que indique la Batalla de Cannas, en la que Aníbal derrotó al ejército de Roma. Esto sucedió el 2 de agosto del 216 a.C. mi.:
public static void main(String[] args) {
   GregorianCalendar cannes = new GregorianCalendar(216, Calendar.AUGUST, 2);
   cannes.set(Calendar.ERA, GregorianCalendar.BC);

   DateFormat df = new SimpleDateFormat("dd MMM yyy GG");
   System.out.println(df.format(cannes.getTime()));
}
Aquí usamos la clase SimpleDateFormat para mostrar la fecha en un formato que nos resulte más comprensible (como se indicó anteriormente, las letras “GG” son responsables de mostrar la época). Conclusión:

02 авг 216 до н.э.

Formato de fecha Java

Aquí hay otro caso. Supongamos que este formato de fecha no nos conviene:

Sat Nov 25 10:42:12 MSK 2017
Asi que aqui esta. Usando nuestras capacidades en formato de fecha java, puede cambiarlo al suyo sin mucha dificultad:
public static void main(String[] args) {

   SimpleDateFormat dateFormat = new SimpleDateFormat("EEEE, d MMMM 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()));
}
Conclusión:

суббота, 25 Ноябрь 2017
Mucho mejor, ¿verdad? :)
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION