CodeGym /Blog Java /rawak /Bagaimana untuk tidak tersesat dalam masa: DateTime dan K...
John Squirrels
Tahap
San Francisco

Bagaimana untuk tidak tersesat dalam masa: DateTime dan Kalendar

Diterbitkan dalam kumpulan
Hai! Hari ini kami akan mula bekerja dengan jenis data baharu yang belum pernah kami temui sebelum ini, iaitu tarikh. Bagaimana untuk tidak tersesat dalam masa: DateTime dan Kalendar - 1Saya rasa saya tidak perlu menjelaskan apa itu tarikh. :) Pada dasarnya, kita boleh menyimpan tarikh dan masa semasa dalam Java String biasa.

public class Main {
   public static void main(String[] args) {

       String date = "June 11, 2018";
       System.out.println(date);
   }
}
Tetapi pendekatan ini mempunyai banyak kelemahan. Kelas Stringdireka bentuk untuk berfungsi dengan teks, dan kaedahnya sesuai untuk tugas ini. Jika kita perlu memanipulasi tarikh dalam beberapa cara (tambah 2 jam, sebagai contoh), Stringtidak berfungsi dengan baik. Atau jika kita ingin memaparkan tarikh dan masa semasa apabila program disusun. Stringtidak membantu di sini sama ada: pada masa anda menulis kod dan menjalankannya, masa akan berubah dan konsol akan memaparkan maklumat yang salah. Itulah sebabnya pencipta Java menyediakan beberapa kelas untuk bekerja dengan tarikh dan masa. Yang pertama ini ialahjava.util.Date

Kelas tarikh

Kami menyatakan nama penuhnya, kerana pakej Java lain mempunyai kelas java.sql.Date. Jangan campur adukkan! Perkara pertama yang anda perlu tahu mengenainya ialah ia menyimpan tarikh sebagai bilangan milisaat yang telah berlalu sejak 1 Januari 1970. Sistem masa ini malah mempunyai namanya sendiri: " Unix-time " Pendekatan yang agak menarik, akan' awak setuju? :) Perkara kedua yang patut diingat ialah ini: Jika anda mencipta Dateobjek menggunakan pembina lalai, hasilnya mewakili tarikh dan masa semasa pada masa objek itu dicipta . Ingat bahawa kami mengatakan bahawa tarikh yang diwakili sebagai Stringakan bergelut dengan tugas sedemikian? Kelas Datemengendalikannya dengan mudah.

public class Main {
   public static void main(String[] args) {

       Date date = new Date();
       System.out.println(date);
   }
}
Jalankan kod ini beberapa kali, dan anda akan melihat masa berubah berulang kali. :) Ini mungkin kerana masa disimpan sebagai milisaat: ia adalah unit masa yang sangat kecil, jadi hasilnya sangat tepat. Kelas Datepembina lain: anda boleh melepasi bilangan milisaat yang tepat sejak 00:00 pada 1 Januari 1970 hingga tarikh yang diperlukan, dan objek tarikh yang sepadan akan dibuat:

public class Main {
   public static void main(String[] args) {

       Date date = new Date(1212121212121L);
       System.out.println(date);
   }
}
Keluaran konsol: Jum 30 Mei 04:20:12 GMT 2008 Kami mendapat 30 Mei 2008. "Jumaat" menunjukkan hari dalam seminggu (Jumaat, duh), dan GMT ialah zon waktu (Masa Purata Greenwich). Milisaat diluluskan sebagai longs, kerana bilangan milisaat biasanya tidak sesuai dengan int. Jadi, apakah operasi dengan tarikh yang mungkin perlu kami lakukan? Nah, yang paling jelas, sudah tentu, adalah perbandingan . Untuk menentukan sama ada satu tarikh datang sebelum atau selepas yang lain. Ini boleh dilakukan dalam beberapa cara. Sebagai contoh, anda boleh memanggil Date.getTime()kaedah, yang mengembalikan bilangan milisaat yang telah berlalu sejak tengah malam pada 1 Januari 1970. Cuma panggilnya pada dua objek Tarikh dan bandingkan hasilnya:

public class Main {
   public static void main(String[] args) {

       Date date1 = new Date();

       Date date2 = new Date();

       System.out.println((date1.getTime() > date2.getTime())?
               "date1 is later than date2" : "date1 is earlier than date2");
   }
}
Output: date1 lebih awal daripada date2 Tetapi terdapat juga cara yang lebih mudah, iaitu dengan menggunakan kaedah khas yang disediakan oleh kelas Date: before(), after()dan equals(). Kesemuanya mengembalikan nilai boolean. Kaedah before()menyemak sama ada tarikh kami lebih awal daripada tarikh yang diluluskan sebagai hujah:

public class Main {
   public static void main(String[] args) throws InterruptedException {

       Date date1 = new Date();

       Thread.sleep(2000);// Suspend the program for 2 seconds
       Date date2 = new Date();

       System.out.println(date1.before(date2));
   }
}
Output konsol: benar Begitu juga, after()kaedah menyemak untuk melihat sama ada tarikh kami lewat daripada tarikh yang diluluskan sebagai hujah:

public class Main {
   public static void main(String[] args) throws InterruptedException {

       Date date1 = new Date();

       Thread.sleep(2000);// Suspend the program for 2 seconds
       Date date2 = new Date();

       System.out.println(date1.after(date2));
   }
}
Output konsol: palsu Dalam contoh kami, kami "menidurkan program" selama 2 saat, supaya kedua-dua tarikh itu dijamin berbeza. Pada komputer pantas, masa antara penciptaan date1dan date2boleh kurang daripada satu milisaat, menyebabkan kedua-duanya before()dan after()kembali palsu. Tetapi dalam kes ini, equals()kaedah itu akan kembali benar! Lagipun, ia membandingkan bilangan milisaat sejak 00:00 pada 1 Januari 1970 untuk setiap tarikh. Objek dianggap sama hanya jika ia sepadan dengan milisaat :

public static void main(String[] args) {

   Date date1 = new Date();
   Date date2 = new Date();

   System.out.println(date1.getTime());
   System.out.println(date2.getTime());

   System.out.println(date1.equals(date2));
}
Ini satu lagi perkara yang perlu anda perhatikan. Jika anda membuka dokumentasi untuk Datekelas di tapak web Oracle , anda akan melihat bahawa banyak kaedah dan pembinanya telah ditandakan sebagai Dihentikan (iaitu tidak disyorkan untuk digunakan). Inilah yang dikatakan oleh pencipta Java tentang bahagian kelas yang telah ditamatkan:
"Elemen program beranotasi @Deprecated ialah sesuatu yang tidak disyorkan oleh pengaturcara untuk digunakan, biasanya kerana ia berbahaya atau kerana terdapat alternatif yang lebih baik."
Ini tidak bermakna kaedah ini tidak boleh digunakan sama sekali. Jika anda cuba menjalankan kod menggunakan kaedah yang tidak digunakan dalam IDE, kemungkinan besar ia akan berfungsi Sebagai contoh, pertimbangkan Date.getHours()kaedah yang ditamatkan, yang mengembalikan bilangan jam yang dikaitkan dengan Dateobjek.

public static void main(String[] args) {

   Date date1 = new Date();

   System.out.println(date1.getHours());
}
Jika anda memulakan kod pada 14:21 (2:21 PM), ia akan memaparkan nombor 14. Seperti yang anda lihat, kaedah yang ditamatkan dipalang, tetapi ia masih berfungsi. Kaedah ini tidak dialih keluar untuk tidak memecahkan badan besar kod sedia ada yang menggunakannya. Dalam erti kata lain, kaedah ini tidak "pecah" atau "dialih keluar". Mereka tidak disyorkan untuk digunakan kerana alternatif yang lebih mudah tersedia. Secara kebetulan, dokumentasi secara khusus menyebut alternatif ini:
Bagaimana untuk tidak tersesat dalam masa: DateTime dan Kalendar - 2
Kebanyakan Datekaedah kelas telah dipindahkan ke Calendarkelas yang dipertingkatkan dan diperluaskan. Kita akan berkenalan dengan kelas itu seterusnya. :)

Kelas kalendar

JDK 1.1 memperkenalkan kelas baharu: Calendar. Ia menjadikan kerja dengan tarikh di Jawa agak lebih mudah daripada sebelumnya. Satu-satunya pelaksanaan kelas Calendaryang kami akan bekerjasama ialah GregorianCalendarkelas. Ia melaksanakan kalendar Gregorian, yang diperhatikan oleh kebanyakan negara di dunia. Kelebihan utamanya ialah ia boleh berfungsi dengan tarikh dalam format yang lebih mudah. Sebagai contoh, ia boleh:
  • Tambahkan bulan atau hari pada tarikh semasa
  • Semak sama ada tahun tersebut adalah tahun lompat;
  • Kembalikan komponen individu tarikh (contohnya, ekstrak nombor bulan daripada tarikh keseluruhan)
  • Ia juga mengandungi sistem pemalar yang sangat mudah (banyak yang akan kita lihat di bawah).
Satu lagi peningkatan penting kelas Calendarialah pemalar Kalendarnya. ERA : anda boleh menunjukkan tarikh sebelum era biasa (SM - sebelum Kristus) atau dalam era biasa (AD - Anno Domini). Mari kita lihat semua ini dengan contoh. Mari buat calendarobjek dengan tarikh 25 Januari 2017:

public static void main(String[] args) {

  Calendar calendar = new GregorianCalendar(2017, 0 , 25);
}
Dalam Calendarkelas (serta Datekelas untuk perkara itu), bulan bermula dari sifar , jadi kami lulus nombor 0 sebagai hujah kedua. Apabila bekerja dengan Calendarkelas, penting untuk memahami bahawa ini hanyalah kalendar , bukan tarikh individu. Bagaimana untuk tidak tersesat dalam masa: DateTime dan Kalendar - 3 Tarikh hanyalah beberapa nombor yang menunjukkan selang masa tertentu. Kalendar ialah sistem keseluruhan yang membolehkan anda melakukan banyak perkara dengan tarikh. :) Ini amat ketara jika anda cuba memaparkan Calendarobjek: Output: java.util.GregorianCalendar[time=?,areFieldsSet=false,areAllFieldsSet=false,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Europe/London",offset=0,dstSavings=0,useDaylight= false,transitions=79,lastRule=null],firstDayOfMinggu=2,minimalDaysInFirstWeek=1,ERA=?,YEAR=2017,MONTH=0,WEEK_OF_YEAR=?,WEEK_OF_MONTH=?,DAY_OF_MONTH=25,DAY_OF_DAY=25,DAY_OF_DAY ,HARI_MINGGU_DALAM_BULAN=?,AM_PM=0,JAM=0,JAM_HARI=0,MINUT=0,KEDUA=0,MILISECOND=?,ZON_OFFSET=?,DST_OFFSET=?] Lihat berapa banyak maklumat yang anda dapat! Kalendar mempunyai sekumpulan sifat yang tidak ada pada tarikh biasa, dan kesemuanya dipaparkan (begini caratoString()kaedah itu berfungsi dalamCalendarkelas). Jika anda hanya perlu mendapatkan tarikh ringkas dari kalendar, iaitu objekDate, gunakanCalendar.getTime()kaedah (nama itu bukan yang paling logik, tetapi apa yang boleh anda lakukan?):

public static void main(String[] args) {

   Calendar calendar = new GregorianCalendar(2017, 0 , 25);
   Date date = calendar.getTime();
   System.out.println(date);
}
Output: Rab Jan 25 00:00:00 GMT 2017 Kini kami telah mengambil kalendar dan "mengurangkannya" kepada tarikh biasa. Mari pergi lebih jauh. Selain menetapkan bulan mengikut bilangannya, anda boleh menggunakan nilai medan malarCalendar kelas . Pemalar ini adalah medan statik kelas dengan nilai pratetap yang tidak boleh diubah. Ini sebenarnya pilihan yang lebih baik, kerana menggunakannya meningkatkan kebolehbacaan kod anda. Calendar

public static void main(String[] args) {
   GregorianCalendar calendar = new GregorianCalendar(2017, Calendar.JANUARY , 25);
}
Kalendar.JANUARI ialah salah satu pemalar yang mewakili bulan dalam setahun. Menggunakan pemalar bernama ini, tiada siapa yang akan lupa, sebagai contoh, bahawa nombor 3 bermaksud April, dan bukan bulan ketiga, yang kami suka panggil Mac. Hanya tulis Calendar.APRIL , dan anda sudah selesai. :) Semua medan kalendar (nombor, bulan, minit, saat, dll.) boleh ditentukan secara berasingan menggunakanset()kaedah tersebut. Kaedah ini sangat mudah, keranaCalendarkelas mempunyai pemalar untuk setiap medan, dan kod yang dihasilkan sangat mudah dibaca. Dalam contoh terakhir, kami mencipta tarikh, tetapi tidak menetapkan masa untuk tarikh itu. Mari kita tetapkan masa 19:42:12

public static void main(String[] args) {
   Calendar calendar = new GregorianCalendar();
   calendar.set(Calendar.YEAR, 2017);
   calendar.set(Calendar.MONTH, 0);
   calendar.set(Calendar.DAY_OF_MONTH, 25);
   calendar.set(Calendar.HOUR_OF_DAY, 19);
   calendar.set(Calendar.MINUTE, 42);
   calendar.set(Calendar.SECOND, 12);

   System.out.println(calendar.getTime());
}
Output: Rab Jan 25 19:42:12 GMT 2017 Kami memanggil set()kaedah, menghantar pemalar (bergantung pada medan yang ingin kami ubah) dan nilai baharu untuk medan. Ternyata set()kaedah ini adalah sejenis "super-setter" yang tahu cara menetapkan nilai bukan hanya untuk satu medan, tetapi untuk banyak medan. :) CalendarKelas menggunakan add()kaedah untuk menambah dan menolak nilai. Anda lulus dalam medan yang ingin anda ubah, dan nombor (tepatnya berapa banyak yang anda mahu tambah/tolak daripada nilai semasa). Sebagai contoh, mari dapatkan tarikh iaitu 2 bulan sebelum tarikh yang kami buat:

public static void main(String[] args) {
   Calendar calendar = new GregorianCalendar(2017, Calendar.JANUARY , 25);
   calendar.set(Calendar.HOUR, 19);
   calendar.set(Calendar.MINUTE, 42);
   calendar.set(Calendar.SECOND, 12);

   calendar.add(Calendar.MONTH, -2); // To subtract, pass a negative number
   System.out.println(calendar.getTime());
}
Output: Jum 25 Nov 19:42:12 GMT 2016 Sangat bagus! Kami mendapat tarikh 2 bulan yang lalu. Ini bukan sahaja menyebabkan bulan berubah: tahun juga berubah dari 2017 hingga 2016. Sudah tentu, apabila menukar tarikh, tahun semasa dikira secara automatik tanpa perlu anda menjejakinya secara manual. Tetapi jika atas sebab tertentu anda perlu melumpuhkan tingkah laku ini, anda boleh berbuat demikian. Kaedah ini roll()boleh menambah dan menolak nilai tanpa menjejaskan nilai yang tinggal . Sebagai contoh, seperti ini:

public static void main(String[] args) {
   Calendar calendar = new GregorianCalendar(2017, Calendar.JANUARY , 25);
   calendar.set(Calendar.HOUR, 10);
   calendar.set(Calendar.MINUTE, 42);
   calendar.set(Calendar.SECOND, 12);

   calendar.roll(Calendar.MONTH, -2);
   System.out.println(calendar.getTime());
}
Kami melakukan perkara yang sama seperti dalam contoh sebelumnya: kami mengambil masa 2 bulan dari tarikh semasa. Tetapi kini kod melakukan sesuatu yang berbeza: bulan telah berubah dari Januari hingga November, tetapi tahun itu kekal tidak berubah—2017! Output: Sab Nov 25 10:42:12 GMT 2017 Bergerak bersama. Seperti yang kita katakan di atas, kita boleh mendapatkan semua Calendarmedan secara berasingan. Kami melakukan ini dengan get()kaedah:

public static void main(String[] args) {
   GregorianCalendar calendar = new GregorianCalendar(2017, Calendar.JANUARY , 25);
   calendar.set(Calendar.HOUR, 10);
   calendar.set(Calendar.MINUTE, 42);
   calendar.set(Calendar.SECOND, 12);

   System.out.println("Year: " + calendar.get(Calendar.YEAR));
   System.out.println("Month: " + calendar.get(Calendar.MONTH));
   System.out.println("Week in the month: " + calendar.get(Calendar.WEEK_OF_MONTH));// Week in this month?

   System.out.println("Day: " + calendar.get(Calendar.DAY_OF_MONTH));

   System.out.println("Hours: " + calendar.get(Calendar.HOUR));
   System.out.println("Minutes: " + calendar.get(Calendar.MINUTE));
   System.out.println("Seconds: " + calendar.get(Calendar.SECOND));
   System.out.println("Milliseconds: " + calendar.get(Calendar.MILLISECOND));

}
Output: Tahun: 2017 Bulan: 0 Minggu dalam bulan: 5 Hari: 25 Jam: 10 Minit: 42 Saat: 12 Milisaat: 0 Jadi, sebagai tambahan kepada Calendar"super-setter" kelas, terdapat juga "super-getter" ". :) Sudah tentu, satu lagi aspek menarik dalam kelas ini berfungsi dengan era. Untuk mencipta tarikh "SM", anda perlu menggunakan medan Calendar.ERA Contohnya, mari buat tarikh untuk Pertempuran Cannae, di mana Hannibal mengalahkan tentera Rom. Ini berlaku pada 2 Ogos 216 SM:

public static void main(String[] args) {
   GregorianCalendar cannae = new GregorianCalendar(216, Calendar.AUGUST, 2);
   cannae.set(Calendar.ERA, GregorianCalendar.BC);

   DateFormat df = new SimpleDateFormat("MMM dd, yyy GG");
   System.out.println(df.format(cannae.getTime()));
}
Di sini kami menggunakan SimpleDateFormatkelas untuk mencetak tarikh dalam format yang lebih mudah untuk kami fahami (huruf "GG" menunjukkan kami mahu era itu dipaparkan). Keluaran: 02 Ogos 216 SM. Kelas Calendarmempunyai lebih banyak kaedah dan pemalar. Anda boleh membaca tentang mereka dalam dokumentasi . Jika tidak suka format tarikh ini Sab Nov 25 10:42:12 GMT 2017 maka anda boleh gunakan SimpleDateFormatuntuk menjadikannya seperti yang anda inginkan dengan mudah.

public static void main(String[] args) {

   SimpleDateFormat dateFormat = new SimpleDateFormat("EEEE, MMMM d, yyyy");
   Calendar calendar = new GregorianCalendar(2017, Calendar.JANUARY , 25);
   calendar.set(Calendar.HOUR, 10);
   calendar.set(Calendar.MINUTE, 42);
   calendar.set(Calendar.SECOND, 12);

   calendar.roll(Calendar.MONTH, -2);
   System.out.println(dateFormat.format(calendar.getTime()));
}
Output: Sabtu, 25 November 2017 Itu jauh lebih baik, bukan? :)
Komen
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION