diff --git a/drivers/189_tv/help.go b/drivers/189_tv/help.go index cfd9e18ab..2fecaf65b 100644 --- a/drivers/189_tv/help.go +++ b/drivers/189_tv/help.go @@ -72,9 +72,15 @@ func (t *Time) UnmarshalXML(e *xml.Decoder, ee xml.StartElement) error { } func (t *Time) Unmarshal(b []byte) error { bs := strings.Trim(string(b), "\"") + bs = strings.ReplaceAll(bs, "\u202f", " ") + bs = strings.ReplaceAll(bs, "\u00a0", " ") var v time.Time var err error - for _, f := range []string{"2006-01-02 15:04:05 -07", "Jan 2, 2006 15:04:05 PM -07"} { + for _, f := range []string{ + "2006-01-02 15:04:05 -07", + "Jan 2, 2006 15:04:05 PM -07", + "Jan 2, 2006, 3:04:05 PM -07", + } { v, err = time.ParseInLocation(f, bs+" +08", time.Local) if err == nil { break diff --git a/drivers/189_tv/help_test.go b/drivers/189_tv/help_test.go new file mode 100644 index 000000000..f6f86d331 --- /dev/null +++ b/drivers/189_tv/help_test.go @@ -0,0 +1,70 @@ +package _189_tv + +import ( + "testing" + "time" +) + +func TestTimeUnmarshal(t *testing.T) { + tests := []struct { + name string + input string + want time.Time + }{ + { + name: "numeric date", + input: "2026-08-12 03:32:44", + want: time.Date(2026, time.August, 12, 3, 32, 44, 0, time.Local), + }, + { + name: "legacy month date", + input: "Aug 12, 2026 15:32:44 PM", + want: time.Date(2026, time.August, 12, 15, 32, 44, 0, time.Local), + }, + { + name: "updated format with AM", + input: "Aug 12, 2026, 12:35:41 AM", + want: time.Date(2026, time.August, 12, 0, 35, 41, 0, time.Local), + }, + { + name: "updated format with PM", + input: "Aug 12, 2026, 3:32:44 PM", + want: time.Date(2026, time.August, 12, 15, 32, 44, 0, time.Local), + }, + { + name: "narrow no-break space", + input: "Aug 12, 2026, 3:32:44\u202fPM", + want: time.Date(2026, time.August, 12, 15, 32, 44, 0, time.Local), + }, + { + name: "no-break space", + input: "Aug 12, 2026, 3:32:44\u00a0AM", + want: time.Date(2026, time.August, 12, 3, 32, 44, 0, time.Local), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var got Time + if err := got.Unmarshal([]byte(tt.input)); err != nil { + t.Fatalf("Time.Unmarshal(%q) returned error: %v", tt.input, err) + } + gotTime := time.Time(got) + gotYear, gotMonth, gotDay := gotTime.Date() + wantYear, wantMonth, wantDay := tt.want.Date() + gotHour, gotMinute, gotSecond := gotTime.Clock() + wantHour, wantMinute, wantSecond := tt.want.Clock() + if gotYear != wantYear || gotMonth != wantMonth || gotDay != wantDay || + gotHour != wantHour || gotMinute != wantMinute || gotSecond != wantSecond { + t.Fatalf("Time.Unmarshal(%q) = %v, want %v", tt.input, gotTime, tt.want) + } + }) + } +} + +func TestTimeUnmarshalRejectsInvalidTime(t *testing.T) { + var got Time + if err := got.Unmarshal([]byte("Aug 12, 2026, 25:32:44 PM")); err == nil { + t.Fatal("Time.Unmarshal accepted an invalid time") + } +} diff --git a/drivers/189pc/help.go b/drivers/189pc/help.go index 6f6c59f30..c4dd1ad80 100644 --- a/drivers/189pc/help.go +++ b/drivers/189pc/help.go @@ -116,9 +116,15 @@ func (t *Time) UnmarshalXML(e *xml.Decoder, ee xml.StartElement) error { } func (t *Time) Unmarshal(b []byte) error { bs := strings.Trim(string(b), "\"") + bs = strings.ReplaceAll(bs, "\u202f", " ") + bs = strings.ReplaceAll(bs, "\u00a0", " ") var v time.Time var err error - for _, f := range []string{"2006-01-02 15:04:05 -07", "Jan 2, 2006 15:04:05 PM -07"} { + for _, f := range []string{ + "2006-01-02 15:04:05 -07", + "Jan 2, 2006 15:04:05 PM -07", + "Jan 2, 2006, 3:04:05 PM -07", + } { v, err = time.ParseInLocation(f, bs+" +08", time.Local) if err == nil { break diff --git a/drivers/189pc/help_test.go b/drivers/189pc/help_test.go new file mode 100644 index 000000000..6faa8860a --- /dev/null +++ b/drivers/189pc/help_test.go @@ -0,0 +1,65 @@ +package _189pc + +import ( + "testing" + "time" +) + +func TestTimeUnmarshal(t *testing.T) { + tests := []struct { + name string + input string + want time.Time + }{ + { + name: "numeric date", + input: "2026-08-12 03:32:44", + want: time.Date(2026, time.August, 12, 3, 32, 44, 0, time.Local), + }, + { + name: "legacy month date", + input: "Aug 12, 2026 15:32:44 PM", + want: time.Date(2026, time.August, 12, 15, 32, 44, 0, time.Local), + }, + { + name: "updated month date", + input: "Aug 12, 2026, 3:32:44 AM", + want: time.Date(2026, time.August, 12, 3, 32, 44, 0, time.Local), + }, + { + name: "narrow no-break space before meridiem", + input: "Aug 12, 2026, 3:32:44\u202fAM", + want: time.Date(2026, time.August, 12, 3, 32, 44, 0, time.Local), + }, + { + name: "no-break space before meridiem", + input: "Aug 12, 2026, 3:32:44\u00a0AM", + want: time.Date(2026, time.August, 12, 3, 32, 44, 0, time.Local), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var got Time + if err := got.Unmarshal([]byte(tt.input)); err != nil { + t.Fatalf("Time.Unmarshal(%q) returned error: %v", tt.input, err) + } + gotTime := time.Time(got) + gotYear, gotMonth, gotDay := gotTime.Date() + wantYear, wantMonth, wantDay := tt.want.Date() + gotHour, gotMinute, gotSecond := gotTime.Clock() + wantHour, wantMinute, wantSecond := tt.want.Clock() + if gotYear != wantYear || gotMonth != wantMonth || gotDay != wantDay || + gotHour != wantHour || gotMinute != wantMinute || gotSecond != wantSecond { + t.Fatalf("Time.Unmarshal(%q) = %v, want %v", tt.input, gotTime, tt.want) + } + }) + } +} + +func TestTimeUnmarshalRejectsInvalidTime(t *testing.T) { + var got Time + if err := got.Unmarshal([]byte("Aug 12, 2026, 25:32:44 AM")); err == nil { + t.Fatal("Time.Unmarshal accepted an invalid time") + } +}