So sánh hai java.util.Dates để xem chúng có trong cùng một ngày không


249

Tôi cần so sánh hai Dates (ví dụ date1date2) và đưa ra một boolean sameDaycái đúng với hai Dates cùng một ngày và sai nếu không.

Tôi có thể làm cái này như thế nào? Dường như có một cơn lốc nhầm lẫn ở đây ... và tôi muốn tránh kéo theo những phụ thuộc khác ngoài JDK nếu có thể.

để làm rõ: nếu date1date2chia sẻ cùng năm, tháng và ngày, thì sameDayđúng, nếu không thì là sai. Tôi nhận ra điều này đòi hỏi kiến ​​thức về múi giờ ... sẽ rất tuyệt nếu vượt qua múi giờ nhưng tôi có thể sống với GMT hoặc giờ địa phương miễn là tôi biết hành vi đó là gì.

một lần nữa, để làm rõ:

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

Chỉ cần làm rõ - bạn muốn biết nếu hai đối tượng Ngày rơi vào cùng một ngày trong tuần?
Rob Heiser

Bạn có muốn so sánh ngày đầy đủ (ngày, tháng, năm) hoặc chỉ tháng ngày?
XpiritO

1
@Rob: không, cùng ngày / tháng / năm ... tôi sẽ làm rõ.
Jason S

Vậy thì tại sao bạn không sử dụng "bằng"?
XpiritO

7
Bởi vì chúng không bằng nhau nếu giờ / phút / giây khác nhau.
Jason S

Câu trả lời:


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

Lưu ý rằng "cùng một ngày" không phải là một khái niệm đơn giản như âm thanh khi các múi giờ khác nhau có thể được tham gia. Mã ở trên sẽ cho cả hai ngày tính ngày liên quan đến múi giờ được sử dụng bởi máy tính mà nó đang chạy. Nếu đây không phải là những gì bạn cần, bạn phải chuyển (các) múi giờ liên quan đến các Calendar.getInstance()cuộc gọi, sau khi bạn đã quyết định chính xác ý bạn là gì với "cùng một ngày".

Và vâng, Joda Time LocalDatesẽ làm cho mọi thứ sạch sẽ và dễ dàng hơn nhiều (mặc dù những khó khăn tương tự liên quan đến múi giờ sẽ xuất hiện).


Cảm ơn, có vẻ như nó sẽ làm những gì tôi muốn. Trong trường hợp của tôi, tôi đang so sánh các ngày liên tiếp trong một chuỗi, vì vậy có vẻ như tôi chỉ có thể sử dụng các phiên bản Lịch thay vì các phiên bản Ngày trong chuỗi của mình.
Jason S

1
@Jason Điều đó có thể hoặc không thể là một ý tưởng tốt. Vấn đề chính với Lịch là nó là một lớp rất nặng với nhiều trạng thái bên trong, một số được sử dụng trong triển khai bằng () của nó. Nếu bạn không copmpare ngày của bạn cho bình đẳng và không đưa chúng vào HashMaps, bạn sẽ ổn thôi.
Michael Borgwardt

tuyệt, cảm ơn, tôi chỉ sử dụng logic so sánh hiện tại và ngày trước được hỏi ở đây. các hàm "bằng" và "mã băm" sẽ không bao giờ được gọi.
Jason S

1
@UmerHayat: mã so sánh ngày trong năm, không phải ngày trong tháng. Một ít so sánh cần thiết, mã ngắn hơn.
Michael Borgwardt

2
Gợi ý: Trước tiên hãy so sánh DAY_OF_YEAR, nó sẽ không phải kiểm tra năm. Ok không giống như so sánh int thực sự tốn kém nhưng ...
Martin P.

332

Làm thế nào về:

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

Bạn cũng có thể đặt múi giờ thành SimpleDateFormat, nếu cần.


2
(Tôi thực sự đang sử dụng SimpleDateFormat trong trường hợp của mình, vì vậy nó có vẻ phù hợp.)
Jason S

8
Tôi thực sự ngạc nhiên khi thấy điều này, nhưng ngay cả từ quan điểm hiệu suất, SimpleDateFormatphương pháp này thực sự nhanh hơn phương pháp khác được đề cập ở đây bằng Calendars. Trung bình phải mất một nửa thời gian như Calendarphương pháp. (Ít nhất là trên hệ thống của tôi). Thanh danh!
Michael Plautz

Hầu hết chi phí của câu trả lời này là trên việc tạo SimpleDateFormat. Đặt nó trong một trường chủ đề trong một đơn vị nếu bạn muốn hiệu suất cao hơn nữa
Thierry

1
Tôi cũng làm điều này trong Swift. Thật đáng ngạc nhiên khi một điều đơn giản như vậy lại yêu cầu hack giống nhau ở hầu hết các ngôn ngữ. C # là ngoại lệ - if (d1.Date == d2.Date) ...
alpsystems.com

2
Điều này thật xấu xí; ngạc nhiên khi thấy một hack như thế này được nâng cấp.
Zsolt Safrany

162

Tôi sử dụng gói "apache commons lang" để làm điều này (cụ thể là org.apache.commons.lang.time.DateUtils )

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

2
Điều này sử dụng một phụ thuộc bên ngoài ... nhưng thật tốt khi biết tương lai.
Jason S

20
Chỉ cần sao chép nguồn và gọi nó là một ngày :)
Anton Kuzmin

Nhưng nó ném một ngoại lệ đối số bất hợp pháp nếu bất kỳ ngày nào là null, điều này không lý tưởng trong một số trường hợp.
raviraja

1
Như đã nhận xét trong stackoverflow.com/a/2517824/1665809 giải pháp này có thể không phù hợp nếu các múi giờ khác nhau có liên quan.
mrod

24

Bạn có thể tránh các phụ thuộc bên ngoài và hiệu suất của việc sử dụng Lịch bằng cách tính Số ngày Julian cho mỗi ngày và sau đó so sánh các ngày sau:

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
Nhưng hãy cẩn thận rằng điều này không tính đến các thay đổi về độ dài của ngày do tiết kiệm ánh sáng ban ngày. Nhưng sau đó, Dates không đóng gói khái niệm múi giờ nên không có cách nào để tự ý sửa lỗi này.
AyeJay

3
Đây là cách giải quyết nhanh nhất - cảm ơn rất nhiều, chính xác những gì tôi đang tìm kiếm; vì tôi phải kiểm tra rất nhiều ngày ...
Vô lý

2
Nitpick: date1.getTime () không trả về Số Ngày Julian, nhưng mili giây kể từ 1970-01-01. Sự khác biệt lớn.
Per Lindberg

2
Điều này thực hiện so sánh trong múi giờ UTC. Nếu bạn muốn múi giờ địa phương của bạn hoặc một số địa điểm khác trên hành tinh, bạn không thể biết liệu kết quả bạn nhận được có chính xác hay không.
Ole VV

20

Joda-Thời gian

Đối với việc thêm một phụ thuộc, tôi sợ java.util.Date & .CalWiki thực sự tệ đến mức điều đầu tiên tôi làm cho bất kỳ dự án mới nào là thêm thư viện Joda-Time. Trong Java 8, bạn có thể sử dụng gói java.time mới, lấy cảm hứng từ Joda-Time.

Cốt lõi của Joda-Time là DateTimelớp học. Không giống như java.util.Date, nó hiểu múi giờ được chỉ định ( DateTimeZone). Khi chuyển đổi từ juDate, chỉ định một vùng.

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

LocalDate

Một cách để xác minh nếu hai lần đất vào cùng một ngày là chuyển đổi thành LocalDatecác đối tượng.

Chuyển đổi đó phụ thuộc vào múi giờ được chỉ định. Để so sánh LocalDatecác đối tượng, chúng phải được chuyển đổi với cùng một vùng.

Đây là một phương pháp tiện ích nhỏ.

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

Tốt hơn sẽ là một đối số khác, chỉ định múi giờ thay vì giả sử sử dụng múi giờ của đối tượng DateTime đầu tiên.

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

Đại diện chuỗi

Một cách tiếp cận khác là tạo một chuỗi đại diện cho phần ngày của mỗi thời gian, sau đó so sánh các chuỗi.

Một lần nữa, múi giờ được chỉ định là rất quan trọng.

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;

Khoảng thời gian

Giải pháp tổng quát là xác định một khoảng thời gian, sau đó hỏi xem khoảng đó có chứa mục tiêu của bạn không. Mã ví dụ này nằm trong Joda-Time 2.4. Lưu ý rằng các lớp liên quan đến "nửa đêm" không được dùng nữa. Thay vào đó hãy sử dụng withTimeAtStartOfDayphương pháp. Joda-Time cung cấp ba lớp để thể hiện một khoảng thời gian theo nhiều cách khác nhau: Khoảng thời gian, Thời gian và Thời lượng.

Sử dụng phương pháp "Nửa mở" trong đó phần đầu của nhịp được bao gồm và phần cuối độc quyền.

Múi giờ của mục tiêu có thể khác với múi giờ của khoảng thời gian.

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 trở lên đi kèm với khung java.time . Lấy cảm hứng từ Joda-Time, được xác định bởi JSR 310 và được mở rộng bởi dự án ThreeTen-Extra. Xem hướng dẫn .

Các nhà sản xuất của Joda-Time đã hướng dẫn tất cả chúng ta chuyển sang java.time ngay khi thuận tiện. Trong khi đó, Joda-Time tiếp tục là một dự án được duy trì tích cực. Nhưng mong đợi công việc trong tương lai chỉ xảy ra trong java.time và ThreeTen-Extra chứ không phải Joda-Time.

Để tóm tắt java.time trong một tóm tắt, An An Instantlà một khoảnh khắc trên dòng thời gian trong UTC. Áp dụng múi giờ ( ZoneId) để lấy ZonedDateTimeđối tượng. Để di chuyển ra khỏi dòng thời gian, để có được những ý tưởng vô hạn mơ hồ của một ngày thời gian, sử dụng các lớp "địa phương": LocalDateTime, LocalDate, LocalTime.

Logic được thảo luận trong phần Joda-Time của Câu trả lời này áp dụng cho java.time.

Lớp java.util.Date cũ có một toInstantphương thức mới để chuyển đổi sang java.time.

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

Xác định một ngày đòi hỏi một múi giờ.

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

Chúng tôi áp dụng đối tượng múi giờ đó Instantđể có được a ZonedDateTime. Từ đó, chúng tôi trích xuất một giá trị chỉ ngày (a LocalDate) vì mục tiêu của chúng tôi là so sánh ngày (không phải giờ, phút, v.v.).

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

Làm tương tự với java.util.Dateđối tượng thứ hai chúng ta cần so sánh. Tôi sẽ chỉ sử dụng thời điểm hiện tại thay thế.

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

Sử dụng isEqualphương pháp đặc biệt để kiểm tra cho cùng một giá trị ngày.

Boolean sameDate = localDate1.isEqual( localDate2 );

java.time: Hoặc bạn chỉ có thể làmLocalDate localDate = LocalDate.fromDateFields(yourJavaUtilDate);
CyberMew

1
@CyberMew Er? Không có fromDateFields()trong lớp học của tôiLocalDate .
Ole VV

1
Xin lỗi, tôi nên đã rõ ràng hơn. Nhận xét có liên quan đến lớp Joda-Time LocalDate .
CyberMew

7

Chuyển đổi ngày thành Java 8 java.time.LocalDate như được thấy ở đây .

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

Đây là câu trả lời yêu thích của tôi. Nếu bạn muốn kiểm soát múi giờ được sử dụng thay vì dựa vào cài đặt của máy tính, ví dụ ZoneId.of("America/Phoenix") , đơn giản để đặt vào, ZoneId.of("Europe/Budapest")thay vì mặc định của hệ thống.
Ole VV

6

Java 8

Nếu bạn đang sử dụng Java 8 trong dự án của mình và so sánh java.sql.Timestamp, bạn có thể sử dụng LocalDatelớp:

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

Nếu bạn đang sử dụng java.util.Date, hãy xem câu trả lời của Istvan ít mơ hồ hơn.


Sử dụng Java 8 là một ý tưởng tốt. Bạn đang giả định date1date2đang java.sql.Timestamp? Theo câu hỏi họ Date, tôi sẽ hiểu java.util.Date. Ngoài ra mã của bạn sử dụng cài đặt múi giờ của máy tính, với nhiều mục đích sẽ ổn; Tuy nhiên, tôi muốn làm cho sự thật này rõ ràng trong mã.
Ole VV

@ OleV.V. cảm ơn bạn đã bình luận của bạn, câu trả lời ban đầu của tôi thực sự là về java.sql.Timestamp. Như bạn đã nói, điều này thường tốt hơn để đặt múi giờ rõ ràng.
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

CHO NGƯỜI SỬ DỤNG ANDROID:

Bạn có thể sử dụng DateUtils.isToday(dateMilliseconds)để kiểm tra xem ngày đã cho có phải là ngày hiện tại hay không.

Tham chiếu API: https://developer.android.com/reference/android/text/format/DateUtils.html#isToday(long)


1
Phương pháp này sử dụng đối tượng Thời gian không dùng nữa kể từ API 22: developer.android.com/reference/android/text/format/Time.html
Oleksandr Bodashko

2
+1 cho nhà phát triển Android. phương thức này sử dụng tham số nguyên thủy như tham số, chỉ cần sử dụng DateUtils.isToday(x.getTime())(x là một ví dụ java.util.date) sẽ làm
jackycflau

1

ngoài giải pháp 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;
}

sử dụng

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

1

Đối với nhà phát triển Kotlin, đây là phiên bản so sánh cách tiếp cận chuỗi được định dạng:

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

Đó không phải là cách tốt nhất, nhưng nó ngắn và hiệu quả.


1
Các SimpleDateFormatlớp học đã được thay thế năm trước bởi sự hiện đại java.time.DateTimeFormatterlớp được định nghĩa trong JSR 310. Đề xuất việc sử dụng nó vào năm 2019 là lời khuyên nghèo.
Basil Bourque

2
Mã này bỏ qua vấn đề quan trọng của múi giờ. Đối với bất kỳ thời điểm nào, ngày thay đổi trên toàn cầu theo khu vực.
Basil Bourque

-4

bạn có thể áp dụng logic tương tự như giải pháp SimpleDateFormat mà không cần dựa vào SimpleDateFormat

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

6
Các phương thức này không được dùng trong java.util.Date. Bạn nên sử dụng Lịch để làm điều này. Ngoài ra, đó là getYear, không phải getFullYear
Kris
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.