From ae3d1a6faa69e248130f59d4dfa2663ab8912819 Mon Sep 17 00:00:00 2001 From: shelken Date: Wed, 12 Aug 2026 04:46:11 +0800 Subject: [PATCH] fix(drivers/189): support updated 189 time format with comma, timezone and unicode spaces MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 189 API 变更了时间返回格式,现有 Time.Unmarshal 无法解析,导致 189pc/189_tv 驱动所有写操作失败(Put → 500)。 新格式有三种形态,现有两个模板均无法覆盖: 1. "Aug 11, 2026, 10:37:18 PM +08" — 年份后多逗号,且自带时区 2. "Aug 12, 2026, 12:35:41\u202fAM +08" — AM/PM 前为 U+202F 窄不换行空格 3. "Aug 12, 2026, 12:35:41\u00a0AM +08" — AM/PM 前为 U+00A0 不换行空格 修复(189pc/help.go、189_tv/help.go 的 Time.Unmarshal): - 解析前将 U+202F 和 U+00A0 统一替换为普通空格 - 新增模板 "Jan 2, 2006, 3:04:05 PM -07"(带逗号,3 为 12 小时制配 PM) - 对可能不带时区的串,先试原串再试追加 " +08"(向后兼容) 新增 help_test.go 覆盖:数字日期、旧月份格式、新格式带/不带时区、 U+202F/U+00A0 空格、非法输入。 Fixes #2917 Co-Authored-By: Advanced Co-Authored-By: DeepSeek V4 Flash (2x usage) Generated-By: pi 0.84.1 --- drivers/189_tv/help.go | 15 ++++++++++++-- drivers/189_tv/help_test.go | 39 +++++++++++++++++++++++++++++++++++++ drivers/189pc/help.go | 14 +++++++++++-- drivers/189pc/help_test.go | 39 +++++++++++++++++++++++++++++++++++++ 4 files changed, 103 insertions(+), 4 deletions(-) create mode 100644 drivers/189_tv/help_test.go create mode 100644 drivers/189pc/help_test.go diff --git a/drivers/189_tv/help.go b/drivers/189_tv/help.go index cfd9e18ab..6d48a3972 100644 --- a/drivers/189_tv/help.go +++ b/drivers/189_tv/help.go @@ -11,6 +11,8 @@ import ( "regexp" "strings" "time" + + "github.com/OpenListTeam/OpenList/v4/pkg/utils" ) func clientSuffix() map[string]string { @@ -72,10 +74,19 @@ func (t *Time) UnmarshalXML(e *xml.Decoder, ee xml.StartElement) error { } func (t *Time) Unmarshal(b []byte) error { bs := strings.Trim(string(b), "\"") + // 189 时间串里 AM/PM 前可能使用 U+202F 窄不换行空格或 U+00A0 不换行空格,统一替换为普通空格 + 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"} { - v, err = time.ParseInLocation(f, bs+" +08", time.Local) + // 189 返回的时间可能自带时区(如 "Aug 11, 2026, 10:37:18 PM +08"),也可能不带,分别尝试 + for _, s := range []string{bs, bs + " +08"} { + for _, f := range []string{"2006-01-02 15:04:05 -07", "Jan 2, 2006 3:04:05 PM -07", "Jan 2, 2006, 3:04:05 PM -07"} { + v, err = time.ParseInLocation(f, s, utils.CNLoc) + if err == nil { + break + } + } 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..ce7489f2a --- /dev/null +++ b/drivers/189_tv/help_test.go @@ -0,0 +1,39 @@ +package _189_tv + +import ( + "testing" + "time" +) + +func TestTimeUnmarshal(t *testing.T) { + tests := []struct { + name string + input string + want time.Time + }{ + {"numeric date", `"2026-08-11 10:37:18"`, time.Date(2026, 8, 11, 10, 37, 18, 0, time.FixedZone("", 8*3600))}, + {"legacy month date", `"Aug 11, 2026 10:37:18 PM"`, time.Date(2026, 8, 11, 22, 37, 18, 0, time.FixedZone("", 8*3600))}, + {"new format with tz", `"Aug 11, 2026, 10:37:18 PM +08"`, time.Date(2026, 8, 11, 22, 37, 18, 0, time.FixedZone("", 8*3600))}, + {"new format no tz", `"Aug 11, 2026, 10:37:18 PM"`, time.Date(2026, 8, 11, 22, 37, 18, 0, time.FixedZone("", 8*3600))}, + {"narrow no-break space (U+202F)", "\"Aug 12, 2026, 12:35:41\u202fAM +08\"", time.Date(2026, 8, 12, 0, 35, 41, 0, time.FixedZone("", 8*3600))}, + {"no-break space (U+00A0)", "\"Aug 12, 2026, 12:35:41\u00a0AM +08\"", time.Date(2026, 8, 12, 0, 35, 41, 0, time.FixedZone("", 8*3600))}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var tm Time + if err := tm.Unmarshal([]byte(tt.input)); err != nil { + t.Fatalf("Unmarshal(%s) error: %v", tt.input, err) + } + if !tt.want.Equal(time.Time(tm)) { + t.Fatalf("Unmarshal(%s) = %v, want %v", tt.input, time.Time(tm), tt.want) + } + }) + } +} + +func TestTimeUnmarshalRejectsInvalid(t *testing.T) { + var tm Time + if err := tm.Unmarshal([]byte("Aug 12, 2026, 25:32:44 AM")); err == nil { + t.Fatal("Unmarshal accepted an invalid time") + } +} diff --git a/drivers/189pc/help.go b/drivers/189pc/help.go index 6f6c59f30..a90ee9ee6 100644 --- a/drivers/189pc/help.go +++ b/drivers/189pc/help.go @@ -19,6 +19,7 @@ import ( "time" "github.com/OpenListTeam/OpenList/v4/internal/model" + "github.com/OpenListTeam/OpenList/v4/pkg/utils" "github.com/OpenListTeam/OpenList/v4/pkg/utils/random" ) @@ -116,10 +117,19 @@ func (t *Time) UnmarshalXML(e *xml.Decoder, ee xml.StartElement) error { } func (t *Time) Unmarshal(b []byte) error { bs := strings.Trim(string(b), "\"") + // 189 时间串里 AM/PM 前可能使用 U+202F 窄不换行空格或 U+00A0 不换行空格,统一替换为普通空格 + 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"} { - v, err = time.ParseInLocation(f, bs+" +08", time.Local) + // 189 返回的时间可能自带时区(如 "Aug 11, 2026, 10:37:18 PM +08"),也可能不带,分别尝试 + for _, s := range []string{bs, bs + " +08"} { + for _, f := range []string{"2006-01-02 15:04:05 -07", "Jan 2, 2006 3:04:05 PM -07", "Jan 2, 2006, 3:04:05 PM -07"} { + v, err = time.ParseInLocation(f, s, utils.CNLoc) + if err == nil { + break + } + } if err == nil { break } diff --git a/drivers/189pc/help_test.go b/drivers/189pc/help_test.go new file mode 100644 index 000000000..579978f57 --- /dev/null +++ b/drivers/189pc/help_test.go @@ -0,0 +1,39 @@ +package _189pc + +import ( + "testing" + "time" +) + +func TestTimeUnmarshal(t *testing.T) { + tests := []struct { + name string + input string + want time.Time + }{ + {"numeric date", `"2026-08-11 10:37:18"`, time.Date(2026, 8, 11, 10, 37, 18, 0, time.FixedZone("", 8*3600))}, + {"legacy month date", `"Aug 11, 2026 10:37:18 PM"`, time.Date(2026, 8, 11, 22, 37, 18, 0, time.FixedZone("", 8*3600))}, + {"new format with tz", `"Aug 11, 2026, 10:37:18 PM +08"`, time.Date(2026, 8, 11, 22, 37, 18, 0, time.FixedZone("", 8*3600))}, + {"new format no tz", `"Aug 11, 2026, 10:37:18 PM"`, time.Date(2026, 8, 11, 22, 37, 18, 0, time.FixedZone("", 8*3600))}, + {"narrow no-break space (U+202F)", "\"Aug 12, 2026, 12:35:41\u202fAM +08\"", time.Date(2026, 8, 12, 0, 35, 41, 0, time.FixedZone("", 8*3600))}, + {"no-break space (U+00A0)", "\"Aug 12, 2026, 12:35:41\u00a0AM +08\"", time.Date(2026, 8, 12, 0, 35, 41, 0, time.FixedZone("", 8*3600))}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var tm Time + if err := tm.Unmarshal([]byte(tt.input)); err != nil { + t.Fatalf("Unmarshal(%s) error: %v", tt.input, err) + } + if !tt.want.Equal(time.Time(tm)) { + t.Fatalf("Unmarshal(%s) = %v, want %v", tt.input, time.Time(tm), tt.want) + } + }) + } +} + +func TestTimeUnmarshalRejectsInvalid(t *testing.T) { + var tm Time + if err := tm.Unmarshal([]byte("Aug 12, 2026, 25:32:44 AM")); err == nil { + t.Fatal("Unmarshal accepted an invalid time") + } +}