Membandingkan dua java.util.Dates untuk melihat apakah mereka ada di hari yang sama

250

Saya perlu membandingkan dua Dates (misalnya date1dan date2) dan menghasilkan boolean sameDayyang benar dari bagian dua Datepada hari yang sama, dan salah jika mereka tidak.

Bagaimana saya bisa melakukan ini? Sepertinya ada angin puyuh kebingungan di sini ... dan saya ingin menghindari menarik ketergantungan lain di luar JDK jika memungkinkan.

untuk mengklarifikasi: jika date1dan date2berbagi tahun, bulan, dan hari yang sama, maka sameDayitu benar, jika tidak maka itu salah. Saya menyadari ini membutuhkan pengetahuan tentang zona waktu ... akan menyenangkan untuk lulus dalam zona waktu tetapi saya dapat hidup dengan GMT atau waktu setempat selama saya tahu apa perilakunya.

lagi, untuk memperjelas:

date1 = 2008 Jun 03 12:56:03
date2 = 2008 Jun 03 12:59:44
  => sameDate = true

date1 = 2009 Jun 03 12:56:03
date2 = 2008 Jun 03 12:59:44
  => sameDate = false

date1 = 2008 Aug 03 12:00:00
date2 = 2008 Jun 03 12:00:00
  => sameDate = false
Jason S
sumber
Hanya untuk memperjelas - Anda ingin tahu apakah dua objek Date jatuh pada hari yang sama dalam seminggu?
Rob Heiser
Apakah Anda ingin membandingkan tanggal lengkap (hari, bulan, tahun) atau hanya hari bulan?
XpiritO
1
@Rob: tidak, pada hari / bulan / tahun yang sama ... Saya akan mengklarifikasi.
Jason S
lalu mengapa Anda tidak menggunakan "sama dengan"?
XpiritO
7
Karena mereka tidak sama jika jam / menit / detik berbeda.
Jason S

Jawaban:

416
Calendar cal1 = Calendar.getInstance();
Calendar cal2 = Calendar.getInstance();
cal1.setTime(date1);
cal2.setTime(date2);
boolean sameDay = cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR) &&
                  cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR);

Perhatikan bahwa "hari yang sama" bukan konsep yang sederhana seperti kedengarannya ketika zona waktu yang berbeda dapat terlibat. Kode di atas akan untuk kedua tanggal menghitung hari relatif terhadap zona waktu yang digunakan oleh komputer yang menjalankannya. Jika ini bukan yang Anda butuhkan, Anda harus melewati zona waktu yang relevan untuk Calendar.getInstance()panggilan, setelah Anda memutuskan apa yang sebenarnya Anda maksud dengan "hari yang sama".

Dan ya, Joda Time LocalDateakan membuat semuanya lebih bersih dan mudah (meskipun kesulitan yang sama yang melibatkan zona waktu akan hadir).

Michael Borgwardt
sumber
Terima kasih, sepertinya itu akan melakukan apa yang saya inginkan. Dalam kasus saya, saya membandingkan tanggal berurutan dalam satu seri, jadi sepertinya saya hanya bisa menggunakan instance Kalender alih-alih instance Date dalam seri saya.
Jason S
1
@ Alasan Itu mungkin atau mungkin bukan ide yang bagus. Masalah utama dengan Kalender adalah bahwa itu adalah kelas yang sangat kelas berat dengan banyak kondisi internal, beberapa di antaranya digunakan dalam implementasi equals (). Jika Anda tidak menyalin tanggal untuk persamaan dan tidak memasukkannya ke dalam HashMaps, Anda harus baik-baik saja.
Michael Borgwardt
keren, terima kasih, saya hanya menggunakan logika bandingkan-sekarang-dan-hari sebelumnya bertanya di sini. fungsi "equals" dan "hashcode" tidak boleh dipanggil.
Jason S
1
@UmerHayat: kode ini membandingkan hari dalam setahun, bukan hari dalam sebulan. Satu kurang perlu perbandingan, kode lebih pendek.
Michael Borgwardt
2
Saran: Bandingkan DAY_OF_YEAR terlebih dahulu, tidak perlu memeriksa tahun. Ok itu tidak seperti membandingkan int benar-benar mahal tapi ...
Martin P.
332

Bagaimana tentang:

SimpleDateFormat fmt = new SimpleDateFormat("yyyyMMdd");
return fmt.format(date1).equals(fmt.format(date2));

Anda juga dapat mengatur zona waktu ke SimpleDateFormat, jika perlu.

Binil Thomas
sumber
2
(Saya sebenarnya menggunakan SimpleDateFormat dalam kasus saya, jadi sepertinya cocok.)
Jason S
8
Saya sebenarnya terkejut melihat ini, tetapi bahkan dari sudut pandang kinerja, SimpleDateFormatmetode ini sebenarnya lebih cepat daripada yang lain yang disebutkan di sini menggunakan Calendars. Rata-rata dibutuhkan separuh waktu sebagai Calendarmetode. (Setidaknya di sistem saya). Pujian!
Michael Plautz
Sebagian besar biaya jawaban ini ada pada kreasi SimpleDateFormat. Letakkan di bidang threadLocal di singleton jika Anda menginginkan kinerja yang lebih baik
Thierry
1
Saya melakukan ini di Swift juga. Sungguh menakjubkan bagaimana hal sederhana seperti itu membutuhkan hack yang sama di sebagian besar bahasa. C # menjadi pengecualian - if (d1.Date == d2.Date) ...
alpsystems.com
2
Ini jelek; terkejut melihat retasan seperti ini terbalik.
Zsolt Safrany
162

Saya menggunakan paket "apache commons lang" untuk melakukan ini (yaitu org.apache.commons.lang.time.DateUtils )

boolean samedate = DateUtils.isSameDay(date1, date2);  //Takes either Calendar or Date objects
Brent Watson
sumber
2
Ini menggunakan ketergantungan eksternal ... tapi ada baiknya untuk mengetahui masa depan.
Jason S
20
Cukup salin sumbernya dan panggil sehari :)
Anton Kuzmin
Tapi itu melemparkan pengecualian argumen ilegal jika ada hari-hari yang nol, yang tidak ideal dalam beberapa kasus.
raviraja
1
Seperti yang dikomentari di stackoverflow.com/a/2517824/1665809 solusi ini mungkin tidak sesuai jika ada zona waktu yang berbeda.
mrod
24

Anda dapat menghindari ketergantungan eksternal dan hit kinerja menggunakan Kalender dengan menghitung Nomor Hari Julian untuk masing-masing tanggal dan kemudian membandingkan ini:

public static boolean isSameDay(Date date1, Date date2) {

    // Strip out the time part of each date.
    long julianDayNumber1 = date1.getTime() / MILLIS_PER_DAY;
    long julianDayNumber2 = date2.getTime() / MILLIS_PER_DAY;

    // If they now are equal then it is the same day.
    return julianDayNumber1 == julianDayNumber2;
}
AyeJay
sumber
4
Namun berhati-hatilah bahwa ini gagal memperhitungkan perubahan panjang hari karena penghematan siang hari. Tapi kemudian lurus Datetidak merangkum gagasan zona waktu sehingga tidak ada cara untuk memperbaikinya secara sewenang-wenang.
AyeJay
3
Ini adalah solusi tercepat - terima kasih banyak, persis apa yang saya cari; karena saya harus memeriksa banyak tanggal ...
Ridcully
2
Nitpick: date1.getTime () tidak mengembalikan Nomor Hari Julian, tetapi milidetik sejak 1970-01-01. Perbedaan besar
Per Lindberg
2
Ini melakukan perbandingan di zona waktu UTC. Jika Anda menginginkan zona waktu setempat atau tempat lain di planet ini, Anda tidak dapat mengetahui apakah hasil yang Anda dapatkan benar.
Ole VV
20

Joda-Time

Sedangkan untuk menambahkan dependensi, saya khawatir java.util.Date & .Calendar sangat buruk sehingga hal pertama yang saya lakukan untuk proyek baru adalah menambahkan perpustakaan Joda-Time. Di Java 8 Anda dapat menggunakan paket java.time baru, terinspirasi oleh Joda-Time.

Inti dari Joda-Time adalah DateTimekelas. Tidak seperti java.util.Date, ia memahami zona waktu yang ditentukan ( DateTimeZone). Saat mengkonversi dari juDate, tetapkan zona.

DateTimeZone zone = DateTimeZone.forID( "America/Montreal" );
DateTime dateTimeQuébec = new DateTime( date , zone );

LocalDate

Salah satu cara untuk memverifikasi apakah dua kali tanggal mendarat pada tanggal yang sama adalah dengan mengonversi ke LocalDateobjek.

Konversi itu tergantung pada zona waktu yang ditetapkan. Untuk membandingkan LocalDateobjek, mereka harus dikonversi dengan zona yang sama.

Berikut ini adalah sedikit metode utilitas.

static public Boolean sameDate ( DateTime dt1 , DateTime dt2 )
{
    LocalDate ld1 = new LocalDate( dt1 );
    // LocalDate determination depends on the time zone.
    // So be sure the date-time values are adjusted to the same time zone.
    LocalDate ld2 = new LocalDate( dt2.withZone( dt1.getZone() ) );
    Boolean match = ld1.equals( ld2 );
    return match;
}

Lebih baik akan menjadi argumen lain, menentukan zona waktu daripada mengasumsikan zona waktu objek DateTime pertama harus digunakan.

static public Boolean sameDate ( DateTimeZone zone , DateTime dt1 , DateTime dt2 )
{
    LocalDate ld1 = new LocalDate( dt1.withZone( zone ) );
    // LocalDate determination depends on the time zone.
    // So be sure the date-time values are adjusted to the same time zone.
    LocalDate ld2 = new LocalDate( dt2.withZone( zone ) );
    return ld1.equals( ld2 );
}

Representasi String

Pendekatan lain adalah membuat representasi string dari bagian tanggal setiap waktu-tanggal, kemudian membandingkan string.

Sekali lagi, zona waktu yang ditetapkan sangat penting.

DateTimeFormatter formatter = ISODateTimeFormat.date();  // Static method.
String s1 = formatter.print( dateTime1 );
String s2 = formatter.print( dateTime2.withZone( dt1.getZone() )  );
Boolean match = s1.equals( s2 );
return match;

Rentang waktu

Solusi umum adalah menentukan rentang waktu, lalu tanyakan apakah rentang tersebut berisi target Anda. Contoh kode ini adalah dalam Joda-Time 2.4. Perhatikan bahwa kelas terkait "tengah malam" sudah usang. Alih-alih menggunakan withTimeAtStartOfDaymetode ini. Joda-Time menawarkan tiga kelas untuk mewakili rentang waktu dengan berbagai cara: Interval, Periode, dan Durasi.

Menggunakan pendekatan "Setengah Terbuka" di mana awal rentang mencakup inklusif dan akhir.

Zona waktu target dapat berbeda dari zona waktu interval.

DateTimeZone timeZone = DateTimeZone.forID( "Europe/Paris" );
DateTime target = new DateTime( 2012, 3, 4, 5, 6, 7, timeZone );
DateTime start = DateTime.now( timeZone ).withTimeAtStartOfDay();
DateTime stop = start.plusDays( 1 ).withTimeAtStartOfDay();
Interval interval = new Interval( start, stop );
boolean containsTarget = interval.contains( target );

waktu java

Java 8 dan yang lebih baru hadir dengan kerangka java.time . Terinspirasi oleh Joda-Time, didefinisikan oleh JSR 310, dan diperpanjang oleh proyek ThreeTen-Extra. Lihat Tutorial .

Pembuat Joda-Time telah menginstruksikan kita semua untuk pindah ke java.time secepat mungkin. Sementara itu Joda-Time berlanjut sebagai proyek yang dipelihara secara aktif. Tapi berharap pekerjaan di masa depan hanya terjadi di java.time dan ThreeTen-Extra daripada Joda-Time.

Untuk meringkas java.time secara singkat ... An Instantadalah momen di timeline di UTC. Terapkan zona waktu ( ZoneId) untuk mendapatkan ZonedDateTimeobjek. Untuk memindahkan off timeline, untuk mendapatkan ide tak terbatas kabur dari tanggal-waktu, gunakan "lokal" kelas: LocalDateTime, LocalDate, LocalTime.

Logika yang dibahas di bagian Joda-Time dari Jawaban ini berlaku untuk java.time.

Kelas java.util.Date yang lama memiliki toInstantmetode baru untuk konversi ke java.time.

Instant instant = yourJavaUtilDate.toInstant(); // Convert into java.time type.

Menentukan tanggal membutuhkan zona waktu.

ZoneId zoneId = ZoneId.of( "America/Montreal" );

Kami menerapkan objek zona waktu itu Instantuntuk mendapatkan a ZonedDateTime. Dari itu kami mengekstrak nilai hanya tanggal (a LocalDate) karena tujuan kami adalah membandingkan tanggal (bukan jam, menit, dll.).

ZonedDateTime zdt1 = ZonedDateTime.ofInstant( instant , zoneId );
LocalDate localDate1 = LocalDate.from( zdt1 );

Lakukan hal yang sama untuk java.util.Dateobjek kedua yang kita butuhkan untuk perbandingan. Saya hanya akan menggunakan momen saat ini saja.

ZonedDateTime zdt2 = ZonedDateTime.now( zoneId );
LocalDate localDate2 = LocalDate.from( zdt2 );

Gunakan isEqualmetode khusus untuk menguji nilai tanggal yang sama.

Boolean sameDate = localDate1.isEqual( localDate2 );
Basil Bourque
sumber
java.time: Atau Anda bisa melakukannyaLocalDate localDate = LocalDate.fromDateFields(yourJavaUtilDate);
CyberMew
1
@CyberMew Er? Tidak ada fromDateFields()di kelas sayaLocalDate .
Ole VV
1
Maaf, saya seharusnya lebih jelas. Komentar ini relevan untuk kelas Joda-Time LocalDate .
CyberMew
7

Konversi tanggal ke Java 8 java.time.LocalDate seperti yang terlihat di sini .

LocalDate localDate1 = date1.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
LocalDate localDate2 = date2.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();

// compare dates
assertTrue("Not on the same day", localDate1.equals(localDate2));
Istvan
sumber
Ini jawaban favorit saya. Jika Anda ingin mengontrol zona waktu yang digunakan alih-alih mengandalkan pengaturan komputer, langsung saja dimasukkan, misalnya ZoneId.of("America/Phoenix") atau ZoneId.of("Europe/Budapest")alih-alih standar sistem.
Ole VV
6

Java 8

Jika Anda menggunakan Java 8 dalam proyek Anda dan membandingkan java.sql.Timestamp, Anda bisa menggunakan LocalDatekelas:

sameDate = date1.toLocalDateTime().toLocalDate().equals(date2.toLocalDateTime().toLocalDate());

Jika Anda menggunakan java.util.Date, lihat jawaban Istvan yang kurang ambigu.

amanteaux
sumber
Menggunakan Java 8 adalah ide yang bagus. Apakah Anda berasumsi date1dan date2sedang java.sql.Timestamp? Menurut pertanyaan mereka Date, saya akan mengerti java.util.Date. Kode Anda juga menggunakan pengaturan zona waktu komputer, yang untuk banyak tujuan akan baik-baik saja; masih saya lebih suka untuk membuat fakta ini eksplisit dalam kode.
Ole VV
@ OleV.V. terima kasih atas komentar Anda, jawaban asli saya memang tentang java.sql.Timestamp. Seperti yang Anda katakan ini umumnya lebih baik untuk secara eksplisit mengatur zona waktu.
amanteaux
5
private boolean isSameDay(Date date1, Date date2) {
        Calendar calendar1 = Calendar.getInstance();
        calendar1.setTime(date1);
        Calendar calendar2 = Calendar.getInstance();
        calendar2.setTime(date2);
        boolean sameYear = calendar1.get(Calendar.YEAR) == calendar2.get(Calendar.YEAR);
        boolean sameMonth = calendar1.get(Calendar.MONTH) == calendar2.get(Calendar.MONTH);
        boolean sameDay = calendar1.get(Calendar.DAY_OF_MONTH) == calendar2.get(Calendar.DAY_OF_MONTH);
        return (sameDay && sameMonth && sameYear);
    }
evya
sumber
5

UNTUK PENGGUNA ANDROID:

Anda dapat menggunakan DateUtils.isToday(dateMilliseconds)untuk memeriksa apakah tanggal yang diberikan adalah hari saat ini atau tidak.

Referensi API: https://developer.android.com/reference/android/text/format/DateUtils.html#isToday(long)

Hemant Kaushik
sumber
1
Metode ini menggunakan objek Waktu yang sudah tidak digunakan lagi sejak API 22: developer.android.com/reference/android/text/format/Time.html
Oleksandr Bodashko
2
+1 untuk pengembang android. metode ini menggunakan panjang primitif sebagai parameter, cukup gunakan DateUtils.isToday(x.getTime())(x adalah contoh java.util.date) yang akan dilakukan
jackycflau
1

selain solusi Binil Thomas

public static boolean isOnSameDay(Timestamp... dates) {
    SimpleDateFormat fmt = new SimpleDateFormat("yyyyMMdd");
    String date1 = fmt.format(dates[0]);
    for (Timestamp date : dates) {
        if (!fmt.format(date).equals(date1)) {
            return false;
        }
    }
    return true;
}

pemakaian

    isOnSameDay(date1,date2,date3 ...);
//or 
    isOnSameDay(mydates);
Fareed Alnamrouti
sumber
1

Untuk para pengembang Kotlin, ini adalah versi dengan membandingkan pendekatan string terformat:

val sdf = SimpleDateFormat("yyMMdd")
if (sdf.format(date1) == sdf.format(date2)) {
    // same day
}

Ini bukan cara terbaik, tetapi pendek dan berhasil.

Micer
sumber
1
The SimpleDateFormatkelas itu digantikan tahun yang lalu oleh modern java.time.DateTimeFormatterkelas didefinisikan dalam JSR 310. Menyarankan penggunaannya tahun 2019 adalah saran yang buruk.
Basil Bourque
2
Kode ini mengabaikan masalah penting zona waktu. Untuk setiap momen tertentu, tanggalnya bervariasi di seluruh dunia berdasarkan zona.
Basil Bourque
-4

Anda dapat menerapkan logika yang sama dengan solusi SimpleDateFormat tanpa mengandalkan SimpleDateFormat

date1.getFullYear()*10000 + date1.getMonth()*100 + date1.getDate() == 
date2.getFullYear()*10000 + date2.getMonth()*100 + date2.getDate()
jawa
sumber
6
Metode-metode ini sudah usang di java.util.Date. Anda harus menggunakan Kalender untuk melakukan ini. Juga getYear, not getFullYear
Kris