parser: fallback zone-less source timestamps to Europe/Moscow
This commit is contained in:
33
internal/parser/timezone.go
Normal file
33
internal/parser/timezone.go
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
package parser
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const fallbackTimezoneName = "Europe/Moscow"
|
||||||
|
|
||||||
|
var (
|
||||||
|
fallbackTimezoneOnce sync.Once
|
||||||
|
fallbackTimezone *time.Location
|
||||||
|
)
|
||||||
|
|
||||||
|
// DefaultArchiveLocation returns the timezone used for source timestamps
|
||||||
|
// that do not contain an explicit offset.
|
||||||
|
func DefaultArchiveLocation() *time.Location {
|
||||||
|
fallbackTimezoneOnce.Do(func() {
|
||||||
|
loc, err := time.LoadLocation(fallbackTimezoneName)
|
||||||
|
if err != nil {
|
||||||
|
fallbackTimezone = time.FixedZone("MSK", 3*60*60)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fallbackTimezone = loc
|
||||||
|
})
|
||||||
|
return fallbackTimezone
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseInDefaultArchiveLocation parses timestamps without timezone information
|
||||||
|
// using Europe/Moscow as the assumed source timezone.
|
||||||
|
func ParseInDefaultArchiveLocation(layout, value string) (time.Time, error) {
|
||||||
|
return time.ParseInLocation(layout, value, DefaultArchiveLocation())
|
||||||
|
}
|
||||||
3
internal/parser/vendors/inspur/sel.go
vendored
3
internal/parser/vendors/inspur/sel.go
vendored
@@ -6,6 +6,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.mchus.pro/mchus/logpile/internal/models"
|
"git.mchus.pro/mchus/logpile/internal/models"
|
||||||
|
"git.mchus.pro/mchus/logpile/internal/parser"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ParseSELList parses selelist.csv file with SEL events
|
// ParseSELList parses selelist.csv file with SEL events
|
||||||
@@ -81,7 +82,7 @@ func parseSELTimestamp(dateStr, timeStr string) time.Time {
|
|||||||
timestampStr := dateStr + " " + timeStr
|
timestampStr := dateStr + " " + timeStr
|
||||||
|
|
||||||
// Try parsing with MM/DD/YYYY format
|
// Try parsing with MM/DD/YYYY format
|
||||||
t, err := time.Parse("01/02/2006 15:04:05", timestampStr)
|
t, err := parser.ParseInDefaultArchiveLocation("01/02/2006 15:04:05", timestampStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Fallback to current time
|
// Fallback to current time
|
||||||
return time.Now()
|
return time.Now()
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ func parseVerboseRunTestStartTimes(content []byte) map[string]time.Time {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
ts, err := time.ParseInLocation("2006-01-02 15:04:05", strings.TrimSpace(matches[1]), time.UTC)
|
ts, err := parser.ParseInDefaultArchiveLocation("2006-01-02 15:04:05", strings.TrimSpace(matches[1]))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -135,7 +135,7 @@ func parseRunLogTestStartTimes(content []byte) map[string]time.Time {
|
|||||||
if len(matches) != 2 {
|
if len(matches) != 2 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
parsed, err := time.ParseInLocation("Mon, 02 Jan 2006 15:04:05", strings.TrimSpace(matches[1]), time.UTC)
|
parsed, err := parser.ParseInDefaultArchiveLocation("Mon, 02 Jan 2006 15:04:05", strings.TrimSpace(matches[1]))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -178,7 +178,7 @@ func parseModsStartTime(content []byte) time.Time {
|
|||||||
if tsRaw == "" {
|
if tsRaw == "" {
|
||||||
return time.Time{}
|
return time.Time{}
|
||||||
}
|
}
|
||||||
ts, err := time.ParseInLocation("Mon Jan 2 15:04:05 2006", tsRaw, time.UTC)
|
ts, err := parser.ParseInDefaultArchiveLocation("Mon Jan 2 15:04:05 2006", tsRaw)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return time.Time{}
|
return time.Time{}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,10 +23,10 @@ func TestParseVerboseRunTestStartTimes(t *testing.T) {
|
|||||||
if gpu.IsZero() {
|
if gpu.IsZero() {
|
||||||
t.Fatalf("expected gpu_fieldiag timestamp")
|
t.Fatalf("expected gpu_fieldiag timestamp")
|
||||||
}
|
}
|
||||||
if nvs.Format(time.RFC3339) != "2026-01-22T09:11:32Z" {
|
if nvs.UTC().Format(time.RFC3339) != "2026-01-22T06:11:32Z" {
|
||||||
t.Fatalf("unexpected nvswitch timestamp: %s", nvs.Format(time.RFC3339))
|
t.Fatalf("unexpected nvswitch timestamp: %s", nvs.Format(time.RFC3339))
|
||||||
}
|
}
|
||||||
if gpu.Format(time.RFC3339) != "2026-01-22T09:45:36Z" {
|
if gpu.UTC().Format(time.RFC3339) != "2026-01-22T06:45:36Z" {
|
||||||
t.Fatalf("unexpected gpu_fieldiag timestamp: %s", gpu.Format(time.RFC3339))
|
t.Fatalf("unexpected gpu_fieldiag timestamp: %s", gpu.Format(time.RFC3339))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -40,13 +40,13 @@ Testing nvswitch OK [ 9:25s ]
|
|||||||
`)
|
`)
|
||||||
|
|
||||||
got := parseRunLogTestStartTimes(content)
|
got := parseRunLogTestStartTimes(content)
|
||||||
if got["gpumem"].Format(time.RFC3339) != "2026-01-22T07:42:26Z" {
|
if got["gpumem"].UTC().Format(time.RFC3339) != "2026-01-22T04:42:26Z" {
|
||||||
t.Fatalf("unexpected gpumem start: %s", got["gpumem"].Format(time.RFC3339))
|
t.Fatalf("unexpected gpumem start: %s", got["gpumem"].Format(time.RFC3339))
|
||||||
}
|
}
|
||||||
if got["gpustress"].Format(time.RFC3339) != "2026-01-22T08:08:38Z" {
|
if got["gpustress"].UTC().Format(time.RFC3339) != "2026-01-22T05:08:38Z" {
|
||||||
t.Fatalf("unexpected gpustress start: %s", got["gpustress"].Format(time.RFC3339))
|
t.Fatalf("unexpected gpustress start: %s", got["gpustress"].Format(time.RFC3339))
|
||||||
}
|
}
|
||||||
if got["nvswitch"].Format(time.RFC3339) != "2026-01-22T08:15:48Z" {
|
if got["nvswitch"].UTC().Format(time.RFC3339) != "2026-01-22T05:15:48Z" {
|
||||||
t.Fatalf("unexpected nvswitch start: %s", got["nvswitch"].Format(time.RFC3339))
|
t.Fatalf("unexpected nvswitch start: %s", got["nvswitch"].Format(time.RFC3339))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -101,10 +101,10 @@ func TestCollectGPUAndNVSwitchCheckTimes_FromVerboseRun(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
got := CollectGPUAndNVSwitchCheckTimes(files)
|
got := CollectGPUAndNVSwitchCheckTimes(files)
|
||||||
if got.GPUDefault.Format(time.RFC3339) != "2026-01-22T09:45:36Z" {
|
if got.GPUDefault.UTC().Format(time.RFC3339) != "2026-01-22T06:45:36Z" {
|
||||||
t.Fatalf("unexpected GPU check time: %s", got.GPUDefault.Format(time.RFC3339))
|
t.Fatalf("unexpected GPU check time: %s", got.GPUDefault.Format(time.RFC3339))
|
||||||
}
|
}
|
||||||
if got.NVSwitchDefault.Format(time.RFC3339) != "2026-01-22T09:11:32Z" {
|
if got.NVSwitchDefault.UTC().Format(time.RFC3339) != "2026-01-22T06:11:32Z" {
|
||||||
t.Fatalf("unexpected NVSwitch check time: %s", got.NVSwitchDefault.Format(time.RFC3339))
|
t.Fatalf("unexpected NVSwitch check time: %s", got.NVSwitchDefault.Format(time.RFC3339))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -128,16 +128,16 @@ MODS start: Thu Jan 22 09:11:32 2026
|
|||||||
}
|
}
|
||||||
|
|
||||||
got := CollectGPUAndNVSwitchCheckTimes(files)
|
got := CollectGPUAndNVSwitchCheckTimes(files)
|
||||||
if got.GPUBySerial["1653925025497"].Format(time.RFC3339) != "2026-01-22T09:45:36Z" {
|
if got.GPUBySerial["1653925025497"].UTC().Format(time.RFC3339) != "2026-01-22T06:45:36Z" {
|
||||||
t.Fatalf("unexpected GPU serial check time: %s", got.GPUBySerial["1653925025497"].Format(time.RFC3339))
|
t.Fatalf("unexpected GPU serial check time: %s", got.GPUBySerial["1653925025497"].Format(time.RFC3339))
|
||||||
}
|
}
|
||||||
if got.GPUBySlot["GPUSXM5"].Format(time.RFC3339) != "2026-01-22T09:45:36Z" {
|
if got.GPUBySlot["GPUSXM5"].UTC().Format(time.RFC3339) != "2026-01-22T06:45:36Z" {
|
||||||
t.Fatalf("unexpected GPU slot check time: %s", got.GPUBySlot["GPUSXM5"].Format(time.RFC3339))
|
t.Fatalf("unexpected GPU slot check time: %s", got.GPUBySlot["GPUSXM5"].Format(time.RFC3339))
|
||||||
}
|
}
|
||||||
if got.NVSwitchBySlot["NVSWITCH0"].Format(time.RFC3339) != "2026-01-22T09:11:32Z" {
|
if got.NVSwitchBySlot["NVSWITCH0"].UTC().Format(time.RFC3339) != "2026-01-22T06:11:32Z" {
|
||||||
t.Fatalf("unexpected NVSwitch0 check time: %s", got.NVSwitchBySlot["NVSWITCH0"].Format(time.RFC3339))
|
t.Fatalf("unexpected NVSwitch0 check time: %s", got.NVSwitchBySlot["NVSWITCH0"].Format(time.RFC3339))
|
||||||
}
|
}
|
||||||
if got.NVSwitchBySlot["NVSWITCH3"].Format(time.RFC3339) != "2026-01-22T09:11:32Z" {
|
if got.NVSwitchBySlot["NVSWITCH3"].UTC().Format(time.RFC3339) != "2026-01-22T06:11:32Z" {
|
||||||
t.Fatalf("unexpected NVSwitch3 check time: %s", got.NVSwitchBySlot["NVSWITCH3"].Format(time.RFC3339))
|
t.Fatalf("unexpected NVSwitch3 check time: %s", got.NVSwitchBySlot["NVSWITCH3"].Format(time.RFC3339))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -235,8 +235,8 @@ func TestNVIDIAParser_ComponentCheckTimes_RealArchive07900(t *testing.T) {
|
|||||||
t.Fatalf("expected hardware in parsed result")
|
t.Fatalf("expected hardware in parsed result")
|
||||||
}
|
}
|
||||||
|
|
||||||
expectedGPU := time.Date(2026, 1, 22, 9, 45, 36, 0, time.UTC)
|
expectedGPU := time.Date(2026, 1, 22, 6, 45, 36, 0, time.UTC)
|
||||||
expectedNVSwitch := time.Date(2026, 1, 22, 9, 11, 32, 0, time.UTC)
|
expectedNVSwitch := time.Date(2026, 1, 22, 6, 11, 32, 0, time.UTC)
|
||||||
|
|
||||||
if len(result.Hardware.GPUs) == 0 {
|
if len(result.Hardware.GPUs) == 0 {
|
||||||
t.Fatalf("expected GPUs in parsed result")
|
t.Fatalf("expected GPUs in parsed result")
|
||||||
|
|||||||
2
internal/parser/vendors/unraid/parser.go
vendored
2
internal/parser/vendors/unraid/parser.go
vendored
@@ -559,7 +559,7 @@ func parseSyslogLine(line string) (time.Time, string, models.Severity) {
|
|||||||
|
|
||||||
// Parse timestamp (add current year)
|
// Parse timestamp (add current year)
|
||||||
year := time.Now().Year()
|
year := time.Now().Year()
|
||||||
if ts, err := time.Parse("Jan 2 15:04:05 2006", timeStr+" "+strconv.Itoa(year)); err == nil {
|
if ts, err := parser.ParseInDefaultArchiveLocation("Jan 2 15:04:05 2006", timeStr+" "+strconv.Itoa(year)); err == nil {
|
||||||
timestamp = ts
|
timestamp = ts
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
2
internal/parser/vendors/xigmanas/parser.go
vendored
2
internal/parser/vendors/xigmanas/parser.go
vendored
@@ -431,7 +431,7 @@ func parseEventTimestamp(line string) time.Time {
|
|||||||
prefixRe := regexp.MustCompile(`^[A-Z][a-z]{2}\s+\d{1,2}\s+\d{2}:\d{2}:\d{2}`)
|
prefixRe := regexp.MustCompile(`^[A-Z][a-z]{2}\s+\d{1,2}\s+\d{2}:\d{2}:\d{2}`)
|
||||||
if prefix := prefixRe.FindString(line); prefix != "" {
|
if prefix := prefixRe.FindString(line); prefix != "" {
|
||||||
year := time.Now().Year()
|
year := time.Now().Year()
|
||||||
if ts, err := time.Parse("Jan 2 15:04:05 2006", prefix+" "+strconv.Itoa(year)); err == nil {
|
if ts, err := parser.ParseInDefaultArchiveLocation("Jan 2 15:04:05 2006", prefix+" "+strconv.Itoa(year)); err == nil {
|
||||||
return ts
|
return ts
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user