From ce2edf0daca00089d6a7fdf1905265b0b9cea606 Mon Sep 17 00:00:00 2001 From: Rongsong Shen Date: Sat, 9 May 2026 07:00:49 +0000 Subject: [PATCH] refactor: represent time fields as int64 Unix seconds throughout the stack MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Collapses a four-step conversion chain (time_t → int64 → time.Time → RFC3339 string → string → time.Time) to a direct int64 pass-through: C time_t → rawJobData int64 → JobInfo int64 → JSON integer → QueryJob int64 → time.Unix() for display Changes: - apiserver/models/jobs.go: SubmitTime, QueueTime, StartTime, EndTime, ReserveTime in JobInfo and JobAdditionalFields: time.Time → int64 - apiserver/models/common.go: ActionControl.SubmitTime: time.Time → int64 - backend/convert_pure.go: remove time.Unix() calls; assign int64 directly - backend/convert_test.go: update time assertions to compare int64 values - xcecli/models/jobs.go: SubmitTime, StartTime, EndTime, ReserveTime in QueryJob: string/time.Time → int64; remove time import - xcecli/common/output/time.go: replace parseJobTime(string) with unixToTime(int64) and fmtUnixTime(int64) helpers - xcecli/common/output/page_print_jobs.go: replace parseJobTime() calls with unixToTime()/fmtUnixTime(); remove !startTime.IsZero() guard (unixToTime returns false for ts<=0, making it redundant) - xcecli/common/output/page_print_array_elements_jobs.go: replace string-split+time.Parse submit-time block with fmtUnixTime(); remove unused time import Side effect: eliminates the "0001-01-01T00:00:00Z" zero-time serialization issue at the source — zero timestamps are now 0 (integer) and unixToTime/fmtUnixTime return false/"-" for ts<=0 without special- casing needed in callers. --- .../apiserver/backend/convert_pure.go | 17 ++++--------- .../apiserver/backend/convert_test.go | 17 +++++++------ src/frontend/apiserver/models/common.go | 12 ++++------ src/frontend/apiserver/models/jobs.go | 20 ++++++++-------- .../output/page_print_array_elements_jobs.go | 19 ++------------- .../xcecli/common/output/page_print_jobs.go | 18 ++++++-------- src/frontend/xcecli/common/output/time.go | 24 ++++++++++++------- src/frontend/xcecli/models/jobs.go | 10 ++++---- 8 files changed, 55 insertions(+), 82 deletions(-) diff --git a/src/frontend/apiserver/backend/convert_pure.go b/src/frontend/apiserver/backend/convert_pure.go index 20d4ca4..7d5348c 100644 --- a/src/frontend/apiserver/backend/convert_pure.go +++ b/src/frontend/apiserver/backend/convert_pure.go @@ -3,7 +3,6 @@ package backend import ( "apiserver/models" "encoding/json" - "time" ) // JobID bit layout constants — mirrors src/include/config.h. @@ -179,18 +178,10 @@ func rawJobToModel(r rawJobData) *models.JobInfo { info.ExecUser = si.execUser } } - if r.subTime > 0 { - info.SubmitTime = time.Unix(r.subTime, 0) - } - if r.queueTime > 0 { - info.QueueTime = time.Unix(r.queueTime, 0) - } - if r.startTime > 0 { - info.StartTime = time.Unix(r.startTime, 0) - } - if r.finishTime > 0 { - info.EndTime = time.Unix(r.finishTime, 0) - } + info.SubmitTime = r.subTime + info.QueueTime = r.queueTime + info.StartTime = r.startTime + info.EndTime = r.finishTime if len(r.execHosts) > 0 { type hostSlot struct { HostName string `json:"host_name"` diff --git a/src/frontend/apiserver/backend/convert_test.go b/src/frontend/apiserver/backend/convert_test.go index b18ff63..6ec9f0c 100644 --- a/src/frontend/apiserver/backend/convert_test.go +++ b/src/frontend/apiserver/backend/convert_test.go @@ -3,7 +3,6 @@ package backend import ( "apiserver/models" "testing" - "time" ) func TestRawJobToModel_BasicFields(t *testing.T) { @@ -81,17 +80,17 @@ func TestRawJobToModel_Timestamps(t *testing.T) { info := rawJobToModel(r) - if info.SubmitTime != time.Unix(1700000000, 0) { - t.Errorf("SubmitTime mismatch") + if info.SubmitTime != 1700000000 { + t.Errorf("SubmitTime: got %d, want 1700000000", info.SubmitTime) } - if info.QueueTime != time.Unix(1700000010, 0) { - t.Errorf("QueueTime mismatch") + if info.QueueTime != 1700000010 { + t.Errorf("QueueTime: got %d, want 1700000010", info.QueueTime) } - if info.StartTime != time.Unix(1700000020, 0) { - t.Errorf("StartTime mismatch") + if info.StartTime != 1700000020 { + t.Errorf("StartTime: got %d, want 1700000020", info.StartTime) } - if info.EndTime != time.Unix(1700000030, 0) { - t.Errorf("EndTime mismatch") + if info.EndTime != 1700000030 { + t.Errorf("EndTime: got %d, want 1700000030", info.EndTime) } } diff --git a/src/frontend/apiserver/models/common.go b/src/frontend/apiserver/models/common.go index 7bbdb6c..9a5fd08 100644 --- a/src/frontend/apiserver/models/common.go +++ b/src/frontend/apiserver/models/common.go @@ -1,7 +1,5 @@ package models -import "time" - const ( CONTROL_JOB = 1 CONTROL_HOST = 2 @@ -11,9 +9,9 @@ const ( ) type ActionControl struct { - TargetType int `json:"target_type"` - ActionType int `json:"action_type"` - Content Jsonb `json:"content"` - SubmitTime time.Time `json:"submit_time"` - Submitter string `json:"submitter"` + TargetType int `json:"target_type"` + ActionType int `json:"action_type"` + Content Jsonb `json:"content"` + SubmitTime int64 `json:"submit_time"` + Submitter string `json:"submitter"` } diff --git a/src/frontend/apiserver/models/jobs.go b/src/frontend/apiserver/models/jobs.go index f730491..b85cca8 100644 --- a/src/frontend/apiserver/models/jobs.go +++ b/src/frontend/apiserver/models/jobs.go @@ -489,7 +489,7 @@ type JobInfo struct { ArrayIdx int `json:"array_idx"` //new SubmitUser string `json:"submit_user"` FromHost string `json:"from_host"` - SubmitTime time.Time `json:"submit_time"` + SubmitTime int64 `json:"submit_time"` JobStatus int `json:"job_status"` JobName string `json:"job_name"` Command string `json:"command"` @@ -535,10 +535,10 @@ type JobInfo struct { Tags Jsonb `json:"tags"` // new, JSONB as []byte EffectiveResReq string `json:"effective_resreq"` //new EffectiveUserGroup string `json:"effective_user_group"` //new - QueueTime time.Time `json:"queue_time"` - StartTime time.Time `json:"start_time"` - EndTime time.Time `json:"end_time"` - ReserveTime time.Time `json:"reserve_time"` + QueueTime int64 `json:"queue_time"` + StartTime int64 `json:"start_time"` + EndTime int64 `json:"end_time"` + ReserveTime int64 `json:"reserve_time"` Duration int `json:"duration"` CPUTime int `json:"cpu_time"` ExitStatus Jsonb `json:"exit_status"` //change type, JSONB as []byte @@ -589,12 +589,12 @@ type JobAdditionalFields struct { JobID int64 `json:"job_id"` SubmitUser string `json:"submit_user"` JobStatus int `json:"job_status"` - SubmitTime time.Time `json:"submit_time"` - FromHost string `json:"from_host"` - ExecHosts string `json:"exec_hosts"` + SubmitTime int64 `json:"submit_time"` + FromHost string `json:"from_host"` + ExecHosts string `json:"exec_hosts"` - StartTime time.Time `json:"start_time"` - EndTime time.Time `json:"end_time"` + StartTime int64 `json:"start_time"` + EndTime int64 `json:"end_time"` JobRusage Jsonb `json:"job_rusage"` MaxMem float64 `json:"max_mem"` AvgMem float64 `json:"avg_mem"` diff --git a/src/frontend/xcecli/common/output/page_print_array_elements_jobs.go b/src/frontend/xcecli/common/output/page_print_array_elements_jobs.go index 5988c04..8b66688 100644 --- a/src/frontend/xcecli/common/output/page_print_array_elements_jobs.go +++ b/src/frontend/xcecli/common/output/page_print_array_elements_jobs.go @@ -4,7 +4,6 @@ import ( "encoding/json" "fmt" "strings" - "time" jobfmt "xcecli/common/format" "xcecli/models" ) @@ -96,7 +95,7 @@ func (p *ArrayElementsPagePrinter) initColumnWidths(jobs *models.JobsResponse) { case models.HEADER_JOB_NAME: width = len(job.JobName) case models.HEADER_JOB_SUBMIT_TIME: - width = len(job.SubmitTime) + width = len(fmtUnixTime(job.SubmitTime)) } if !p.wideFormat && width > 15 { @@ -190,21 +189,7 @@ func (p *ArrayElementsPagePrinter) formatRows(jobs *models.JobsResponse, truncat case models.HEADER_JOB_NAME: mainRow = appendOrDefault(mainRow, FormatStringField(job.JobName, width, truncatFlag, true), "-") case models.HEADER_JOB_SUBMIT_TIME: - // remove the timezone part and parse the time - timeStr := strings.Split(job.SubmitTime, " ")[0:2] - timeStrWithoutTZ := strings.Join(timeStr, " ") - - if submitTime, err := time.Parse("2006-01-02 15:04:05", timeStrWithoutTZ); err == nil { - month := submitTime.Format("Jan") - displayTime := fmt.Sprintf("%s %02d %02d:%02d", - month, - submitTime.Day(), - submitTime.Hour(), - submitTime.Minute()) - mainRow = appendOrDefault(mainRow, FormatStringField(displayTime, width, truncatFlag, true), "-") - } else { - mainRow = appendOrDefault(mainRow, fmt.Sprintf("%"+align+"*s", width, "-"), "-") - } + mainRow = appendOrDefault(mainRow, FormatStringField(fmtUnixTime(job.SubmitTime), width, truncatFlag, true), "-") } } rows = append(rows, mainRow) diff --git a/src/frontend/xcecli/common/output/page_print_jobs.go b/src/frontend/xcecli/common/output/page_print_jobs.go index 9bb44c6..487936f 100644 --- a/src/frontend/xcecli/common/output/page_print_jobs.go +++ b/src/frontend/xcecli/common/output/page_print_jobs.go @@ -98,7 +98,7 @@ func (p *JobsPagePrinter) initColumnWidths(jobs *models.JobsResponse) { case models.HEADER_JOB_NAME: width = len(job.JobName) case models.HEADER_JOB_SUBMIT_TIME: - width = len(job.SubmitTime) + width = len(fmtUnixTime(job.SubmitTime)) case models.HEADER_JOB_CORE_LIMIT: width = len(fmt.Sprintf("%f", job.CoreLimit)) case models.HEADER_JOB_MEM_LIMIT: @@ -201,11 +201,7 @@ func (p *JobsPagePrinter) formatRows(jobs *models.JobsResponse, truncatFlag bool mainRow = appendOrDefault(mainRow, fmt.Sprintf("%"+align+"*s", width, "-"), "-") } case models.HEADER_JOB_SUBMIT_TIME: - if submitTime, err := parseJobTime(job.SubmitTime); err == nil { - mainRow = appendOrDefault(mainRow, FormatStringField(formatJobTime(submitTime), width, truncatFlag, true), "-") - } else { - mainRow = appendOrDefault(mainRow, fmt.Sprintf("%"+align+"*s", width, "-"), "-") - } + mainRow = appendOrDefault(mainRow, FormatStringField(fmtUnixTime(job.SubmitTime), width, truncatFlag, true), "-") // normal fields case models.HEADER_JOB_NAME: @@ -223,9 +219,9 @@ func (p *JobsPagePrinter) formatRows(jobs *models.JobsResponse, truncatFlag bool case models.HEADER_JOB_PROJECT_NAME: mainRow = appendOrDefault(mainRow, FormatStringField(job.ProjectName, width, truncatFlag, true), "-") case models.HEADER_JOB_START_TIME: - mainRow = appendOrDefault(mainRow, FormatStringField(job.StartTime, width, truncatFlag, true), "-") + mainRow = appendOrDefault(mainRow, FormatStringField(fmtUnixTime(job.StartTime), width, truncatFlag, true), "-") case models.HEADER_JOB_END_TIME: - mainRow = appendOrDefault(mainRow, FormatStringField(job.EndTime, width, truncatFlag, true), "-") + mainRow = appendOrDefault(mainRow, FormatStringField(fmtUnixTime(job.EndTime), width, truncatFlag, true), "-") case models.HEADER_JOB_CWD: mainRow = appendOrDefault(mainRow, FormatStringField(job.CWD, width, truncatFlag, true), "-") case models.HEADER_JOB_LAUNCH_TYPE: @@ -303,7 +299,7 @@ func printJobsVerboseOutput(jobs *models.JobsResponse, filter []string) { // (2) print the submit time info submitLine := "" - if submitTime, err := parseJobTime(job.SubmitTime); err == nil { + if submitTime, ok := unixToTime(job.SubmitTime); ok { submitLine = submitLine + fmt.Sprintf("%s %s %02d:%02d:%02d: ", submitTime.Format("Jan"), submitTime.Format("02"), submitTime.Hour(), submitTime.Minute(), submitTime.Second()) @@ -335,7 +331,7 @@ func printJobsVerboseOutput(jobs *models.JobsResponse, filter []string) { if job.JobStatus == "PEND" || job.JobStatus == "PSUSP" { goto printEndLine } - if startTime, err := parseJobTime(job.StartTime); err == nil && !startTime.IsZero() { + if startTime, ok := unixToTime(job.StartTime); ok { month := startTime.Format("Jan") day := startTime.Format("02") exec_hostname := "" @@ -395,7 +391,7 @@ func printJobsVerboseOutput(jobs *models.JobsResponse, filter []string) { // (4) print end line endLine := "" if job.JobStatus == "DONE" || job.JobStatus == "EXIT" { - if endTime, err := parseJobTime(job.EndTime); err == nil { + if endTime, ok := unixToTime(job.EndTime); ok { endLine = endLine + fmt.Sprintf("%s %s %02d:%02d:%02d: ", endTime.Format("Jan"), endTime.Format("02"), endTime.Hour(), endTime.Minute(), endTime.Second()) diff --git a/src/frontend/xcecli/common/output/time.go b/src/frontend/xcecli/common/output/time.go index 96f2938..9b35f90 100644 --- a/src/frontend/xcecli/common/output/time.go +++ b/src/frontend/xcecli/common/output/time.go @@ -5,17 +5,23 @@ import ( "time" ) -// parseJobTime parses a job time string supporting RFC3339 and space-separated formats. -func parseJobTime(s string) (time.Time, error) { - if s == "" { - return time.Time{}, fmt.Errorf("empty time string") +// unixToTime converts a Unix-second timestamp to time.Time. +// Returns (time, true) for ts > 0; (zero, false) for unset/zero values. +func unixToTime(ts int64) (time.Time, bool) { + if ts <= 0 { + return time.Time{}, false } - for _, f := range []string{time.RFC3339Nano, time.RFC3339, "2006-01-02 15:04:05", "2006-01-02 15:04:05 MST"} { - if t, err := time.Parse(f, s); err == nil { - return t, nil - } + return time.Unix(ts, 0), true +} + +// fmtUnixTime formats a Unix timestamp as "Jan 02 15:04" for table display. +// Returns "-" for zero/unset values. +func fmtUnixTime(ts int64) string { + t, ok := unixToTime(ts) + if !ok { + return "-" } - return time.Time{}, fmt.Errorf("cannot parse time: %q", s) + return formatJobTime(t) } // formatJobTime formats a time.Time as "Jan 02 15:04" for table display. diff --git a/src/frontend/xcecli/models/jobs.go b/src/frontend/xcecli/models/jobs.go index 8125148..31e44c2 100755 --- a/src/frontend/xcecli/models/jobs.go +++ b/src/frontend/xcecli/models/jobs.go @@ -3,8 +3,6 @@ package models import ( "encoding/json" "fmt" - "time" - ) const ( @@ -115,10 +113,10 @@ type QueryJob struct { TermTime string `json:"term_time"` UserPriority int `json:"user_priority"` DependCond string `json:"depend_cond"` - SubmitTime string `json:"submit_time"` - ReserveTime time.Time `json:"reserve_time"` - StartTime string `json:"start_time"` - EndTime string `json:"end_time"` + SubmitTime int64 `json:"submit_time"` + ReserveTime int64 `json:"reserve_time"` + StartTime int64 `json:"start_time"` + EndTime int64 `json:"end_time"` JobRusage string `json:"job_rusage"` MaxMem float64 `json:"max_mem"` AvgMem float64 `json:"avg_mem"`