aboutsummaryrefslogtreecommitdiff
path: root/pkg/adapters/xlsx/model.go
blob: ff92defc32166720994636088d2f16d6c085a20a (plain)
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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
package xlsx

import (
	"airlines/pkg/model"
	"errors"
	"strconv"
	"strings"
	"time"

	"airlines/pkg/airports"
)

type Ticket struct {
	Sheet        string
	Passenger    string
	Title        string
	FlightNumber string
	FromCity     string
	FromCountry  string
	FromAirport  string
	FromCoords   model.LatLong
	ToCity       string
	ToCountry    string
	ToAirport    string
	ToCoords     model.LatLong
	FlightDate   string // expected YYYY-MM-DD
	FlightTime   string // expected HH-MM or HH:MM
	PNR          string
	Card         string
	TicketNumber string
}

func (t Ticket) DateTime() (time.Time, *time.Location, error) {
	loc := t.inferLocationFromAirports()
	date := strings.TrimLeft(strings.TrimSpace(t.FlightDate), "'")
	hm := strings.TrimLeft(strings.TrimSpace(t.FlightTime), "'")
	hm = strings.ReplaceAll(hm, "-", ":")

	if date == "" || hm == "" {
		return time.Time{}, loc, errors.New("missing FlightDate or FlightTime")
	}
	ts, err := time.ParseInLocation("2006-01-02 15:04", date+" "+hm, loc)
	return ts, loc, err
}

func (t Ticket) inferLocationFromAirports() *time.Location {
	if loc := iataToLocation(t.FromAirport); loc != nil {
		return loc
	}
	if loc := iataToLocation(t.ToAirport); loc != nil {
		return loc
	}
	return time.Local
}

func iataToLocation(code string) *time.Location {
	iata := strings.ToUpper(strings.TrimSpace(code))
	if len(iata) != 3 {
		return nil
	}
	ap, err := airports.LookupIATA(iata)
	if err != nil {
		return nil
	}
	// prefer IATA tz name
	if tz := strings.TrimSpace(ap.Tz); tz != "" && tz != `\N` {
		if loc, err := time.LoadLocation(tz); err == nil {
			return loc
		}
	}
	// fallback to fixed offset (no DST)
	if ap.Timezone != 0 {
		sec := int(ap.Timezone * 3600.0)
		return time.FixedZone("UTC"+offsetLabel(sec), sec)
	}
	return nil
}

func offsetLabel(sec int) string {
	sign := "+"
	if sec < 0 {
		sign = "-"
		sec = -sec
	}
	h := sec / 3600
	m := (sec % 3600) / 60
	return sign + two(h) + ":" + two(m)
}
func two(x int) string {
	if x < 10 {
		return "0" + strconv.Itoa(x)
	}
	return strconv.Itoa(x)
}