LocalTime() difference between two times
Asked Answered
S

2

11

I have a flight itinerary program where I need to get difference between departure and arrival time. I get these specified times as String from the data. Here is my problem:

import static java.time.temporal.ChronoUnit.MINUTES;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;

public class test2 {
public static void main(String[] args) {

    DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("HHmm");
    LocalTime departure = LocalTime.parse("1139", dateFormat);
    LocalTime arrival = LocalTime.parse("1435", dateFormat);
    LocalTime a = LocalTime.parse("0906", dateFormat);
    System.out.println(MINUTES.between(departure, arrival));
    System.out.println(MINUTES.between(arrival, a));
}
}

Output:

176
-329

The first time returns the difference between 11:39 and 14:35 just fine. But the second difference is only 5 hours while it should be 19 hours. How can I fix this, what am I doing wrong here?

Any help would be greatly appreciated.

EDIT: I am using graphs to store my data in. An example of a shortest route between two airports is like this:

Route for Edinburgh to Sydney
1 Edinburgh, 1139, TK3245, Istanbul, 1435
2 Istanbul, 0906, TK4557, Singapore, 1937
3 Singapore, 0804, QF1721, Sydney, 1521

These are the 3 flights that takes us from EDI to SYD. The format of the output above is (City, Departure Time, Flight No., Destination, Arrival Time).

Sibby answered 12/11, 2018 at 0:2 Comment(11)
You know, the problem with time is just the period from midnight AM to midnight PM, it has no concept of what exists beyond those boundaries. Instead, you should be using a LocalDateTime which allows for the transition from one day to another – Dunfermline
While your example is probably pretty simple, I would recommend having a look at ZonedDateTime, as you might be passing between different time zones, making the calculations rather ... weird :P (not unheard of arriving before you left πŸ€ͺ) – Dunfermline
I don't see the issue. The results you get are correct. Why the difference between 09:05 to 14:35 should be 19 hours? – Volition
@Volition Sorry I made a mistake and forgot to change my code before posting. The difference should be from 14:35 to 09:05. In this case, as MadProgrammer has mentioned, the date is changing so the values are getting lost or something. I tried the suggestion MadProgrammer suggested, but LocalDateTime only works if I provide the date with it (at least it does as far as I have tested it right now). The data I am getting does not provide me with date, it's simply two times. The dates do not matter. – Sibby
@Dunfermline Flying concorde London-NY would make you arrive before you left. So the issue has existed, and may yet come to exist again in the future... – Kesler
If the result you get is below zero then sum it with 24*60 and you will get the correct result. – Volition
@Kesler Never said it didn't exist, which was my point ;) – Dunfermline
@Volition Except there aren't actually 24 hours in every day - basic mathematics should never be applied to date/time values - you have an entire API dedicated to solving the problem, better to make use of it – Dunfermline
@NaeemKhan "The format of the output above is (City, Departure Time, Flight No., Destination, Arrival Time)" - Based on what anchor date? – Dunfermline
The Javadoc for the between method is clear on this: "the amount of time between temporal1Inclusive and temporal2Exclusive in terms of this unit; positive if temporal2Exclusive is later than temporal1Inclusive, negative if earlier". You may want the output to be 19 hours, but it's actually 5 hours negative as it should be. (And 24 - 5 is 19....) – Klepac
Does 176 make sense as the duration in minutes of the first flight? I would suppose that the times, 1139 and 1435, are local times in Edinburgh and Istanbul, and those are not in the same time zone, so their times cannot be compared like that. 19 hours, or rather 18 hours 29 minutes, is correct for the waiting time in Istanbul since both 1435 and 0906 are in the same time zone (assuming summer time (DST) doesn’t begin or end that night). – Cookson
O
19

The total number of minutes in 24 hours is 1440. So when the difference is below zero (but you need a positive one) then you should add a whole day to your result:

int diff = MINUTES.between(arrival, a);
if (diff < 0) {
    diff += 1440;
}

You can achieve the same thing using this:

int diff = (MINUTES.between(arrival, a) + 1440) % 1440;
Operand answered 12/11, 2018 at 0:33 Comment(3)
Thank you so much! This fixed it. Can you please explain how that works though? – Sibby
@NaeemKhan Glad it helped. Check the updates. If you want a more detailed explanation then read about modular addition/substraction. – Operand
I understand that part. I was 5 AM that night so my brain wasnt functioning properly. I didn't realize that 1440 was the number of minutes in the day. Thank you for the explanation. – Sibby
D
3

The primary issue at hand is LocalTime is only a representation of time between midnight AM to midnight PM, it has no concept of any range beyond those bounds.

What you really need is a date value, associated with the time value, this would then allow you to calculate durations beyond a 24 hour period.

Failing that, you will need to "fudge" the values yourself. This means, that when the next time is less than previous time, you will need to manually add an additional period, maybe a day.

There are probably a few ways to do this, this is just one.

It takes a ZonedDateTime (set to the current date) and uses it as a "anchor" date. Each Time of the schedule is then applied to this. When it detects that last arrival time is before the next departure time, it adds a day to the values.

import java.time.Duration;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;

public class Test {

  public static void main(String[] args) {
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HHmm");

    List<Schedule> route = new ArrayList<>(4);
    route.add(new Schedule(LocalTime.parse("1139", formatter), LocalTime.parse("1435", formatter)));
    route.add(new Schedule(LocalTime.parse("0906", formatter), LocalTime.parse("1937", formatter)));
    route.add(new Schedule(LocalTime.parse("0804", formatter), LocalTime.parse("1521", formatter)));

    // Anchor time...
    ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("UTC"));
    ZonedDateTime lastArrival = null;
    Duration totalDuration = Duration.ZERO;
    for (Schedule schedule : route) {
      ZonedDateTime depart = zdt.with(schedule.getDepart());
      ZonedDateTime arrive = zdt.with(schedule.getArrive());

      if (lastArrival != null) {
        if (lastArrival.isAfter(depart)) {
          // Most likely, we've shifted to a new day...
          // Updat the anchor and target values
          zdt = zdt.plusDays(1);
          depart = depart.plusDays(1);
          arrive = arrive.plusDays(1);
        }
        Duration duration = Duration.between(lastArrival, depart);
        totalDuration = totalDuration.plus(duration);
        System.out.println("...Wait for " + duration.toHoursPart() + "h " + duration.toMinutesPart() + "m");
      }

      Duration duration = Duration.between(depart, arrive);
      System.out.println(duration.toHoursPart() + "h " + duration.toMinutesPart() + "m");
      totalDuration = totalDuration.plus(duration);

      lastArrival = arrive;
    }
    System.out.println("Total duration of " + totalDuration.toHoursPart() + "d " + totalDuration.toHoursPart() + "h " + totalDuration.toMinutesPart() + "m");

  }

  public static class Schedule {

    private LocalTime depart;
    private LocalTime arrive;

    public Schedule(LocalTime depart, LocalTime arrive) {
      this.depart = depart;
      this.arrive = arrive;
    }

    public LocalTime getDepart() {
      return depart;
    }

    public LocalTime getArrive() {
      return arrive;
    }

  }
}

Which will output...

2h 56m
...Wait for 18h 31m
10h 31m
...Wait for 12h 27m
7h 17m
Total duration of 3d 3h 42m

But why do this? Because date/time manipulation is a complete mess, with leap seconds, years and other stupid rules which are meant to stop it from all falling apart into chaos (this is why there are no flights at 12:00 :/) ... and don't get me started on daylight savings...

The Java date/time API takes care of all of this for us.

I've chosen to maintain the date/time information in a single unit of work, ie UTC, which allows all the values to have some kind of useful meaning. You could easily use a different anchor time as well as convert to a different time zone for use in presentation - so you could display the times in local time at the destination, but the calculations would continue to be done against a single point of truth.

Dunfermline answered 12/11, 2018 at 1:44 Comment(3)
to be picky, the primary issue is, IMHO, that we have no date information in the input data - logically, assuming normal flights, if the arrival time is before the departure one, we can assume it must be one day later... but if it is a ship travel, it could be a couple of days later...(most flight plans {I know of} have something like a +1 to indicate it is the next day) – Romeyn
@CarlosHeuberger My "preferred" solution would have the values with both date and times in a single timezone, so you don't end up with to much weirdness, but, it could still be physically possible to arrive before you left even in that scenario. What I'm "trying" to do is get people to "stop" performing mathematic manipulation of date/time values, as that's even worse (IMO) – Dunfermline
I will look into implementing this with my program when it is finished, for now I need to focus on finishing it. Thank you so much for this! – Sibby

© 2022 - 2024 β€” McMap. All rights reserved.