Java 8: ความแตกต่างระหว่างสอง LocalDateTime ในหลาย ๆ หน่วย


266

ฉันพยายามคำนวณความแตกต่างระหว่างสองLocalDateTimeอย่าง

y years m months d days h hours m minutes s secondsความต้องการส่งออกไปเป็นรูปแบบ นี่คือสิ่งที่ฉันเขียน:

import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.Period;
import java.time.ZoneId;

public class Main {

    static final int MINUTES_PER_HOUR = 60;
    static final int SECONDS_PER_MINUTE = 60;
    static final int SECONDS_PER_HOUR = SECONDS_PER_MINUTE * MINUTES_PER_HOUR;

    public static void main(String[] args) {
        LocalDateTime toDateTime = LocalDateTime.of(2014, 9, 9, 19, 46, 45);
        LocalDateTime fromDateTime = LocalDateTime.of(1984, 12, 16, 7, 45, 55);

        Period period = getPeriod(fromDateTime, toDateTime);
        long time[] = getTime(fromDateTime, toDateTime);

        System.out.println(period.getYears() + " years " + 
                period.getMonths() + " months " + 
                period.getDays() + " days " +
                time[0] + " hours " +
                time[1] + " minutes " +
                time[2] + " seconds.");


    }

    private static Period getPeriod(LocalDateTime dob, LocalDateTime now) {
        return Period.between(dob.toLocalDate(), now.toLocalDate());
    }

    private static long[] getTime(LocalDateTime dob, LocalDateTime now) {
        LocalDateTime today = LocalDateTime.of(now.getYear(),
                now.getMonthValue(), now.getDayOfMonth(), dob.getHour(), dob.getMinute(), dob.getSecond());
        Duration duration = Duration.between(today, now);

        long seconds = duration.getSeconds();

        long hours = seconds / SECONDS_PER_HOUR;
        long minutes = ((seconds % SECONDS_PER_HOUR) / SECONDS_PER_MINUTE);
        long secs = (seconds % SECONDS_PER_MINUTE);

        return new long[]{hours, minutes, secs};
    }
}

29 years 8 months 24 days 12 hours 0 minutes 50 secondsการแสดงผลที่ฉันได้รับคือ ฉันตรวจสอบผลลัพธ์จากเว็บไซต์นี้(ด้วยค่า12/16/1984 07:45:55และ09/09/2014 19:46:45) ภาพหน้าจอต่อไปนี้แสดงผลลัพธ์:

Epoch Converter

ฉันค่อนข้างมั่นใจว่าเขตข้อมูลหลังจากค่าเดือนกำลังมาจากรหัสของฉัน ข้อเสนอแนะใด ๆ จะเป็นประโยชน์มาก

ปรับปรุง

ฉันได้ทดสอบผลลัพธ์ของฉันจากเว็บไซต์อื่นและผลลัพธ์ที่ฉันได้แตกต่างกัน นี่คือ: คำนวณระยะเวลาระหว่างวันที่สองวัน (ผลลัพธ์: 29 ปี, 8 เดือน, 24 วัน, 12 ชั่วโมง, 0 นาทีและ 50 วินาที)

ปรับปรุง

เนื่องจากฉันได้ผลลัพธ์ที่แตกต่างกันสองรายการจากสองไซต์ที่ต่างกันฉันจึงสงสัยว่าอัลกอริทึมในการคำนวณของฉันนั้นถูกต้องหรือไม่ ถ้าฉันใช้LocalDateTimeวัตถุสองอย่างต่อไปนี้:

LocalDateTime toDateTime = LocalDateTime.of(2014, 9, 10, 6, 40, 45);
LocalDateTime fromDateTime = LocalDateTime.of(1984, 12, 16, 7, 45, 55);

จากนั้นกำลังส่งออก: 29 years 8 months 25 days -1 hours -5 minutes -10 seconds.

จากนี้การเชื่อมโยง29 years 8 months 24 days 22 hours, 54 minutes and 50 secondsมันควรจะเป็น ดังนั้นอัลกอริทึมจำเป็นต้องจัดการกับจำนวนลบเช่นกัน

หมายเหตุคำถามไม่เกี่ยวกับเว็บไซต์ใดที่ให้ผลลัพธ์แบบใดฉันต้องรู้อัลกอริทึมที่ถูกต้องและต้องการผลลัพธ์ที่ถูกต้อง


แค่เดา ​​แต่อาจPeriod.between()ใช้การปัดเศษบ้าง
โทมัส

3
ฉันเพิ่งดูรหัสอีกครั้งและดูเหมือนว่าเว็บไซต์นั้นผิด (ลองคำนวณด้วยตัวเอง) หากคุณไม่ใส่วันที่นั่นคือความแตกต่างในปีเดือนและวันคุณจะได้รับเวลาเริ่มต้น7:45:55และเวลาสิ้นสุด19:46:45(หรือ7:46:45PM) ดังนั้นความแตกต่างระหว่างสองครั้งนี้คือ 12 ชั่วโมง 0 นาทีและ 50 วินาทีและไม่เคย 23 ชั่วโมง 34 นาทีและ 12 วินาที ดังนั้นการคำนวณจริงของคุณดูเหมือนจะถูกต้องอย่างน้อยในส่วนของเวลา
โทมัส

1
ปรากฏการณ์ที่น่าสนใจในเว็บไซต์นั้น: เพิ่ม 10 ปีถึงวันที่เริ่มต้นและความแตกต่างของชั่วโมงเปลี่ยนจาก 23 เป็น 8 - แน่นอนว่าเป็นสัญญาณของข้อผิดพลาด
โทมัส

8
โปรดทราบว่าเนื่องจากLocalDateTimeไม่มีเขตเวลาจึงอาจไม่มีคำตอบที่ไม่ซ้ำกัน แม้ว่าคุณจะสมมติว่าเขตเวลาเริ่มต้นและสิ้นสุดเหมือนกัน แต่ในบางโซนวันเช่น 2014-09-09 จะอยู่ในการปรับเวลาตามฤดูกาลหรือเวลาฤดูร้อนและอื่น ๆ จะไม่เหมือนกัน นี่อาจทำให้สิ่งต่าง ๆ หลุดไปภายในหนึ่งชั่วโมง ดังนั้นการคำนวณความแตกต่างกับวินาทีจึงไม่มีความหมายนอกจากจะได้รับการแก้ไข
Stuart Marks

2
คุณเข้าใจหรือไม่ว่าการใช้LocalDateTimeให้ผลที่ไม่สมจริงเนื่องจากคลาสนั้นไม่มีแนวคิดเกี่ยวกับเขตเวลาหรือออฟเซ็ตจาก UTC สำหรับค่าเหตุผล, กำหนดเขตเวลาที่ผ่านการใช้งานZoneId ZonedDateTime
Basil Bourque

คำตอบ:


163

น่าเสียดายที่ดูเหมือนจะไม่มีคลาสคาบเวลาที่ครอบคลุมเวลาเช่นกันดังนั้นคุณอาจต้องทำการคำนวณด้วยตัวเอง

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

LocalDateTime fromDateTime = LocalDateTime.of(1984, 12, 16, 7, 45, 55);
LocalDateTime toDateTime = LocalDateTime.of(2014, 9, 10, 6, 40, 45);

LocalDateTime tempDateTime = LocalDateTime.from( fromDateTime );

long years = tempDateTime.until( toDateTime, ChronoUnit.YEARS );
tempDateTime = tempDateTime.plusYears( years );

long months = tempDateTime.until( toDateTime, ChronoUnit.MONTHS );
tempDateTime = tempDateTime.plusMonths( months );

long days = tempDateTime.until( toDateTime, ChronoUnit.DAYS );
tempDateTime = tempDateTime.plusDays( days );


long hours = tempDateTime.until( toDateTime, ChronoUnit.HOURS );
tempDateTime = tempDateTime.plusHours( hours );

long minutes = tempDateTime.until( toDateTime, ChronoUnit.MINUTES );
tempDateTime = tempDateTime.plusMinutes( minutes );

long seconds = tempDateTime.until( toDateTime, ChronoUnit.SECONDS );

System.out.println( years + " years " + 
        months + " months " + 
        days + " days " +
        hours + " hours " +
        minutes + " minutes " +
        seconds + " seconds.");

//prints: 29 years 8 months 24 days 22 hours 54 minutes 50 seconds.

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

ข้อจำกัดความรับผิดชอบสุดท้าย : ฉันไม่ได้คำนึงถึงเขตเวลาที่แตกต่างกัน (ทั้งสองวันควรอยู่ในเขตเวลาเดียวกัน) และฉันยังไม่ได้ทดสอบ / ตรวจสอบว่าการปรับเวลาตามฤดูกาลหรือการเปลี่ยนแปลงอื่น ๆ ในปฏิทิน (เช่นการเปลี่ยนแปลงเขตเวลาในซามัว) ส่งผลกระทบต่อการคำนวณนี้ ดังนั้นควรใช้ด้วยความระมัดระวัง


1
คุณมีความคิดพื้นฐานเดียวกันดังนั้นการโหวตของฉัน แต่โปรดลองใช้อินพุตเดียวกันมิฉะนั้นผลลัพธ์ที่แตกต่างกันก็ทำให้เกิดความสับสน
Meno Hochschild

@MenoHochschild เป็นอินพุตเดียวกันเพิ่งนำมาจากการอัปเดตที่ OP มีปัญหากับเวลาลบ ;)
โทมัส

1
อัลกอริทึมที่ดี แต่พิมพ์ผิด (ดูข้อจำกัดความรับผิดชอบของโทมัส) ก่อนทำการคำนวณคุณควรแปลงตัวแปร LocalDateTime เป็น ZonedDateTime โดยใช้เขตเวลาเริ่มต้นเช่น (หรือเขตเวลาใด ๆ ที่คุณต้องการ): ZonedDateTime จาก ZonedDateTime = จากDateTime.atZone (ZoneId.systemDefault ());
อุโมงค์

2
@Thomas ตัวอย่างของคุณใช้ Java 8 เช่นกัน - และคำถามถูกติดแท็กเป็น java-8 ในความเป็นจริงตัวอย่างของคุณยังใช้ChronoUnit:) แต่ทำงาน "ด้วยมือ"
Eugene Beresovsky

3
@EugeneBeresovsky โอ้ใช่คุณพูดถูก พลาดไปโดยสิ้นเชิง;) - นอกจากนั้นคุณยังต้องลบหน่วยที่มีขนาดใหญ่ออกจากช่วงเวลาเนื่องจากตัวอย่างlong minutes = ChronoUnit.MINUTES.between(fromDate, toDate);จะส่งกลับตัวเลขที่มากกว่า 59 สำหรับช่วงเวลาใด ๆ อย่างน้อย 1 ชั่วโมง - และนั่นไม่ใช่สิ่งที่ OP ต้องการ โดยคำนึงถึงว่าคุณไม่สามารถโทรChronoUnit#between()และโทรออกได้ 6 สาย
โทมัส

503

ฉันพบวิธีที่ดีที่สุดในการทำเช่นนี้กับChronoUnit

long minutes = ChronoUnit.MINUTES.between(fromDate, toDate);
long hours = ChronoUnit.HOURS.between(fromDate, toDate);

เอกสารเพิ่มเติมอยู่ที่นี่: https://docs.oracle.com/javase/tutorial/datetime/iso/period.html


8
ฉันไม่เห็นการปรับปรุงที่แท้จริง เมื่อเทียบกับคำตอบที่ได้รับการยอมรับของโทมัสคุณเพียงแทนที่ด้วยtempDateTime.until( toDateTime, ChronoUnit.YEARS) ChronoUnit.YEARS.between(fromDate, toDate)แต่tempDateTime.plusYears( years )คำตอบของคุณมีเส้นเพิ่มเติมที่สำคัญเช่นฯลฯ หายไปอย่างสมบูรณ์ดังนั้นจึงไม่ช่วย OP อัลกอริทึมที่สมบูรณ์นั้นสำคัญ!
Meno Hochschild

9
ฉันชอบสิ่งนี้ดีกว่าเพราะมันสั้นกว่าและอ่านได้มากขึ้น นี่เป็นวิธีที่ดีที่สุดในการค้นหาความแตกต่างระหว่างวันที่สองวัน
Somaiah Kumbera

7
@SomaiahKumbera คุณไม่พลาดจุดสำคัญอย่างสมบูรณ์ OP ไม่ต้องการระยะเวลาในหน่วยเดียว แต่หลายหน่วยและดังนั้นคำตอบนี้ไม่ใช่คำตอบที่แท้จริงเลย ข้อกังวลของ OP ไม่ได้รับการแก้ไข โปรดกรุณาอ่านคำถามอีกครั้ง (ดูเหมือนว่าผู้ต่อต้านจำนวนมากมักเข้าใจผิดคำถาม)
Meno Hochschild

125
@MenoHochschild ฉันคิดว่าคุณเป็นคนที่ขาดจุดสำคัญ OP ได้รับคำตอบเมื่อปีที่แล้ว ประชาชน 36,000 คนที่กำลังดูคำถามนี้ไม่สนใจคำถามเฉพาะของ OP พวกเขานำที่นี่โดย google และคำตอบของฉันทำให้พวกเขามีสิ่งที่พวกเขากำลังมองหา - ทางออกที่สะอาดและเรียบง่ายสำหรับการรับความแตกต่างระหว่างสองวัน
satnam

10
ฉันต้องการความแตกต่างวินาทีระหว่างวันที่สองครั้งและเริ่มใช้โซลูชันของฉันเอง ดูเหมือนว่าจะซับซ้อนในเวลาสั้น ๆ และฉันก็ใช้ google คำตอบ satnam อาจไม่ตอบคำถาม OP แต่แน่นอนว่าช่วยฉันได้มากและฉันพนันได้หลายอย่างเช่นฉัน
Julian

43

นี่คือตัวอย่างเดียวโดยใช้ Duration และ TimeUnit เพื่อรับรูปแบบ 'hh: mm: ss'

Duration dur = Duration.between(localDateTimeIni, localDateTimeEnd);
long millis = dur.toMillis();

String.format("%02d:%02d:%02d", 
        TimeUnit.MILLISECONDS.toHours(millis),
        TimeUnit.MILLISECONDS.toMinutes(millis) - 
        TimeUnit.HOURS.toMinutes(TimeUnit.MILLISECONDS.toHours(millis)),
        TimeUnit.MILLISECONDS.toSeconds(millis) - 
        TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(millis)));

2
String.format("%02d:%02d:%02d",dur.toHoursPart(), dur.toMinutesPart(), dur.toSecondsPart());มันเป็นไปได้จริงที่จะทำเช่นนี้ วิธีการส่วนหนึ่งให้ตัวเลขที่ถูกต้องในการสร้างสตริง ฉันคิดว่ามันอ่านง่ายขึ้น
Daantie

หมายเหตุด้านข้างสำหรับความคิดเห็นก่อนหน้าของฉัน: วิธีการส่วนใช้ได้เฉพาะจาก JDK 9 ขึ้นไป
Daantie

40

มันควรจะง่ายกว่านี้!

Duration.between(startLocalDateTime, endLocalDateTime).toMillis();

1
ฉันเชื่อว่านี่เป็นทางออกที่ถูกต้องทางความหมายเริ่มต้นจาก Java 8 และก่อนหน้านั้น JodaTime ก็ทำเช่นเดียวกัน
Vrakfall

9

TL; DR

Duration duration = Duration.between(start, end);
duration = duration.minusDays(duration.toDaysPart()); // essentially "duration (mod 1 day)"
Period period = Period.between(start.toLocalDate(), end.toLocalDate());

แล้วใช้วิธีการperiod.getYears(), period.getMonths(), period.getDays(), duration.toHoursPart(), ,duration.toMinutesPart()duration.toSecondsPart()


คำตอบที่ขยาย

ฉันจะตอบคำถามเดิมคือทำอย่างไรถึงจะได้ความแตกต่างของเวลาระหว่างสองLocalDateTimesในปีเดือนวันชั่วโมงและนาทีเช่นนั้น "ผลรวม" (ดูหมายเหตุด้านล่าง) ของค่าทั้งหมดสำหรับหน่วยต่าง ๆ รวมเท่ากับ ความแตกต่างชั่วคราวและเช่นว่าค่าในแต่ละหน่วยมีขนาดเล็กกว่าขนาดใหญ่ต่อไปหน่วยเช่นminutes < 60, hours < 24และอื่น ๆ

ให้สองLocalDateTimes startและendเช่น

LocalDateTime start = LocalDateTime.of(2019, 11, 29, 17, 15);
LocalDateTime end = LocalDateTime.of(2020, 11, 30, 18, 44);

เราสามารถเป็นตัวแทนของช่วงเวลาที่แน่นอนระหว่างสองกับDuration-perhaps Duration.between(start, end)ใช้ แต่หน่วยที่ใหญ่ที่สุดที่เราสามารถแยกออกได้Durationคือa (เป็นหน่วยชั่วคราวเท่ากับ 24 ชั่วโมง) - ดูบันทึกด้านล่างเพื่อดูคำอธิบาย หากต้องการใช้หน่วยที่ใหญ่ขึ้น (เดือนปี) เราสามารถแสดงสิ่งนี้Durationด้วยคู่ของ ( Period, Duration) โดยที่การPeriodวัดความแตกต่างได้ถึงความแม่นยำของวันและส่วนDurationที่เหลือแสดงถึง:

Duration duration = Duration.between(start, end);
duration = duration.minusDays(duration.toDaysPart()); // essentially "duration (mod 1 day)"
Period period = Period.between(start.toLocalDate(), end.toLocalDate());

ตอนนี้เราสามารถใช้วิธีการที่กำหนดไว้ในPeriodและDurationแยกแต่ละหน่วย:

System.out.printf("%d years, %d months, %d days, %d hours, %d minutes, %d seconds",
        period.getYears(), period.getMonths(), period.getDays(), duration.toHoursPart(),
        duration.toMinutesPart(), duration.toSecondsPart());
1 years, 0 months, 1 days, 1 hours, 29 minutes, 0 seconds

หรือใช้รูปแบบเริ่มต้น:

System.out.println(period + " + " + duration);
P1Y1D + PT1H29M

หมายเหตุเกี่ยวกับปีเดือนและวัน

โปรดทราบว่าในjava.timeความคิดของ "หน่วย" เช่น "เดือน" หรือ "ปี" ไม่ได้เป็นตัวแทนของค่าเวลาชั่วคราวแน่นอน - พวกเขากำลังวันที่และขึ้นอยู่กับปฏิทินตามตัวอย่างต่อไปนี้แสดงให้เห็นถึง:

LocalDateTime
        start1 = LocalDateTime.of(2020, 1, 1, 0, 0),
        end1 = LocalDateTime.of(2021, 1, 1, 0, 0),
        start2 = LocalDateTime.of(2021, 1, 1, 0, 0),
        end2 = LocalDateTime.of(2022, 1, 1, 0, 0);
System.out.println(Period.between(start1.toLocalDate(), end1.toLocalDate()));
System.out.println(Duration.between(start1, end1).toDays());
System.out.println(Period.between(start2.toLocalDate(), end2.toLocalDate()));
System.out.println(Duration.between(start2, end2).toDays());
P1Y
366
P1Y
365

5

มีปัญหาบางอย่างสำหรับรหัส Tapas Bose และรหัส Thomas ถ้าเวลามีค่าเป็นลบอาร์เรย์จะได้รับค่าลบ ตัวอย่างเช่นถ้า

LocalDateTime toDateTime = LocalDateTime.of(2014, 9, 10, 6, 46, 45);
LocalDateTime fromDateTime = LocalDateTime.of(2014, 9, 9, 7, 46, 45);

มันส่งคืน 0 ปี 0 เดือน 1 วัน -1 ชั่วโมง 0 นาที 0 วินาที

ฉันคิดว่าผลลัพธ์ที่ถูกต้องคือ: 0 ปี 0 เดือน 0 วัน 23 ชั่วโมง 0 นาที 0 วินาที

ฉันเสนอให้แยกอินสแตนซ์ LocalDateTime บนอินสแตนซ์ LocalDate และ LocalTime หลังจากนั้นเราสามารถรับอินสแตนซ์ Java 8 Period และ Duration อินสแตนซ์ระยะเวลาจะถูกแยกออกจากจำนวนวันและค่าเวลาตลอดทั้งวัน (<24 ชม.) พร้อมการแก้ไขค่าระยะเวลาที่ตามมา เมื่อค่า LocalTime ที่สองอยู่ก่อนค่า firstLocalTime จำเป็นต้องลดระยะเวลาหนึ่งวัน

นี่คือวิธีของฉันในการคำนวณความแตกต่างของ LocalDateTime:

private void getChronoUnitForSecondAfterFirst(LocalDateTime firstLocalDateTime, LocalDateTime secondLocalDateTime, long[] chronoUnits) {
    /*Separate LocaldateTime on LocalDate and LocalTime*/
    LocalDate firstLocalDate = firstLocalDateTime.toLocalDate();
    LocalTime firstLocalTime = firstLocalDateTime.toLocalTime();

    LocalDate secondLocalDate = secondLocalDateTime.toLocalDate();
    LocalTime secondLocalTime = secondLocalDateTime.toLocalTime();

    /*Calculate the time difference*/
    Duration duration = Duration.between(firstLocalDateTime, secondLocalDateTime);
    long durationDays = duration.toDays();
    Duration throughoutTheDayDuration = duration.minusDays(durationDays);
    Logger.getLogger(PeriodDuration.class.getName()).log(Level.INFO,
            "Duration is: " + duration + " this is " + durationDays
            + " days and " + throughoutTheDayDuration + " time.");

    Period period = Period.between(firstLocalDate, secondLocalDate);

    /*Correct the date difference*/
    if (secondLocalTime.isBefore(firstLocalTime)) {
        period = period.minusDays(1);
        Logger.getLogger(PeriodDuration.class.getName()).log(Level.INFO,
                "minus 1 day");
    }

    Logger.getLogger(PeriodDuration.class.getName()).log(Level.INFO,
            "Period between " + firstLocalDateTime + " and "
            + secondLocalDateTime + " is: " + period + " and duration is: "
            + throughoutTheDayDuration
            + "\n-----------------------------------------------------------------");

    /*Calculate chrono unit values and  write it in array*/
    chronoUnits[0] = period.getYears();
    chronoUnits[1] = period.getMonths();
    chronoUnits[2] = period.getDays();
    chronoUnits[3] = throughoutTheDayDuration.toHours();
    chronoUnits[4] = throughoutTheDayDuration.toMinutes() % 60;
    chronoUnits[5] = throughoutTheDayDuration.getSeconds() % 60;
}

วิธีการด้านบนสามารถใช้ในการคำนวณความแตกต่างของค่าวันที่และเวลาท้องถิ่นตัวอย่างเช่น:

public long[] getChronoUnits(String firstLocalDateTimeString, String secondLocalDateTimeString) {
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    LocalDateTime firstLocalDateTime = LocalDateTime.parse(firstLocalDateTimeString, formatter);
    LocalDateTime secondLocalDateTime = LocalDateTime.parse(secondLocalDateTimeString, formatter);

    long[] chronoUnits = new long[6];
    if (secondLocalDateTime.isAfter(firstLocalDateTime)) {
        getChronoUnitForSecondAfterFirst(firstLocalDateTime, secondLocalDateTime, chronoUnits);
    } else {
        getChronoUnitForSecondAfterFirst(secondLocalDateTime, firstLocalDateTime, chronoUnits);
    }
    return chronoUnits;
}

สะดวกในการเขียนการทดสอบหน่วยสำหรับวิธีการข้างต้น (ทั้งคู่เป็นสมาชิกคลาส PeriodDuration) นี่คือรหัส:

@RunWith(Parameterized.class)
public class PeriodDurationTest {

private final String firstLocalDateTimeString;
private final String secondLocalDateTimeString;
private final long[] chronoUnits;

public PeriodDurationTest(String firstLocalDateTimeString, String secondLocalDateTimeString, long[] chronoUnits) {
    this.firstLocalDateTimeString = firstLocalDateTimeString;
    this.secondLocalDateTimeString = secondLocalDateTimeString;
    this.chronoUnits = chronoUnits;
}

@Parameters
public static Collection<Object[]> periodValues() {
    long[] chronoUnits0 = {0, 0, 0, 0, 0, 0};
    long[] chronoUnits1 = {0, 0, 0, 1, 0, 0};
    long[] chronoUnits2 = {0, 0, 0, 23, 0, 0};
    long[] chronoUnits3 = {0, 0, 0, 1, 0, 0};
    long[] chronoUnits4 = {0, 0, 0, 23, 0, 0};
    long[] chronoUnits5 = {0, 0, 1, 23, 0, 0};
    long[] chronoUnits6 = {29, 8, 24, 12, 0, 50};
    long[] chronoUnits7 = {29, 8, 24, 12, 0, 50};
    return Arrays.asList(new Object[][]{
        {"2015-09-09 21:46:44", "2015-09-09 21:46:44", chronoUnits0},
        {"2015-09-09 21:46:44", "2015-09-09 22:46:44", chronoUnits1},
        {"2015-09-09 21:46:44", "2015-09-10 20:46:44", chronoUnits2},
        {"2015-09-09 21:46:44", "2015-09-09 20:46:44", chronoUnits3},
        {"2015-09-10 20:46:44", "2015-09-09 21:46:44", chronoUnits4},
        {"2015-09-11 20:46:44", "2015-09-09 21:46:44", chronoUnits5},
        {"1984-12-16 07:45:55", "2014-09-09 19:46:45", chronoUnits6},
        {"2014-09-09 19:46:45", "1984-12-16 07:45:55", chronoUnits6}
    });
}

@Test
public void testGetChronoUnits() {
    PeriodDuration instance = new PeriodDuration();
    long[] expResult = this.chronoUnits;
    long[] result = instance.getChronoUnits(this.firstLocalDateTimeString, this.secondLocalDateTimeString);
    assertArrayEquals(expResult, result);
}

}

การทดสอบทั้งหมดประสบความสำเร็จไม่ว่าค่าของ LocalDateTime แรกจะอยู่ก่อนหน้าหรือไม่และสำหรับค่า LocalTime ใด ๆ


ฉันไม่สามารถทำซ้ำคำสั่งของคุณว่ารหัสโทมัสโดยใช้การป้อนข้อมูลของคุณด้านบนผลิตสัญญาณผสม ผลลัพธ์ของฉันคือ: "0 ปี 0 เดือน 0 วัน 23 ชั่วโมง 0 นาที 0 วินาที" และฉันได้ทำการทดสอบแล้ว
Meno Hochschild

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

รหัส Thomas และห้องสมุด Time4J ของฉันที่ใช้อัลกอริทึมเดียวกันสำหรับการคำนวณระยะเวลาสามารถสร้างสัญญาณเชิงลบได้ แต่ตลอดระยะเวลาทั้งหมดเท่านั้น นั่นคือจุดสำคัญ! เครื่องหมายที่เกี่ยวข้องกับระยะเวลาทั้งหมดและด้วยเหตุนี้จะอธิบายว่าการเริ่มต้นช้ากว่าสิ้นสุดและไม่เกี่ยวข้องกับองค์ประกอบระยะเวลาเดียว สัญญาณผสมเป็นไปไม่ได้ที่นี่และจะป้องกันการตีความเริ่มต้นเมื่อเทียบกับจุดสิ้นสุด (ตัวอย่างตัวนับคือ Joda-Time-period หรือjava.time.Periodที่ผู้ใช้สามารถบังคับ / สร้างสัญญาณผสมดังกล่าวเนื่องจากการออกแบบภายในที่แตกต่างกัน)
Meno Hochschild

5

และเวอร์ชันของ @Thomas ในGroovyพร้อมรับหน่วยที่ต้องการในรายการแทนการเข้ารหัสค่า การนำไปใช้นี้ (ซึ่งสามารถพอร์ตไปยัง Java ได้อย่างง่ายดาย - ฉันได้ประกาศฟังก์ชันอย่างชัดเจน) ทำให้ Thomas เข้าถึงวิธีการนำมาใช้ใหม่ได้มากขึ้น

def fromDateTime = LocalDateTime.of(1968, 6, 14, 0, 13, 0)
def toDateTime = LocalDateTime.now()
def listOfUnits = [
    ChronoUnit.YEARS, ChronoUnit.MONTHS, ChronoUnit.DAYS,
    ChronoUnit.HOURS, ChronoUnit.MINUTES, ChronoUnit.SECONDS,
    ChronoUnit.MILLIS]

println calcDurationInTextualForm(listOfUnits, fromDateTime, toDateTime)    

String calcDurationInTextualForm(List<ChronoUnit> listOfUnits, LocalDateTime ts, LocalDateTime to)
{
    def result = []

    listOfUnits.each { chronoUnit ->
        long amount = ts.until(to, chronoUnit)
        ts = ts.plus(amount, chronoUnit)

        if (amount) {
            result << "$amount ${chronoUnit.toString()}"
        }
    }

    result.join(', ')
}

ในช่วงเวลาของการเขียนนี้รหัสข้างต้นกลับ47 Years, 8 Months, 9 Days, 22 Hours, 52 Minutes, 7 Seconds, 140 Millisมา และสำหรับการป้อนข้อมูล @Gennady Kolomoets 23 Hoursผลตอบแทนรหัส

เมื่อคุณระบุรายการของหน่วยมันจะต้องเรียงตามขนาดของหน่วย (ใหญ่ที่สุดก่อน):

def listOfUnits = [ChronoUnit.WEEKS, ChronoUnit.DAYS, ChronoUnit.HOURS]
// returns 2495 Weeks, 3 Days, 8 Hours

4

นี่คือคำตอบง่ายๆสำหรับคำถามของคุณ มันได้ผล.

import java.time.*;
import java.util.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;

public class MyClass {
public static void main(String args[]) {
    DateTimeFormatter T = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm");
    Scanner h = new Scanner(System.in);

    System.out.print("Enter date of birth[dd/mm/yyyy hh:mm]: ");
    String b = h.nextLine();

    LocalDateTime bd = LocalDateTime.parse(b,T);
    LocalDateTime cd = LocalDateTime.now();

    long minutes = ChronoUnit.MINUTES.between(bd, cd);
    long hours = ChronoUnit.HOURS.between(bd, cd);

    System.out.print("Age is: "+hours+ " hours, or " +minutes+ " minutes old");
}
}

1
ฉันควรพูดถึงสิ่งนี้ไม่ถูกต้อง ลองกับสิ่งนี้ มันบอกว่าแตกต่าง 1 นาที LocalDateTime bd = LocalDateTime.of (2019, 11, 26, 15, 03, 55); LocalDateTime cd = LocalDateTime.of (2019, 11, 26, 15, 04, 45);
Mark Amabile

Age is: 0 years,0 months, 1 days, -10 hours, -48 minutes oldฉันป้อนเวลาคืนที่ผ่านมาและได้รับ ฉันไม่คิดว่านี่คือสิ่งที่ต้องการ
Ole VV

@ OleV.V แก้ไขปัญหานั้น
SavedBeau

1

Joda เวลา

เนื่องจากคำตอบหลายข้อต้องการการสนับสนุนAPI 26และ min min ของฉันคือ 23 ฉันจึงแก้ไขด้วยโค้ดด้านล่าง:

import org.joda.time.Days

LocalDate startDate = Something
LocalDate endDate = Something
// The difference would be exclusive of both dates, 
// so in most of use cases we may need to increment it by 1
Days.daysBetween(startDate, endDate).days

1
Joda เวลาโครงการในการบำรุงรักษาโหมดผู้สร้างสตีเฟ่น Colebourne มีหายไปในการสร้างjava.timeเรียนที่กำหนดไว้ใน JSR 310 ส่วนใหญ่ของjava.timeการทำงานย้อนรังเพลิง Java 6 และ 7 ในThreeTen-โครงการBackport ดัดแปลงเพิ่มเติมสำหรับ Android ต้นในโครงการThreeTenABP
Basil Bourque

1

หลังจากห้าปีที่ผ่านมาฉันตอบคำถามของฉัน ฉันคิดว่าปัญหาที่มีระยะเวลาติดลบสามารถแก้ไขได้ด้วยการแก้ไขง่ายๆ:

LocalDateTime fromDateTime = LocalDateTime.of(2014, 9, 9, 7, 46, 45);
LocalDateTime toDateTime = LocalDateTime.of(2014, 9, 10, 6, 46, 45);

Period period = Period.between(fromDateTime.toLocalDate(), toDateTime.toLocalDate());
Duration duration = Duration.between(fromDateTime.toLocalTime(), toDateTime.toLocalTime());

if (duration.isNegative()) {
    period = period.minusDays(1);
    duration = duration.plusDays(1);
}
long seconds = duration.getSeconds();
long hours = seconds / SECONDS_PER_HOUR;
long minutes = ((seconds % SECONDS_PER_HOUR) / SECONDS_PER_MINUTE);
long secs = (seconds % SECONDS_PER_MINUTE);
long time[] = {hours, minutes, secs};
System.out.println(period.getYears() + " years "
            + period.getMonths() + " months "
            + period.getDays() + " days "
            + time[0] + " hours "
            + time[1] + " minutes "
            + time[2] + " seconds.");

หมายเหตุ:เว็บไซต์https://www.epochconverter.com/date-difference จะคำนวณความแตกต่างของเวลาได้อย่างถูกต้อง

ขอบคุณทุกท่านสำหรับการสนทนาและคำแนะนำ


ขอบคุณสำหรับคำตอบ. สำหรับชั่วโมงและขนาดเล็กที่คุณสามารถได้รับประโยชน์จากtoHours, toMinutesและวิธีการของtoSeconds Durationตั้งแต่ Java 9 มากขึ้นจากและtoMinutesPart() toSecondsPart()และฉันไม่เห็นจุดในการห่อชั่วโมงนาทีและวินาทีในอาร์เรย์เท่านั้นที่จะนำพวกเขาออกมาอีกครั้ง โดยทั่วไปแล้วคำตอบที่ดีแม้ว่า
Ole VV

หลังจากห้าปีที่ผ่านมาฉันตอบคำถามของฉัน คำถามถูกโพสต์จากบัญชีที่มีชื่อชื่อและที่ตั้งที่แตกต่างกันดูเหมือนจะไม่มีความคล้ายคลึงกันมากนัก? ไม่ใช่ว่ามันมีความสำคัญใด ๆ เพียงแค่สงสัย
Ole VV

"หลังจากห้าปีที่ผ่านมาฉันตอบคำถามของฉัน" ฉันคิดว่าฉันเป็น OP :)
Tapas Bose
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.