A flight itinerary in Bash (HPR Show 2469)

Dave Morriss


Table of Contents

Introduction

My daughter flew out to New Zealand before Christmas 2017 to spend some time with her brother, who had been there with his girlfriend since November. I saw her flight itinerary from the airline, but had no idea of how the times related to time back home, so I wrote a little Bash script to calculate times in UTC (my local timezone).

Both of my children have travelled a fair bit in the past few years. I like to keep track of where they are and how they are progressing through their journeys because otherwise I tend to worry. This one was a reasonably simple journey, two flights via Doha in Qatar, with not too long a wait between them. The overall journey was long of course.

When my daughter flew out to Indonesia in 2015 (4 flights and a boat trip, over 38 hours travel time) I built a spreadsheet. Just whatever provides a good distraction!

Script

Algorithm

I had the start and arrival times of each flight as well as the flight durations. I also had the connection time. I decided to use the date command to perform date and time calculations.

The date command can take a date specification with an offset. So, for example adding 1 week to the current time can be computed as:

$ date -d 'today + 1 week'
Sat  6 Jan 11:13:12 GMT 2018

(it was 11:13 on Saturday 30th December when I ran that command)

However, giving date a start date and time leads to some odd results:

date -d '2017-12-24 14:55 + 415 minutes'
Sun 24 Dec 10:41:00 GMT 2017

Adding 415 minutes (6h 55min) to 14:55 should result in 21:50.

I think this problem is caused by the date and time being separated by a space, so I tried another approach:

$ date -d "$(date -Iminutes -d '2017-12-24 14:55') + 415 minutes"
Sun 24 Dec 21:50:00 GMT 2017

This uses the fact that the date command can output and interpret ISO 8601 dates, so the inner date in the command substitution produces:

$ date -Iminutes -d '2017-12-24 14:55'
2017-12-24T14:55+00:00

Now, adding 415 minutes to the ISO date and time is no problem for date1.

I also discovered that I didn’t have to convert my time offset to minutes as in the examples above, so the following also works:

$ date -d '2017-12-24T14:55+00:00 + 6 hours + 55 minutes'
Sun 24 Dec 21:50:00 GMT 2017

Not surprisingly, the offset in the form of ‘6:55’ didn’t work.

Code

Here is the script itself, which may be downloaded from the HPR website as edi_akl. It is not generic, more of a throwaway script, but it does demonstrate some principles:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#!/bin/bash

#===============================================================================
# Flight details starting 2017-12-24
# Edinburgh (EDI) to Doha (DOH)
# Doha (DOH) to Auckland (AKL)
#
# Departure EDI: 14:55 (+0000)
# Arrival DOH:   12:55 (+0300)
# Flight time:   6hr 55min
#
# Connection:    1hr 40min
#
# Departure DOH: 02:25 (+0300)
# Arrival AKL:   05:00 (+1300)
# Flight time:   16hr 10min
#===============================================================================

minutes () {
    local hhmm="${1:?Usage: minutes time variable}"
    local -n ref="${2:?Usage: minutes time variable}"
    local hh mm

    hh=${hhmm%%:*}
    mm=${hhmm##*:}

    ref=$((hh * 60 + mm))
}

echo "Outward flights:"

dep_edi="$(date -Iminutes -d "2017-12-24 14:55")"
minutes '6:55' flight1
arr_doh=$(date -Iminutes -d "$dep_edi + $flight1 minutes")

printf "%-20s %s\n" "Leave Edinburgh at" "$(date -d "$dep_edi" '+%F %R %z')"
printf "%-20s %s\n\n" "Arrive Doha at" "$(date -d "$arr_doh" '+%F %R %z')"

minutes '1:40' doha # connection

dep_doh=$(date -Iminutes -d "$arr_doh + $doha minutes")
minutes '16:10' flight2
arr_akl=$(date -Iminutes -d "$dep_doh + $flight2 minutes")

printf "%-20s %s\n" "Leave Doha at" "$(date -d "$dep_doh" '+%F %R %z')"
printf "%-20s %s\n\n" "Arrive Auckland at" "$(date -d "$arr_akl" '+%F %R %z')"

printf "%-20s %s\n" "New Zealand time" \
    "$(TZ="Pacific/Auckland" date -d "$(date -Iminutes -d "$arr_akl")" '+%F %R %z')"

dur=$((flight1 + doha + flight2))
printf "%-20s %s\n" "Duration" "$((dur/60))hr $((dur%60))min"

I created a function called minutes which takes a time of the form ‘6:55’ as its first argument and turns it into minutes, returning the result to the caller. The second argument is the name of the variable to receive the result. The method used is a ‘nameref’ which I’ll explain more fully in an upcoming show.

Rather than converting to minutes I could have split the time into components and embedded them in an expression like ‘6 hours + 55 minutes’ as we saw earlier. I decided to do it using minutes.

I computed departure and arrival times based on the ISO 8601 format and saved them in variables to make computation easier. I then printed the values, reformatting these dates into something more readable. As you can see from the output below '+%F %R %z' generates a date in the format YYYY-MM-DD and a 24-hour 'HH:MM' time followed by the (local) timezone.

All times are in my local time (the default), but at the end I worked out the arrival time in Auckland using a timezone specification, as you can see in lines 48 and 49, and finished off by adding all the durations to generate a total journey time in hours and minutes. Here is what the output looks like:

Outward flights:
Leave Edinburgh at   2017-12-24 14:55 +0000
Arrive Doha at       2017-12-24 21:50 +0000

Leave Doha at        2017-12-24 23:30 +0000
Arrive Auckland at   2017-12-25 15:40 +0000

New Zealand time     2017-12-26 04:40 +1300
Duration             24hr 45min

Conclusion

Writing a Bash script provided the necessary catharsis and distraction, as did watching flight progress on https://www.flightradar24.com/. I also learnt some stuff about the date command I didn’t know. My wandering children head back in mid-January, and I have the script for the return flight already written!

  • GNU documentation for date (You can also use man date or info date for the full details. I prefer the HTML version because I don’t like the info tool very much).
  • The GNU Bash Reference Manual
  • Dann Wasko’s “Linux in the Shell” episode hpr1182 :: LiTS 023: Date, which is full of useful information.
  • Resources:
    • The script I wrote, called edi_akl (named to denote the starting and ending airports).

  1. In the audio I mentioned using the epoch date. This can be obtained using the %s specifier as in:

    epoch=$(date +%s)

    and can be input to date like this:

    $ date -d @$epoch '+%F %R'
    2018-01-01 11:17

    You can’t use it for date computations though:

    $ date -d "@$epoch + 1 week" '+%F %R'
    date: invalid date ‘@1514805475 + 1 week’