Search code examples
javadatetimeunix-timestamp

java.time: Compare two Instants - get the number of hours, minutes, seconds, years, months between the two


I tried this code:

public class TimePassed {
    private long seconds;
    private long minutes;
    private long hours;
    private long days;
    private long years;
    ...

    public TimePassed(double unixSeconds)  {
        Instant now = Instant.now();
        Instant ago = Instant.ofEpochSecond((long) unixSeconds);

        this.seconds = ChronoUnit.SECONDS.between(
            ago.atZone(ZoneId.systemDefault()),
            now.atZone(ZoneId.systemDefault()));  //6100
        this.minutes = ChronoUnit.MINUTES.between(
            ago.atZone(ZoneId.systemDefault()),
            now.atZone(ZoneId.systemDefault()));  //101
        this.hours = ChronoUnit.HOURS.between(
            ago.atZone(ZoneId.systemDefault()),
            now.atZone(ZoneId.systemDefault()));  //1
        this.days = ChronoUnit.DAYS.between(
            ago.atZone(ZoneId.systemDefault()),
            now.atZone(ZoneId.systemDefault()));  //0
        this.years = ChronoUnit.YEARS.between(
            ago.atZone(ZoneId.systemDefault()),
            now.atZone(ZoneId.systemDefault()));  //0
    }
}

However then the TimePassed object would have seconds = 6100 and minutes = 101 and hours = 1, while I want it to be hours = 1, minutes = 41, seconds = 40, so that 60*60 + 41*60 + 40 = 6100. Is it possible to do with java.time package? Because as of now I can only either get passed seconds, passed minutes or passed hours, etc. And neither would account for the other.


Solution

  • Java 9 answer: Duration.toXxxPart methods

    Basic idea, not complete:

        Duration dur = Duration.between(ago, now);
    
        this.seconds = dur.toSecondsPart(); // 40
        this.minutes = dur.toMinutesPart(); // 41
        this.hours = dur.toHoursPart(); // 1
        this.days = dur.toDaysPart(); // 0
    

    Tested with instants that are 6100 seoncds apart like yours from the question. The toXxxPart methods were introduced in Java 9. For Java 8 (or ThreeTen Backport) you will need to start from the coarser units, the days, and subtract them from the duration before getting the next finer unit. See this answer by lauhub for an example

    The years and days are a bit tricky to get completely correct, though. To get only the days that exceed the whole years here’s the full code:

        ZoneId zone = ZoneId.systemDefault();
        ZonedDateTime agoZdt = ago.atZone(zone);
        ZonedDateTime nowZdt = now.atZone(zone);
        this.years = ChronoUnit.YEARS.between(agoZdt, nowZdt);
        ZonedDateTime afterWholeYears = agoZdt.plusYears(this.years);
    
        Duration dur = Duration.between(afterWholeYears, nowZdt);
    
        this.seconds = dur.toSecondsPart(); // 40
        this.minutes = dur.toMinutesPart(); // 41
        this.hours = dur.toHoursPart(); // 1
        this.days = dur.toDays(); // 0
    

    I am on purpose reading ZoneId.systemDefault() only once just for the unlikely case that someone changes the default time zone setting underway.