diff --git a/taf.go b/taf.go index cdec7f9..f0d595c 100644 --- a/taf.go +++ b/taf.go @@ -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) } diff --git a/taf_test.go b/taf_test.go index dbee9c6..b618c65 100644 --- a/taf_test.go +++ b/taf_test.go @@ -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) } diff --git a/time.go b/time.go index 9ca74dc..223c9c2 100644 --- a/time.go +++ b/time.go @@ -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 }