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.

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.


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

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

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 .


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.


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

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
pertanyaannya adalah secara khusus mencari joda-waktu.
kapad
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.