Di java.util.Calendar
, Januari didefinisikan sebagai bulan 0, bukan bulan 1. Apakah ada alasan khusus untuk itu?
Saya telah melihat banyak orang bingung tentang itu ...
Di java.util.Calendar
, Januari didefinisikan sebagai bulan 0, bukan bulan 1. Apakah ada alasan khusus untuk itu?
Saya telah melihat banyak orang bingung tentang itu ...
Jawaban:
Itu hanya bagian dari kekacauan mengerikan yang merupakan API tanggal / waktu Java. Mendaftar apa yang salah dengan itu akan memakan waktu sangat lama (dan saya yakin saya tidak tahu setengah dari masalahnya). Harus diakui, bekerja dengan tanggal dan waktu memang sulit, tetapi toh tetap saja.
Bantulah diri Anda sendiri dan gunakan Joda Time , atau mungkin JSR-310 .
EDIT: Adapun alasan mengapa - seperti yang disebutkan dalam jawaban lain, itu bisa jadi karena API C lama, atau hanya perasaan umum memulai semuanya dari 0 ... kecuali hari-hari itu dimulai dengan 1, tentu saja. Saya ragu apakah ada orang di luar tim implementasi asli yang benar-benar dapat menyatakan alasan - tetapi sekali lagi, saya mendorong pembaca untuk tidak terlalu khawatir tentang mengapa keputusan yang buruk diambil, untuk melihat keseluruhan keseluruhan kekesalan java.util.Calendar
dan menemukan sesuatu yang lebih baik.
Satu titik yang merupakan mendukung menggunakan 0 berbasis indeks adalah bahwa hal itu membuat hal-hal seperti "array dari nama" lebih mudah:
// I "know" there are 12 months
String[] monthNames = new String[12]; // and populate...
String name = monthNames[calendar.get(Calendar.MONTH)];
Tentu saja, ini gagal segera setelah Anda mendapatkan kalender dengan 13 bulan ... tetapi setidaknya ukuran yang ditentukan adalah jumlah bulan yang Anda harapkan.
Ini bukan yang baik alasan, tapi itu sebuah alasan ...
EDIT: Sebagai komentar, minta beberapa ide tentang apa yang saya pikir salah dengan Tanggal / Kalender:
Date
dan Calendar
sebagai hal yang berbeda, tetapi pemisahan nilai "lokal" vs "dikategorikan" tidak ada, seperti tanggal / waktu vs tanggal vs waktuDate.toString()
pelaksanaan yang selalu menggunakan sistem zona waktu lokal (yang ini bingung banyak pengguna Stack Overflow sebelum sekarang)Karena melakukan matematika dengan berbulan-bulan jauh lebih mudah.
1 bulan setelah Desember adalah Januari, tetapi untuk mengetahui hal ini secara normal Anda harus mengambil angka bulan dan melakukan matematika
12 + 1 = 13 // What month is 13?
Aku tahu! Saya dapat memperbaiki ini dengan cepat menggunakan modulus 12.
(12 + 1) % 12 = 1
Ini berfungsi dengan baik selama 11 bulan hingga November ...
(11 + 1) % 12 = 0 // What month is 0?
Anda dapat membuat semua ini berfungsi lagi dengan mengurangi 1 sebelum Anda menambahkan bulan, lalu lakukan modulus Anda dan akhirnya tambahkan 1 kembali ... alias selesaikan masalah yang mendasarinya.
((11 - 1 + 1) % 12) + 1 = 12 // Lots of magical numbers!
Sekarang mari kita pikirkan masalah dengan bulan 0 - 11.
(0 + 1) % 12 = 1 // February
(1 + 1) % 12 = 2 // March
(2 + 1) % 12 = 3 // April
(3 + 1) % 12 = 4 // May
(4 + 1) % 12 = 5 // June
(5 + 1) % 12 = 6 // July
(6 + 1) % 12 = 7 // August
(7 + 1) % 12 = 8 // September
(8 + 1) % 12 = 9 // October
(9 + 1) % 12 = 10 // November
(10 + 1) % 12 = 11 // December
(11 + 1) % 12 = 0 // January
Semua bulan bekerja sama dan bekerja di sekitar tidak perlu.
((11 - 1 + 1) % 12) + 1 = 12
hanya (11 % 12) + 1
untuk bulan 1..12 Anda hanya perlu menambahkan 1 setelah melakukan modulo. Tidak perlu sihir.
Bahasa berbasis C menyalin C sampai tingkat tertentu. The tm
Struktur (didefinisikan dalam time.h
) memiliki bidang bilangan bulat tm_mon
dengan (berkomentar) berbagai 0-11.
Bahasa berbasis C mulai array di indeks 0. Jadi ini nyaman untuk menghasilkan string dalam array nama bulan, dengan tm_mon
sebagai indeks.
Ada banyak jawaban untuk ini, tetapi saya tetap akan memberikan pandangan saya tentang masalah ini. Alasan di balik perilaku aneh ini, seperti yang dinyatakan sebelumnya, berasal dari POSIX C di time.h
mana bulan disimpan dalam sebuah int dengan kisaran 0-11. Untuk menjelaskan alasannya, lihatlah seperti ini; tahun dan hari dianggap angka dalam bahasa lisan, tetapi bulan memiliki nama sendiri. Jadi karena Januari adalah bulan pertama akan disimpan sebagai offset 0, elemen array pertama. monthname[JANUARY]
akan "January"
. Bulan pertama dalam tahun tersebut adalah elemen array bulan pertama.
Angka hari di sisi lain, karena mereka tidak memiliki nama, menyimpannya dalam sebuah int sebagai 0-30 akan membingungkan, menambahkan banyak day+1
instruksi untuk menghasilkan dan, tentu saja, rentan terhadap banyak bug.
Yang sedang berkata, inkonsistensi membingungkan, terutama dalam javascript (yang juga telah mewarisi "fitur" ini), bahasa scripting di mana ini harus diabstraksi jauh dari langague.
TL; DR : Karena bulan memiliki nama dan hari dalam sebulan tidak.
Di Java 8, ada Date / Time API JSR 310 baru yang lebih waras. Lead spec sama dengan penulis utama JodaTime dan mereka memiliki banyak konsep dan pola yang sama.
Saya akan mengatakan kemalasan. Array mulai dari 0 (semua orang tahu itu); bulan-bulan dalam setahun adalah sebuah array, yang membuat saya percaya bahwa beberapa insinyur di Sun tidak mau repot-repot memasukkan sedikit kebaikan ini ke dalam kode Java.
Mungkin karena "struct tm" C melakukan hal yang sama.
Karena programmer terobsesi dengan indeks berbasis 0. OK, ini sedikit lebih rumit dari itu: lebih masuk akal ketika Anda bekerja dengan logika tingkat rendah untuk menggunakan pengindeksan berbasis 0. Tetapi pada umumnya, saya masih akan tetap dengan kalimat pertama saya.
Secara pribadi, saya menganggap keanehan API kalender Jawa sebagai indikasi bahwa saya harus menceraikan diri dari pola pikir Gregorian-centric dan mencoba memprogram lebih agnostik dalam hal itu. Secara khusus, saya belajar sekali lagi untuk menghindari konstanta hardcode untuk hal-hal seperti berbulan-bulan.
Manakah dari berikut ini yang lebih benar?
if (date.getMonth() == 3) out.print("March");
if (date.getMonth() == Calendar.MARCH) out.print("March");
Ini menggambarkan satu hal yang membuat saya sedikit jengkel tentang Joda Time - itu mungkin mendorong programmer untuk berpikir dalam hal konstanta hardcode. (Hanya sedikit, meskipun. Ini bukan seolah-olah Joda memaksa programmer untuk memprogram dengan buruk.)
Bagi saya, tidak ada yang menjelaskannya lebih baik daripada mindpro.com :
Gotcha
java.util.GregorianCalendar
memiliki bug dan Gotcha jauh lebih sedikit daripadaold java.util.Date
kelas tetapi masih ada piknik.Seandainya ada programmer ketika Daylight Saving Time pertama kali diusulkan, mereka akan memveto itu sebagai gila dan tidak bisa dilaksanakan. Dengan penghematan siang hari, ada ambiguitas mendasar. Pada musim gugur ketika Anda menyetel jam kembali satu jam pada jam 2 pagi ada dua contoh waktu yang keduanya disebut jam 1:30 pagi waktu setempat. Anda dapat membedakannya hanya jika Anda merekam apakah Anda bermaksud menghemat siang hari atau waktu standar dengan pembacaan.
Sayangnya, tidak ada cara untuk mengatakan
GregorianCalendar
yang Anda maksudkan. Anda harus menggunakan waktu setempat untuk memberitahukannya dengan TimeZone UTC dummy untuk menghindari ambiguitas. Pemrogram biasanya menutup mata mereka terhadap masalah ini dan hanya berharap tidak ada yang melakukan apa pun selama jam ini.Bug milenium. Bug masih belum keluar dari kelas Kalender. Bahkan di JDK (Java Development Kit) 1.3 ada bug tahun 2001. Pertimbangkan kode berikut:
GregorianCalendar gc = new GregorianCalendar(); gc.setLenient( false ); /* Bug only manifests if lenient set false */ gc.set( 2001, 1, 1, 1, 0, 0 ); int year = gc.get ( Calendar.YEAR ); /* throws exception */
Bug menghilang pada pukul 07:00 pada 2001/01/01 untuk MST.
GregorianCalendar
dikendalikan oleh raksasa tumpukan konstanta sihir int yang tidak diketik. Teknik ini benar-benar menghancurkan harapan pengecekan kesalahan waktu kompilasi. Misalnya untuk mendapatkan bulan yang Anda gunakanGregorianCalendar. get(Calendar.MONTH));
GregorianCalendar
memilikiGregorianCalendar.get(Calendar.ZONE_OFFSET)
penghematan mentah dan siang hariGregorianCalendar. get( Calendar. DST_OFFSET)
, tetapi tidak ada cara untuk mendapatkan offset zona waktu yang sebenarnya digunakan. Anda harus mendapatkan keduanya secara terpisah dan menambahkannya bersama.
GregorianCalendar.set( year, month, day, hour, minute)
tidak mengatur detik ke 0.
DateFormat
danGregorianCalendar
tidak bertautan dengan benar. Anda harus menentukan Kalender dua kali, sekali secara tidak langsung sebagai Tanggal.Jika pengguna belum mengonfigurasi zona waktunya dengan benar, defaultnya akan diam-diam ke PST atau GMT.
Di GregorianCalendar, Bulan diberi nomor mulai Januari = 0, bukan 1 seperti yang dilakukan orang lain di planet ini. Namun hari-hari dimulai pada 1 seperti halnya hari dalam seminggu dengan Minggu = 1, Senin = 2, ... Sabtu = 7. Namun DateFormat. parse berperilaku dengan cara tradisional dengan Januari = 1.
java.util.Month
Java memberi Anda cara lain untuk menggunakan 1 indeks berbasis selama berbulan-bulan. Gunakan java.time.Month
enum. Satu objek sudah ditentukan untuk masing-masing dua belas bulan. Mereka memiliki nomor yang ditetapkan untuk masing-masing 1-12 untuk Januari-Desember; panggilan getValue
untuk nomor tersebut.
Manfaatkan Month.JULY
(Memberi Anda 7) alih-alih Calendar.JULY
(Memberi Anda 6).
(import java.time.*;)
Month.FEBRUARY.getValue() // February → 2.
2
The Answer oleh Jon Skeet benar.
Sekarang kami memiliki pengganti modern untuk kelas lama tanggal waktu warisan sebelumnya: kelas java.time .
java.time.Month
Di antara kelas-kelas itu adalah enum . Enum membawa satu atau lebih objek yang telah ditetapkan, objek yang secara otomatis dipakai saat kelas memuat. Pada kami memiliki selusin benda seperti itu, masing-masing diberi nama: , , , dan sebagainya. Masing-masing adalah konstanta kelas. Anda dapat menggunakan dan meneruskan objek-objek ini di mana saja dalam kode Anda. Contoh:Month
Month
JANUARY
FEBRUARY
MARCH
static final public
someMethod( Month.AUGUST )
Untungnya, mereka memiliki penomoran yang waras, 1-12 di mana 1 adalah Januari dan 12 adalah Desember.
Dapatkan Month
objek untuk nomor bulan tertentu (1-12).
Month month = Month.of( 2 ); // 2 → February.
Ke arah lain, tanyakan Month
objek untuk nomor bulannya.
int monthNumber = Month.FEBRUARY.getValue(); // February → 2.
Banyak metode praktis lainnya di kelas ini, seperti mengetahui jumlah hari dalam setiap bulan . Kelas bahkan dapat menghasilkan nama lokal bulan itu.
Anda bisa mendapatkan nama lokal bulan itu, dalam berbagai panjang atau singkatan.
String output =
Month.FEBRUARY.getDisplayName(
TextStyle.FULL ,
Locale.CANADA_FRENCH
);
février
Juga, Anda harus melewatkan objek enum ini di sekitar basis kode Anda daripada hanya angka integer . Melakukannya memberikan keamanan jenis, memastikan rentang nilai yang valid, dan membuat kode Anda lebih banyak mendokumentasikan diri. Lihat Tutorial Oracle jika tidak terbiasa dengan fasilitas enum yang sangat kuat di Jawa.
Anda juga dapat menemukan kelas Year
dan berguna YearMonth
.
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
, & java.text.SimpleDateFormat
.
Proyek Joda-Time , sekarang dalam mode pemeliharaan , menyarankan migrasi ke java.time.
Untuk mempelajari lebih lanjut, lihat Tutorial Oracle . Dan cari Stack Overflow untuk banyak contoh dan penjelasan. Spesifikasi adalah JSR 310 .
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 .
Itu tidak persis didefinisikan sebagai nol per se, itu didefinisikan sebagai Kalender. Januari. Ini adalah masalah menggunakan int sebagai konstanta alih-alih enum. Calendar.January == 0.
Karena menulis bahasa lebih sulit daripada yang terlihat, dan menangani waktu pada khususnya jauh lebih sulit daripada yang dipikirkan kebanyakan orang. Untuk sebagian kecil masalah (pada kenyataannya, bukan Jawa), lihat video YouTube "Masalah dengan Waktu & Zona Waktu - Computerphile" di https://www.youtube.com/watch?v=-5wpm-gesOY . Jangan terkejut jika kepala Anda jatuh karena tertawa kebingungan.
Selain jawaban kemalasan DannySmurf, saya akan menambahkan bahwa itu mendorong Anda untuk menggunakan konstanta, seperti Calendar.JANUARY
.
Karena semuanya dimulai dengan 0. Ini adalah fakta dasar pemrograman di Java. Jika ada satu hal yang menyimpang dari itu, maka itu akan menyebabkan seluruh kebingungan. Jangan berdebat pembentukan mereka dan kode dengan mereka.