1. Gambaran keseluruhan
Dalam penulisan ringkas ini, kami akan meneroka pelbagai kemungkinan untuk mengira perbezaan antara dua tarikh di Jawa.
2. Java Teras
2.1. Menggunakan java.util.Date untuk Mencari Perbezaan dalam Hari
Mari kita mulakan dengan menggunakan API inti Java untuk melakukan pengiraan dan menentukan jumlah hari antara dua tarikh:
@Test public void givenTwoDatesBeforeJava8_whenDifferentiating_thenWeGetSix() throws ParseException { SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy", Locale.ENGLISH); Date firstDate = sdf.parse("06/24/2017"); Date secondDate = sdf.parse("06/30/2017"); long diffInMillies = Math.abs(secondDate.getTime() - firstDate.getTime()); long diff = TimeUnit.DAYS.convert(diffInMillies, TimeUnit.MILLISECONDS); assertEquals(6, diff); }
2.2. Menggunakan java.time.temporal.ChronoUnit untuk Mencari Perbezaan
Time API di Java 8 mewakili unit waktu-waktu, misalnya detik atau hari, menggunakan antara muka TemporalUnit . Setiap unit menyediakan implementasi untuk metode yang dinamakan antara untuk menghitung jumlah waktu antara dua objek temporal dari segi unit tertentu .
Sebagai contoh, untuk mengira detik antara dua LocalDateTime s:
@Test public void givenTwoDateTimesInJava8_whenDifferentiatingInSeconds_thenWeGetTen() { LocalDateTime now = LocalDateTime.now(); LocalDateTime tenSecondsLater = now.plusSeconds(10); long diff = ChronoUnit.SECONDS.between(now, tenSecondsLater); assertEquals(10, diff); }
ChronoUnit menyediakan satu set unit masa konkrit dengan melaksanakan antara muka TemporalUnit . Ia sangat disyorkan untuk import statik yang ChronoUnit enum nilai untuk mencapai pembacaan yang lebih baik:
import static java.time.temporal.ChronoUnit.SECONDS; // omitted long diff = SECONDS.between(now, tenSecondsLater);
Juga, kita dapat menyampaikan dua objek temporal yang serasi ke kaedah antara , bahkan ZonedDateTime .
Apa yang hebat mengenai ZonedDateTime ialah pengiraan akan berjalan walaupun mereka ditetapkan ke zon waktu yang berbeza:
@Test public void givenTwoZonedDateTimesInJava8_whenDifferentiating_thenWeGetSix() { LocalDateTime ldt = LocalDateTime.now(); ZonedDateTime now = ldt.atZone(ZoneId.of("America/Montreal")); ZonedDateTime sixMinutesBehind = now .withZoneSameInstant(ZoneId.of("Asia/Singapore")) .minusMinutes(6); long diff = ChronoUnit.MINUTES.between(sixMinutesBehind, now); assertEquals(6, diff); }
2.3. Menggunakan java.time.temporal.Temporal # hingga ()
Sebarang objek Temporal , seperti LocalDate atau ZonedDateTime, menyediakan kaedah hingga untuk mengira jumlah masa sehingga Temporal lain dari segi unit yang ditentukan:
@Test public void givenTwoDateTimesInJava8_whenDifferentiatingInSecondsUsingUntil_thenWeGetTen() { LocalDateTime now = LocalDateTime.now(); LocalDateTime tenSecondsLater = now.plusSeconds(10); long diff = now.until(tenSecondsLater, ChronoUnit.SECONDS); assertEquals(10, diff); }
The Temporal # sehingga dan TemporalUnit # antara dua API yang berbeza untuk fungsi yang sama.
2.4. Menggunakan java.time.Duration dan java.time.Period
Di Java 8, Time API memperkenalkan dua kelas baru: Durasi dan Tempoh .
Sekiranya kita ingin mengira perbezaan antara dua tarikh-tarikh dalam jangka masa (jam, minit, atau saat) berdasarkan masa, kita boleh menggunakan kelas Durasi :
@Test public void givenTwoDateTimesInJava8_whenDifferentiating_thenWeGetSix() { LocalDateTime now = LocalDateTime.now(); LocalDateTime sixMinutesBehind = now.minusMinutes(6); Duration duration = Duration.between(now, sixMinutesBehind); long diff = Math.abs(duration.toMinutes()); assertEquals(6, diff); }
Namun, kita harus berhati-hati jika kita menggunakan kelas Period untuk menunjukkan perbezaan antara dua tarikh .
Contoh akan menerangkannya dengan cepat. Mari kira berapa hari antara dua tarikh menggunakan kelas Period :
@Test public void givenTwoDatesInJava8_whenUsingPeriodGetDays_thenWorks() { LocalDate aDate = LocalDate.of(2020, 9, 11); LocalDate sixDaysBehind = aDate.minusDays(6); Period period = Period.between(aDate, sixDaysBehind); int diff = Math.abs(period.getDays()); assertEquals(6, diff); }
Sekiranya kita menjalankan ujian di atas, ia akan lulus. Kami mungkin menganggap kelas Period sesuai untuk menyelesaikan masalah kami. Setakat ini, begitu baik.
Sekiranya cara ini berfungsi dengan selisih enam hari, kita tidak akan ragu ia akan berfungsi selama 60 hari juga.
Seterusnya, mari kita ubah 6 dalam ujian di atas menjadi 60 , dan lihat apa yang berlaku:
@Test public void givenTwoDatesInJava8_whenUsingPeriodGetDays_thenDoesNotWork() { LocalDate aDate = LocalDate.of(2020, 9, 11); LocalDate sixtyDaysBehind = aDate.minusDays(60); Period period = Period.between(aDate, sixtyDaysBehind); int diff = Math.abs(period.getDays()); assertEquals(60, diff); }
Sekarang, jika kita menjalankan ujian lagi, kita akan melihat:
java.lang.AssertionError: Expected :60 Actual :29
Alamak! Mengapa kelas Period melaporkan perbezaannya sebagai 29 hari?
Ini kerana kelas Period mewakili jumlah masa berdasarkan tarikh dalam format " x tahun, bulan y dan hari z ". Apabila kita memanggil kaedah getDays () , ia hanya mengembalikan bahagian " z days ".
Oleh itu, objek tempoh dalam ujian di atas mempunyai nilai: " 0 tahun, 1 bulan dan 29 hari ":
@Test public void givenTwoDatesInJava8_whenUsingPeriod_thenWeGet0Year1Month29Days() { LocalDate aDate = LocalDate.of(2020, 9, 11); LocalDate sixtyDaysBehind = aDate.minusDays(60); Period period = Period.between(aDate, sixtyDaysBehind); int years = Math.abs(period.getYears()); int months = Math.abs(period.getMonths()); int days = Math.abs(period.getDays()); assertArrayEquals(new int[] { 0, 1, 29 }, new int[] { years, months, days }); }
Sekiranya kita ingin mengira perbezaan dalam hari menggunakan Java Time's API API, kaedah ChronoUnit.DAYS.between () adalah kaedah yang paling mudah.
3. Perpustakaan Luar
3.1. JodaTime
Kami juga boleh melakukan pelaksanaan yang agak mudah dengan JodaTime :
joda-time joda-time 2.9.9
Anda boleh mendapatkan versi terbaru Joda-time dari Maven Central.
Kes LocalDate :
@Test public void givenTwoDatesInJodaTime_whenDifferentiating_thenWeGetSix() { org.joda.time.LocalDate now = org.joda.time.LocalDate.now(); org.joda.time.LocalDate sixDaysBehind = now.minusDays(6); long diff = Math.abs(Days.daysBetween(now, sixDaysBehind).getDays()); assertEquals(6, diff); }
Begitu juga dengan LocalDateTime :
@Test public void givenTwoDateTimesInJodaTime_whenDifferentiating_thenWeGetSix() { org.joda.time.LocalDateTime now = org.joda.time.LocalDateTime.now(); org.joda.time.LocalDateTime sixMinutesBehind = now.minusMinutes(6); long diff = Math.abs(Minutes.minutesBetween(now, sixMinutesBehind).getMinutes()); assertEquals(6, diff); }
3.2. Tarikh4J
Date4j juga menyediakan pelaksanaan yang mudah dan mudah, tanpa perhatikan bahawa, dalam kes ini, kita perlu memberikan TimeZone secara eksplisit .
Mari mulakan dengan kebergantungan Maven:
com.darwinsys hirondelle-date4j 1.5.1
Inilah ujian pantas yang berfungsi dengan DateTime standard :
@Test public void givenTwoDatesInDate4j_whenDifferentiating_thenWeGetSix() { DateTime now = DateTime.now(TimeZone.getDefault()); DateTime sixDaysBehind = now.minusDays(6); long diff = Math.abs(now.numDaysFrom(sixDaysBehind)); assertEquals(6, diff); }
4. Kesimpulan
Dalam tutorial ini, kami menggambarkan beberapa cara untuk mengira perbezaan antara tarikh (dengan dan tanpa waktu), baik di Jawa biasa dan juga menggunakan perpustakaan luaran.
Kod sumber penuh artikel boleh didapati di GitHub.