Java: ¿Por qué el constructor Date está en desuso y qué uso en su lugar?

212

Vengo del mundo de C #, así que todavía no tengo mucha experiencia con Java. Eclipse me dijo que Dateestaba en desuso:

Person p = new Person();
p.setDateOfBirth(new Date(1985, 1, 1));

¿Por qué? ¿Y qué (especialmente en casos como los anteriores) debería usarse en su lugar?

Svish
fuente
37
Estoy experimentando una curva de aprendizaje similar, que también va de C # a Java. La otra cosa que me mordió es que el mes del año es un sistema basado en 0 (0 a 11 donde enero = 0 y diciembre = 11) pero los días del mes están basados ​​en 1 (1 a 31). ¡Atención a eso!
Paul Sasik
2
@Paul Sasik, sí, pero hay una constante Calendar.JANUARY, por ejemplo, y una para cada mes
Diogo
55
@PaulSasik lol. Sí, estúpido Java. Tuve que cambiar de C # a Java y OMG el dolor y la miseria.
cbmeeks
8
Los comentarios sarcásticos "jajaja" sobre Java de la gente de C # me hicieron reír porque .Net consiguió su biblioteca decente de fecha y hora ( Noda Time ) de un puerto de la excelente biblioteca de Java Joda-Time .
Basil Bourque

Respuestas:

98

El Dateconstructor específico está en desuso, y Calendardebería usarse en su lugar. La FechaJavaDoc for describe qué constructores están en desuso y cómo reemplazarlos con a .Calendar

Rubén
fuente
25
Calendar requiere un objeto adicional y como 8 líneas más de código para hacer lo mismo que es crear un objeto Date a partir de lo que puedo decir. Es confuso y parece innecesario cuando solo necesita una fecha y no una variable ajustada por zona horaria.
G_V
55
Para su información, las antiguas clases de fecha y hora terriblemente problemáticas como java.util.Date, java.util.Calendary java.text.SimpleDateFormatahora son heredadas , suplantadas por las clases java.time integradas en Java 8 y posteriores. Ver Tutorial de Oracle .
Basil Bourque
250

La java.util.Dateclase en realidad no está en desuso, solo ese constructor, junto con un par de otros constructores / métodos están en desuso. Fue obsoleto porque ese tipo de uso no funciona bien con la internacionalización. La Calendarclase debería usarse en su lugar:

Calendar cal = Calendar.getInstance();
cal.set(Calendar.YEAR, 1988);
cal.set(Calendar.MONTH, Calendar.JANUARY);
cal.set(Calendar.DAY_OF_MONTH, 1);
Date dateRepresentation = cal.getTime();

Echa un vistazo a la fecha Javadoc:

http://download.oracle.com/javase/6/docs/api/java/util/Date.html

BuffaloBuffalo
fuente
2
Esta debería ser la mejor respuesta. Gracias.
jordaniac89
Al decir "no funciona bien con la internacionalización", ¿quiere decir que para Date no se le puede asignar TimeZone? Gracias
DiveInto
¿Qué fecha hizo fue analizar una cadena, por lo que ahora tenemos que subcadenar una cadena que contiene el año, mes y día? Parece una molestia adicional para algo que en la mayoría de los casos no necesita una lógica y métodos tan complejos.
G_V
1
Solo para agregar, esto tendrá en cuenta la zona horaria predeterminada. Si queremos especificar cualquier otra zona horaria, podemos usarCalendar cal = Calendar.getInstance(TimeZone.getTimeZone(<timezone id>));
Vikas Prasad
1
"La clase Calendario debería usarse en su lugar" - Para Java 8 y posterior, las java.time.*clases son la mejor opción ... si está cambiando su código.
Stephen C
73

tl; dr

LocalDate.of( 1985 , 1 , 1 )

…o…

LocalDate.of( 1985 , Month.JANUARY , 1 )

Detalles

Las java.util.Date, java.util.Calendary java.text.SimpleDateFormatlas clases se precipitó demasiado rápido cuando se lanzó por primera vez en Java y evolucionó. Las clases no fueron bien diseñadas o implementadas. Se intentaron mejoras, por lo tanto, las desvalorizaciones que ha encontrado. Lamentablemente, los intentos de mejora fracasaron en gran medida. Debes evitar estas clases por completo. Se suplantan en Java 8 por nuevas clases.

Problemas en tu código

Un java.util.Date tiene una porción de fecha y hora. Ignoraste la porción de tiempo en tu código. Por lo tanto, la clase Fecha tomará el comienzo del día según lo definido por la zona horaria predeterminada de su JVM y aplicará esa hora al objeto Fecha. Por lo tanto, los resultados de su código variarán según la máquina que ejecute o la zona horaria establecida. Probablemente no sea lo que quieres.

Si solo desea la fecha, sin la porción de tiempo, como para una fecha de nacimiento, es posible que no desee utilizar un Dateobjeto. Es posible que desee almacenar solo una cadena de la fecha, en formato ISO 8601 de YYYY-MM-DD. O use un LocalDateobjeto de Joda-Time (ver más abajo).

Joda-Time

Lo primero que debe aprender en Java: evite las clases java.util.Date y java.util.Calendar notoriamente problemáticas incluidas en Java.

Como se señaló correctamente en la respuesta del usuario3277382 , use Joda-Time o el nuevo paquete java.time. * En Java 8.

Código de ejemplo en Joda-Time 2.3

DateTimeZone timeZoneNorway = DateTimeZone.forID( "Europe/Oslo" );
DateTime birthDateTime_InNorway = new DateTime( 1985, 1, 1, 3, 2, 1, timeZoneNorway );

DateTimeZone timeZoneNewYork = DateTimeZone.forID( "America/New_York" );
DateTime birthDateTime_InNewYork = birthDateTime_InNorway.toDateTime( timeZoneNewYork ); 

DateTime birthDateTime_UtcGmt = birthDateTime_InNorway.toDateTime( DateTimeZone.UTC );

LocalDate birthDate = new LocalDate( 1985, 1, 1 );

Volcado a la consola ...

System.out.println( "birthDateTime_InNorway: " + birthDateTime_InNorway );
System.out.println( "birthDateTime_InNewYork: " + birthDateTime_InNewYork );
System.out.println( "birthDateTime_UtcGmt: " + birthDateTime_UtcGmt );
System.out.println( "birthDate: " + birthDate );

Cuando corres ...

birthDateTime_InNorway: 1985-01-01T03:02:01.000+01:00
birthDateTime_InNewYork: 1984-12-31T21:02:01.000-05:00
birthDateTime_UtcGmt: 1985-01-01T02:02:01.000Z
birthDate: 1985-01-01

java.time

En este caso, el código para java.time es casi idéntico al de Joda-Time .

Obtenemos una zona horaria ( ZoneId) y construimos un objeto de fecha y hora asignado a esa zona horaria ( ZonedDateTime). Luego, utilizando el patrón de Objetos inmutables , creamos nuevas fechas y horas basadas en el mismo instante del objeto antiguo (recuento de nanosegundos desde la época ) pero con otra zona horaria asignada. Por último, obtenemos una LocalDateque no tiene hora del día ni zona horaria, aunque tenga en cuenta que la zona horaria se aplica al determinar esa fecha (un nuevo día amanece antes en Oslo que en Nueva York, por ejemplo).

ZoneId zoneId_Norway = ZoneId.of( "Europe/Oslo" );
ZonedDateTime zdt_Norway = ZonedDateTime.of( 1985 , 1 , 1 , 3 , 2 , 1 , 0 , zoneId_Norway );

ZoneId zoneId_NewYork = ZonedId.of( "America/New_York" );
ZonedDateTime zdt_NewYork = zdt_Norway.withZoneSameInstant( zoneId_NewYork );

ZonedDateTime zdt_Utc = zdt_Norway.withZoneSameInstant( ZoneOffset.UTC );  // Or, next line is similar.
Instant instant = zdt_Norway.toInstant();  // Instant is always in UTC.

LocalDate localDate_Norway = zdt_Norway.toLocalDate();

Sobre java.time

El marco java.time está integrado en Java 8 y posterior. Estas clases suplantar la vieja problemáticos heredados clases de fecha y hora como java.util.Date, Calendar, y SimpleDateFormat.

El proyecto Joda-Time , ahora en modo de mantenimiento , aconseja la migración a las clases java.time .

Para obtener más información, consulte el Tutorial de Oracle . Y busque Stack Overflow para obtener muchos ejemplos y explicaciones. La especificación es JSR 310 .

Puede intercambiar objetos java.time directamente con su base de datos. Utilice un controlador JDBC compatible con JDBC 4.2 o posterior. No hay necesidad de cadenas, no hay necesidad de java.sql.*clases.

¿Dónde obtener las clases java.time?

El proyecto ThreeTen-Extra extiende java.time con clases adicionales. Este proyecto es un campo de pruebas para posibles adiciones futuras a java.time. Usted puede encontrar algunas clases útiles aquí, como Interval, YearWeek, YearQuarter, y más .

Albahaca Bourque
fuente
55
Apoyos para mencionar realmente la causa raíz y proporcionar cobertura de todas las alternativas disponibles.
mabi
Esta es la respuesta correcta y se debe evitar la clase Calendario. ThreeTen es la mejor opción
ant2009
11

Una razón por la que el constructor está en desuso es que el significado del parámetro año no es el que cabría esperar. El javadoc dice:

A partir de JDK versión 1.1, reemplazado por Calendar.set(year + 1900, month, date).

Tenga en cuenta que el campo año es la cantidad de años transcurridos desde entonces 1900, por lo que su código de muestra probablemente no hará lo que espera que haga. Y ese es el punto.

En general, la DateAPI solo admite el calendario occidental moderno, tiene componentes especificados idiosincráticamente y se comporta de manera inconsistente si establece campos.

Las API Calendary las GregorianCalendarAPI son mejores que Date, y las API de Joda-time de terceros generalmente se consideraron las mejores. En Java 8, introdujeron los java.timepaquetes, y ahora son la alternativa recomendada.

Stephen C
fuente
Esta solución funciona muy bien, excepto que no utiliza el tiempo cero durante horas-minutos-segundos-milisegundos como lo hizo la nueva Fecha (año, mes, día). Entonces necesitamos algo como: Calendar cal = Calendar.getInstance (); cal.clear (); cal.set (año + 1900, mes, fecha); Fecha dateRepresentation = cal.getTime ();
Joel Richard Koett
11

Encontré esta pregunta como un duplicado de una pregunta más nueva que preguntaba cuál era la forma no obsoleta de obtener un Dateaño, mes y día específicos.

Las respuestas aquí hasta ahora dicen usar la Calendarclase, y eso fue cierto hasta que salió Java 8. Pero a partir de Java 8, la forma estándar de hacerlo es:

LocalDate localDate = LocalDate.of(1985, 1, 1);

Y luego, si realmente necesita un java.util.Date, puede utilizar las sugerencias de esta pregunta .

Para obtener más información, consulte la API o los tutoriales para Java 8.

Kevin Workman
fuente
7

Tenga en cuenta que Calendar.getTime()no es determinista en el sentido de que la parte del día se ajusta de manera predeterminada a la hora actual.

Para reproducir, intente ejecutar el siguiente código un par de veces:

Calendar c = Calendar.getInstance();
c.set(2010, 2, 7); // NB: 2 means March, not February!
System.err.println(c.getTime());

Salida ej .:

Sun Mar 07 10:46:21 CET 2010

Ejecutar exactamente el mismo código un par de minutos después produce:

Sun Mar 07 10:57:51 CET 2010

Entonces, mientras set()obliga a los campos correspondientes a corregir los valores, pierde el tiempo del sistema para los otros campos. (Probado anteriormente con Sun jdk6 y jdk7)

Tijn Porcelijn
fuente
2

Como el constructor Date está en desuso, puede probar este código.

import java.util.Calendar;

  Calendar calendar = Calendar.getInstance();
     calendar.set(Calendar.HOUR_OF_DAY, 6);// for 6 hour
     calendar.set(Calendar.MINUTE, 0);// for 0 min
     calendar.set(Calendar.SECOND, 0);// for 0 sec
     calendar.set(1996,0,26);// for Date [year,month(0 to 11), date]

    Date date = new Date(calendar.getTimeInMillis());// calendar gives long value

    String mConvertedDate = date.toString();// Fri Jan 26 06:00:00 GMT+05:30 1996
Divyanshu Kumar
fuente
1
Estas clases terribles ahora son heredadas, habiendo sido suplantadas hace años por las clases java.time definidas en JSR 310. Sugerir Datey Calendaren 2019 es un mal consejo.
Basil Bourque
@BasilBourque gracias por la sugerencia, espero actualizarlo pronto.
Divyanshu Kumar
1

Puedes hacer un método como new Date(year,month,date)en tu código usando Calendarclass.

private Date getDate(int year,int month,int date){
    Calendar cal = Calendar.getInstance();
    cal.set(Calendar.YEAR, year);
    cal.set(Calendar.MONTH, month-1);
    cal.set(Calendar.DAY_OF_MONTH, day);
    return cal.getTime();
}

Funcionará igual que el constructor obsoleto de Date

Sudhanshu Dubey
fuente
1
Estas clases terriblemente problemáticas fueron suplantadas hace años por las modernas clases java.time , específicamente Instanty ZonedDateTime.
Basil Bourque
La solución anterior debería agregar 1900 al año, y NO debería restar 1 del mes, y también debería ejecutar cal.clear () después de la línea Calendar.getInstance (), para que las horas / minutos / segundos / milisegundos se pongan a cero (como lo hacen en el constructor Fecha).
Joel Richard Koett
1

El constructor Fecha espera años en el formato de años desde 1900, meses basados ​​en cero, días basados ​​en uno y establece las horas / minutos / segundos / milisegundos en cero.

Date result = new Date(year, month, day);

Entonces, usando el reemplazo del Calendario (años basados ​​en cero, meses basados ​​en cero, días basados ​​en uno) para el constructor de Fecha obsoleto, necesitamos algo como:

Calendar calendar = Calendar.getInstance();
calendar.clear(); // Sets hours/minutes/seconds/milliseconds to zero
calendar.set(year + 1900, month, day);
Date result = calendar.getTime();

O usando Java 1.8 (que tiene un año basado en cero y meses y días basados ​​en uno):

Date result = Date.from(LocalDate.of(year + 1900, month + 1, day).atStartOfDay(ZoneId.systemDefault()).toInstant());

Aquí hay versiones iguales de Date, Calendar y Java 1.8:

int year = 1985; // 1985
int month = 1; // January
int day = 1; // 1st

// Original, 1900-based year, zero-based month, one-based day
Date date1 = new Date(year - 1900, month - 1, day);

// Calendar, zero-based year, zero-based month, one-based day
Calendar calendar = Calendar.getInstance();
calendar.clear(); // Sets hours/minutes/seconds/milliseconds to zero
calendar.set(year, month - 1, day);
Date date2 = calendar.getTime();

// Java-time back to Date, zero-based year, one-based month, one-based day
Date date3 = Date.from(LocalDate.of(year, month, day).atStartOfDay(ZoneId.systemDefault()).toInstant());

SimpleDateFormat format = new SimpleDateFormat("yyyy-MMM-dd HH:mm:ss.SSS");

// All 3 print "1985-Jan-01 00:00:00.000"
System.out.println(format.format(date1));
System.out.println(format.format(date2));
System.out.println(format.format(date3));
Joel Richard Koett
fuente
1
Ambas clases terribles fueron suplantadas hace años por las clases java.time definidas en JSR 310.
Basil Bourque
Respuesta actualizada para mostrar las versiones de Fecha, Calendario y Java 1.8.
Joel Richard Koett
0
new GregorianCalendar(1985, Calendar.JANUARY, 1).getTime();

(la forma anterior a Java-8)

Curtis Yallop
fuente
Probablemente sea lo mejor que puede hacer sin java.time. Sin embargo, también antes de Java 8 puede usar java.time, hay un backport: la biblioteca ThreeTen Backport .
Ole VV