Jumlah hari antara dua tanggal di Joda-Time

336

Bagaimana cara menemukan perbedaan dalam Days antara dua instance Joda-Time DateTime ? Dengan 'perbedaan hari' maksud saya jika mulai pada hari Senin dan akhir pada hari Selasa saya mengharapkan nilai pengembalian 1 terlepas dari jam / menit / detik dari tanggal mulai dan berakhir.

Days.daysBetween(start, end).getDays() memberi saya 0 jika mulai di malam hari dan berakhir di pagi hari.

Saya juga mengalami masalah yang sama dengan bidang tanggal lainnya sehingga saya berharap akan ada cara umum untuk 'mengabaikan' bidang yang kurang penting.

Dengan kata lain, bulan antara Februari dan 4 Maret juga akan menjadi 1, seperti halnya jam antara 14:45 dan 15:12. Namun perbedaan jam antara 14:01 dan 14:55 adalah 0.

pvgoddijn
sumber

Jawaban:

393

Yang menjengkelkan, jawaban withTimeAtStartOfDay salah, tetapi hanya sesekali. Kamu ingin:

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

Ternyata "tengah malam / awal hari" kadang-kadang berarti 01:00 (penghematan siang hari terjadi seperti ini di beberapa tempat), yang Days.daysBetween tidak menangani dengan benar.

// 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

Akan LocalDatemenghindar seluruh masalah.

Alice Purcell
sumber
Bisakah Anda memberikan contoh kasus dengan data spesifik yang menurut Anda Days.daysBetweentidak benar?
Basil Bourque
Ketika Anda mengatakan untuk menggunakan Instant, Anda tidak hanya berbicara tentang start.toInstant(), bukan?
Patrick M
@ Patrickrick Ya, benar. Kalau dipikir-pikir, tidak jelas apa kendala yang dimaksudkan untuk memaksakan ini, jadi saya akan menghapus kalimat terakhir itu. Terima kasih!
Alice Purcell
@chrispy daysBetween doc mengatakan ini mengembalikan jumlah SELURUH hari. Dalam kasus saya 2 hari dan 1 jam harus mengembalikan saya 3 hari. Dalam contoh Anda ini mengembalikan 2 hari. Apakah ada cara untuk mencapai ini?
Sujit Joshi
@ SujitJoshi Saya khawatir saya tidak mengerti pertanyaan Anda. Saya sarankan memposting pertanyaan Stack Overflow penuh dan menempelkan tautan di sini untuk saya :)
Alice Purcell
186

Days Kelas

Menggunakan Dayskelas dengan withTimeAtStartOfDaymetode harus bekerja:

Days.daysBetween(start.withTimeAtStartOfDay() , end.withTimeAtStartOfDay() ).getDays() 
Michael Borgwardt
sumber
1
Terima kasih! Saya mencoba untuk mencapai perilaku android.text.format.DateUtils.getRelativeTimeSpanString () dengan joda dan ini sangat berguna.
gosho_ot_pochivka
1
Pak, dengan mengacu pada posting ini metode toDateMidnight()dari jenis DateTime sudah usang.
Aniket Kulkarni
2
Anda sekarang harus menggunakan .withTimeAtStartOfDay () alih-alih .toDateMidnight ()
bgolson
2
bagaimana jika endsebelum start, apakah mengembalikan hari negatif?
akhy
7
@akhyar: kenapa kamu tidak mencobanya?
Michael Borgwardt
86

Anda bisa menggunakan LocalDate:

Days.daysBetween(new LocalDate(start), new LocalDate(end)).getDays() 
Bozho
sumber
Pak, dengan mengacu pada posting ini metode toDateMidnight()dari jenis DateTime sudah usang.
Aniket Kulkarni
Mengapa digunakan new LocalDate(date)berlebihan date.toLocalDate()?
SARose
9

tl; dr

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

…atau…

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

waktu java

FYI, proyek Joda-Time sekarang dalam mode pemeliharaan , dengan tim menyarankan migrasi ke kelas java.time .

Setara dengan Joda-Time DateTimeadalah ZonedDateTime.

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

Tampaknya Anda ingin menghitung hari berdasarkan tanggal, artinya Anda ingin mengabaikan waktu dalam sehari. Misalnya, mulai satu menit sebelum tengah malam dan berakhir satu menit setelah tengah malam akan menghasilkan dalam satu hari. Untuk perilaku ini, ekstrak a LocalDatedari Anda ZonedDateTime. The LocalDatekelas mewakili nilai tanggal-hanya tanpa waktu-of-hari dan tanpa zona waktu.

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

Gunakan ChronoUnitenum untuk menghitung hari yang berlalu atau unit lainnya.

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

Memotong

Adapun Anda bertanya tentang cara yang lebih umum untuk melakukan penghitungan ini di mana Anda tertarik delta jam sebagai jam-jam daripada jam lengkap sebagai rentang waktu enam puluh menit, gunakan truncatedTo metode ini.

Ini adalah contoh Anda dari 14:45 sampai 15:12 pada hari yang sama.

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

Ini tidak berfungsi selama berhari-hari. Gunakan toLocalDate () dalam kasus ini.


Tentang java.time

The java.time kerangka dibangun ke Jawa 8 dan kemudian. Kelas-kelas ini menggantikan tua merepotkan warisan kelas tanggal-waktu seperti java.util.Date, Calendar, & SimpleDateFormat.

Proyek Joda-Time , sekarang dalam mode pemeliharaan , menyarankan migrasi ke java.time kelas .

Untuk mempelajari lebih lanjut, lihat Tutorial Oracle . Dan cari Stack Overflow untuk banyak contoh dan penjelasan. Spesifikasi adalah JSR 310 .

Anda dapat bertukar objek java.time secara langsung dengan database Anda. Gunakan driver JDBC yang sesuai dengan JDBC 4.2 atau yang lebih baru. Tidak perlu untuk string, tidak perlu untuk java.sql.*kelas.

Di mana mendapatkan kelas java.time?

Proyek ThreeTen-Extra memperpanjang java.time dengan kelas tambahan. Proyek ini adalah ajang pembuktian untuk kemungkinan penambahan masa depan ke java.time. Anda mungkin menemukan beberapa kelas berguna di sini seperti Interval, YearWeek, YearQuarter, dan lebih .

Proyek ThreeTen-Extra memperpanjang java.time dengan kelas tambahan. Proyek ini adalah ajang pembuktian untuk kemungkinan penambahan masa depan ke java.time. Anda mungkin menemukan beberapa kelas berguna di sini seperti Interval, YearWeek, YearQuarter, dan lebih .

Basil Bourque
sumber
TL; DR Anda selama berhari-hari menggunakan truncate falls memangsa bug yang dirinci dalam jawaban yang diterima, yaitu bug off-by-one saat dimulainya jam 1 pagi. Saya telah memperbaruinya untuk menggunakan jawaban yang benar yang Anda berikan lebih jauh, menggunakan toLocalDate.
Alice Purcell
2

Jawaban yang diterima membangun dua LocalDateobjek, yang cukup mahal jika Anda membaca banyak data. Saya menggunakan ini:

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

Dengan menelepon getMillis()Anda menggunakan variabel yang sudah ada.
MILLISECONDS.toDays()kemudian, menggunakan perhitungan aritmatika sederhana, tidak membuat objek apa pun.

JBoy
sumber
1
Jawaban ini hanya berfungsi jika definisi 'hari' Anda tepat 24 jam tanpa memperhatikan zona waktu dan tanggal. Jika demikian, gunakan pendekatan ini. Jika tidak, lihat jawaban lain yang membahas zona waktu.
Basil Bourque
@ BasilBourque, Anda benar, masih, saya akan mencari solusi berdasarkan perhitungan aritmatika daripada membangun objek mahal untuk setiap pemanggilan metode, ada banyak rasa di luar sana untuk perhitungan seperti itu, jika Anda membaca input dari halaman web, mungkin baik-baik saja, tetapi jika Anda sedang memproses file log dengan ratusan ribu baris ini hanya membuatnya sangat lambat
JBoy
1
Java akan mengoptimalkan alokasi objek jika loop Anda panas. Lihat docs.oracle.com/javase/7/docs/technotes/guides/vm/…
Alice Purcell
1

java.time.Period

Gunakan java.time.Periodkelas untuk menghitung hari .

Karena Java 8 menghitung perbedaan lebih intuitif menggunakan LocalDate, LocalDateTimeuntuk mewakili dua tanggal

    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);
AzizSM
sumber
0
DateTime  dt  = new DateTime(laterDate);        

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

System.out.println("No of days : " + newDate.getDayOfYear() - 1 );    
Arnab Bhui
sumber
-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;
}
pengguna1091978
sumber
1
pertanyaannya adalah secara khusus mencari joda-waktu.
kapad