mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-06-28 01:11:31 +08:00
🐛 fix(export): 修复导出时间时区误偏移
This commit is contained in:
@@ -614,13 +614,23 @@ func parseTemporalString(raw string) (time.Time, bool) {
|
||||
return time.Time{}, false
|
||||
}
|
||||
|
||||
layouts := []string{
|
||||
layoutsWithZone := []string{
|
||||
"2006-01-02 15:04:05.999999999 -0700 MST",
|
||||
"2006-01-02 15:04:05 -0700 MST",
|
||||
"2006-01-02 15:04:05.999999999 -0700",
|
||||
"2006-01-02 15:04:05 -0700",
|
||||
time.RFC3339Nano,
|
||||
time.RFC3339,
|
||||
}
|
||||
|
||||
for _, layout := range layoutsWithZone {
|
||||
parsed, err := time.Parse(layout, text)
|
||||
if err == nil {
|
||||
return parsed, true
|
||||
}
|
||||
}
|
||||
|
||||
layoutsWithoutZone := []string{
|
||||
"2006-01-02 15:04:05.999999999",
|
||||
"2006-01-02 15:04:05",
|
||||
"2006-01-02",
|
||||
@@ -628,8 +638,8 @@ func parseTemporalString(raw string) (time.Time, bool) {
|
||||
"15:04:05",
|
||||
}
|
||||
|
||||
for _, layout := range layouts {
|
||||
parsed, err := time.Parse(layout, text)
|
||||
for _, layout := range layoutsWithoutZone {
|
||||
parsed, err := time.ParseInLocation(layout, text, time.Local)
|
||||
if err == nil {
|
||||
return parsed, true
|
||||
}
|
||||
@@ -2208,9 +2218,9 @@ func formatExportCellText(val interface{}) string {
|
||||
return text
|
||||
default:
|
||||
text := fmt.Sprintf("%v", val)
|
||||
// 字符串型日期时间值(如 RFC3339 "2026-03-10T17:01:55+08:00")格式化为本地时区 yyyy-MM-dd HH:mm:ss
|
||||
// 字符串型日期时间值(如 RFC3339 "2026-03-10T17:01:55+08:00")统一格式化为 yyyy-MM-dd HH:mm:ss
|
||||
if parsed, ok := parseTemporalString(text); ok {
|
||||
return parsed.Local().Format("2006-01-02 15:04:05")
|
||||
return parsed.Format("2006-01-02 15:04:05")
|
||||
}
|
||||
return text
|
||||
}
|
||||
@@ -2223,15 +2233,15 @@ func normalizeExportJSONValue(val interface{}) interface{} {
|
||||
|
||||
switch v := val.(type) {
|
||||
case time.Time:
|
||||
return v.Local().Format("2006-01-02 15:04:05")
|
||||
return v.Format("2006-01-02 15:04:05")
|
||||
case *time.Time:
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
return v.Local().Format("2006-01-02 15:04:05")
|
||||
return v.Format("2006-01-02 15:04:05")
|
||||
case string:
|
||||
if parsed, ok := parseTemporalString(v); ok {
|
||||
return parsed.Local().Format("2006-01-02 15:04:05")
|
||||
return parsed.Format("2006-01-02 15:04:05")
|
||||
}
|
||||
return v
|
||||
case float32:
|
||||
|
||||
@@ -141,6 +141,69 @@ func TestWriteRowsToFile_JSON_NumberKeepPlainText(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalizeExportJSONValue_LocalDateTimeString_NoTimezoneShift(t *testing.T) {
|
||||
originalLocal := time.Local
|
||||
time.Local = time.FixedZone("UTC+8", 8*60*60)
|
||||
defer func() { time.Local = originalLocal }()
|
||||
|
||||
got := normalizeExportJSONValue("2026-04-07 18:44:32")
|
||||
if got != "2026-04-07 18:44:32" {
|
||||
t.Fatalf("本地无时区字符串不应发生时区偏移,want=%q got=%v", "2026-04-07 18:44:32", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatExportCellText_TimeValue_KeepWallClock(t *testing.T) {
|
||||
originalLocal := time.Local
|
||||
time.Local = time.FixedZone("UTC+8", 8*60*60)
|
||||
defer func() { time.Local = originalLocal }()
|
||||
|
||||
utc := time.Date(2026, 4, 7, 10, 44, 32, 0, time.UTC)
|
||||
got := formatExportCellText(utc)
|
||||
if got != "2026-04-07 10:44:32" {
|
||||
t.Fatalf("time.Time 导出应保持原始钟表时间,want=%q got=%q", "2026-04-07 10:44:32", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseTemporalString_LocalDateTime_NoTimezoneShift(t *testing.T) {
|
||||
originalLocal := time.Local
|
||||
time.Local = time.FixedZone("UTC+8", 8*60*60)
|
||||
defer func() { time.Local = originalLocal }()
|
||||
|
||||
parsed, ok := parseTemporalString("2026-04-07 18:44:32")
|
||||
if !ok {
|
||||
t.Fatal("parseTemporalString 应成功解析本地日期时间")
|
||||
}
|
||||
if parsed.Local().Format("2006-01-02 15:04:05") != "2026-04-07 18:44:32" {
|
||||
t.Fatalf("无时区时间解析后不应发生偏移,got=%q", parsed.Local().Format("2006-01-02 15:04:05"))
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseTemporalString_RFC3339_KeepWallClock(t *testing.T) {
|
||||
originalLocal := time.Local
|
||||
time.Local = time.FixedZone("UTC+8", 8*60*60)
|
||||
defer func() { time.Local = originalLocal }()
|
||||
|
||||
parsed, ok := parseTemporalString("2026-04-07T10:44:32Z")
|
||||
if !ok {
|
||||
t.Fatal("parseTemporalString 应成功解析 RFC3339")
|
||||
}
|
||||
if parsed.Format("2006-01-02 15:04:05") != "2026-04-07 10:44:32" {
|
||||
t.Fatalf("RFC3339 解析后应保持原始钟表时间,got=%q", parsed.Format("2006-01-02 15:04:05"))
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalizeExportJSONValue_TimeValue_KeepWallClock(t *testing.T) {
|
||||
originalLocal := time.Local
|
||||
time.Local = time.FixedZone("UTC+8", 8*60*60)
|
||||
defer func() { time.Local = originalLocal }()
|
||||
|
||||
utc := time.Date(2026, 4, 7, 18, 44, 32, 0, time.UTC)
|
||||
got := normalizeExportJSONValue(utc)
|
||||
if got != "2026-04-07 18:44:32" {
|
||||
t.Fatalf("JSON 导出 time.Time 应保持原始钟表时间,want=%q got=%v", "2026-04-07 18:44:32", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestQueryDataForExport_UsesMinimumTimeout(t *testing.T) {
|
||||
fake := &fakeExportQueryDB{
|
||||
data: []map[string]interface{}{{"v": 1}},
|
||||
|
||||
Reference in New Issue
Block a user