diff --git a/server/internal/handler/forward.go b/server/internal/handler/forward.go index 7086ba9..e99f46d 100644 --- a/server/internal/handler/forward.go +++ b/server/internal/handler/forward.go @@ -45,7 +45,10 @@ func quoteBody(m *models.Mail) string { } b.WriteString("\n") b.WriteString(fmt.Sprintf("> **主题** %s\n", m.Subject)) - b.WriteString(fmt.Sprintf("> **时间** %s\n", m.CreatedAt.Format("2006-01-02 15:04:05"))) + // ★ 2026-09-15 用户报:转发引用里显示「2026-09-15 01:23:19」,而实际是 09:23:19 —— + // 原文时间按 **UTC** 原样格式化了。库里存的是 UTC,界面各处都转本地,只有这一处漏了 + // (同一族的写法在 scheduler/calendar.go 里是对的:e.EventTime.Local().Format(...))。 + b.WriteString(fmt.Sprintf("> **时间** %s\n", m.CreatedAt.Local().Format("2006-01-02 15:04:05"))) if len(m.CCList) > 0 { names := make([]string, 0, len(m.CCList)) for _, c := range m.CCList { diff --git a/server/internal/handler/forward_test.go b/server/internal/handler/forward_test.go index 384b67d..2bc2e10 100644 --- a/server/internal/handler/forward_test.go +++ b/server/internal/handler/forward_test.go @@ -51,7 +51,9 @@ func TestQuoteBodyPrefixesEveryLine(t *testing.T) { } // 元信息必须齐全,否则收件人不知道这封转发的来路 - for _, want := range []string{"opencode@/root", "巡检结果", "2026-09-02 10:30:00", "pi@root.new"} { + // ★ 2026-09-15:这里原先写死 "2026-09-02 10:30:00"(UTC 原样),而 fixture 是 time.UTC —— + // 等于这条断言一直在**保护那个 bug**(引用块显示 UTC 时间)。时间一项按本地算,不写死字面量。 + for _, want := range []string{"opencode@/root", "巡检结果", m.CreatedAt.Local().Format("2006-01-02 15:04:05"), "pi@root.new"} { if !strings.Contains(out, want) { t.Errorf("引用块缺少 %q\n%s", want, out) } @@ -75,3 +77,28 @@ func TestQuoteBodyOmitsEmptyCC(t *testing.T) { t.Error("无抄送时不应出现「抄送」行") } } + +// TestQuoteBodyTimeIsLocal —— 转发引用里的「时间」必须是**本地**时间,不是 UTC 原样。 +// +// 2026-09-15 用户报:引用块显示 01:23:19,而实际发生时间是 09:23:19(本机 UTC+8)。 +// 库里存 UTC、界面各处都转本地,只有 quoteBody 漏了 .Local()。 +// +// ★ 这条判据在 **TZ=UTC 的机器上是空转的**(本地 == UTC,两种写法同结果)—— +// 本机是 UTC+8,所以这里真会红。CI 若在 UTC 下跑,它是"只能验形态"的那一类, +// 形态判据另有一条:不得与 UTC 原样相等(当两者不同时)。 +func TestQuoteBodyTimeIsLocal(t *testing.T) { + // 取一个"本地与 UTC 不同"的瞬间来测(否则本判据无从区分) + m := &models.Mail{ + FromName: "zcode", + Subject: "时间判定", + CreatedAt: time.Date(2026, 9, 15, 1, 23, 19, 0, time.UTC), + } + got := quoteBody(m) + want := m.CreatedAt.Local().Format("2006-01-02 15:04:05") + if !strings.Contains(got, "> **时间** "+want) { + t.Fatalf("引用里的时间不是本地时间:想要 %q,实际渲染:\n%s", want, got) + } + if u := m.CreatedAt.UTC().Format("2006-01-02 15:04:05"); u != want && strings.Contains(got, "> **时间** "+u) { + t.Fatalf("引用里出现的是 UTC 原样 %q(应为本地 %q)", u, want) + } +}