Java: Чому конструктор дат застарів, і що я використовую замість цього?


212

Я родом із світу C #, тому ще не надто досвідчений з Java. Мені щойно сказали Eclipse, який Dateбув застарілим:

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

Чому? І що (особливо у подібних випадках) замість цього використовувати?


37
Я переживаю подібну криву навчання, також переходячи від C # до Java. Інше, що мене вразило, - це те, що місяць року є системою, заснованою на 0 (від 11 до 11 січня = 0 і грудень = 11), але дні місяця - 1 (31). Голова на цю!
Пол Сасик

2
@Paul Sasik, так, але є календар.ЯНЮАРІЙСЬКА константа, наприклад, і один на кожен місяць
Diogo

5
@PaulSasik lol. Так, дурна Ява. Довелося перейти з C # на Java та OMG від болю і нещастя.
cbmeeks


8
Смутні "lol" зауваження щодо Java від C # людей змусили мене сміятися, тому що .Net отримав свою гідну бібліотеку часу ( Noda Time ) з порту чудової бібліотеки Java Joda-Time .
Василь Бурк

Відповіді:


98

Конкретний Dateконструктор застарілий, і його Calendarслід використовувати замість цього. JavaDocДля дати описує , які конструктори є застарілими і як замінити їх використання Calendar.


25
Для календаря потрібен додатковий об'єкт і, як 8 рядків коду, щоб зробити те саме, що створити об'єкт Date з того, що я можу сказати. Це заплутано і здається непотрібним, коли вам просто потрібна змінна Date, а не змінна часового поясу.
G_V

5
FYI, жахливі старі класи дату, такі як 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


2
Це має бути найкращою відповіддю. Дякую.
jordaniac89

Кажучи, що "не працює добре з інтернаціоналізацією", ви маєте на увазі, що для Date ви не можете призначити для неї TimeZone? Спасибі
DiveInto

Що було зроблено, було проаналізовано рядок, тож замість цього тепер нам належить підстрочити рядок, що містить рік, місяць і день? Здається, багато зайвих клопотів щодо того, що в більшості випадків не потребує такої складної логіки та методів, що додаються до нього.
G_V

1
Додамо, це врахує часовий пояс за замовчуванням. Якщо ми хочемо вказати будь-який інший часовий пояс, ми можемо використовуватиCalendar cal = Calendar.getInstance(TimeZone.getTimeZone(<timezone id>));
Vikas Prasad

1
"Клас календаря слід використовувати замість цього" - Для 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 8601 форматіYYYY-MM-DD . Або використовувати LocalDateоб’єкт Joda-Time (див. Нижче).

Joda-Time

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

Як правильно зазначено у відповіді від користувача3277382 , використовуйте або 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 майже ідентичний коду -Time .

Ми отримуємо часовий пояс ( ZoneId) і будуємо об'єкт дати-часу, призначений цьому часовому поясу ( ZonedDateTime). Тоді, використовуючи шаблон Immutable Objects , ми створюємо нові дати-дати на основі того самого моменту старого об'єкта (кількість наносекунд з епохи ), але присвоюємо інший часовий пояс. Нарешті, ми отримуємо число, 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 . І шукайте переповнення стека за багатьма прикладами та поясненнями. Специфікація - 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
Реквізити для фактичного згадування першопричини та забезпечення висвітлення всіх доступних альтернатив.
mabi

Це правильна відповідь і слід уникати заняття з календаря. ThreeTen - найкращий варіант
ant2009

11

Однією з причин того, що конструктор застарів, є те, що значення параметра року - це не те, що ви очікували. Явадок каже:

Станом на версію 1.1 JDK, замінено на Calendar.set(year + 1900, month, date).

Зауважте, що поле року - це кількість років з тих пір 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)




2

Оскільки конструктор дат застарілий, ви можете спробувати цей код.

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

Конструктор дат очікує років у форматі років, починаючи з 1900 року, з нульовими місяцями, днями, що базуються на одній, і встановлює години / хвилини / секунди / мілісекунди до нуля.

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

Отож, використовуючи заміну календаря (нульові роки, нульові місяці, дні на основі однієї дати) для застарілого конструктора дат, нам потрібно щось на зразок:

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.
Джоел Річард Коетт

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.