Số ngày giữa hai ngày trong Joda-Time


335

Làm cách nào để tìm thấy sự khác biệt về Ngày giữa hai trường hợp Joda-Time DateTime ? Với 'sự khác biệt về số ngày' Ý tôi là nếu bắt đầu vào Thứ Hai và kết thúc vào Thứ Ba, tôi mong đợi giá trị trả về là 1 bất kể giờ / phút / giây của ngày bắt đầu và ngày kết thúc.

Days.daysBetween(start, end).getDays() cho tôi 0 nếu bắt đầu vào buổi tối và kết thúc vào buổi sáng.

Tôi cũng gặp vấn đề tương tự với các trường ngày khác vì vậy tôi hy vọng sẽ có một cách chung để 'bỏ qua' các trường có ý nghĩa ít hơn.

Nói cách khác, các tháng từ tháng 2 đến 4 tháng 3 cũng sẽ là 1, cũng như các giờ trong khoảng từ 14:45 đến 15:12. Tuy nhiên, chênh lệch giờ giữa 14:01 và 14:55 sẽ là 0.

Câu trả lời:


393

Khó chịu, câu trả lời withTimeAtStartOfDay là sai, nhưng chỉ thỉnh thoảng. Bạn muốn:

Days.daysBetween(start.toLocalDate(), end.toLocalDate()).getDays()

Hóa ra "nửa đêm / bắt đầu ngày" đôi khi có nghĩa là 1 giờ sáng (tiết kiệm ánh sáng ban ngày xảy ra theo cách này ở một số nơi), mà Days.daysB between không xử lý đúng cách.

// 5am on the 20th to 1pm on the 21st, October 2013, Brazil
DateTimeZone BRAZIL = DateTimeZone.forID("America/Sao_Paulo");
DateTime start = new DateTime(2013, 10, 20, 5, 0, 0, BRAZIL);
DateTime end = new DateTime(2013, 10, 21, 13, 0, 0, BRAZIL);
System.out.println(daysBetween(start.withTimeAtStartOfDay(),
                               end.withTimeAtStartOfDay()).getDays());
// prints 0
System.out.println(daysBetween(start.toLocalDate(),
                               end.toLocalDate()).getDays());
// prints 1

Đi qua một LocalDatebên cho toàn bộ vấn đề.


Bạn có thể cung cấp một trường hợp ví dụ với dữ liệu cụ thể mà bạn cho Days.daysBetweenlà không chính xác?
Basil Bourque

Khi bạn nói để sử dụng Instant, bạn không chỉ nói về start.toInstant(), phải không?
Patrick M

@PatrickM Vâng, tôi đã. Về phản xạ, không rõ ràng chính xác những hạn chế này nhằm áp đặt, vì vậy tôi sẽ xóa câu cuối cùng đó. Cảm ơn!
Alice Purcell

@chrispy ngày Giữa các tài liệu nói rằng nó trả về số ngày WHOLE . Trong trường hợp của tôi 2 ngày và 1 giờ nên trả lại cho tôi 3 ngày. Trong ví dụ của bạn, nó trả về 2 ngày. Có cách nào để đạt được điều này?
Sujit Joshi

@SujitJoshi Tôi sợ tôi không hiểu câu hỏi của bạn. Tôi đề nghị đăng một câu hỏi Stack Overflow đầy đủ và dán một liên kết ở đây cho tôi :)
Alice Purcell

186

Days Lớp học

Sử dụng Dayslớp với withTimeAtStartOfDayphương thức sẽ hoạt động:

Days.daysBetween(start.withTimeAtStartOfDay() , end.withTimeAtStartOfDay() ).getDays() 

1
cảm ơn! Tôi đã cố gắng để đạt được hành vi của android.text.format.DateUtils.getRelativeTimeSpanString () với joda và điều này thực sự hữu ích.
gosho_ot_pochivka

1
Thưa ông, với tham chiếu đến bài đăng này , phương pháp toDateMidnight()từ loại DateTime không được dùng nữa.
Aniket Kulkarni

2
Bây giờ bạn nên sử dụng .withTimeAtStartOfDay () thay vì .toDate REnight ()
bgolson

2
Điều gì xảy ra nếu endtrước ngày start, nó có trả về ngày âm không?
akhy 4/2/2015

7
@akhyar: tại sao bạn không thử?
Michael Borgwardt

86

bạn có thể sử dụng LocalDate:

Days.daysBetween(new LocalDate(start), new LocalDate(end)).getDays() 

Thưa ông, với tham chiếu đến bài đăng này , phương pháp toDateMidnight()từ loại DateTime không được dùng nữa.
Aniket Kulkarni

Tại sao sử dụng new LocalDate(date)hơn date.toLocalDate()?
SARose

9

tl; dr

java.time.temporal.ChronoUnit.DAYS.between( 
    earlier.toLocalDate(), 
    later.toLocalDate() 
)

…hoặc là…

java.time.temporal.ChronoUnit.HOURS.between( 
    earlier.truncatedTo( ChronoUnit.HOURS )  , 
    later.truncatedTo( ChronoUnit.HOURS ) 
)

java.time

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 .

Tương đương với Joda-Time DateTimeZonedDateTime.

ZoneId z = ZoneId.of( "Pacific/Auckland" ) ;
ZonedDateTime now = ZonedDateTime.now( z ) ;

Rõ ràng bạn muốn đếm ngày theo ngày, nghĩa là bạn muốn bỏ qua thời gian trong ngày. Ví dụ, bắt đầu một phút trước nửa đêm và kết thúc một phút sau nửa đêm sẽ dẫn đến một ngày. Đối với hành vi này, trích xuất một LocalDatetừ của bạn ZonedDateTime. Các LocalDatelớp đại diện cho một giá trị ngày tháng chỉ mà không cần thời gian của ngày và không có múi giờ.

LocalDate localDateStart = zdtStart.toLocalDate() ;
LocalDate localDateStop = zdtStop.toLocalDate() ;

Sử dụng ChronoUnitenum để tính ngày trôi qua hoặc các đơn vị khác.

long days = ChronoUnit.DAYS.between( localDateStart , localDateStop ) ;

Cắt ngắn

Đối với bạn hỏi về một cách tổng quát hơn để thực hiện việc này, trong đó bạn quan tâm đến đồng bằng giờ là giờ đồng hồ thay vì giờ hoàn thành trong khoảng thời gian sáu mươi phút, hãy sử dụng truncatedTophương pháp này.

Dưới đây là ví dụ của bạn về 14:45 đến 15:12 cùng ngày.

ZoneId z = ZoneId.of( "America/Montreal" ); 
ZonedDateTime start = ZonedDateTime.of( 2017 , 1 , 17 , 14 , 45 , 0 , 0 , z );
ZonedDateTime stop = ZonedDateTime.of( 2017 , 1 , 17 , 15 , 12 , 0 , 0 , z );

long hours = ChronoUnit.HOURS.between( start.truncatedTo( ChronoUnit.HOURS ) , stop.truncatedTo( ChronoUnit.HOURS ) );

1

Điều này không hoạt động trong nhiều ngày. Sử dụng toLocalDate () trong trường hợp này.


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 các java.time lớp.

Để 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 .

Bạn có thể trao đổi các đối tượng java.time trực tiếp với cơ sở dữ liệu của bạn. Sử dụng trình điều khiển JDBC tương thích với JDBC 4.2 trở lên. Không cần chuỗi, không cần java.sql.*lớp.

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

  • Java SE 8 , Java SE 9 , Java SE 10 và mới hơ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 Java 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 phiên bản sau của triển khai gói Android của các lớp java.time.
    • Đối với Android trước đó (<26), các ThreeTenABP dự án thích nghi ThreeTen-backport (nêu trên). Xem Cách sử dụng ThreeTenABP .

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 .

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 .


TL; DR của bạn trong nhiều ngày sử dụng cắt cụt rơi vào lỗi được nêu chi tiết trong câu trả lời được chấp nhận, cụ thể là lỗi này khi bắt đầu vào lúc 1 giờ sáng. Tôi đã cập nhật nó để sử dụng câu trả lời chính xác mà bạn đã đưa ra, sử dụng toLocalDate.
Alice Purcell

2

Câu trả lời được chấp nhận xây dựng hai LocalDateđối tượng, khá tốn kém nếu bạn đang đọc nhiều dữ liệu. Tôi sử dụng cái này:

  public static int getDaysBetween(DateTime earlier, DateTime later)
  {
    return (int) TimeUnit.MILLISECONDS.toDays(later.getMillis()- earlier.getMillis());
  }

Bằng cách gọi getMillis()bạn sử dụng các biến đã có sẵn.
MILLISECONDS.toDays()sau đó, sử dụng phép tính số học đơn giản, không tạo ra bất kỳ đối tượng nào.


1
Câu trả lời này chỉ hoạt động nếu định nghĩa của bạn về 'ngày' chính xác là 24 giờ mà không quan tâm đến múi giờ và ngày. Nếu vậy, sử dụng phương pháp này. Nếu không, hãy tìm đến các câu trả lời khác có địa chỉ múi giờ.
Basil Bourque

@BasilBourque, bạn đã đúng, tuy nhiên, tôi sẽ tìm giải pháp dựa trên tính toán số học thay vì xây dựng các đối tượng đắt tiền cho mỗi cuộc gọi phương thức, có rất nhiều hương vị cho các tính toán như vậy, nếu bạn đang đọc một đầu vào từ một trang web, có thể ổn, nhưng nếu bạn đang xử lý một tệp nhật ký với hàng trăm ngàn dòng thì điều này sẽ làm cho nó rất chậm
JBoy

1
Java sẽ tối ưu hóa việc phân bổ đối tượng nếu vòng lặp của bạn nóng. Xem docs.oracle.com/javase/7/docs/technotes/guides/vm/iêu
Alice Purcell

1

java.time.Period

Sử dụng java.time.Periodlớp để đếm ngày .

Do Java 8 tính toán sự khác biệt trực quan hơn bằng cách sử dụng LocalDate, LocalDateTimeđể thể hiện hai ngày

    LocalDate now = LocalDate.now();
    LocalDate inputDate = LocalDate.of(2018, 11, 28);

    Period period = Period.between( inputDate, now);
    int diff = period.getDays();
    System.out.println("diff = " + diff);

0
DateTime  dt  = new DateTime(laterDate);        

DateTime newDate = dt.minus( new  DateTime ( previousDate ).getMillis());

System.out.println("No of days : " + newDate.getDayOfYear() - 1 );    

-11
public static int getDifferenceIndays(long timestamp1, long timestamp2) {
    final int SECONDS = 60;
    final int MINUTES = 60;
    final int HOURS = 24;
    final int MILLIES = 1000;
    long temp;
    if (timestamp1 < timestamp2) {
        temp = timestamp1;
        timestamp1 = timestamp2;
        timestamp2 = temp;
    }
    Calendar startDate = Calendar.getInstance(TimeZone.getDefault());
    Calendar endDate = Calendar.getInstance(TimeZone.getDefault());
    endDate.setTimeInMillis(timestamp1);
    startDate.setTimeInMillis(timestamp2);
    if ((timestamp1 - timestamp2) < 1 * HOURS * MINUTES * SECONDS * MILLIES) {
        int day1 = endDate.get(Calendar.DAY_OF_MONTH);
        int day2 = startDate.get(Calendar.DAY_OF_MONTH);
        if (day1 == day2) {
            return 0;
        } else {
            return 1;
        }
    }
    int diffDays = 0;
    startDate.add(Calendar.DAY_OF_MONTH, diffDays);
    while (startDate.before(endDate)) {
        startDate.add(Calendar.DAY_OF_MONTH, 1);
        diffDays++;
    }
    return diffDays;
}

1
câu hỏi đặc biệt là tìm kiếm thời gian joda.
kapad 7/07/2015
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.