Tính toán sự khác biệt giữa hai cá thể ngày Java


433

Tôi đang sử dụng java.util.Datelớp của Java trong Scala và muốn so sánh một Dateđối tượng và thời điểm hiện tại. Tôi biết tôi có thể tính toán delta bằng cách sử dụng getTime ():

(new java.util.Date()).getTime() - oldDate.getTime()

Tuy nhiên, điều này chỉ để lại cho tôi một longphần nghìn giây. Có cách nào đơn giản hơn, đẹp hơn để có được đồng bằng thời gian không?


10
Tại sao không có tình yêu cho thời gian joda? Đó là khá nhiều lựa chọn tốt nhất nếu bạn sẽ xử lý các ngày trong java.
Bác sĩ Jones

7
Vui lòng kiểm tra giải pháp lót 2 thanh lịch của tôi, không sử dụng Joda và đưa ra kết quả trong bất kỳ TimeUnit nào tại stackoverflow.com/a/10650881/82609
Sebastien Lorber

2
Thật xấu hổ cho tất cả những người giới thiệu thời gian của Joda và không đề xuất một câu trả lời Java thực sự ...
Zizouz212

2
@ Zizouz212 Liên quan đến việc giới thiệu Joda-Time, các lớp thời gian cũ đi kèm với Java là xấu, thực sự xấu, được thiết kế kém, khó hiểu và rắc rối. Tệ đến nỗi Joda-Time trở nên cực kỳ thành công khi họ thay thế. Tệ đến nỗi ngay cả Sun / Oracle cũng từ bỏ chúng và chấp nhận gói java.time như một phần của Java 8 trở lên. Các lớp java.time được lấy cảm hứng từ Joda-Time. Cả Joda-Time và java.time đều được dẫn dắt bởi cùng một người đàn ông, Stephen Colbourne . Tôi xin nói, “Xấu hổ về bất cứ ai giới thiệu sử dụng Date, CalendarSimpleDateFormat”.
Basil Bourque

2
Trong khi Joda Time có lẽ là một câu trả lời hay khi câu hỏi được hỏi, thì hôm nay câu trả lời tốt nhất cho bất kỳ ai có thể sử dụng Java 8 là sử dụng java.time.Periodvà / hoặc Duration. Xem câu trả lời của Basil Bourque dưới đây .
Ole VV

Câu trả lời:


198

DateAPI JDK bị hỏng một cách khủng khiếp. Tôi khuyên bạn nên sử dụng thư viện Joda Time .

Joda Time có một khái niệm về khoảng thời gian :

Interval interval = new Interval(oldTime, new Instant());

EDIT: Nhân tiện, Joda có hai khái niệm: Intervalđể biểu thị một khoảng thời gian giữa hai thời gian thực (đại diện cho thời gian từ 8 giờ sáng đến 10 giờ sáng) và Durationđại diện cho một khoảng thời gian không có ranh giới thời gian thực tế (ví dụ: đại diện cho hai giờ!)

Nếu bạn chỉ quan tâm đến việc so sánh thời gian, hầu hết các Datetriển khai (bao gồm cả JDK) đều thực hiện Comparablegiao diện cho phép bạn sử dụngComparable.compareTo()


btw - bạn có nghĩa là Comparable.compareTo(), không phải Comparable.compare().
Scott Morrison

Joda-Time có ba lớp để mô tả một khoảng thời gian theo nhiều cách khác nhau: Khoảng thời gian, Thời lượng và Thời gian. Câu trả lời đúng này thảo luận về hai đầu tiên này. Xem câu trả lời của tôi để biết thông tin về Thời gian.
Basil Bourque

Java 8 có một ngày và thời gian api mới như joda.
tbodt

11
java.timeGói mới trong Java 8 được lấy cảm hứng từ Joda-Time nhưng không phải là sự thay thế thả xuống. Mỗi thứ đều có ưu và nhược điểm riêng. May mắn thay, bạn không phải lựa chọn giữa chúng. Sử dụng từng điểm mạnh của nó miễn là bạn cẩn thận với các importtuyên bố của mình . Xem câu trả lời khác này cho ví dụ về java.time.
Basil Bourque

6
FYI, dự án Joda-Time hiện đang ở chế độ bảo trì , với nhóm tư vấn di chuyển đến các lớp java.time . Xem Hướng dẫn của Oracle .
Basil Bourque

550

Khác biệt đơn giản (không có lib)

/**
 * Get a diff between two dates
 * @param date1 the oldest date
 * @param date2 the newest date
 * @param timeUnit the unit in which you want the diff
 * @return the diff value, in the provided unit
 */
public static long getDateDiff(Date date1, Date date2, TimeUnit timeUnit) {
    long diffInMillies = date2.getTime() - date1.getTime();
    return timeUnit.convert(diffInMillies,TimeUnit.MILLISECONDS);
}

Và sau đó bạn có thể gọi:

getDateDiff(date1,date2,TimeUnit.MINUTES);

để có được độ lệch của 2 ngày tính theo đơn vị phút.

TimeUnitjava.util.concurrent.TimeUnit, một enum Java tiêu chuẩn đi từ nano đến ngày.


Khác biệt có thể đọc được của con người (không có lib)

public static Map<TimeUnit,Long> computeDiff(Date date1, Date date2) {

    long diffInMillies = date2.getTime() - date1.getTime();

    //create the list
    List<TimeUnit> units = new ArrayList<TimeUnit>(EnumSet.allOf(TimeUnit.class));
    Collections.reverse(units);

    //create the result map of TimeUnit and difference
    Map<TimeUnit,Long> result = new LinkedHashMap<TimeUnit,Long>();
    long milliesRest = diffInMillies;

    for ( TimeUnit unit : units ) {

        //calculate difference in millisecond 
        long diff = unit.convert(milliesRest,TimeUnit.MILLISECONDS);
        long diffInMilliesForUnit = unit.toMillis(diff);
        milliesRest = milliesRest - diffInMilliesForUnit;

        //put the result in the map
        result.put(unit,diff);
    }

    return result;
}

http://ideone.com/5dXeu6

Đầu ra là một cái gì đó như Map:{DAYS=1, HOURS=3, MINUTES=46, SECONDS=40, MILLISECONDS=0, MICROSECONDS=0, NANOSECONDS=0}, với các đơn vị đặt hàng.

Bạn chỉ cần chuyển đổi bản đồ đó thành một chuỗi thân thiện với người dùng.


Cảnh báo

Đoạn mã trên tính toán một sự khác biệt đơn giản giữa 2 instants. Nó có thể gây ra vấn đề trong một công tắc tiết kiệm ánh sáng ban ngày, như được giải thích trong bài viết này . Điều này có nghĩa là nếu bạn tính chênh lệch giữa các ngày mà không có thời gian bạn có thể thiếu một ngày / giờ.

Theo tôi thì ngày diff là loại chủ quan, đặc biệt là vào những ngày. Bạn có thể:

  • đếm số lượng thời gian đã trôi qua 24h: ngày + 1 - ngày = 1 ngày = 24h

  • đếm số lượng thời gian đã trôi qua, chăm sóc tiết kiệm ánh sáng ban ngày: ngày + 1 - ngày = 1 = 24h (nhưng sử dụng thời gian nửa đêm và tiết kiệm ánh sáng ban ngày có thể là 0 ngày và 23h)

  • đếm số lượng day switches, có nghĩa là ngày + 1 giờ chiều - ngày 11 giờ = 1 ngày, ngay cả khi thời gian đã trôi qua chỉ là 2h (hoặc 1h nếu có tiết kiệm ánh sáng ban ngày: p)

Câu trả lời của tôi là hợp lệ nếu định nghĩa ngày khác của bạn vào các ngày khớp với trường hợp thứ nhất

Với JodaTime

Nếu bạn đang sử dụng JodaTime, bạn có thể nhận được diff cho 2 ngày (millies được hỗ trợ ReadableInstant) với:

Interval interval = new Interval(oldInstant, new Instant());

Nhưng bạn cũng có thể nhận được khác biệt cho ngày / lần địa phương:

// returns 4 because of the leap year of 366 days
new Period(LocalDate.now(), LocalDate.now().plusDays(365*5), PeriodType.years()).getYears() 

// this time it returns 5
new Period(LocalDate.now(), LocalDate.now().plusDays(365*5+1), PeriodType.years()).getYears() 

// And you can also use these static methods
Years.yearsBetween(LocalDate.now(), LocalDate.now().plusDays(365*5)).getYears()

Wow, bạn thực sự có thể dạy một con chó già thủ thuật mới! Chưa bao giờ nghe về điều này trước đây, rất hữu ích để dịch các khác biệt ngày thành các chuỗi có ý nghĩa.
Jeremy Goodell

@SebastienLorber Có cách nào trong TimeUnit để tính toán sự khác biệt như vậy không? "Báo thức được đặt trong 3 ngày, 4 giờ và 12 phút kể từ bây giờ".
likejudo

công cụ tuyệt vời thực sự tiết kiệm rất nhiều thời gian của tôi. Đánh giá cao sự giúp đỡ của bạn.
Lyju I Edwinson

Tôi đã lãng phí 3 ngày trong cuộc đời mình để đối phó với các Ngày Java và tôi đã ném nó đi rất nhiều và thay thế bằng thứ này. Cảm ơn bạn.
1091524

Đây là câu trả lời hay nhất mà tôi đã thấy kể từ khi tôi tham gia stackoverflow! : D: D: D
Lạnh

153
int diffInDays = (int)( (newerDate.getTime() - olderDate.getTime()) 
                 / (1000 * 60 * 60 * 24) )

Lưu ý rằng điều này hoạt động với ngày UTC, vì vậy sự khác biệt có thể là một ngày nghỉ nếu bạn nhìn vào ngày địa phương. Và để nó hoạt động chính xác với ngày địa phương đòi hỏi một cách tiếp cận hoàn toàn khác do thời gian tiết kiệm ánh sáng ban ngày.


6
Điều này thực sự không hoạt động chính xác trong Android. Lỗi làm tròn tồn tại. Ví dụ từ 19 đến 21 tháng 5 cho biết 1 ngày vì nó chuyển từ 1.99 đến 1. Sử dụng vòng trước khi truyền tới int.
Pratik Mandrekar

4
Đây là câu trả lời tốt nhất và đơn giản nhất. Khi tính toán sự khác biệt giữa hai ngày, các múi giờ địa phương trừ nhau ... để câu trả lời đúng (dưới dạng gấp đôi) được đưa ra đơn giản bởi ((double) (mới.getTime () - old.getTime ()) / (86400.0 * 1000.0); ... là một đôi, bạn cũng có thể dễ dàng chuyển đổi thành HH: MM: ss.
scottb

8
@scottb: vấn đề với ngày địa phương là bạn có thể có thời gian tiết kiệm ánh sáng ban ngày, có nghĩa là một số ngày có 23 hoặc 25 giờ, có khả năng làm hỏng kết quả.
Michael Borgwardt

1
@Steve: đó là 28 đối với tôi khi xác định chúng như Ngày mới này (115, 2, 26); (chú ý đến tài liệu API cho các thông số). Có thể là bạn đang phân tích cú pháp chúng bằng định dạng ngày ở Hoa Kỳ và ở chế độ khoan hồng, để ngày 26/03/2015 được hiểu là ngày thứ 3 của tháng 26 năm 2015?
Michael Borgwardt

1
@Steve: hm, không chắc tại sao tôi lại nhận được kết quả khác trước đây, nhưng bây giờ tôi có thể tái tạo lỗi của bạn. Các niềng răng trong mã của bạn là khác nhau hơn trong câu trả lời của tôi, gây (endDate.getTime() - startDate.getTime())được đúc để intthay vì kết quả cuối cùng, và bạn nhận được một lỗi tràn số nguyên.
Michael Borgwardt

54

Sử dụng khung java.time được tích hợp trong Java 8+:

ZonedDateTime now = ZonedDateTime.now();
ZonedDateTime oldDate = now.minusDays(1).minusMinutes(10);
Duration duration = Duration.between(oldDate, now);
System.out.println("ISO-8601: " + duration);
System.out.println("Minutes: " + duration.toMinutes());

Đầu ra:

ISO-8601: PT24H10M

Phút: 1450

Để biết thêm thông tin, hãy xem Hướng dẫn của Oracletiêu chuẩn ISO 8601 .


5
Điều này, hoặc sử dụng Periodthay vì Durationcho ngày.
Aphex

53

Bạn cần xác định vấn đề của bạn rõ ràng hơn. Bạn chỉ có thể lấy số mili giây giữa hai Dateđối tượng và chia cho số mili giây trong 24 giờ, ví dụ ... nhưng:

  • Điều này sẽ không xem xét các múi giờ - Dateluôn có trong UTC
  • Điều này sẽ không mất thời gian tiết kiệm ánh sáng ban ngày (ví dụ, nơi có thể có những ngày chỉ dài 23 giờ)
  • Ngay cả trong UTC, có bao nhiêu ngày trong 16 tháng 11, 11 giờ tối đến 18 tháng 8, 2 giờ sáng? Chỉ có 27 giờ, vậy có nghĩa là một ngày? Hoặc nên là ba ngày bởi vì nó bao gồm ba ngày?

1
Tôi nghĩ java.util. Ngày chỉ là một trình bao bọc nhỏ xung quanh biểu diễn mili giây, được diễn giải theo múi giờ địa phương (mặc định). In ra một ngày cho tôi một đại diện múi giờ địa phương. Tôi có nhầm lẫn ở đây không?
Adriaan Koster

46

tl; dr

Chuyển đổi các java.util.Dateđối tượng lỗi thời của bạn để thay thế chúng , java.time.Instant. Sau đó tính thời gian trôi qua là a Duration.

Duration d = 
    Duration.between(                   // Calculate the span of time between two moments as a number of hours, minutes, and seconds.
        myJavaUtilDate.toInstant() ,    // Convert legacy class to modern class by calling new method added to the old class.
        Instant.now()                   // Capture the current moment in UTC. About two and a half hours later in this example.
    )
;

d.toString (): PT2H34M56S

d.toMinutes (): 154

d.toMinutesPart (): 34

Định dạng ISO 8601: PnYnMnDTnHnMnS

Tiêu chuẩn hợp lý ISO 8601 định nghĩa một biểu diễn văn bản ngắn gọn của một khoảng thời gian là một số năm, tháng, ngày, giờ, v.v. Tiêu chuẩn gọi như vậy trong một khoảng thời gian . Định dạng là PnYnMnDTnHnMnSnơi Pcó nghĩa là "Thời gian", phần Ttách biệt ngày với phần thời gian và ở giữa là các số được theo sau bởi một chữ cái.

Ví dụ:

  • P3Y6M4DT12H30M5S
    ba năm, sáu tháng, bốn ngày, mười hai giờ, ba mươi phút và năm giây
  • PT4H30M
    Bốn giờ rưỡi

java.time

Các java.time khuôn khổ xây dựng vào Java 8 và sau đó chiếm chỗ cũ phiền hà java.util.Date/ java.util.Calendarlớp. Các lớp mới được lấy cảm hứng từ khuôn khổ Joda-Time rất thành công , dự định là người kế thừa của nó, tương tự về khái niệm nhưng được tái kiến ​​trúc. Được xác định bởi JSR 310 . Mở rộng bởi dự án ThreeTen-Extra . Xem hướng dẫn .

Chốc lát

Các Instantlớp đại diện cho một thời điểm trên Timeline trong UTC với độ phân giải nano giây (lên đến chín (9) chữ số của một phân số thập phân).

Instant instant = Instant.now() ;  // Capture current moment in UTC.

Tốt nhất để tránh các lớp kế thừa như Date/ Calendar. Nhưng nếu bạn phải tương tác với mã cũ chưa được cập nhật lên java.time , hãy chuyển đổi qua lại. Gọi các phương thức chuyển đổi mới được thêm vào các lớp cũ. Để chuyển từ a java.util.Datesang an Instant, hãy gọi Date::toInstant.

Instant instant = myJavaUtilDate.toInstant() ;  // Convert from legacy `java.util.Date` class to modern `java.time.Instant` class.

Khoảng thời gian

Các lớp java.time đã chia ý tưởng này đại diện cho một khoảng thời gian là một số năm, tháng, ngày, giờ, phút, giây thành hai nửa:

  • Period trong nhiều năm, tháng, ngày
  • Duration cho ngày, giờ, phút, giây

Đây là một ví dụ.

ZoneId zoneId = ZoneId.of ( "America/Montreal" );
ZonedDateTime now = ZonedDateTime.now ( zoneId );
ZonedDateTime future = now.plusMinutes ( 63 );
Duration duration = Duration.between ( now , future );

Đổ vào bàn điều khiển.

Cả hai PeriodDurationsử dụng tiêu chuẩn ISO 8601 để tạo đại diện Chuỗi cho giá trị của chúng.

System.out.println ( "now: " + now + " to future: " + now + " = " + duration );

ngay bây giờ: 2015-11-26T00: 46: 48.016-05: 00 [America / Montreal] tới tương lai: 2015-11-26T00: 46: 48.016-05: 00 [America / Montreal] = PT1H3M

Java 9 thêm các phương thức để Durationcó được phần ngày, phần giờ, phần phút và phần giây.

Bạn có thể nhận được tổng số ngày hoặc giờ hoặc phút hoặc giây hoặc mili giây hoặc nano giây trong toàn bộ Thời lượng.

long totalHours = duration.toHours();

Trong Java 9, Durationlớp nhận được các phương thức mới để trả về các phần khác nhau của ngày, giờ, phút, giây, mili giây / nano giây. Gọi to…Partphương pháp: toDaysPart(), toHoursPart(), và vân vân.

ChronoUnit

Nếu bạn chỉ quan tâm đến độ chi tiết thời gian lớn hơn đơn giản hơn, chẳng hạn như số ngày trôi qua, hãy sử dụng ChronoUnitenum.

long daysElapsed = ChronoUnit.DAYS.between( earlier , later );

Một vi dụ khac.

Instant now = Instant.now();
Instant later = now.plus( Duration.ofHours( 2 ) );

long minutesElapsed = ChronoUnit.MINUTES.between( now , later );

120


Giới thiệu về java.time

Các java.time khung được xây dựng vào Java 8 và sau đó. Những lớp học thay thế cái cũ phiền hà di sản lớp học ngày thời gian như java.util.Date, Calendar, & SimpleDateFormat.

Các Joda thời gian dự án, bây giờ trong chế độ bảo trì , khuyên chuyển đổi sang java.time.

Để tìm hiểu thêm, xem Hướng dẫn Oracle . Và tìm kiếm Stack Overflow cho nhiều ví dụ và giải thích. Đặc điểm kỹ thuật là JSR 310 .

Nơi để có được các lớp java.time?

  • Java SE 8 SE 9 trở lên
    • Được xây dựng trong.
    • Một phần của API Java tiêu chuẩn với việc triển khai đi kèm.
    • Java 9 thêm một số tính năng nhỏ và sửa lỗi.
  • Java SE 6 SE 7
    • Phần lớn chức năng java.time được chuyển ngược lại sang Java 6 & 7 trong ThreeTen-Backport .
  • Android

Các ThreeTen-Extra dự án mở rộng java.time với các lớp bổ sung. Dự án này là một nền tảng chứng minh cho các bổ sung có thể trong tương lai cho java.time. Bạn có thể tìm thấy một số các lớp học hữu ích ở đây chẳng hạn như Interval, YearWeek, YearQuarter, và nhiều hơn nữa .


Joda-Thời gian

CẬP NHẬT: Dự án Joda-Time hiện đang ở chế độ bảo trì , với nhóm tư vấn di chuyển sang các lớp java.time . Tôi để phần này nguyên vẹn cho lịch sử.

Thư viện Joda-Time sử dụng ISO 8601 cho các mặc định của nó. PeriodLớp của nó phân tích cú pháp và tạo ra các chuỗi PnYnMnDTnHnMnS này.

DateTime now = DateTime.now(); // Caveat: Ignoring the important issue of time zones.
Period period = new Period( now, now.plusHours( 4 ).plusMinutes( 30));
System.out.println( "period: " + period );

Riders:

period: PT4H30M

Thật buồn cười khi bạn bắt đầu một bức tường văn bản không liên quan đến câu hỏi với "tl; dr". Câu hỏi không phải là về cách lấy dấu thời gian từ X trước hoặc theo đơn vị thời gian X, mà là làm thế nào để có được đồng bằng giữa hai ngày. Đó là nó.
Trâu

@Buffalo Tôi không hiểu bình luận của bạn. Câu trả lời của tôi liên quan đến Period, DurationChronoUnit, ba lớp có mục đích xác định đồng bằng giữa các thời điểm. Vui lòng cho biết những phần nào trong bức tường văn bản của tôi là không liên quan. Và bạn không chính xác khi nói Câu hỏi yêu cầu một vùng đồng bằng giữa các ngày Ngày: Câu hỏi yêu cầu một đồng bằng giữa Datecác đối tượng, đó là những khoảnh khắc thời gian, không phải là ngày hẹn hò mặc dù việc đặt tên không may của lớp đó.
Basil Bourque

Không có gì trong bình luận của bạn xử lý hai đối tượng java.util.Date hiện có. Tôi đã thử sử dụng các đoạn mã khác nhau trong mã của mình và không tìm thấy gì sử dụng đối tượng java.util.Date.
Trâu

@Buffalo Đủ công bằng, phê bình tốt. Tôi đã thêm mã trong phần "tl; dr" chuyển đổi từ a java.util.Datesang Instant(chỉ cần gọi .toInstant). Và tôi đã thêm Momentphần để giải thích. Bạn đã đề cập đến một cặp Dateđối tượng, nhưng thực tế Câu hỏi chỉ sử dụng một Dateđối tượng hiện có và sau đó ghi lại khoảnh khắc hiện tại, vì vậy tôi cũng làm như vậy. Cảm ơn vì bạn đã phản hồi! Mẹo: Từ bỏ việc sử dụng Date- Những lớp kế thừa đó thực sự là một mớ hỗn độn tồi tệ.
Basil Bourque


23

Một thay thế đơn giản hơn một chút:

System.currentTimeMillis() - oldDate.getTime()

Đối với "đẹp hơn": tốt, chính xác những gì bạn cần? Vấn đề với việc biểu thị thời lượng là một số giờ và ngày, vv là nó có thể dẫn đến sự không chính xác và kỳ vọng sai do sự phức tạp của ngày (ví dụ: ngày có thể có 23 hoặc 25 giờ do thời gian tiết kiệm ánh sáng ban ngày).


22

Sử dụng phương pháp mili giây có thể gây ra vấn đề ở một số địa phương.

Ví dụ, hãy lấy sự khác biệt giữa hai ngày 24/03/2007 và 25/03/2007 là 1 ngày;

Tuy nhiên, sử dụng tuyến đường mili giây, bạn sẽ nhận được 0 ngày, nếu bạn chạy tuyến đường này ở Vương quốc Anh!

/** Manual Method - YIELDS INCORRECT RESULTS - DO NOT USE**/  
/* This method is used to find the no of days between the given dates */  
public long calculateDays(Date dateEarly, Date dateLater) {  
   return (dateLater.getTime() - dateEarly.getTime()) / (24 * 60 * 60 * 1000);  
} 

Cách tốt hơn để thực hiện điều này là sử dụng java.util.CalWiki

/** Using Calendar - THE CORRECT WAY**/  
public static long daysBetween(Calendar startDate, Calendar endDate) {  
  Calendar date = (Calendar) startDate.clone();  
  long daysBetween = 0;  
  while (date.before(endDate)) {  
    date.add(Calendar.DAY_OF_MONTH, 1);  
    daysBetween++;  
  }  
  return daysBetween;  
}  

19
Bạn có thể vui lòng ghi nhận tác giả gốc của mã này và câu thứ ba, mục nhập blog có từ năm 2007 không?
wchargein

Nó không phải là một chút không hiệu quả?
Gangnus

Nó không hoạt động chính xác. daysB between (new GregorianCalWiki (2014,03,01), GregorianCalWiki mới (2014,04,02))); trả về 31 và nó sẽ trả về 32: timeanddate.com/date/ từ
marcolopes

5
@marcolopes - Bạn đã sai - vì tháng theo lịch là không có. Tôi chắc chắn rằng bạn có nghĩa là tháng ba / tháng tư, nhưng những gì bạn đang kiểm tra là tháng tư / tháng sáu là 31. Để an toàn hãy viết nó như thế -> GregorianCalWiki mới (2014, Lịch.MARCH, 1) ....
Bác Iroh

@UncleIroh, bạn nói đúng! Tôi đã bỏ lỡ thực tế quan trọng đó (tháng dương lịch là không dựa trên). Tôi phải xem lại câu hỏi này một lần nữa.
marcolopes

22

Có nhiều cách bạn có thể tìm thấy sự khác biệt giữa ngày và thời gian. Một trong những cách đơn giản nhất mà tôi biết sẽ là:

      Calendar calendar1 = Calendar.getInstance();
      Calendar calendar2 = Calendar.getInstance();
      calendar1.set(2012, 04, 02);
      calendar2.set(2012, 04, 04);
      long milsecs1= calendar1.getTimeInMillis();
      long milsecs2 = calendar2.getTimeInMillis();
      long diff = milsecs2 - milsecs1;
      long dsecs = diff / 1000;
      long dminutes = diff / (60 * 1000);
      long dhours = diff / (60 * 60 * 1000);
      long ddays = diff / (24 * 60 * 60 * 1000);

      System.out.println("Your Day Difference="+ddays);

Câu lệnh in chỉ là một ví dụ - bạn có thể định dạng nó, theo cách bạn muốn.


5
@ manoj kumar bardhan: welcome to stack overflow: Như bạn thấy câu hỏi và câu trả lời đã cũ. Câu trả lời của bạn nên thêm nhiều câu hỏi / câu trả lời hơn câu hỏi hiện có.
Jayan

3
Còn những ngày có 23 hoặc 25 giờ do chuyển đổi DST thì sao?
Joni

19

Vì tất cả các câu trả lời ở đây đều đúng nhưng sử dụng các lib của java hoặc bên thứ 3 như joda hoặc tương tự, tôi sẽ bỏ cách khác bằng cách sử dụng các lớp java.time mới trong Java 8 trở lên. Xem Hướng dẫn của Oracle .

Sử dụng LocalDateChronoUnit:

LocalDate d1 = LocalDate.of(2017, 5, 1);
LocalDate d2 = LocalDate.of(2017, 5, 18);

long days = ChronoUnit.DAYS.between(d1, d2);
System.out.println( days );

9

Trừ các ngày tính bằng mili giây hoạt động (như được mô tả trong bài đăng khác), nhưng bạn phải sử dụng HOUR_OF_DAY và không GIỜ khi xóa các phần thời gian trong ngày của bạn:

public static final long MSPERDAY = 60 * 60 * 24 * 1000;
...
final Calendar dateStartCal = Calendar.getInstance();
dateStartCal.setTime(dateStart);
dateStartCal.set(Calendar.HOUR_OF_DAY, 0); // Crucial.
dateStartCal.set(Calendar.MINUTE, 0);
dateStartCal.set(Calendar.SECOND, 0);
dateStartCal.set(Calendar.MILLISECOND, 0);
final Calendar dateEndCal = Calendar.getInstance();
dateEndCal.setTime(dateEnd);
dateEndCal.set(Calendar.HOUR_OF_DAY, 0); // Crucial.
dateEndCal.set(Calendar.MINUTE, 0);
dateEndCal.set(Calendar.SECOND, 0);
dateEndCal.set(Calendar.MILLISECOND, 0);
final long dateDifferenceInDays = ( dateStartCal.getTimeInMillis()
                                  - dateEndCal.getTimeInMillis()
                                  ) / MSPERDAY;
if (dateDifferenceInDays > 15) {
    // Do something if difference > 15 days
}

1
Đây là phương pháp tốt nhất để xác định sự khác biệt giữa hai ngày theo lịch mà không cần đi ra ngoài các thư viện tiêu chuẩn. Hầu hết các câu trả lời khác coi một ngày là khoảng thời gian 24 giờ tùy ý. Nếu các giây nhuận đã bị trừ đi thay vì được thêm vào, phép tính cuối cùng có thể bị tắt do một lần cắt, vì sự khác biệt tuyệt đối có thể nhỏ hơn một chút so với toàn bộ số bội của MSPERDAY.
JulianSymes

8

Nếu bạn không muốn sử dụng JodaTime hoặc tương tự, giải pháp tốt nhất có lẽ là:

final static long MILLIS_PER_DAY = 24 * 3600 * 1000;
long msDiff= date1.getTime() - date2.getTime();
long daysDiff = Math.round(msDiff / ((double)MILLIS_PER_DAY));

Số ms mỗi ngày không phải lúc nào cũng giống nhau (vì thời gian tiết kiệm ánh sáng ban ngày và giây nhuận), nhưng nó rất gần và ít nhất là sai lệch do thời gian tiết kiệm ánh sáng ban ngày hủy bỏ trong thời gian dài hơn. Do đó, việc chia và làm tròn sẽ cho kết quả chính xác (ít nhất là miễn là lịch địa phương được sử dụng không chứa các bước nhảy kỳ lạ khác với DST và giây nhuận).

Lưu ý rằng điều này vẫn giả định rằng date1date2được đặt cùng thời gian trong ngày. Đối với các thời điểm khác nhau trong ngày, trước tiên bạn phải xác định "chênh lệch ngày" nghĩa là gì, như Jon Skeet đã chỉ ra.


Vấn đề là ở date1.getTime()vs date2.getTime(). Vì vậy, khác biệt 2016-03-03 đến 2016-03-31 sẽ tính 27 ngày trong khi bạn muốn 28 ngày.
malat

@malat: Xin lỗi, tôi không thể theo dõi. Tôi vừa thử nó - từ 2016-03-03 đến 2016-03-31 với mã của tôi tính 28 ngày. Nếu nó tính toán một cái gì đó khác trong ví dụ của bạn, hãy xem xét việc hỏi đó như một câu hỏi riêng biệt.
sleske

Ngoài ra, bạn đã đọc lời cảnh báo của tôi? "Điều này vẫn cho rằng date1 và date2 được đặt cùng thời gian trong ngày". Bạn có thể sử dụng thời gian khác nhau trong ngày trong bài kiểm tra của bạn?
sleske

À đúng rồi! Tôi đã bỏ lỡ mánh khóe với Math.round(), điều đó dường như an toàn trong lịch 'thế giới thực'. Xin lỗi vì sự ồn ào.
malat

8

Hãy xem Joda Time , đây là API Ngày / Giờ được cải tiến cho Java và sẽ hoạt động tốt với Scala.


được thay thế bằng java.time (JSR-310) trong Java 8
malat

5

Hãy để tôi cho thấy sự khác biệt giữa Joda Interval và Days:

DateTime start = new DateTime(2012, 2, 6, 10, 44, 51, 0);
DateTime end = new DateTime(2012, 2, 6, 11, 39, 47, 1);
Interval interval = new Interval(start, end);
Period period = interval.toPeriod();
System.out.println(period.getYears() + " years, " + period.getMonths() + " months, " + period.getWeeks() + " weeks, " + period.getDays() + " days");
System.out.println(period.getHours() + " hours, " + period.getMinutes() + " minutes, " + period.getSeconds() + " seconds ");
//Result is:
//0 years, 0 months, *1 weeks, 1 days*
//0 hours, 54 minutes, 56 seconds 

//Period can set PeriodType,such as PeriodType.yearMonthDay(),PeriodType.yearDayTime()...
Period p = new Period(start, end, PeriodType.yearMonthDayTime());
System.out.println(p.getYears() + " years, " + p.getMonths() + " months, " + p.getWeeks() + " weeks, " + p.getDays() + "days");
System.out.println(p.getHours() + " hours, " + p.getMinutes() + " minutes, " + p.getSeconds() + " seconds ");
//Result is:
//0 years, 0 months, *0 weeks, 8 days*
//0 hours, 54 minutes, 56 seconds 

5

Nếu bạn cần một chuỗi trả về được định dạng như "2 ngày 03h 42m 07s", hãy thử điều này:

public String fill2(int value)
{
    String ret = String.valueOf(value);

    if (ret.length() < 2)
        ret = "0" + ret;            
    return ret;
}

public String get_duration(Date date1, Date date2)
{                   
    TimeUnit timeUnit = TimeUnit.SECONDS;

    long diffInMilli = date2.getTime() - date1.getTime();
    long s = timeUnit.convert(diffInMilli, TimeUnit.MILLISECONDS);

    long days = s / (24 * 60 * 60);
    long rest = s - (days * 24 * 60 * 60);
    long hrs = rest / (60 * 60);
    long rest1 = rest - (hrs * 60 * 60);
    long min = rest1 / 60;      
    long sec = s % 60;

    String dates = "";
    if (days > 0) dates = days + " Days ";

    dates += fill2((int) hrs) + "h ";
    dates += fill2((int) min) + "m ";
    dates += fill2((int) sec) + "s ";

    return dates;
}

2
Whoa, tại sao cuộn riêng khi bạn Joda thời gian cung cấp Thời gian lớp học đã được viết và sửa lỗi?
Basil Bourque

8
Tại sao tôi nên tải xuống Thư viện có kích thước FileS nén 4,1 MB và thêm nó vào Dự án của mình, khi tôi chỉ cần 32 dòng mã ???
Ingo

1
Bởi vì Joda-Time giống như khoai tây chiên: bạn không thể ăn chỉ một. Bạn sẽ sử dụng Joda-Time ở mọi nơi. Và bởi vì Joda-Time là mã được kiểm tra tốt và mòn. Và bởi vì Joda-Time phân tích cú pháp và tạo các chuỗi Thời lượng ISO 8601 tiêu chuẩn có thể thuận tiện cho việc tuần tự hóa hoặc báo cáo các giá trị này. Và bởi vì Joda-Time bao gồm các trình định dạng để in các biểu diễn cục bộ của các giá trị này.
Basil Bourque

Không phải tất cả mã của bạn được kiểm tra. stdở trên là không xác định.
Basil Bourque

@Basil Bourque: Thx cho gợi ý. Tôi đã lấy nguồn. Lỗi này xuất phát từ bản dịch sang tiếng Anh.
Ingo

5

Lưu ý: startDate và endDates là -> java.util.Date

import org.joda.time.Duration;
import org.joda.time.Interval;
// Use .getTime() unless it is a joda DateTime object
Interval interval = new Interval(startDate.getTime(), endDate.getTime());
Duration period = interval.toDuration();
//gives the number of days elapsed between start 
period.getStandardDays();

và ngày kết thúc

Tương tự như ngày, bạn cũng có thể nhận được giờ, phút và giây

period.getStandardHours();
period.getStandardMinutes();
period.getStandardSeconds();

4

Kiểm tra ví dụ ở đây http://www.roseindia.net/java/beginners/DateDifferent.shtml Ví dụ này cung cấp cho bạn sự khác biệt về ngày, giờ, phút, giây và milli giây :).

import java.util.Calendar;
import java.util.Date;

public class DateDifferent {
    public static void main(String[] args) {
        Date date1 = new Date(2009, 01, 10);
        Date date2 = new Date(2009, 07, 01);
        Calendar calendar1 = Calendar.getInstance();
        Calendar calendar2 = Calendar.getInstance();
        calendar1.setTime(date1);
        calendar2.setTime(date2);
        long milliseconds1 = calendar1.getTimeInMillis();
        long milliseconds2 = calendar2.getTimeInMillis();
        long diff = milliseconds2 - milliseconds1;
        long diffSeconds = diff / 1000;
        long diffMinutes = diff / (60 * 1000);
        long diffHours = diff / (60 * 60 * 1000);
        long diffDays = diff / (24 * 60 * 60 * 1000);
        System.out.println("\nThe Date Different Example");
        System.out.println("Time in milliseconds: " + diff + " milliseconds.");
        System.out.println("Time in seconds: " + diffSeconds + " seconds.");
        System.out.println("Time in minutes: " + diffMinutes + " minutes.");
        System.out.println("Time in hours: " + diffHours + " hours.");
        System.out.println("Time in days: " + diffDays + " days.");
    }
}

3
-1 Điều đó sai trừ khi các phiên bản Ngày được lấy từ thời UTC. Xem câu trả lời của Jon Skeet.
sleske

4

Sử dụng múi giờ GMT để lấy phiên bản của Lịch, đặt thời gian bằng cách sử dụng phương thức đã đặt của lớp Lịch. Múi giờ GMT có 0 bù (không thực sự quan trọng) và cờ thời gian tiết kiệm ánh sáng ban ngày được đặt thành sai.

    final Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));

    cal.set(Calendar.YEAR, 2011);
    cal.set(Calendar.MONTH, 9);
    cal.set(Calendar.DAY_OF_MONTH, 29);
    cal.set(Calendar.HOUR, 0);
    cal.set(Calendar.MINUTE, 0);
    cal.set(Calendar.SECOND, 0);
    final Date startDate = cal.getTime();

    cal.set(Calendar.YEAR, 2011);
    cal.set(Calendar.MONTH, 12);
    cal.set(Calendar.DAY_OF_MONTH, 21);
    cal.set(Calendar.HOUR, 0);
    cal.set(Calendar.MINUTE, 0);
    cal.set(Calendar.SECOND, 0);
    final Date endDate = cal.getTime();

    System.out.println((endDate.getTime() - startDate.getTime()) % (1000l * 60l * 60l * 24l));

Đừng quên phần 0 mili giây khác, đó là một câu trả lời tốt trong ngữ cảnh của các lớp cũ, java.util.Datev.v ... Sử dụng hack để đặt múi giờ thành GMT làm cho mã không nhạy cảm với hiệu ứng tiết kiệm ánh sáng ban ngày.
Meno Hochschild

4

Mã sau có thể cung cấp cho bạn đầu ra mong muốn:

String startDate = "Jan 01 2015";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MMM dd yyyy");
LocalDate date = LocalDate.parse(startDate, formatter);

String currentDate = "Feb 11 2015";
LocalDate date1 = LocalDate.parse(currentDate, formatter);

System.out.println(date1.toEpochDay() - date.toEpochDay());

3
Bạn có thể giải thích chính xác những gì mã của bạn làm? Câu hỏi dường như được hỏi về đồng bằng thời gian và, thoạt nhìn, mã của bạn dường như sẽ trả về một ngày delta?
GHC

4
public static String getDifferenceBtwTime(Date dateTime) {

    long timeDifferenceMilliseconds = new Date().getTime() - dateTime.getTime();
    long diffSeconds = timeDifferenceMilliseconds / 1000;
    long diffMinutes = timeDifferenceMilliseconds / (60 * 1000);
    long diffHours = timeDifferenceMilliseconds / (60 * 60 * 1000);
    long diffDays = timeDifferenceMilliseconds / (60 * 60 * 1000 * 24);
    long diffWeeks = timeDifferenceMilliseconds / (60 * 60 * 1000 * 24 * 7);
    long diffMonths = (long) (timeDifferenceMilliseconds / (60 * 60 * 1000 * 24 * 30.41666666));
    long diffYears = (long)(timeDifferenceMilliseconds / (1000 * 60 * 60 * 24 * 365));

    if (diffSeconds < 1) {
        return "one sec ago";
    } else if (diffMinutes < 1) {
        return diffSeconds + " seconds ago";
    } else if (diffHours < 1) {
        return diffMinutes + " minutes ago";
    } else if (diffDays < 1) {
        return diffHours + " hours ago";
    } else if (diffWeeks < 1) {
        return diffDays + " days ago";
    } else if (diffMonths < 1) {
        return diffWeeks + " weeks ago";
    } else if (diffYears < 12) {
        return diffMonths + " months ago";
    } else {
        return diffYears + " years ago";
    }
}   

3
int daysDiff = (date1.getTime() - date2.getTime()) / MILLIS_PER_DAY;

5
-1 Điều đó sai trừ khi các phiên bản Ngày được lấy từ thời UTC. Xem câu trả lời của Jon Skeet.
sleske

5
@sleske bạn không đúng. Anh ta đã mất thông tin múi giờ bằng cách sử dụng Date, vì vậy so sánh này là tốt nhất có thể đạt được trong hoàn cảnh.
Bozho

1
OK, đoán chúng ta đều đúng. Đúng là nó phụ thuộc vào trường hợp Date đến từ đâu. Tuy nhiên, đó là một lỗi phổ biến mà nó nên được đề cập.
sleske

Khi tôi sử dụng giải pháp của bạn, nó nói: Nhập không khớp: không thể chuyển đổi từ dài sang int. Tôi nghĩ bạn cũng nên thêm casting hay tôi đang thiếu thứ gì?
Quảng cáo Infinitum

3

Điều tốt nhất để làm là

(Date1-Date2)/86 400 000 

Con số đó là số mili giây trong một ngày.

Một ngày - ngày khác cung cấp cho bạn sự khác biệt tính bằng mili giây.

Thu thập câu trả lời trong một biến kép .


Có tại sao không? Tôi không hiểu ý nghĩa của câu hỏi theo một số cách: OP có sự khác biệt về ms ... và có một số ms nhất định trong một ngày (OK với một số điều chỉnh thiên văn nhất định một lần trong một mặt trăng xanh, nhưng OP không 't nói rằng nó phải chính xác như các nhà thiên văn học cần nó ...)
loài gặm nhấm

3

Đây là một giải pháp Java 7 chính xác trong O (1) mà không có bất kỳ phụ thuộc nào.

public static int countDaysBetween(Date date1, Date date2) {

    Calendar c1 = removeTime(from(date1));
    Calendar c2 = removeTime(from(date2));

    if (c1.get(YEAR) == c2.get(YEAR)) {

        return Math.abs(c1.get(DAY_OF_YEAR) - c2.get(DAY_OF_YEAR)) + 1;
    }
    // ensure c1 <= c2
    if (c1.get(YEAR) > c2.get(YEAR)) {
        Calendar c = c1;
        c1 = c2;
        c2 = c;
    }
    int y1 = c1.get(YEAR);
    int y2 = c2.get(YEAR);
    int d1 = c1.get(DAY_OF_YEAR);
    int d2 = c2.get(DAY_OF_YEAR);

    return d2 + ((y2 - y1) * 365) - d1 + countLeapYearsBetween(y1, y2) + 1;
}

private static int countLeapYearsBetween(int y1, int y2) {

    if (y1 < 1 || y2 < 1) {
        throw new IllegalArgumentException("Year must be > 0.");
    }
    // ensure y1 <= y2
    if (y1 > y2) {
        int i = y1;
        y1 = y2;
        y2 = i;
    }

    int diff = 0;

    int firstDivisibleBy4 = y1;
    if (firstDivisibleBy4 % 4 != 0) {
        firstDivisibleBy4 += 4 - (y1 % 4);
    }
    diff = y2 - firstDivisibleBy4 - 1;
    int divisibleBy4 = diff < 0 ? 0 : diff / 4 + 1;

    int firstDivisibleBy100 = y1;
    if (firstDivisibleBy100 % 100 != 0) {
        firstDivisibleBy100 += 100 - (firstDivisibleBy100 % 100);
    }
    diff = y2 - firstDivisibleBy100 - 1;
    int divisibleBy100 = diff < 0 ? 0 : diff / 100 + 1;

    int firstDivisibleBy400 = y1;
    if (firstDivisibleBy400 % 400 != 0) {
        firstDivisibleBy400 += 400 - (y1 % 400);
    }
    diff = y2 - firstDivisibleBy400 - 1;
    int divisibleBy400 = diff < 0 ? 0 : diff / 400 + 1;

    return divisibleBy4 - divisibleBy100 + divisibleBy400;
}


public static Calendar from(Date date) {

    Calendar c = Calendar.getInstance();
    c.setTime(date);

    return c;
}


public static Calendar removeTime(Calendar c) {

    c.set(HOUR_OF_DAY, 0);
    c.set(MINUTE, 0);
    c.set(SECOND, 0);
    c.set(MILLISECOND, 0);

    return c;
}

2

Đó có lẽ là cách đơn giản nhất để làm điều đó - có lẽ đó là vì tôi đã mã hóa bằng Java (với các thư viện ngày và giờ được thừa nhận của nó) trong một thời gian, nhưng mã đó có vẻ "đơn giản và tốt đẹp" đối với tôi!

Bạn có hài lòng với kết quả được trả về sau mili giây hay là một phần câu hỏi của bạn mà bạn muốn trả lại ở một số định dạng thay thế?


2

Không sử dụng API tiêu chuẩn, không. Bạn có thể tự mình làm một cái gì đó như thế này:

class Duration {
    private final TimeUnit unit;
    private final long length;
    // ...
}

Hoặc bạn có thể sử dụng Joda :

DateTime a = ..., b = ...;
Duration d = new Duration(a, b);

2

Chỉ để trả lời câu hỏi ban đầu:

Đặt mã sau vào Hàm như Long getAge () {}

Date dahora = new Date();
long MillisToYearsByDiv = 1000l *60l * 60l * 24l * 365l;
long javaOffsetInMillis = 1990l * MillisToYearsByDiv;
long realNowInMillis = dahora.getTime() + javaOffsetInMillis;
long realBirthDayInMillis = this.getFechaNac().getTime() + javaOffsetInMillis;
long ageInMillis = realNowInMillis - realBirthDayInMillis;

return ageInMillis / MillisToYearsByDiv;

Điều quan trọng nhất ở đây là làm việc với các số dài khi nhân và chia. Và tất nhiên, phần bù mà Java áp dụng trong phép tính Ngày của nó.

:)


2

Vì câu hỏi được gắn thẻ với Scala,

import scala.concurrent.duration._
val diff = (System.currentTimeMillis() - oldDate.getTime).milliseconds
val diffSeconds = diff.toSeconds
val diffMinutes = diff.toMinutes
val diffHours = diff.toHours
val diffDays = diff.toDays

2

Sau khi lội qua tất cả các câu trả lời khác, để giữ loại Ngày của Java 7 nhưng chính xác hơn / tiêu chuẩn hơn với cách tiếp cận khác biệt của Java 8,

public static long daysBetweenDates(Date d1, Date d2) {
    Instant instant1 = d1.toInstant();
    Instant instant2 = d2.toInstant();
    long diff = ChronoUnit.DAYS.between(instant1, instant2);
    return diff;
}

1

thử cái này:

int epoch = (int) (new java.text.SimpleDateFormat("MM/dd/yyyy HH:mm:ss").parse("01/01/1970  00:00:00").getTime() / 1000);

bạn có thể chỉnh sửa chuỗi trong phương thức parse () param.

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.