Fix time parsing and make tests deterministic

This commit is contained in:
Elara 2023-08-22 19:11:58 -07:00
parent c9846adfe5
commit 275e9f7497
3 changed files with 100 additions and 71 deletions

26
taf.go
View File

@ -50,6 +50,14 @@ type Options struct {
// If this is set, all speed units in the forecast will
// be converted to the given unit
SpeedUnit units.Speed
// The Year field is used to calculate the full date that this
// report was published. If it's unset, the current year will be used.
Year int
// The Month field is used to calculate the full date that this
// report was published. If it's unset, the current year will be used.
Month time.Month
}
// ParseWithOptions parses the data in a reader and returns a Forecast
@ -67,6 +75,14 @@ func ParseWithOptions(r io.Reader, opts Options) (*Forecast, error) {
filename = "string"
}
if opts.Year == 0 {
opts.Year = time.Now().Year()
}
if opts.Month == 0 {
opts.Month = time.Now().Month()
}
ast, err := parser.Parser.Parse(filename, r)
if err != nil {
return nil, err
@ -83,7 +99,7 @@ func ParseWithOptions(r io.Reader, opts Options) (*Forecast, error) {
fc.Airport = a
}
case item.Time != nil:
t, err := parseTime(*item.Time)
t, err := parseTime(*item.Time, opts.Month, opts.Year)
if err != nil {
return nil, participle.Errorf(item.Pos, "time: %s", err)
}
@ -91,7 +107,7 @@ func ParseWithOptions(r io.Reader, opts Options) (*Forecast, error) {
// The Time item always comes with a Valid as well because
// of the way it's parsed into the AST
vp, err := parseValid(item.Valid)
vp, err := parseValid(item.Valid, opts.Month, opts.Year)
if err != nil {
return nil, participle.Errorf(item.Pos, "time: %s", err)
}
@ -125,7 +141,7 @@ func ParseWithOptions(r io.Reader, opts Options) (*Forecast, error) {
CloudType: convertCloudType(item.SkyCondition.CloudType),
})
case item.Temperature != nil:
vt, err := parseValidTime(item.Temperature.Time)
vt, err := parseValidTime(item.Temperature.Time, opts.Month, opts.Year)
if err != nil {
return nil, participle.Errorf(item.Temperature.Pos, "temp: %s", err)
}
@ -277,13 +293,13 @@ func ParseWithOptions(r io.Reader, opts Options) (*Forecast, error) {
// FM changes don't have a valid pair, they only come with a single time string
if ch.Type == From {
t, err := parseTime(item.Change.Time)
t, err := parseTime(item.Change.Time, opts.Month, opts.Year)
if err != nil {
return nil, participle.Errorf(item.Change.Pos, "changes: %s", err)
}
ch.Valid = ValidPair{From: t}
} else {
vp, err := parseValid(item.Change.Valid)
vp, err := parseValid(item.Change.Valid, opts.Month, opts.Year)
if err != nil {
return nil, participle.Errorf(item.Change.Pos, "changes: %s", err)
}

View File

@ -32,10 +32,10 @@ func TestKLAX(t *testing.T) {
Longitude: -118.4079971,
Timezone: "America/Los_Angeles",
},
PublishTime: time.Date(2023, time.September, 21, 20, 11, 0, 0, time.UTC),
PublishTime: time.Date(2023, time.August, 21, 20, 11, 0, 0, time.UTC),
Valid: ValidPair{
From: time.Date(2023, time.September, 21, 20, 0, 0, 0, time.UTC),
To: time.Date(2023, time.September, 23, 0, 0, 0, 0, time.UTC),
From: time.Date(2023, time.August, 21, 20, 0, 0, 0, time.UTC),
To: time.Date(2023, time.August, 23, 0, 0, 0, 0, time.UTC),
Duration: time.Duration(100800000000000),
},
Visibility: Visibility{
@ -68,7 +68,7 @@ func TestKLAX(t *testing.T) {
{
Type: From,
Valid: ValidPair{
From: time.Date(2023, time.September, 21, 22, 0, 0, 0, time.UTC),
From: time.Date(2023, time.August, 21, 22, 0, 0, 0, time.UTC),
},
Visibility: Visibility{
Plus: true,
@ -92,7 +92,7 @@ func TestKLAX(t *testing.T) {
{
Type: From,
Valid: ValidPair{
From: time.Date(2023, time.September, 22, 3, 0, 0, 0, time.UTC),
From: time.Date(2023, time.August, 22, 3, 0, 0, 0, time.UTC),
},
Visibility: Visibility{
Plus: true,
@ -116,7 +116,7 @@ func TestKLAX(t *testing.T) {
{
Type: From,
Valid: ValidPair{
From: time.Date(2023, time.September, 22, 10, 0, 0, 0, time.UTC),
From: time.Date(2023, time.August, 22, 10, 0, 0, 0, time.UTC),
},
Visibility: Visibility{
Plus: true,
@ -140,7 +140,7 @@ func TestKLAX(t *testing.T) {
{
Type: From,
Valid: ValidPair{
From: time.Date(2023, time.September, 22, 17, 0, 0, 0, time.UTC),
From: time.Date(2023, time.August, 22, 17, 0, 0, 0, time.UTC),
},
Visibility: Visibility{
Plus: true,
@ -164,7 +164,7 @@ func TestKLAX(t *testing.T) {
{
Type: From,
Valid: ValidPair{
From: time.Date(2023, time.September, 22, 20, 0, 0, 0, time.UTC),
From: time.Date(2023, time.August, 22, 20, 0, 0, 0, time.UTC),
},
Visibility: Visibility{
Plus: true,
@ -188,7 +188,10 @@ func TestKLAX(t *testing.T) {
},
}
fc, err := Parse(strings.NewReader(data))
fc, err := ParseWithOptions(strings.NewReader(data), Options{
Month: time.August,
Year: 2023,
})
if err != nil {
t.Fatalf("Error during parsing: %s", err)
}
@ -217,10 +220,10 @@ func TestZGSZ(t *testing.T) {
Longitude: 113.8109970093,
Timezone: "Asia/Shanghai",
},
PublishTime: time.Date(2023, time.September, 21, 19, 7, 0, 0, time.UTC),
PublishTime: time.Date(2023, time.August, 21, 19, 7, 0, 0, time.UTC),
Valid: ValidPair{
From: time.Date(2023, time.September, 21, 18, 0, 0, 0, time.UTC),
To: time.Date(2023, time.September, 22, 18, 0, 0, 0, time.UTC),
From: time.Date(2023, time.August, 21, 18, 0, 0, 0, time.UTC),
To: time.Date(2023, time.August, 22, 18, 0, 0, 0, time.UTC),
Duration: time.Duration(86400000000000),
},
Visibility: Visibility{
@ -244,20 +247,20 @@ func TestZGSZ(t *testing.T) {
{
Type: High,
Value: 32,
Time: time.Date(2023, time.September, 22, 6, 0, 0, 0, time.UTC),
Time: time.Date(2023, time.August, 22, 6, 0, 0, 0, time.UTC),
},
{
Type: Low,
Value: 28,
Time: time.Date(2023, time.September, 21, 22, 0, 0, 0, time.UTC),
Time: time.Date(2023, time.August, 21, 22, 0, 0, 0, time.UTC),
},
},
Changes: []*Change{
{
Type: Temporary,
Valid: ValidPair{
From: time.Date(2023, time.September, 21, 20, 0, 0, 0, time.UTC),
To: time.Date(2023, time.September, 22, 2, 0, 0, 0, time.UTC),
From: time.Date(2023, time.August, 21, 20, 0, 0, 0, time.UTC),
To: time.Date(2023, time.August, 22, 2, 0, 0, 0, time.UTC),
Duration: time.Duration(21600000000000),
},
SkyCondition: []SkyCondition{
@ -281,8 +284,8 @@ func TestZGSZ(t *testing.T) {
{
Type: Temporary,
Valid: ValidPair{
From: time.Date(2023, time.September, 22, 4, 0, 0, 0, time.UTC),
To: time.Date(2023, time.September, 22, 8, 0, 0, 0, time.UTC),
From: time.Date(2023, time.August, 22, 4, 0, 0, 0, time.UTC),
To: time.Date(2023, time.August, 22, 8, 0, 0, 0, time.UTC),
Duration: time.Duration(14400000000000),
},
SkyCondition: []SkyCondition{
@ -306,7 +309,10 @@ func TestZGSZ(t *testing.T) {
},
}
fc, err := Parse(strings.NewReader(data))
fc, err := ParseWithOptions(strings.NewReader(data), Options{
Month: time.August,
Year: 2023,
})
if err != nil {
t.Fatalf("Error during parsing: %s", err)
}
@ -337,10 +343,10 @@ func TestLFBD(t *testing.T) {
Longitude: -0.7155560255,
Timezone: "Europe/Paris",
},
PublishTime: time.Date(2023, time.September, 21, 17, 0, 0, 0, time.UTC),
PublishTime: time.Date(2023, time.August, 21, 17, 0, 0, 0, time.UTC),
Valid: ValidPair{
From: time.Date(2023, time.September, 21, 18, 0, 0, 0, time.UTC),
To: time.Date(2023, time.September, 23, 0, 0, 0, 0, time.UTC),
From: time.Date(2023, time.August, 21, 18, 0, 0, 0, time.UTC),
To: time.Date(2023, time.August, 23, 0, 0, 0, 0, time.UTC),
Duration: time.Duration(108000000000000),
},
Wind: Wind{
@ -354,20 +360,20 @@ func TestLFBD(t *testing.T) {
{
Type: High,
Value: 37,
Time: time.Date(2023, time.September, 22, 14, 0, 0, 0, time.UTC),
Time: time.Date(2023, time.August, 22, 14, 0, 0, 0, time.UTC),
},
{
Type: Low,
Value: 22,
Time: time.Date(2023, time.September, 22, 5, 0, 0, 0, time.UTC),
Time: time.Date(2023, time.August, 22, 5, 0, 0, 0, time.UTC),
},
},
Changes: []*Change{
{
Type: Becoming,
Valid: ValidPair{
From: time.Date(2023, time.September, 21, 18, 0, 0, 0, time.UTC),
To: time.Date(2023, time.September, 21, 20, 0, 0, 0, time.UTC),
From: time.Date(2023, time.August, 21, 18, 0, 0, 0, time.UTC),
To: time.Date(2023, time.August, 21, 20, 0, 0, 0, time.UTC),
Duration: time.Duration(7200000000000),
},
Wind: Wind{
@ -381,8 +387,8 @@ func TestLFBD(t *testing.T) {
{
Type: Becoming,
Valid: ValidPair{
From: time.Date(2023, time.September, 22, 0, 0, 0, 0, time.UTC),
To: time.Date(2023, time.September, 22, 2, 0, 0, 0, time.UTC),
From: time.Date(2023, time.August, 22, 0, 0, 0, 0, time.UTC),
To: time.Date(2023, time.August, 22, 2, 0, 0, 0, time.UTC),
Duration: time.Duration(7200000000000),
},
Wind: Wind{
@ -396,8 +402,8 @@ func TestLFBD(t *testing.T) {
{
Type: Becoming,
Valid: ValidPair{
From: time.Date(2023, time.September, 22, 13, 0, 0, 0, time.UTC),
To: time.Date(2023, time.September, 22, 15, 0, 0, 0, time.UTC),
From: time.Date(2023, time.August, 22, 13, 0, 0, 0, time.UTC),
To: time.Date(2023, time.August, 22, 15, 0, 0, 0, time.UTC),
Duration: time.Duration(7200000000000),
},
Wind: Wind{
@ -411,8 +417,8 @@ func TestLFBD(t *testing.T) {
{
Type: Becoming,
Valid: ValidPair{
From: time.Date(2023, time.September, 22, 22, 0, 0, 0, time.UTC),
To: time.Date(2023, time.September, 23, 0, 0, 0, 0, time.UTC),
From: time.Date(2023, time.August, 22, 22, 0, 0, 0, time.UTC),
To: time.Date(2023, time.August, 23, 0, 0, 0, 0, time.UTC),
Duration: time.Duration(7200000000000),
},
Wind: Wind{
@ -429,7 +435,10 @@ func TestLFBD(t *testing.T) {
},
}
fc, err := Parse(strings.NewReader(data))
fc, err := ParseWithOptions(strings.NewReader(data), Options{
Month: time.August,
Year: 2023,
})
if err != nil {
t.Fatalf("Error during parsing: %s", err)
}
@ -462,10 +471,10 @@ func TestUUEE(t *testing.T) {
Longitude: 37.4146003723,
Timezone: "Europe/Moscow",
},
PublishTime: time.Date(2023, time.September, 21, 19, 58, 0, 0, time.UTC),
PublishTime: time.Date(2023, time.August, 21, 19, 58, 0, 0, time.UTC),
Valid: ValidPair{
From: time.Date(2023, time.September, 21, 21, 0, 0, 0, time.UTC),
To: time.Date(2023, time.September, 22, 21, 0, 0, 0, time.UTC),
From: time.Date(2023, time.August, 21, 21, 0, 0, 0, time.UTC),
To: time.Date(2023, time.August, 22, 21, 0, 0, 0, time.UTC),
Duration: time.Duration(86400000000000),
},
Visibility: Visibility{
@ -489,20 +498,20 @@ func TestUUEE(t *testing.T) {
{
Type: High,
Value: 20,
Time: time.Date(2023, time.September, 22, 12, 0, 0, 0, time.UTC),
Time: time.Date(2023, time.August, 22, 12, 0, 0, 0, time.UTC),
},
{
Type: Low,
Value: 12,
Time: time.Date(2023, time.September, 22, 2, 0, 0, 0, time.UTC),
Time: time.Date(2023, time.August, 22, 2, 0, 0, 0, time.UTC),
},
},
Changes: []*Change{
{
Type: Temporary,
Valid: ValidPair{
From: time.Date(2023, time.September, 21, 21, 0, 0, 0, time.UTC),
To: time.Date(2023, time.September, 22, 4, 0, 0, 0, time.UTC),
From: time.Date(2023, time.August, 21, 21, 0, 0, 0, time.UTC),
To: time.Date(2023, time.August, 22, 4, 0, 0, 0, time.UTC),
Duration: time.Duration(25200000000000),
},
SkyCondition: []SkyCondition{
@ -516,8 +525,8 @@ func TestUUEE(t *testing.T) {
{
Type: Temporary,
Valid: ValidPair{
From: time.Date(2023, time.September, 21, 21, 0, 0, 0, time.UTC),
To: time.Date(2023, time.September, 22, 4, 0, 0, 0, time.UTC),
From: time.Date(2023, time.August, 21, 21, 0, 0, 0, time.UTC),
To: time.Date(2023, time.August, 22, 4, 0, 0, 0, time.UTC),
Duration: time.Duration(25200000000000),
},
Visibility: Visibility{
@ -533,8 +542,8 @@ func TestUUEE(t *testing.T) {
{
Type: Becoming,
Valid: ValidPair{
From: time.Date(2023, time.September, 22, 4, 0, 0, 0, time.UTC),
To: time.Date(2023, time.September, 22, 6, 0, 0, 0, time.UTC),
From: time.Date(2023, time.August, 22, 4, 0, 0, 0, time.UTC),
To: time.Date(2023, time.August, 22, 6, 0, 0, 0, time.UTC),
Duration: time.Duration(7200000000000),
},
Wind: Wind{
@ -549,8 +558,8 @@ func TestUUEE(t *testing.T) {
{
Type: Temporary,
Valid: ValidPair{
From: time.Date(2023, time.September, 22, 9, 0, 0, 0, time.UTC),
To: time.Date(2023, time.September, 22, 18, 0, 0, 0, time.UTC),
From: time.Date(2023, time.August, 22, 9, 0, 0, 0, time.UTC),
To: time.Date(2023, time.August, 22, 18, 0, 0, 0, time.UTC),
Duration: time.Duration(32400000000000),
},
SkyCondition: []SkyCondition{
@ -571,7 +580,10 @@ func TestUUEE(t *testing.T) {
},
}
fc, err := Parse(strings.NewReader(data))
fc, err := ParseWithOptions(strings.NewReader(data), Options{
Month: time.August,
Year: 2023,
})
if err != nil {
t.Fatalf("Error during parsing: %s", err)
}
@ -602,10 +614,10 @@ func TestEGLL(t *testing.T) {
Longitude: -0.4619410038,
Timezone: "Europe/London",
},
PublishTime: time.Date(2023, time.September, 21, 16, 58, 0, 0, time.UTC),
PublishTime: time.Date(2023, time.August, 21, 16, 58, 0, 0, time.UTC),
Valid: ValidPair{
From: time.Date(2023, time.September, 21, 18, 0, 0, 0, time.UTC),
To: time.Date(2023, time.September, 23, 0, 0, 0, 0, time.UTC),
From: time.Date(2023, time.August, 21, 18, 0, 0, 0, time.UTC),
To: time.Date(2023, time.August, 23, 0, 0, 0, 0, time.UTC),
Duration: time.Duration(108000000000000),
},
Visibility: Visibility{
@ -629,8 +641,8 @@ func TestEGLL(t *testing.T) {
{
Type: Becoming,
Valid: ValidPair{
From: time.Date(2023, time.September, 22, 1, 0, 0, 0, time.UTC),
To: time.Date(2023, time.September, 22, 4, 0, 0, 0, time.UTC),
From: time.Date(2023, time.August, 22, 1, 0, 0, 0, time.UTC),
To: time.Date(2023, time.August, 22, 4, 0, 0, 0, time.UTC),
Duration: time.Duration(10800000000000),
},
SkyCondition: []SkyCondition{
@ -644,8 +656,8 @@ func TestEGLL(t *testing.T) {
{
Type: Temporary,
Valid: ValidPair{
From: time.Date(2023, time.September, 22, 2, 0, 0, 0, time.UTC),
To: time.Date(2023, time.September, 22, 6, 0, 0, 0, time.UTC),
From: time.Date(2023, time.August, 22, 2, 0, 0, 0, time.UTC),
To: time.Date(2023, time.August, 22, 6, 0, 0, 0, time.UTC),
Duration: time.Duration(14400000000000),
},
Visibility: Visibility{
@ -662,8 +674,8 @@ func TestEGLL(t *testing.T) {
{
Type: Becoming,
Valid: ValidPair{
From: time.Date(2023, time.September, 22, 7, 0, 0, 0, time.UTC),
To: time.Date(2023, time.September, 22, 10, 0, 0, 0, time.UTC),
From: time.Date(2023, time.August, 22, 7, 0, 0, 0, time.UTC),
To: time.Date(2023, time.August, 22, 10, 0, 0, 0, time.UTC),
Duration: time.Duration(10800000000000),
},
SkyCondition: []SkyCondition{
@ -676,7 +688,10 @@ func TestEGLL(t *testing.T) {
},
}
fc, err := Parse(strings.NewReader(data))
fc, err := ParseWithOptions(strings.NewReader(data), Options{
Month: time.August,
Year: 2023,
})
if err != nil {
t.Fatalf("Error during parsing: %s", err)
}

16
time.go
View File

@ -12,22 +12,21 @@ const (
ValidFormat = "0215"
)
func parseTime(s string) (time.Time, error) {
func parseTime(s string, m time.Month, year int) (time.Time, error) {
t, err := time.Parse(TimeFormat, s)
if err != nil {
return time.Time{}, err
}
now := time.Now().UTC()
return t.AddDate(now.Year(), int(now.Month()), 0), nil
return t.AddDate(year, int(m)-1, 0), nil
}
func parseValid(v *parser.ValidPair) (ValidPair, error) {
start, err := parseValidTime(v.Start)
func parseValid(v *parser.ValidPair, m time.Month, year int) (ValidPair, error) {
start, err := parseValidTime(v.Start, m, year)
if err != nil {
return ValidPair{}, err
}
end, err := parseValidTime(v.End)
end, err := parseValidTime(v.End, m, year)
if err != nil {
return ValidPair{}, err
}
@ -39,7 +38,7 @@ func parseValid(v *parser.ValidPair) (ValidPair, error) {
}, nil
}
func parseValidTime(s string) (time.Time, error) {
func parseValidTime(s string, m time.Month, year int) (time.Time, error) {
addDays := 0
// Go doesn't know what to do with hour 24,
// so we set it to 00 the next day
@ -53,6 +52,5 @@ func parseValidTime(s string) (time.Time, error) {
return time.Time{}, err
}
now := time.Now().UTC()
return t.AddDate(now.Year(), int(now.Month()), addDays), nil
return t.AddDate(year, int(m)-1, addDays), nil
}