Java: почему конструктор Date устарел и что я вместо него использую?

212

Я из мира C #, поэтому еще не слишком разбираюсь в Java. Eclipse только что сказал мне, что Dateон устарел:

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

Зачем? И что (особенно в случаях как выше) должно использоваться вместо этого?

Svish
источник
37
Я испытываю похожую кривую обучения, также переходя с C # на Java. Еще одна вещь, которая меня поразила, это то, что месяц является системой, основанной на 0 (от 0 до 11, где январь = 0 и декабрь = 11), но дни месяца основаны на 1 (от 1 до 31). Heads на этом!
Пол Сасик
2
@Paul Sasik, да, но есть константа Calendar.JANUARY, например, и по одному на каждый месяц
Diogo,
5
@PaulSasik лол. Да, тупая Ява. Пришлось переключаться с C # на Java и OMG страдать и страдать.
cbmeeks
8
Смехотворные «смеющиеся» замечания о Java от C # заставили меня смеяться, потому что .Net получил свою приличную библиотеку даты и времени ( Noda Time ) из порта превосходной библиотеки Java Joda-Time .
Василий Бурк

Ответы:

98

Конкретный Dateконструктор устарел и Calendarдолжен использоваться вместо него. Параметр JavaDocfor Date описывает, какие конструкторы устарели и как их заменить с помощью Calendar.

Ruben
источник
25
Календарь требует дополнительного объекта и, как и 8 строк кода, делает то же самое, что и создание объекта Date из того, что я могу сказать. Это сбивает с толку и кажется ненужным, когда вам просто нужна дата, а не переменная с часовым поясом.
G_V
5
К вашему сведению, ужасно неприятные старые классы даты и времени, такие как java.util.Date, java.util.Calendarи java.text.SimpleDateFormatтеперь унаследованные , вытеснены классами java.time, встроенными в Java 8 и более поздние версии . Смотрите Учебник по Oracle .
Василий Бурк
250

java.util.DateКласс фактически не рекомендуется, так что конструктор, наряду с несколькими другими конструкторами / методы устарели. Это устарело, потому что такое использование плохо работает с интернационализацией. CalendarКласс следует использовать вместо этого:

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

Посмотрите на дату Javadoc:

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

BuffaloBuffalo
источник
2
Это должен быть лучший ответ. Спасибо.
jordaniac89
Говоря «плохо работает с интернационализацией», вы имеете в виду, что для Date вы не можете назначить TimeZone для него? Спасибо
DiveInto
Какая дата выполняла синтаксический анализ строки, поэтому вместо этого мы должны теперь подставить строку, содержащую год, месяц и день? Похоже, много лишних хлопот за то, что в большинстве случаев не требует такой сложной логики и методов, добавленных к нему.
G_V
1
Просто добавим, что будет учитываться часовой пояс по умолчанию. Если мы хотим указать любой другой часовой пояс, мы можем использоватьCalendar cal = Calendar.getInstance(TimeZone.getTimeZone(<timezone id>));
Викас Прасад
1
«Вместо этого следует использовать класс Calendar» - для Java 8 и более поздних java.time.*версий классы являются лучшим вариантом ... если вы изменяете свой код.
Стивен С.
73

ТЛ; др

LocalDate.of( 1985 , 1 , 1 )

…или…

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

подробности

В java.util.Date, java.util.Calendarи java.text.SimpleDateFormatклассы были доставлены слишком быстро , когда Java первого запуска и эволюционировал. Классы не были хорошо разработаны или реализованы. Попытки улучшения были предприняты, поэтому вы устали. К сожалению, попытки улучшения в основном не увенчались успехом. Вы должны избегать этих классов в целом. Они вытесняются в Java 8 новыми классами.

Проблемы в вашем коде

Java.util.Date имеет как дату, так и часть времени. Вы проигнорировали часть времени в своем коде. Таким образом, класс Date будет принимать начало дня в соответствии с часовым поясом JVM по умолчанию и применять это время к объекту Date. Таким образом, результаты вашего кода будут зависеть от того, на каком компьютере он работает или какой часовой пояс установлен. Наверное, не то, что вы хотите.

Если вам нужна только дата, без временной части, например, для даты рождения, вы можете не использовать Dateобъект. Вы можете сохранить только строку даты в формате ISO 8601YYYY-MM-DD . Или используйте LocalDateобъект из Joda-Time (см. Ниже).

Joda времени

Первое, чему нужно научиться на Java: избегайте печально известных классов java.util.Date & java.util.Calendar в комплекте с Java.

Как правильно отмечено в ответе user3277382 , используйте либо Joda-Time, либо новый пакет java.time. * В Java 8.

Пример кода в 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 );

Дамп на консоль…

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

Когда беги…

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

В этом случае код для java.time практически идентичен коду Joda-Time .

Мы получаем часовой пояс ( ZoneId) и создаем объект даты и времени, назначенный этому часовому поясу ( ZonedDateTime). Затем, используя шаблон « Неизменяемые объекты» , мы создаем новое время-дату, основанное на том же мгновении старого объекта (количество наносекунд с начала эпохи ), но назначенном другом часовом поясе. Наконец, мы получаем a, у LocalDateкоторого нет ни времени суток, ни часового пояса, хотя обратите внимание, что часовой пояс применяется при определении этой даты (например, новый день наступает раньше в Осло, чем в Нью-Йорке ).

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

О java.time

Java.time каркас встроен в Java 8 и более поздних версий. Эти классы вытеснять неприятные старые устаревшие классы даты и времени , такие как java.util.Date, Calendar, и SimpleDateFormat.

Проект Joda-Time , находящийся сейчас в режиме обслуживания , рекомендует перейти на классы java.time .

Чтобы узнать больше, смотрите Oracle Tutorial . И поиск переполнения стека для многих примеров и объяснений. Спецификация JSR 310 .

Вы можете обмениваться объектами java.time напрямую с вашей базой данных. Используйте драйвер JDBC, соответствующий JDBC 4.2 или более поздней версии . Нет необходимости в строках, нет необходимости в java.sql.*классах.

Где взять классы java.time?

  • Java SE 8 , Java SE 9 , Java SE 10 и более поздние версии
    • Встроенный.
    • Часть стандартного Java API со встроенной реализацией.
    • Java 9 добавляет некоторые незначительные функции и исправления.
  • Java SE 6 и Java SE 7
    • Большая часть функциональности java.time перенесена на Java 6 и 7 в ThreeTen-Backport .
  • Android
    • Более поздние версии Android связывают реализации классов java.time.
    • Для более ранних версий Android (<26) проект ThreeTenABP адаптирует ThreeTen-Backport (упомянутый выше). Смотрите Как использовать ThreeTenABP… .

Проект ThreeTen-Extra расширяет java.time дополнительными классами. Этот проект является полигоном для возможных будущих дополнений к java.time. Вы можете найти некоторые полезные классы здесь , такие как Interval, YearWeek, YearQuarter, и более .

Базилик Бурк
источник
5
Рекомендуются для фактического упоминания основной причины и обеспечения охвата всех доступных альтернатив.
Маби
Это правильный ответ, и класс Календаря следует избегать. ThreeTen - лучший вариант
ant2009
11

Одна из причин, по которой конструктор устарел, заключается в том, что значение параметра year не соответствует ожидаемому. Javadoc говорит:

Начиная с версии 1.1 JDK, заменено на Calendar.set(year + 1900, month, date).

Обратите внимание, что поле year - это количество лет с тех пор 1900, поэтому ваш пример кода, скорее всего, не будет делать то, что вы ожидаете. И в этом все дело.

В общем, DateAPI поддерживает только современный западный календарь, имеет специфически определенные компоненты и ведет себя непоследовательно, если вы задаете поля.

CalendarИ GregorianCalendarинтерфейсы лучше Date, а третьи сторонние API , Joda времени , как правило , считается лучшим. В Java 8 они представили java.timeпакеты, и теперь они являются рекомендуемой альтернативой.

Стивен С
источник
Это решение прекрасно работает, за исключением того, что оно не использует нулевое время в течение часов, минут, секунд, миллисекунд, как новая дата (год, месяц, день). Поэтому нам нужно что-то вроде: Calendar cal = Calendar.getInstance (); cal.clear (); cal.set (год + 1900, месяц, дата); Date dateRepresentation = cal.getTime ();
Джоэл Ричард Кётт
11

Я столкнулся с этим вопросом как с дубликатом нового вопроса, в котором был задан вопрос о том, каким должен быть недревесный способ получения Dateопределенного года, месяца и дня.

В приведенных здесь ответах сказано, что нужно использовать этот Calendarкласс, и это было правдой до выхода Java 8. Но на Java 8 стандартный способ сделать это:

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

И тогда, если вам действительно это нужно java.util.Date, вы можете воспользоваться предложениями в этом вопросе .

Для получения дополнительной информации, ознакомьтесь с API или учебными пособиями по Java 8.

Кевин Воркман
источник
7

Обратите внимание, что Calendar.getTime()это недетерминировано в том смысле, что дневная часть по умолчанию принимает текущее время.

Чтобы воспроизвести, попробуйте выполнить следующий код пару раз:

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

Выход например:

Sun Mar 07 10:46:21 CET 2010

Выполнение точно такого же кода через пару минут приводит к:

Sun Mar 07 10:57:51 CET 2010

Таким образом, в то время как set()вынуждает соответствующие поля корректировать значения, это приводит к утечке системного времени для других полей. (Проверено выше с Sun jdk6 и jdk7)

Tijn Porcelijn
источник
2

Поскольку конструктор Date устарел, вы можете попробовать этот код.

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
Дивьяншу Кумар
источник
1
Эти ужасные классы теперь унаследованы, они были вытеснены несколько лет назад классами java.time, определенными в JSR 310. Предлагать Dateи Calendarв 2019 году плохой совет.
Базилик Бурк
@BasilBourque спасибо за предложение, я с нетерпением жду, чтобы обновить его в ближайшее время.
Дивьяншу Кумар,
1

Вы можете создать метод, как new Date(year,month,date)в вашем коде, используя Calendarкласс.

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

Это будет работать так же, как устаревший конструктор Date

Судханшу Дубей
источник
1
Эти ужасно неприятные классы были вытеснены несколько лет назад современными классами java.time , в частности, Instantи ZonedDateTime.
Василий Бурк
Приведенное выше решение должно добавить 1900 к году и НЕ должно вычитать 1 из месяца, а также должно запускать cal.clear () после строки Calendar.getInstance (), так что часы / минуты / секунды / миллисекунды обнуляются (как они делают в конструкторе Date).
Джоэл Ричард Кётт
1

Конструктор Date ожидает годы в формате лет с 1900 года, месяцев с нулями, дней с единицами и устанавливает часы / минуты / секунды / миллисекунды в ноль.

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

Таким образом, используя замену Календаря (годы с нулями, месяцы с нулями, дни с единицами) для устаревшего конструктора Date, нам нужно что-то вроде:

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

Или используя Java 1.8 (в котором год начинается с нуля, а месяцы и дни равны единице):

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

Вот равные версии Date, Calendar и 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));
Джоэл Ричард Кётт
источник
1
Оба эти ужасных класса были вытеснены несколько лет назад классами java.time, определенными в JSR 310.
Василий Бурк
Обновленный ответ для отображения даты, календаря и версий Java 1.8.
Джоэл Ричард Кётт
0
new GregorianCalendar(1985, Calendar.JANUARY, 1).getTime();

(путь до Java-8)

Кертис Яллоп
источник
Это, вероятно, лучшее, что вы можете сделать без java.time. Однако также до Java 8 вы можете использовать java.time, есть обратный порт: библиотека ThreeTen Backport .
Оле В.В.