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:
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 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.