จำนวนวันระหว่างสองวันใน Joda-Time


336

ฉันจะค้นหาความแตกต่างในวันระหว่างสองอินสแตนซ์ของJoda-Time ได้ DateTimeอย่างไร ด้วย 'ความแตกต่างในวัน' ฉันหมายความว่าถ้าการเริ่มต้นเป็นวันจันทร์และสิ้นสุดเป็นวันอังคารฉันคาดว่าค่าตอบแทน 1 ไม่ว่าชั่วโมง / นาที / วินาทีของวันที่เริ่มต้นและสิ้นสุดจะเป็นอย่างไร

Days.daysBetween(start, end).getDays() ให้ฉันเป็น 0 ถ้าเริ่มในตอนเย็นและจบในตอนเช้า

ฉันยังมีปัญหาเดียวกันกับเขตข้อมูลวันที่อื่นดังนั้นฉันจึงหวังว่าจะมีวิธีทั่วไปในการ 'เพิกเฉย' สาขาที่มีความสำคัญน้อยกว่า

กล่าวอีกนัยหนึ่งเดือนระหว่างเดือนกุมภาพันธ์ถึง 4 มีนาคมก็จะเป็น 1 เช่นเดียวกับชั่วโมงระหว่าง 14:45 ถึง 15:12 อย่างไรก็ตามความแตกต่างของชั่วโมงระหว่าง 14:01 ถึง 14:55 จะเป็น 0

คำตอบ:


393

น่ารำคาญคำตอบ withTimeAtStartOfDay นั้นผิด แต่มีเพียงบางครั้งเท่านั้น คุณต้องการ:

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

ปรากฎว่าบางครั้ง "เที่ยงคืน / วันเริ่มต้น" หมายถึง 1am (การประหยัดเวลากลางวันเกิดขึ้นในบางสถานที่) ซึ่ง Days.daysBetween ไม่ได้จัดการอย่างเหมาะสม

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

ก้าวผ่านLocalDateปัญหาทั้งหมด


คุณสามารถให้ข้อมูลเฉพาะกรณีตัวอย่างที่คุณคิดว่าDays.daysBetweenไม่ถูกต้องได้หรือไม่?
Basil Bourque

เมื่อคุณพูดว่าจะใช้Instantคุณไม่ได้พูดถึงstart.toInstant()ใช่มั้ย
Patrick M

@PatrickM ใช่ฉันเป็น จากการไตร่ตรองแล้วมันไม่ชัดเจนเลยว่าข้อ จำกัด นี้ตั้งใจจะกำหนดดังนั้นฉันจะลบประโยคสุดท้ายนั้น ขอบคุณ!
Alice Purcell

@chrispy daysB ระหว่าง doc บอกว่ามันคืนค่าจำนวนวันทั้งหมด ในกรณีของฉัน 2 วันและ 1 ชั่วโมงควรกลับมาฉัน 3 วัน ในตัวอย่างของคุณจะส่งคืน 2 วัน มีวิธีที่จะบรรลุเป้าหมายนี้หรือไม่?
Sujit Joshi

@SujitJoshi ฉันกลัวว่าฉันไม่เข้าใจคำถามของคุณ ผมขอแนะนำให้โพสต์เต็มรูปแบบคำถามกองมากเกินและวางการเชื่อมโยงที่นี่สำหรับฉัน :)
อลิซเพอร์เซลล์

186

Days ชั้น

การใช้Daysคลาสด้วยwithTimeAtStartOfDayเมธอดควรทำงาน:

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

1
ขอบคุณ! ฉันพยายามที่จะบรรลุพฤติกรรมของ android.text.format.DateUtils.getRelativeTimeSpanString () กับ joda และนี่มีประโยชน์จริงๆ
gosho_ot_pochivka

1
ท่านที่มีการอ้างอิงถึงโพสต์นี้วิธีการtoDateMidnight()จากประเภท DateTime จะเลิก
Aniket Kulkarni

2
ตอนนี้คุณควรใช้. withTimeAtStartOfDay () แทน. toDateMidnight ()
bgolson

2
เกิดอะไรขึ้นถ้าendก่อนหน้าstartนั้นมันจะคืนค่าลบหรือไม่?
akhy

7
@akhyar: ทำไมคุณไม่ลอง?
Michael Borgwardt

86

คุณสามารถใช้LocalDate:

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

ท่านที่มีการอ้างอิงถึงโพสต์นี้วิธีการtoDateMidnight()จากประเภท DateTime จะเลิก
Aniket Kulkarni

ทำไมต้องใช้new LocalDate(date)มากกว่าdate.toLocalDate()?
SARose

9

TL; DR

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

…หรือ…

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

java.time

FYI โครงการJoda-Timeขณะนี้อยู่ในโหมดบำรุงรักษาโดยทีมงานให้คำแนะนำการย้ายถิ่นไปยังคลาสjava.time

เทียบเท่า Joda เวลาคือDateTimeZonedDateTime

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

เห็นได้ชัดว่าคุณต้องการที่จะนับวันตามวันที่หมายถึงคุณต้องการที่จะไม่สนใจเวลาของวัน ตัวอย่างเช่นการเริ่มต้นหนึ่งนาทีก่อนเที่ยงคืนและสิ้นสุดหนึ่งนาทีหลังเที่ยงคืนควรทำให้เกิดวันเดียว สำหรับพฤติกรรมนี้แยกจากLocalDate ชั้นหมายถึงค่าวันเท่านั้นโดยไม่ต้องเวลาของวันและไม่มีโซนเวลาZonedDateTimeLocalDate

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

ใช้ChronoUnitenum เพื่อคำนวณวันที่ผ่านไปหรือหน่วยอื่น ๆ

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

ตัด

สำหรับคุณที่ถามเกี่ยวกับวิธีการทั่วไปในการนับจำนวนนี้มากขึ้นโดยที่คุณสนใจเดลต้าชั่วโมงเป็นชั่วโมงต่อชั่วโมงแทนที่จะเป็นชั่วโมงที่สมบูรณ์ในช่วงเวลาหกสิบนาทีให้ใช้truncatedToวิธีนี้

นี่คือตัวอย่างของคุณ 14:45 ถึง 15:12 ในวันเดียวกัน

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

สิ่งนี้ไม่ทำงานหลายวัน ใช้ toLocalDate () ในกรณีนี้


เกี่ยวกับjava.time

java.timeกรอบถูกสร้างขึ้นใน Java 8 และต่อมา ชั้นเรียนเหล่านี้แย่งลำบากเก่ามรดกเรียนวันที่เวลาเช่นjava.util.Date, และCalendarSimpleDateFormat

Joda เวลาโครงการขณะนี้อยู่ในโหมดการบำรุงรักษาให้คำแนะนำแก่การโยกย้ายไปยังjava.timeชั้นเรียน

ต้องการเรียนรู้เพิ่มเติมโปรดดูที่ออราเคิลกวดวิชา และค้นหา Stack Overflow สำหรับตัวอย่างและคำอธิบายมากมาย สเปกJSR 310

คุณสามารถแลกเปลี่ยนวัตถุjava.timeโดยตรงกับฐานข้อมูลของคุณ ใช้ไดรเวอร์ JDBC ที่สอดคล้องกับJDBC 4.2หรือใหม่กว่า ไม่จำเป็นต้องใช้สตริงไม่จำเป็นต้องมีjava.sql.*คลาส

จะรับคลาส java.time ได้ที่ไหน?

  • Java SE 8 , Java SE 9 , Java SE 10และใหม่กว่า
    • Built-in
    • เป็นส่วนหนึ่งของ Java API มาตรฐานที่มีการใช้งานแบบรวม
    • Java 9 เพิ่มคุณสมบัติและการแก้ไขเล็กน้อย
  • Java SE 6และ Java SE 7
    • มากของการทำงาน java.time จะกลับรังเพลิง Java 6 และ 7 ในThreeTen-ย้ายกลับ
  • Android
    • การใช้งานชุดบันเดิล Android รุ่นต่อมาของคลาส java.time
    • สำหรับก่อนหน้านี้ Android (<26) ที่ThreeTenABPโครงการปรับThreeTen-ย้ายกลับ (ดังกล่าวข้างต้น) ดูวิธีการใช้ ThreeTenABP … .

โครงการThreeTen-Extraขยาย java.time ด้วยคลาสเพิ่มเติม โครงการนี้เป็นพื้นฐานที่พิสูจน์ได้สำหรับการเพิ่มเติมในอนาคตไปยัง java.time คุณอาจพบว่าการเรียนที่มีประโยชน์บางอย่างที่นี่เช่นInterval, YearWeek, YearQuarterและอื่น ๆ อีกมากมาย

โครงการThreeTen-Extraขยาย java.time ด้วยคลาสเพิ่มเติม โครงการนี้เป็นพื้นฐานที่พิสูจน์ได้สำหรับการเพิ่มเติมในอนาคตไปยัง java.time คุณอาจพบว่าการเรียนที่มีประโยชน์บางอย่างที่นี่เช่นInterval, YearWeek, YearQuarterและอื่น ๆ อีกมากมาย


TL; DR ของคุณเป็นเวลาหลายวันโดยใช้ truncate ตกเป็นเหยื่อของข้อผิดพลาดที่มีรายละเอียดในคำตอบที่ยอมรับซึ่งก็คือ off-by-one เมื่อเริ่มต้นวันที่ 1am ฉันได้ทำการปรับปรุงให้ใช้คำตอบที่ถูกต้องที่คุณให้ไว้โดยใช้ toLocalDate
อลิซเพอร์เซลล์

2

คำตอบที่ได้รับการยอมรับจะสร้างLocalDateวัตถุสองชนิดซึ่งค่อนข้างแพงหากคุณกำลังอ่านข้อมูลจำนวนมาก ฉันใช้สิ่งนี้:

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

โดยการโทรgetMillis()คุณใช้ตัวแปรที่มีอยู่แล้ว
MILLISECONDS.toDays()จากนั้นใช้การคำนวณทางคณิตศาสตร์อย่างง่ายไม่ได้สร้างวัตถุใด ๆ


1
คำตอบนี้ใช้ได้เฉพาะในกรณีที่คำจำกัดความของ 'วัน' ของคุณยาว 24 ชั่วโมงโดยไม่คำนึงถึงเขตเวลาและวันที่ ถ้าเป็นเช่นนั้นใช้วิธีนี้ หากไม่ใช่ให้ดูคำตอบอื่น ๆ ที่ระบุเขตเวลา
Basil Bourque

@BasilBourque คุณพูดถูกฉันยังคงหาวิธีแก้ปัญหาการคำนวณทางคณิตศาสตร์แทนที่จะสร้างวัตถุที่มีราคาแพงสำหรับการเรียกใช้แต่ละวิธีมีรสชาติมากมายสำหรับการคำนวณเช่นถ้าคุณกำลังอ่านอินพุตจากเว็บเพจ อาจจะโอเค แต่ถ้าคุณกำลังประมวลผลไฟล์บันทึกที่มีหลายร้อยหลายพันบรรทัดสิ่งนี้จะทำให้มันช้ามาก
JBoy

1
Java จะปรับการจัดสรรวัตถุให้เหมาะสมหากการวนซ้ำของคุณร้อน ดูdocs.oracle.com/javase/7/docs/technotes/guides/vm/…
อลิซเพอร์เซลล์

1

java.time.Period

ใช้java.time.Periodชั้นเรียนเพื่อนับวัน

ตั้งแต่ Java 8 การคำนวณความแตกต่างนั้นใช้งานได้ง่ายLocalDateกว่าLocalDateTimeเพื่อแสดงวันที่สองวัน

    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
คำถามนั้นกำลังมองหาเวลาพิเศษโดยเฉพาะ
kapad
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.