Java: Por que o construtor Date está obsoleto e o que eu uso?

211

Eu venho do mundo C #, então ainda não tenho muita experiência com Java. Foi-me dito pelo Eclipse que Datefoi preterido:

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

Por quê? E o que (especialmente nos casos como acima) deve ser usado?

Svish
fonte
37
Estou experimentando uma curva de aprendizado semelhante, também passando de C # para Java. A outra coisa que me incomodou é que o mês do ano é um sistema baseado em 0 (0 a 11 em que janeiro = 0 e dez = 11), mas os dias do mês são baseados em 1 (1 a 31). Cuidado com isso!
Paul Sasik
2
@ Paulo Sasik, sim, mas não é constante Calendar.JANUARY por exemplo, e uma para cada mês
Diogo
5
@PaulSasik lol. Sim, Java estúpido. Teve que mudar de C # para Java e OMG a dor e a miséria.
Cbmeeks 22/03
8
As observações sarcásticas de "lol" sobre Java de pessoas do C # me fizeram rir porque a .Net obteve sua biblioteca de data e hora decente ( Noda Time ) de uma porta da excelente biblioteca Java Joda-Time .
Basil Bourque

Respostas:

97

O Dateconstrutor específico está obsoleto e Calendardeve ser usado. A DataJavaDoc for descreve quais construtores foram descontinuados e como substituí-los usando a .Calendar

Ruben
fonte
25
O calendário requer um objeto extra e, como 8 linhas a mais de código, é possível criar o objeto Date a partir do que eu sei. É confuso e parece desnecessário quando você só precisa de uma variável ajustada para Data e não para fuso horário.
G_V
5
FYI, os terrivelmente problemáticas aulas em tempo data antigas, tais como java.util.Date, java.util.Calendare java.text.SimpleDateFormatestão agora legado , suplantado pelos java.time classes internas em Java 8 e posterior. Consulte o Tutorial da Oracle .
Basil Bourque 28/11
250

A java.util.Dateclasse não é realmente obsoleta, apenas esse construtor, juntamente com alguns outros construtores / métodos, está obsoleto. Foi preterido porque esse tipo de uso não funciona bem com a internacionalização. A Calendarclasse deve ser usada:

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();

Dê uma olhada na data Javadoc:

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

BuffaloBuffalo
fonte
2
Essa deve ser a melhor resposta. Obrigado.
Jordaniac89 28/05
Ao dizer "não funciona bem com a internacionalização", você quer dizer que para o Date, você não pode atribuir um fuso horário? Obrigado
DiveInto 1/12/15
Que data foi analisar uma String, então agora temos que substring uma String que contém o ano, mês e dia? Parece muito trabalho extra para algo que, na maioria dos casos, não precisa de lógica e métodos tão complexos adicionados a ele.
G_V
1
Só para adicionar, isso levará o fuso horário padrão em consideração. Se quisermos especificar qualquer outro fuso horário, podemos usarCalendar cal = Calendar.getInstance(TimeZone.getTimeZone(<timezone id>));
Vikas Prasad
1
"A classe Calendar deve ser usada em vez disso" - para Java 8 e posterior, as java.time.*classes são a melhor opção ... se você estiver alterando seu código.
Stephen C
73

tl; dr

LocalDate.of( 1985 , 1 , 1 )

…ou…

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

Detalhes

Os java.util.Date, java.util.Calendare java.text.SimpleDateFormatas aulas foram correu muito rapidamente quando o Java primeiro lançado e evoluído. As aulas não foram bem projetadas ou implementadas. Melhorias foram tentadas, portanto, as reprovações que você encontrou. Infelizmente, as tentativas de melhoria falharam amplamente. Você deve evitar essas classes completamente. Eles são suplantados no Java 8 por novas classes.

Problemas no seu código

Um java.util.Date possui uma parte de data e hora. Você ignorou a parte do tempo no seu código. Portanto, a classe Date pegará o início do dia conforme definido pelo fuso horário padrão da sua JVM e aplicará esse horário ao objeto Date. Portanto, os resultados do seu código variam dependendo de qual máquina ele é executado ou de qual fuso horário está definido. Provavelmente não é o que você quer.

Se você deseja apenas a data, sem a parte da hora, como para uma data de nascimento, talvez não queira usar um Dateobjeto. Convém armazenar apenas uma sequência da data, no formato ISO 8601 de YYYY-MM-DD. Ou usar um LocalDateobjeto a partir de Joda-Time (veja abaixo).

Joda-Time

Primeira coisa a aprender em Java: Evite as classes notoriamente problemáticas java.util.Date e java.util.Calendar incluídas no Java.

Conforme observado corretamente na resposta do usuário3277382 , use o Joda-Time ou o novo pacote java.time. * No Java 8.

Exemplo de código no 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 );

Despejar no console…

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 );

Quando correr ...

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

Nesse caso, o código para java.time é quase idêntico ao do Joda-Time .

Obtemos um fuso horário ( ZoneId) e construímos um objeto de data e hora atribuído a esse fuso horário ( ZonedDateTime). Em seguida, usando o padrão Objetos imutáveis , criamos novas datas e horários com base no mesmo instante do objeto antigo (contagem de nanossegundos desde a época ), mas atribuímos outro fuso horário. Por fim, obtemos um LocalDateque não possui hora do dia nem fuso horário, embora observe que o fuso horário se aplica ao determinar essa data (um novo dia começa mais cedo em Oslo do que em Nova York, por exemplo).

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

A estrutura java.time é integrada ao Java 8 e posterior. Essas classes suplantar os velhos problemáticos legados aulas de data e hora, como java.util.Date, Calendar, e SimpleDateFormat.

O projeto Joda-Time , agora em modo de manutenção , aconselha a migração para as classes java.time .

Para saber mais, consulte o Tutorial do Oracle . E procure Stack Overflow para muitos exemplos e explicações. A especificação é JSR 310 .

Você pode trocar objetos java.time diretamente com seu banco de dados. Use um driver JDBC compatível com JDBC 4.2 ou posterior. Não há necessidade de strings, não há necessidade de java.sql.*classes.

Onde obter as classes java.time?

  • Java SE 8 , Java SE 9 , Java SE 10 e posterior
    • Construídas em.
    • Parte da API Java padrão com uma implementação em pacote configurável.
    • O Java 9 adiciona alguns recursos e correções menores.
  • Java SE 6 e Java SE 7
    • Grande parte da funcionalidade java.time é portada para Java 6 e 7 no ThreeTen-Backport .
  • Android
    • Versões posteriores das implementações de pacote configurável do Android das classes java.time.
    • Para anteriormente Android (<26), o ThreeTenABP projeto adapta ThreeTen-Backport (mencionados acima). Consulte Como usar o ThreeTenABP… .

O projeto ThreeTen-Extra estende o java.time com classes adicionais. Este projeto é um campo de prova para possíveis adições futuras ao java.time. Você pode encontrar algumas classes úteis aqui, como Interval, YearWeek, YearQuarter, e mais .

Basil Bourque
fonte
5
Props para realmente mencionar a causa raiz e fornecer cobertura de todas as alternativas disponíveis.
mabi
Essa é a resposta correta e a classe Calendário deve ser evitada. ThreeTen é a melhor opção
ant2009 16/11
11

Uma razão pela qual o construtor foi descontinuado é que o significado do parâmetro year não é o que você esperaria. O javadoc diz:

A partir do JDK versão 1.1, substituído por Calendar.set(year + 1900, month, date).

Observe que o campo ano é o número de anos desde 1900então, portanto, seu código de amostra provavelmente não fará o que você espera. E esse é o ponto.

Em geral, a DateAPI suporta apenas o calendário ocidental moderno, possui componentes especificados idiossincraticamente e se comporta de forma inconsistente se você definir campos.

As APIs Calendare GregorianCalendarsão melhores que Date, e as APIs de tempo de Joda de terceiros geralmente eram consideradas as melhores. No Java 8, eles introduziram os java.timepacotes, e agora são a alternativa recomendada.

Stephen C
fonte
Essa solução funciona muito bem, exceto pelo fato de não usar o tempo zero por horas-minutos-segundos-milissegundos, como a nova Data (ano, mês, dia). Então, precisamos de algo como: Calendar cal = Calendar.getInstance (); cal.clear (); cal.set (ano + 1900, mês, data); Data dateRepresentation = cal.getTime ();
Joel Richard Koett
11

Me deparei com essa pergunta como uma duplicata de uma pergunta mais recente, que perguntava qual era a maneira não obsoleta de obter um Dateano, mês e dia específicos.

As respostas aqui até agora dizem usar a Calendarclasse, e isso foi verdade até o lançamento do Java 8. Mas a partir do Java 8, a maneira padrão de fazer isso é:

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

E então, se você realmente precisa de um java.util.Date, pode usar as sugestões nesta pergunta .

Para obter mais informações, consulte a API ou os tutoriais para Java 8.

Kevin Workman
fonte
7

Observe que Calendar.getTime()não é determinístico no sentido de que a parte da hora do dia é padronizada com a hora atual.

Para reproduzir, tente executar o seguinte código algumas vezes:

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

Saída por exemplo:

Sun Mar 07 10:46:21 CET 2010

A execução exata do mesmo código, alguns minutos depois, produz:

Sun Mar 07 10:57:51 CET 2010

Portanto, enquanto set()força os campos correspondentes a corrigir valores, ele perde tempo do sistema para os outros campos. (Testado acima com o Sun jdk6 e jdk7)

Tijn Porcelijn
fonte
2

Como o construtor Date está obsoleto, você pode tentar 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
fonte
1
Essas classes terríveis agora são herdadas, tendo sido suplantadas anos atrás pelas classes java.time definidas no JSR 310. Sugerir Datee Calendarem 2019 são maus conselhos.
Basil Bourque
@BasilBourque obrigado pela sugestão, estou ansioso para atualizá-lo em breve.
Divyanshu Kumar 25/09/19
1

Você pode criar um método como new Date(year,month,date)no seu código usando a Calendarclasse

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á exatamente como o construtor obsoleto de Date

Sudhanshu Dubey
fonte
1
Essas classes terrivelmente problemáticas foram substituídas há anos pelas modernas classes java.time , especificamente Instante ZonedDateTime.
Basil Bourque
A solução acima deve adicionar 1900 ao ano e NÃO deve subtrair 1 do mês e também deve executar cal.clear () após a linha Calendar.getInstance (), para que as horas / minutos / segundos / milissegundos sejam zerados (como eles fazem no construtor Date).
Joel Richard Koett
1

O construtor Date espera anos no formato de anos desde 1900, meses com base em zero, dias com base em um e define horas / minutos / segundos / milissegundos para zero.

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

Portanto, usando a substituição do Calendário (anos com base em zero, meses com base em zero, dias com base em um) para o construtor Data obsoleto, precisamos de 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();

Ou usando o Java 1.8 (que tem ano e zero e meses e dias com base em um):

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

Aqui estão as versões iguais de Data, Calendário e 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
fonte
1
Ambas essas classes terríveis foram suplantados anos pelos java.time classes definidas no JSR 310.
Basil Bourque
Resposta atualizada para mostrar as versões Data, Calendário e Java 1.8.
Joel Richard Koett
0
new GregorianCalendar(1985, Calendar.JANUARY, 1).getTime();

(a maneira pré-Java-8)

Curtis Yallop
fonte
Provavelmente é o melhor que você pode fazer sem o java.time. No entanto, também antes do Java 8 você pode usar java.time, existe um backport: a biblioteca do ThreeTen Backport .
Ole VV