Aynı gün olup olmadıklarını görmek için iki java.util.Dates karşılaştırması


250

Ben iki Dates (örneğin date1ve date2) karşılaştırmak ve aynı gün boolean sameDayiki Dates payı için doğru ve değilse, yanlış bir ile gelip gerekir.

Bunu nasıl yapabilirim? Burada bir kafa karışıklığı var gibi görünüyor ... ve mümkünse JDK'nın ötesindeki diğer bağımlılıkları çekmekten kaçınmak istiyorum.

netleştirmek için: eğer date1ve date2aynı yıl, ay ve günü paylaşmak, daha sonra sameDayaksi takdirde yanlış, doğrudur. Bunun bir saat dilimi bilgisi gerektirdiğinin farkındayım ... bir saat dilimine geçmek güzel olurdu ama davranışların ne olduğunu bildiğim sürece GMT veya yerel saatle yaşayabilirim.

yine, açıklığa kavuşturmak için:

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

Açıklığa kavuşturmak için - iki Date nesnesinin haftanın aynı gününe düşüp düşmediğini bilmek ister misiniz?
Rob Heiser

Tam tarihi (gün, ay, yıl) veya yalnızca ay gününü karşılaştırmak ister misiniz?
XpiritO

1
@ Rod: hayır, aynı gün / ay / yıl ... Açıklayacağım.
Jason S

o zaman neden "eşittir" kullanmıyorsunuz?
XpiritO

7
Çünkü saat / dakika / saniye farklıysa eşit değildirler.
Jason S

Yanıtlar:


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

"Aynı gün" ün farklı zaman dilimleri dahil edildiğinde göründüğü kadar basit bir kavram olmadığını unutmayın. Yukarıdaki kod her iki tarih için de çalıştığı bilgisayarın kullandığı saat dilimine göre günü hesaplar. İhtiyacınız olan şey bu değilse, ilgili saat dilimleriniCalendar.getInstance() , "aynı gün" ile tam olarak ne demek istediğinize karar verdikten sonra çağrılara iletmeniz gerekir.

Ve evet, Joda Time LocalDateher şeyi daha temiz ve kolay hale getirecekti (saat dilimleri ile ilgili aynı zorluklar olsa da).


Teşekkürler, istediğimi yapacak gibi görünüyor. Benim durumumda bir dizideki birbirini takip eden tarihleri ​​karşılaştırıyorum, bu yüzden serimdeki Tarih örnekleri yerine Takvim örneklerini kullanabileceğim gibi görünüyor.
Jason S

1
@Jason Bu iyi bir fikir olabilir veya olmayabilir. Takvim ile ilgili temel sorun, bir kısmı eşittir () uygulamasında kullanılan çok fazla iç durumu olan çok ağır bir sınıf olmasıdır. Tarihlerinizi eşitlik için uyarmazsanız ve HashMaps'e koymazsanız, iyi olmalısınız.
Michael Borgwardt

güzel, teşekkürler, sadece burada sorulan karşılaştır-güncel ve önceki gün mantığını kullanıyorum. "eşittir" ve "hashcode" işlevleri asla çağrılmamalıdır.
Jason S

1
@UmerHayat: Kod, ayın gününü değil yılın gününü karşılaştırır. Daha az karşılaştırma gerekir, daha kısa kod.
Michael Borgwardt

2
Öneri: Önce DAY_OF_YEAR ile karşılaştırın, yılı kontrol etmek zorunda kalmayacaksınız. Tamam, int karşılaştırmak gerçekten pahalı değil ama ...
Martin P.

332

Nasıl olur:

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

Gerekirse saat dilimini SimpleDateFormat olarak da ayarlayabilirsiniz.


2
(Aslında SimpleDateFormat'ı zaten benim durumumda kullanıyorum, bu yüzden uygun görünüyor.)
Jason S

8
Bunu görmekten gerçekten şaşırdım, ancak performans açısından bile, bu SimpleDateFormatyöntem aslında burada Calendars ile belirtilenlerden daha hızlı . Ortalama olarak Calendaryöntem yarı yarıya sürer . (En azından sistemimde). Kudos!
Michael Plautz

Bu yanıtın maliyetinin çoğu SimpleDateFormat oluşturma işlemidir. Daha da iyi bir performans istiyorsanız, tek bir alanda yerel alana koyun
Thierry

1
Bunu Swift'te de yapıyorum. Böyle basit bir şeyin çoğu dilde aynı hack'i nasıl gerektirdiği şaşırtıcı. C # istisna olmak - eğer (d1.Date == d2.Date) ...
alpsystems.com

2
Bu çirkin; böyle bir kesmek görmek için şaşırttı.
Zsolt Safrany

162

Bunu yapmak için "apache commons lang" paketini kullanıyorum (yani org.apache.commons.lang.time.DateUtils )

boolean samedate = DateUtils.isSameDay(date1, date2);  //Takes either Calendar or Date objects

2
Bu dışsal bir bağımlılık kullanır ... ama gelecek için bilmek iyidir.
Jason S

20
Sadece kaynağı kopyalayın ve bir gün deyin :)
Anton Kuzmin

Ancak, günlerden herhangi biri boşsa, bazı durumlarda ideal olmayan yasadışı bir tartışma istisnası atar.
raviraja

1
Stackoverflow.com/a/2517824/1665809 adresinde yorumlandığı gibi, farklı saat dilimleri söz konusu olduğunda bu çözüm uygun olmayabilir.
mrod

24

Tarihlerin her biri için Jülyen Gün Sayısını hesaplayıp bunları karşılaştırarak harici bağımlılıklardan ve Takvim'i kullanmanın performans vuruşundan kaçınabilirsiniz :

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

4
Ancak bunun , gün ışığından yararlanma nedeniyle günün uzunluğundaki değişiklikleri dikkate almadığını unutmayın . Ama sonra düz Dates zaman dilimleri kavramını kapsamaz, bu yüzden keyfi olarak bunu düzeltmenin bir yolu yoktur.
AyeJay

3
Bu arada en hızlı çözüm - çok teşekkürler, tam olarak aradığım şey; birçok tarihi kontrol etmem gerektiğinden ...
Ridcully

2
Nitpick: date1.getTime (), Julian Gün Numarasını döndürmez, ancak 1970-01-01'den bu yana milisaniye verir. Büyük farklılık.
Per Lindberg

2
Bu, karşılaştırmayı UTC saat diliminde gerçekleştirir. Yerel saat diliminizi veya gezegendeki başka bir yeri istiyorsanız, elde ettiğiniz sonucun doğru olup olmadığını bilemezsiniz.
Ole VV

20

Joda-Time

Bir bağımlılık eklemek gelince, java.util.Date & .Calendar gerçekten çok kötü, herhangi bir yeni projeye yaptığım ilk şey Joda-Time kütüphanesini eklemek. Java 8'de Joda-Time'dan esinlenen yeni java.time paketini kullanabilirsiniz.

Joda-Time'ın çekirdeği DateTimesınıftır. Java.util.Date öğesinin aksine, atanmış saat dilimini ( DateTimeZone) anlar . JuDate'den dönüştürme yaparken bir bölge atayın.

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

LocalDate

Aynı tarihte iki tarih saatinin gelip gelmediğini doğrulamanın bir yolu, LocalDatenesnelere dönüştürmektir .

Bu dönüşüm, atanan saat dilimine bağlıdır. LocalDateNesneleri karşılaştırmak için, aynı bölgeye dönüştürülmüş olmaları gerekir.

İşte küçük bir yardımcı yöntem.

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

İlk DateTime nesnesinin saat diliminin kullanılması gerektiğini varsaymak yerine saat dilimini belirten başka bir argüman daha iyi olur.

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

Dize Temsili

Diğer bir yaklaşım, her bir tarih-saatin tarih bölümünün dize olarak temsilini oluşturmak ve ardından dizeleri karşılaştırmaktır.

Yine, atanan saat dilimi çok önemlidir.

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;

Zaman Aralığı

Genelleştirilmiş çözüm bir zaman aralığı tanımlamak, sonra açıklığın hedefinizi içerip içermediğini sormaktır. Bu örnek kod Joda-Time 2.4'te verilmiştir. "Gece yarısı" ile ilgili sınıfların kullanımdan kaldırıldığını unutmayın. Bunun yerine withTimeAtStartOfDayyöntemi kullanın . Joda-Time, çeşitli zaman aralıklarını temsil etmek için üç sınıf sunar: Aralık, Dönem ve Süre.

Açıklığın başlangıcını ve sonunun münhasır olduğu "Yarı Açık" yaklaşımını kullanmak.

Hedefin saat dilimi, aralığın saat diliminden farklı olabilir.

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

java.time

Java 8 ve üstü java.time çerçevesiyle birlikte gelir . JSR 310 tarafından tanımlanan ve ThreeTen-Extra projesi tarafından genişletilen Joda-Time'dan esinlenmiştir. Eğiticiye Bakın .

Joda-Time'ın yapımcıları bize en kısa sürede java.time'a geçmemizi söylediler. Bu arada Joda-Time aktif olarak sürdürülen bir proje olarak devam ediyor. Ancak gelecekteki çalışmaların Joda-Time yerine yalnızca java.time ve ThreeTen-Extra'da gerçekleşmesini bekleyin.

Özetle java.time özetlemek gerekirse… An InstantUTC zaman çizelgesinde bir andır. Nesneyi ZoneIdalmak için saat dilimi ( ) uygulayın ZonedDateTime. Bir tarih-zaman muğlak belirsiz fikir edinmek için, zaman çizelgesi kapalı taşımak için, "Yerel" sınıflarını kullanın: LocalDateTime, LocalDate,LocalTime .

Bu Yanıtın Joda-Time bölümünde tartışılan mantık java.time için geçerlidir.

Eski java.util.Date sınıfının toInstantjava.time biçimine dönüştürmek için yeni bir yöntemi vardır.

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

Tarih belirlemek için saat dilimi gerekir.

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

Bir saat Instantelde etmek için bu saat dilimi nesnesini ZonedDateTime. Bundan, LocalDatehedefimiz tarihleri ​​karşılaştırmak (yalnızca saat, dakika vb. Değil) için yalnızca tarih (a ) değerini alırız.

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

java.util.DateKarşılaştırma için ihtiyacımız olan ikinci nesneye de aynısını yapın . Bunun yerine sadece şu anı kullanacağım.

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

isEqualAynı tarih değerini test etmek için özel yöntemi kullanın .

Boolean sameDate = localDate1.isEqual( localDate2 );

java.time: Ya da sadece yapabilirsinizLocalDate localDate = LocalDate.fromDateFields(yourJavaUtilDate);
CyberMew

1
@CyberMew Er? Hiçbir yoktur fromDateFields()içinde benim LocalDatesınıfında .
Ole VV

1
Üzgünüm, daha net olmalıydım. Yorum, Joda-Time LocalDate sınıfı için geçerlidir.
CyberMew

7

Tarihleri burada görüldüğü gibi Java 8 java.time.LocalDate biçimine dönüştürün .

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

Bu benim en sevdiğim cevap. Bilgisayarın ayarına bağlı kalmak yerine kullanılan saat dilimini kontrol etmek isterseniz, örneğin ZoneId.of("America/Phoenix") veya ZoneId.of("Europe/Budapest")sistem varsayılanı yerine koymak basittir .
Ole VV

6

Java 8

Projenizde Java 8 kullanıyorsanız ve karşılaştırma java.sql.Timestampyapıyorsanız, LocalDatesınıfı kullanabilirsiniz :

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

Kullanıyorsanız java.util.Date, daha az belirsiz olan Istvan cevabına bir göz atın.


Java 8 kullanmak iyi bir fikirdir. Var mısın date1ve date2öyle java.sql.Timestampmi? Sorulan soruya göre Date, anlarım java.util.Date. Ayrıca kodunuz, birçok amaç için iyi olacak olan bilgisayarın saat dilimi ayarını kullanır; yine de bu gerçeği kodda açık yapmayı tercih ederim.
Ole VV

@ OleV.V. Yorumunuz için teşekkür ederim, orijinal cevabım gerçekten hakkındaydı java.sql.Timestamp. Dediğiniz gibi, bu zaman dilimini açıkça ayarlamak daha iyidir.
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);
    }

5

ANDROID KULLANICILARI İÇİN:

Kullanabilirsiniz DateUtils.isToday(dateMilliseconds)Belirtilen tarihin geçerli gün olup olmadığını kontrol etmek için .

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


1
Bu yöntem API 22'den beri Kullanımdan Kaldırılan Zaman nesnesini kullanır: developer.android.com/reference/android/text/format/Time.html
Oleksandr Bodashko

2
Android geliştirici için +1. Bu yöntem ilkel parametresini sürece kullanır, sadece kullanın DateUtils.isToday(x.getTime())(x bir java.util.date örneğidir)
jackycflau

1

Binil Thomas çözümüne ek olarak

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

kullanım

    isOnSameDay(date1,date2,date3 ...);
//or 
    isOnSameDay(mydates);

1

Kotlin devs için biçimlendirilmiş dizeler yaklaşımını karşılaştıran sürüm budur:

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

Bu en iyi yol değil, ama kısa ve çalışıyor.


1
SimpleDateFormatSınıf modern tarafından yıllar önce supplanted edildi java.time.DateTimeFormatter2019 yılında kullanımını öne süren JSR 310 'da tanımlanan sınıfa kötü tavsiyedir.
Basil Bourque

2
Bu kod, saat diliminin önemli sorununu yok sayar. Belirli bir an için, tarih tüm dünyada bölgeye göre değişir.
Basil Bourque

-4

SimpleDateFormat'a güvenmeden SimpleDateFormat çözümüyle aynı mantığı uygulayabilirsiniz

date1.getFullYear()*10000 + date1.getMonth()*100 + date1.getDate() == 
date2.getFullYear()*10000 + date2.getMonth()*100 + date2.getDate()

6
Bu yöntemler java.util.Date'de kullanımdan kaldırılmıştır. Bunu yapmak için Takvim'i kullanmalısınız. Tam olsun
Kris
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.