Java 8: Sự khác biệt giữa hai LocalDateTime trong nhiều đơn vị


266

Tôi đang cố gắng tính toán sự khác biệt giữa hai LocalDateTime .

Đầu ra cần phải có định dạng y years m months d days h hours m minutes s seconds. Đây là những gì tôi đã viết:

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

Đầu ra mà tôi đang nhận được là 29 years 8 months 24 days 12 hours 0 minutes 50 seconds. Tôi đã kiểm tra kết quả của mình từ trang web này (với các giá trị 12/16/1984 07:45:5509/09/2014 19:46:45). Ảnh chụp màn hình sau đây cho thấy đầu ra:

Chuyển đổi kỷ nguyên

Tôi khá chắc chắn rằng các trường sau giá trị tháng đang đến từ mã của tôi. Bất kỳ đề nghị sẽ rất hữu ích.

Cập nhật

Tôi đã kiểm tra kết quả của mình từ một trang web khác và kết quả tôi nhận được là khác nhau. Đây là: Tính thời lượng giữa hai ngày (kết quả: 29 năm, 8 tháng, 24 ngày, 12 giờ, 0 phút và 50 giây).

Cập nhật

Vì tôi nhận được hai kết quả khác nhau từ hai trang web khác nhau, tôi tự hỏi liệu thuật toán tính toán của tôi có hợp pháp hay không. Nếu tôi sử dụng hai LocalDateTimeđối tượng sau:

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

Sau đó, đầu ra đang đến: 29 years 8 months 25 days -1 hours -5 minutes -10 seconds.

Từ liên kết này nên được 29 years 8 months 24 days 22 hours, 54 minutes and 50 seconds. Vì vậy, thuật toán cần phải xử lý các số âm quá.

Lưu ý câu hỏi không phải là về trang web nào cho tôi kết quả, tôi cần biết đúng thuật toán và cần phải có kết quả đúng.


Chỉ là một phỏng đoán, nhưng có thể Period.between()áp dụng một số làm tròn?
Thomas

3
Tôi chỉ nhìn vào mã một lần nữa và có vẻ như trang web đã sai (Hãy thử tự tính toán). Nếu bạn bỏ qua ngày, tức là sự khác biệt về năm, tháng và ngày, bạn sẽ có được thời gian bắt đầu 7:45:55và thời gian kết thúc 19:46:45(hoặc 7:46:45PM). Vì vậy, sự khác biệt giữa hai lần đó là 12 giờ, 0 phút và 50 giây và không bao giờ 23 giờ, 34 phút và 12 giây. Vì vậy, tính toán thực tế của bạn dường như là chính xác, ít nhất là về phần thời gian.
Thomas

1
Hiện tượng thú vị trên trang web đó: thêm 10 năm kể từ ngày bắt đầu và sự khác biệt về giờ thay đổi từ 23 đến 8 - chắc chắn là một dấu hiệu của lỗi.
Thomas

8
Lưu ý rằng vì LocalDateTimekhông có múi giờ, nên có thể không có câu trả lời duy nhất. Ngay cả khi bạn cho rằng các múi giờ bắt đầu và kết thúc là như nhau, thì trong một số khu vực nhất định như 2014-09-09 sẽ là Giờ tiết kiệm ánh sáng ban ngày hoặc Giờ mùa hè và ở các khu vực khác thì không. Điều này có thể ném mọi thứ đi một giờ. Vì vậy, tính toán sự khác biệt đến lần thứ hai là vô nghĩa trừ khi điều này được giải quyết.
Stuart Marks

2
Bạn có hiểu rằng việc sử dụng LocalDateTimemang lại kết quả không thực tế, vì lớp đó cố tình thiếu bất kỳ khái niệm nào về múi giờ hoặc bù từ UTC? Đối với các giá trị thực tế, chỉ định một múi giờ thông qua ZoneIdđể sử dụng ZonedDateTime.
Basil Bourque

Câu trả lời:


163

Thật không may, dường như không có một lớp thời gian cũng kéo dài thời gian, vì vậy bạn có thể phải tự mình thực hiện các phép tính.

May mắn thay, các lớp ngày và thời gian có rất nhiều phương thức tiện ích giúp đơn giản hóa nó ở một mức độ nào đó. Đây là một cách để tính toán sự khác biệt mặc dù không nhất thiết là nhanh nhất:

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.

Ý tưởng cơ bản là thế này: tạo một ngày bắt đầu tạm thời và kết thúc năm đầy đủ. Sau đó điều chỉnh ngày đó theo số năm để ngày bắt đầu ít hơn một năm kể từ ngày kết thúc. Lặp lại điều đó cho mỗi đơn vị thời gian theo thứ tự giảm dần.

Cuối cùng là từ chối trách nhiệm : Tôi đã không tính đến các múi giờ khác nhau (cả hai ngày phải ở cùng múi giờ) và tôi cũng không kiểm tra / kiểm tra xem thời gian tiết kiệm ánh sáng ban ngày hay các thay đổi khác trong lịch (như thay đổi múi giờ ở Samoa) ảnh hưởng đến tính toán này. Vì vậy, sử dụng cẩn thận.


1
Bạn có cùng một ý tưởng cơ bản, do đó, upvote của tôi, nhưng vui lòng thử sử dụng cùng một đầu vào nếu không kết quả khác nhau là khó hiểu.
Meno Hochschild

@MenoHochschild nó cùng một đầu vào, chỉ được lấy từ bản cập nhật mà OP có vấn đề với thời gian tiêu cực. ;)
Thomas

1
Thuật toán tốt, nhưng loại sai (xem phần từ chối của Thomas). Trước khi thực hiện phép tính của bạn, bạn nên chuyển đổi các biến LocalDateTime của mình thành ZencedDateTime bằng cách sử dụng múi giờ mặc định (hoặc bất kỳ múi giờ nào bạn cần): ZencedDateTime từZencedDateTime = fromDateTime.atZone (ZoneId.systemDefault ());
Tristan

2
@Thomas Ví dụ của bạn cũng đang sử dụng Java 8 - và câu hỏi được gắn thẻ là java-8. Trong thực tế, ví dụ của bạn thậm chí đang sử dụng ChronoUnit:) nhưng thực hiện công việc "bằng tay".
Eugene Beresovsky

3
@EugeneBeresovsky ồ, đúng rồi. Hoàn toàn bỏ lỡ điều đó;) - Bên cạnh đó, bạn vẫn phải trừ các đơn vị lớn hơn khỏi khoảng thời gian vì ví dụ long minutes = ChronoUnit.MINUTES.between(fromDate, toDate);sẽ trả về một số lớn hơn 59 cho bất kỳ khoảng thời gian nào ít nhất là 1 giờ - và đó không phải là điều OP muốn. Nếu tính đến điều đó, bạn sẽ không thể thực hiện 6 cuộc gọi đến ChronoUnit#between()và được thực hiện.
Thomas

503

Tôi tìm thấy cách tốt nhất để làm điều này là với ChronoUnit.

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

Tài liệu bổ sung có tại đây: https://docs.oracle.com/javase/tutorial/datetime/iso/apse.html


8
Tôi không thấy một sự cải thiện thực sự. So với câu trả lời của Thomas được chấp nhận, bạn chỉ cần thay thế tempDateTime.until( toDateTime, ChronoUnit.YEARS)bằng ChronoUnit.YEARS.between(fromDate, toDate). Nhưng các dòng bổ sung quan trọng như tempDateTime.plusYears( years )vv hoàn toàn không có trong câu trả lời của bạn vì vậy nó không giúp ích gì cho OP. Các vấn đề thuật toán đầy đủ!
Meno Hochschild

9
Tôi thích điều này tốt hơn vì nó ngắn gọn hơn, và dễ đọc hơn. Đây là cách tốt nhất để tìm sự khác biệt giữa hai ngày.
Somaiah Kumbera

7
@SomaiahKumbera Không bạn hoàn toàn bỏ lỡ điểm quan trọng. OP không muốn thời lượng chỉ trong một đơn vị mà là nhiều đơn vị , và do đó câu trả lời này hoàn toàn không phải là một câu trả lời. Mối quan tâm của OP đơn giản là không được giải quyết. Xin vui lòng đọc lại câu hỏi. (Rất nhiều upvoters dường như đã hiểu sai câu hỏi).
Meno Hochschild

125
@MenoHochschild, tôi nghĩ bạn thực sự là người thiếu điểm quan trọng. OP đã có câu trả lời của họ hơn một năm trước. 36 nghìn người đang xem câu hỏi này không quan tâm đến câu hỏi cụ thể của OP. Họ đã được dẫn đến đây bởi google và câu trả lời của tôi đã cung cấp cho họ những gì họ đang tìm kiếm - một giải pháp đơn giản và sạch sẽ để có được sự khác biệt giữa hai ngày.
satnam

10
Tôi cần sự khác biệt giây giữa hai lần ngày và bắt đầu thực hiện giải pháp của riêng tôi. Một thời gian ngắn nó có vẻ phức tạp và tôi đã đưa lên google. câu trả lời satnam có thể không trả lời câu hỏi OP nhưng chắc chắn đã giúp tôi rất nhiều và tôi đặt cược nhiều người khác như tôi.
Julian

43

Dưới đây là một ví dụ duy nhất sử dụng Thời lượng và TimeUnit để có định dạng '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
Thật sự có thể làm điều đó như thế này : String.format("%02d:%02d:%02d",dur.toHoursPart(), dur.toMinutesPart(), dur.toSecondsPart());. Các phương thức phần cung cấp cho bạn các số thích hợp để xây dựng một chuỗi. Tôi nghĩ rằng nó dễ đọc hơn.
Daantie

Lưu ý bên cạnh nhận xét trước đây của tôi: các phương thức phần chỉ có sẵn từ JDK 9 trở lên.
Daantie

40

Nó nên đơn giản hơn!

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

1
Tôi tin rằng đây thực sự là giải pháp chính xác về mặt ngữ nghĩa bắt đầu từ Java 8. Và trước phiên bản đó, JodaTime cũng làm như vậy.
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());

và sau đó sử dụng các phương pháp period.getYears(), period.getMonths(), period.getDays(), duration.toHoursPart(), duration.toMinutesPart(), duration.toSecondsPart().


Câu trả lời mở rộng

Tôi sẽ trả lời câu hỏi ban đầu, tức là làm thế nào để có được chênh lệch thời gian giữa hai LocalDateTimesnăm, tháng, ngày, giờ & phút, sao cho "tổng" (xem ghi chú bên dưới) của tất cả các giá trị cho các đơn vị khác nhau bằng tổng số chênh lệch thời gian và sao cho giá trị trong mỗi đơn vị nhỏ hơn đơn vị lớn hơn tiếp theo minutes < 60, tức là ,hours < 24 v.v.

Cho hai LocalDateTimes startend, ví dụ

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

chúng ta có thể biểu diễn khoảng thời gian tuyệt đối giữa hai bằng một Duration bằng cách sử dụng Duration.between(start, end). Nhưng đơn vị lớn nhất chúng ta có thể trích xuất trong một Durationngày là đơn vị tạm thời tương đương với 24h). Hãy xem ghi chú bên dưới để được giải thích. Để sử dụng các đơn vị lớn hơn (tháng, năm), chúng ta có thể biểu thị điều này Durationbằng một cặp ( Period, Duration), trong đó các số Periodđo chênh lệch lên đến độ chính xác của ngày và Durationđại diện cho phần còn lại:

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

Bây giờ chúng ta chỉ cần sử dụng các phương thức được xác định trên PeriodDurationđể trích xuất các đơn vị riêng lẻ:

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

hoặc, sử dụng định dạng mặc định:

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

Lưu ý về năm, tháng và ngày

Lưu ý rằng, trong java.timequan niệm của họ, "các đơn vị" như "tháng" hoặc "năm" không đại diện cho một giá trị tạm thời, cố định tuyệt đối mà họ phụ thuộc vào ngày và lịch, như ví dụ sau minh họa:

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

Có một số vấn đề đối với mã Tapas Bose và mã Thomas. Nếu thời gian khác nhau là âm, mảng sẽ nhận các giá trị âm. Ví dụ nếu

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

nó trả về 0 năm 0 tháng 1 ngày -1 giờ 0 phút 0 giây.

Tôi nghĩ đầu ra đúng là: 0 năm 0 tháng 0 ngày 23 giờ 0 phút 0 giây.

Tôi đề nghị tách riêng các phiên bản LocalDateTime trên các phiên bản LocalDate và LocalTime. Sau đó, chúng ta có thể có được các cá thể Thời gian và Thời lượng 8 của Java. Ví dụ Thời lượng được phân tách theo số ngày và giá trị thời gian trong suốt cả ngày (<24h) với sự điều chỉnh tiếp theo của giá trị thời gian. Khi giá trị LocalTime thứ hai nằm trước giá trị FirstLocalTime, cần phải giảm thời gian trong một ngày.

Đây là cách của tôi để tính chênh lệch 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;
}

Phương pháp trên có thể được sử dụng để tính toán sự khác biệt của bất kỳ giá trị ngày và giờ địa phương nào, ví dụ:

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

Thật thuận tiện khi viết một bài kiểm tra đơn vị cho phương thức trên (cả hai đều là thành viên lớp periodDuration). Đây là mã:

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

}

Tất cả các thử nghiệm đều thành công cho dù giá trị của LocalDateTime đầu tiên có trước hay không đối với bất kỳ giá trị LocalTime nào.


Tôi không thể sao chép tuyên bố của bạn rằng mã Thomas sử dụng đầu vào của bạn ở trên tạo ra các dấu hiệu hỗn hợp. Đầu ra của tôi là: "0 năm 0 tháng 0 ngày 23 giờ 0 phút 0 giây.". Và tôi đã thử nghiệm ngay bây giờ.
Meno Hochschild

Cảm ơn bạn đã bình luận. Tôi đã kiểm tra kỹ mã Thomas, thực sự, nó đang hoạt động chính xác! Phép thuật được thực hiện bởi LocalDateTime cho đến khi phương thức sửa các đơn vị chrono. Các giá trị âm trong mã Thomas xuất hiện nếu DateTime đầu tiên muộn hơn thứ hai. Nhưng điều này có thể dễ dàng sửa chữa, ví dụ, như tôi đã làm trong đoạn mã trên. Cảm ơn một lần nữa.
Gennady Kolomoets

Chà, mã Thomas và thư viện Time4J của tôi sử dụng cùng một thuật toán để tính thời lượng có thể tạo ra các dấu âm nhưng chỉ trong toàn bộ thời lượng. Đó là điểm cốt yếu! Dấu hiệu liên quan đến toàn bộ thời lượng và do đó mô tả nếu bắt đầu muộn hơn kết thúc và không bao giờ liên quan đến các thành phần thời lượng duy nhất. Dấu hiệu hỗn hợp là không thể ở đây và sẽ ngăn cách giải thích như vậy bắt đầu từ kết thúc (ví dụ ngược lại là Joda-Time-time hoặc java.time.Periodnơi người dùng có thể thực thi / tạo ra các dấu hiệu hỗn hợp như vậy do thiết kế bên trong khác nhau).
Meno Hochschild

5

Và phiên bản @Thomas trong Groovy với các đơn vị mong muốn trong danh sách thay vì mã hóa các giá trị. Việc triển khai này (có thể dễ dàng chuyển sang Java - Tôi đã khai báo hàm rõ ràng) làm cho cách tiếp cận Thomas trở nên dễ sử dụng hơn.

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(', ')
}

Tại thời điểm viết bài này, đoạn mã trên trả về 47 Years, 8 Months, 9 Days, 22 Hours, 52 Minutes, 7 Seconds, 140 Millis. Và, đối với đầu vào @Gennady Kolomoets, mã trả về23 Hours .

Khi bạn cung cấp một danh sách các đơn vị, nó phải được sắp xếp theo kích thước của các đơn vị (lớn nhất trước):

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

4

Đây là một câu trả lời rất đơn giản cho câu hỏi của bạn. Nó hoạt động.

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
Tôi nên đề cập đến điều này là không chính xác. Hãy thử với điều này. nó nói chênh lệch 1 phút. LocalDateTime bd = LocalDateTime.of (2019, 11, 26, 15, 03, 55); LocalDateTime cd = LocalDateTime.of (2019, 11, 26, 15, 04, 45);
Đánh dấu Amabile

Tôi bước vào một thời gian tối qua và có Age is: 0 years,0 months, 1 days, -10 hours, -48 minutes old. Tôi không nghĩ rằng đây là những gì mong muốn.
Ole VV

@ OleV.V. Đã khắc phục sự cố đó
SavingBeau

1

Joda-Thời gian

Vì nhiều câu trả lời yêu cầu hỗ trợ API 26 và API tối thiểu của tôi là 23, tôi đã giải quyết nó bằng mã bên dưới:

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
Các Joda thời gian dự án là trong duy trì chế độ, tác giả của nó Stephen Colebourne đã đi vào để tạo ra các java.time lớp được định nghĩa trong JSR 310. Hầu hết các java.time chức năng là back-chuyển đến Java 6 & 7 trong ThreeTen- Dự án Backport . Thích nghi hơn nữa cho Android sớm trong dự án ThreeTenABP .
Basil Bourque

1

Sau hơn năm năm tôi trả lời câu hỏi của tôi. Tôi nghĩ rằng vấn đề với thời lượng âm có thể được giải quyết bằng một chỉnh sửa đơn giản:

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

Lưu ý: Trang web https://www.epochconverter.com/date-difference hiện tính toán chính xác chênh lệch thời gian.

Cảm ơn tất cả các bạn đã thảo luận và đề xuất của bạn.


Cảm ơn câu trả lời. Đối với giờ và nhỏ hơn, bạn có thể hưởng lợi từ toHours, toMinutestoSecondsphương pháp Duration. Kể từ Java 9 thậm chí nhiều hơn từ toMinutesPart()toSecondsPart(). Và tôi không thấy quan điểm trong việc gói giờ, phút và giây thành một mảng chỉ để đưa chúng ra ngoài một lần nữa. Nói chung là một câu trả lời tốt.
Ole VV

Sau hơn năm năm tôi trả lời câu hỏi của tôi. Câu hỏi đã được đăng từ một tài khoản có tên, tiêu đề và địa điểm khác nhau, dường như không có nhiều điểm tương đồng? Không phải là nó có bất kỳ tầm quan trọng, chỉ cần tự hỏi.
Ole VV

"Sau hơn năm năm tôi trả lời câu hỏi của mình" Tôi cho rằng tôi là OP :)
Tapas Bose
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.