Java: Mengapa konstruktor Tanggal sudah tidak digunakan lagi, dan apa yang harus saya gunakan?

212

Saya berasal dari dunia C #, jadi belum terlalu berpengalaman dengan Java. Saya baru saja diberitahu oleh Eclipse yang Datesudah usang:

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

Mengapa? Dan apa (terutama dalam kasus seperti di atas) yang harus digunakan?

Svish
sumber
37
Saya mengalami kurva belajar yang serupa, juga beralih dari C # ke Java. Hal lain yang menggigit saya adalah bahwa bulan dalam setahun adalah sistem berbasis 0 (0 hingga 11 di mana Jan = 0 dan Desember = 11) tetapi hari-hari dalam bulan berbasis 1 (1 hingga 31). Kepala satu itu!
Paul Sasik
2
@ Paul Sasik, ya, tapi ada Calendar.JANUARI konstanta misalnya, dan satu untuk setiap bulan
Diogo
5
@PaulSasik lol. Ya, Jawa bodoh. Harus beralih dari C # ke Jawa dan OMG rasa sakit dan kesengsaraan.
cbmeeks
8
Pernyataan "lol" yang menjengkelkan tentang Jawa dari orang-orang C # membuat saya tertawa karena .Net mendapat perpustakaan tanggal-waktu yang layak ( Noda Time ) dari pelabuhan perpustakaan Jawa yang luar biasa, Joda-Time .
Basil Bourque

Jawaban:

98

DateKonstruktor tertentu sudah usang, dan Calendarharus digunakan sebagai gantinya. The JavaDocuntuk Tanggal menjelaskan konstruktor yang usang dan bagaimana untuk menggantikan mereka menggunakan Calendar.

Ruben
sumber
25
Kalender membutuhkan objek tambahan dan suka 8 baris lebih banyak kode untuk melakukan hal yang sama yaitu membuat objek Date dari apa yang dapat saya katakan. Ini membingungkan dan tampaknya tidak perlu ketika Anda hanya perlu Tanggal dan bukan variabel yang disesuaikan zona waktu.
G_V
5
FYI, sangat merepotkan tua kelas tanggal-waktu seperti java.util.Date, java.util.Calendar, dan java.text.SimpleDateFormatsekarang warisan , digantikan oleh java.time kelas dibangun ke Jawa 8 dan kemudian. Lihat Tutorial oleh Oracle .
Basil Bourque
250

The java.util.Datekelas tidak benar-benar usang, hanya konstruktor yang, bersama dengan beberapa konstruktor lain / metode yang usang. Itu sudah usang karena penggunaan semacam itu tidak bekerja dengan baik dengan internasionalisasi. The Calendarkelas harus digunakan sebagai gantinya:

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

Lihatlah tanggal Javadoc:

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

BuffaloBuffalo
sumber
2
Ini harus menjadi jawaban terbaik. Terima kasih.
jordaniac89
Dengan mengatakan "tidak bekerja dengan baik dengan internasionalisasi", maksud Anda bahwa untuk Date, Anda tidak dapat menetapkan TimeZone untuk itu? Terima kasih
DiveInto
Tanggal apa yang dilakukan adalah mem-parsing sebuah String, jadi alih-alih sekarang kita harus memilah-milah sebuah String yang berisi tahun, bulan dan hari? Sepertinya banyak kerumitan ekstra untuk sesuatu yang dalam banyak kasus tidak perlu logika dan metode yang rumit seperti itu ditambahkan.
G_V
1
Sebagai tambahan, ini akan mempertimbangkan zona waktu default. Jika kita ingin menentukan zona waktu lain, kita dapat menggunakanCalendar cal = Calendar.getInstance(TimeZone.getTimeZone(<timezone id>));
Vikas Prasad
1
"Kelas Kalender harus digunakan sebagai gantinya" - Untuk Java 8 dan yang lebih baru, java.time.*kelas adalah pilihan yang lebih baik ... jika Anda mengubah kode Anda.
Stephen C
73

tl; dr

LocalDate.of( 1985 , 1 , 1 )

…atau…

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

Detail

Kelas java.util.Date,, java.util.Calendardan java.text.SimpleDateFormatkelas dilarikan terlalu cepat ketika Java pertama kali diluncurkan dan berkembang. Kelas tidak dirancang atau diimplementasikan dengan baik. Perbaikan telah dicoba, dengan demikian penghinaan yang Anda temukan. Sayangnya upaya perbaikan sebagian besar gagal. Anda harus menghindari kelas-kelas ini sama sekali. Mereka digantikan di Java 8 dengan kelas baru.

Masalah dalam Kode Anda

Java.util.Date memiliki tanggal dan porsi waktu. Anda mengabaikan porsi waktu dalam kode Anda. Jadi kelas Date akan mengambil awal hari seperti yang ditentukan oleh zona waktu default JVM Anda dan menerapkan waktu itu ke objek Date. Jadi hasil dari kode Anda akan bervariasi tergantung pada mesin mana yang beroperasi atau zona waktu yang ditetapkan. Mungkin bukan yang Anda inginkan.

Jika Anda hanya menginginkan tanggal, tanpa bagian waktu, seperti tanggal lahir, Anda mungkin tidak ingin menggunakan Dateobjek. Anda mungkin ingin menyimpan hanya serangkaian tanggal, dalam format ISO 8601YYYY-MM-DD . Atau gunakan LocalDateobjek dari Joda-Time (lihat di bawah).

Joda-Time

Hal pertama yang harus dipelajari di Java: Hindari kelas java.util.Date & java.util.Calendar yang terkenal bermasalah yang dibundel dengan Java.

Sebagaimana dicatat dengan benar dalam jawaban oleh user3277382 , gunakan paket Joda-Time atau java.time baru . * Di Java 8.

Contoh Kode dalam 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 );

Dump untuk menghibur ...

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

Saat dijalankan ...

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

waktu java

Dalam hal ini kode untuk java.time hampir identik dengan Joda-Time .

Kami mendapatkan zona waktu ( ZoneId), dan membangun objek tanggal-waktu yang ditugaskan ke zona waktu itu ( ZonedDateTime). Kemudian menggunakan pola Objek Immutable , kami membuat tanggal-waktu baru berdasarkan instan objek yang sama (hitungan nanodetik sejak zaman ) tetapi menetapkan zona waktu lainnya. Terakhir kami mendapatkan LocalDateyang tidak memiliki zona waktu atau zona waktu, meskipun memperhatikan zona waktu berlaku ketika menentukan tanggal tersebut (hari yang baru tiba lebih awal di Oslo daripada di New York misalnya).

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

Tentang java.time

The java.time kerangka dibangun ke Jawa 8 dan kemudian. Kelas-kelas ini menggantikan tua merepotkan warisan kelas tanggal-waktu seperti java.util.Date,Calendar , & SimpleDateFormat.

Proyek Joda-Time , sekarang dalam mode pemeliharaan , menyarankan migrasi ke kelas java.time .

Untuk mempelajari lebih lanjut, lihat Tutorial Oracle . Dan cari Stack Overflow untuk banyak contoh dan penjelasan. Spesifikasi adalah JSR 310 .

Anda dapat bertukar objek java.time secara langsung dengan database Anda. Gunakan driver JDBC yang sesuai dengan JDBC 4.2 atau yang lebih baru. Tidak perlu untuk string, tidak perlu untuk java.sql.*kelas.

Di mana mendapatkan kelas java.time?

Proyek ThreeTen-Extra memperpanjang java.time dengan kelas tambahan. Proyek ini adalah ajang pembuktian untuk kemungkinan penambahan masa depan ke java.time. Anda mungkin menemukan beberapa kelas berguna di sini seperti Interval, YearWeek, YearQuarter, dan lebih .

Basil Bourque
sumber
5
Alat peraga untuk benar-benar menyebutkan penyebab utama dan menyediakan cakupan semua alternatif yang tersedia.
mabi
Ini adalah jawaban yang benar dan kelas Kalender harus dihindari. ThreeTen adalah pilihan terbaik
ant2009
11

Salah satu alasan mengapa konstruktor tidak digunakan adalah bahwa makna parameter tahun bukanlah yang Anda harapkan. Javadoc mengatakan:

Pada JDK versi 1.1, digantikan oleh Calendar.set(year + 1900, month, date).

Perhatikan bahwa bidang tahun adalah jumlah tahun sejak itu 1900 , jadi kode sampel Anda kemungkinan besar tidak akan melakukan apa yang Anda harapkan. Dan itu intinya.

Secara umum, DateAPI hanya mendukung kalender barat modern, memiliki komponen yang ditentukan khusus, dan berperilaku tidak konsisten jika Anda menetapkan bidang.

The Calendardan GregorianCalendarAPI lebih baik dari Date, dan pihak ke-3 Joda-time API umumnya dianggap yang terbaik. Di Java 8, mereka memperkenalkan java.timepaket - paket tersebut, dan ini sekarang merupakan alternatif yang direkomendasikan.

Stephen C
sumber
Solusi ini berfungsi dengan baik, kecuali tidak menggunakan waktu nol selama beberapa jam-menit-detik-milidetik seperti Tanggal baru (tahun, bulan, hari). Jadi kita membutuhkan sesuatu seperti: Kalender cal = Calendar.getInstance (); cal.clear (); cal.set (tahun + 1900, bulan, tanggal); Date dateRepresentation = cal.getTime ();
Joel Richard Koett
11

Saya menemukan pertanyaan ini sebagai duplikat dari pertanyaan yang lebih baru yang menanyakan apa cara non-usang untuk mendapatkanDate pada tahun, bulan, dan hari tertentu.

Jawaban di sini sejauh ini mengatakan untuk menggunakan Calendarkelas, dan itu benar sampai Java 8 keluar. Tetapi pada Java 8, cara standar untuk melakukan ini adalah:

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

Dan kemudian jika Anda benar-benar membutuhkannya java.util.Date, Anda dapat menggunakan saran-saran dalam pertanyaan ini .

Untuk info lebih lanjut, lihat API atau tutorial untuk Java 8.

Kevin Workman
sumber
7

Harap dicatat bahwa tidak Calendar.getTime()ditentukan dalam arti bahwa bagian waktu hari default ke waktu saat ini.

Untuk mereproduksi, coba jalankan kode berikut beberapa kali:

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

Output misalnya:

Sun Mar 07 10:46:21 CET 2010

Menjalankan kode yang sama persis beberapa menit kemudian menghasilkan:

Sun Mar 07 10:57:51 CET 2010

Jadi, sementara set()memaksa bidang terkait untuk mengoreksi nilai, ia membocorkan waktu sistem untuk bidang lainnya. (Diuji di atas dengan Sun jdk6 & jdk7)

Tijn Porcelijn
sumber
2

Karena konstruktor Tanggal sudah tidak digunakan lagi, Anda dapat mencoba kode ini.

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
sumber
1
Kelas-kelas yang mengerikan ini sekarang adalah warisan, yang telah digantikan bertahun-tahun yang lalu oleh kelas java.time yang didefinisikan dalam JSR 310. Menyarankan Datedan Calendarpada 2019 adalah saran yang buruk.
Basil Bourque
@BasilBourque terima kasih atas sarannya, saya berharap segera memperbaruinya.
Divyanshu Kumar
1

Anda dapat membuat metode seperti new Date(year,month,date)dalam kode Anda dengan menggunakan Calendarkelas.

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

Ini akan berfungsi seperti konstruktor yang sudah usang Date

Sudhanshu Dubey
sumber
1
Kelas - kelas yang sangat sulit ini digantikan tahun yang lalu oleh kelas java.time modern , khususnya Instantdan ZonedDateTime.
Basil Bourque
Solusi di atas harus menambahkan 1900 ke tahun, dan TIDAK harus mengurangi 1 dari bulan, dan juga harus menjalankan cal.clear () setelah baris Calendar.getInstance (), sehingga jam / menit / detik / milidetik mendapatkan nol (seperti mereka lakukan di konstruktor Tanggal).
Joel Richard Koett
1

Tanggal konstruktor mengharapkan tahun dalam format tahun sejak 1900, bulan berbasis nol, hari berbasis satu, dan menetapkan jam / menit / detik / milidetik ke nol.

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

Jadi menggunakan penggantian Kalender (tahun berbasis nol, bulan berbasis nol, hari berbasis satu) untuk konstruktor Tanggal yang sudah tidak digunakan lagi, kita memerlukan sesuatu seperti:

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

Atau menggunakan Java 1.8 (yang memiliki tahun berbasis nol, dan bulan dan hari berbasis satu):

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

Berikut adalah versi yang sama dari Tanggal, Kalender, dan 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
sumber
1
Kedua kelas mengerikan ini digantikan tahun yang lalu oleh kelas java.time yang didefinisikan dalam JSR 310.
Basil Bourque
Jawaban yang diperbarui untuk menampilkan tanggal, Kalender, dan versi Java 1.8.
Joel Richard Koett
0
new GregorianCalendar(1985, Calendar.JANUARY, 1).getTime();

(cara pra-Jawa-8)

Curtis Yallop
sumber
Itu mungkin yang terbaik yang bisa Anda lakukan tanpa java.time. Namun juga sebelum Java 8 Anda dapat menggunakan java.time, ada backport: pustaka ThreeTen Backport .
Ole VV