{"record":{"id":"2b262c693a41d4b9","repo":"Billionmail/BillionMail","slug":"end-time-must-greater-than-start-time-2b262c","errorCode":null,"errorMessage":"end_time must greater than start_time","messagePattern":"end_time must greater than start_time","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/internal/service/maillog_stat/overview.go","lineNumber":34,"sourceCode":"\t\"github.com/gogf/gf/v2/util/gconv\"\n)\n\n// Overview maillog data overview structure\ntype Overview struct{}\n\n// NewOverview new overview instance\nfunc NewOverview() *Overview {\n\treturn &Overview{}\n}\n\n// filterAndPrepareTimeSection filter and provide time section\nfunc (o *Overview) filterAndPrepareTimeSection(startTime, endTime int64) (int64, int64) {\n\tif startTime > 0 && endTime < 0 {\n\t\tendTime = time.Now().Unix()\n\t}\n\n\tif startTime > endTime {\n\t\tpanic(public.Lang(\"end_time must greater than start_time\"))\n\t}\n\n\t// Maximum time range is 1 year\n\tif endTime-startTime > 31622400 {\n\t\tstartTime = endTime - 31622400 // 1 year\n\t}\n\n\treturn startTime, endTime\n}\n\n// buildBaseQuery build basic query\nfunc (o *Overview) buildBaseQuery(campaignID int64, domain string, startTime, endTime int64) *gdb.Model {\n\tsubQuery := \"SELECT * FROM mailstat_send_mails WHERE true\"\n\n\tif startTime > 0 {\n\t\tsubQuery += fmt.Sprintf(\" AND log_time_millis > %d\", startTime*1000)\n\t}\n","sourceCodeStart":16,"sourceCodeEnd":52,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/service/maillog_stat/overview.go#L16-L52","documentation":"filterAndPrepareTimeSection panics (via public.Lang localized message) when the requested query range is inverted: startTime is greater than endTime. The overview/statistics API cannot compute metrics over a negative time window, so it aborts the request instead of returning empty data.","triggerScenarios":"Calling Overview, chartSendMail, chartBounceRate, chartOpenRate, sendMailDashboard or overviewProviders with query params where start > end — e.g. start=1700000000&end=1600000000, or a UI sending swapped date-picker values. Note: negative endTime is silently corrected to now, so only genuinely swapped positive ranges trigger the panic.","commonSituations":"Frontend date-range picker emitting reversed values; API clients constructing timestamps in ms instead of s (milliseconds look huge and can invert ranges); timezone handling shifting one boundary past the other; hardcoded default ranges where start defaults exceed a passed end.","solutions":["Validate in the caller/controller that startTime < endTime before invoking overview APIs and return a 400 instead","Fix the frontend date-range component so it never emits start > end","Convert timestamps consistently to Unix seconds (divide ms timestamps by 1000)","Optionally harden filterAndPrepareTimeSection to swap/return an error instead of panicking"],"exampleFix":"// before\nstartTime, _ := strconv.ParseInt(ctx.Request.Get(\"start_time\"), 10, 64)\nendTime, _ := strconv.ParseInt(ctx.Request.Get(\"end_time\"), 10, 64)\no.filterAndPrepareTimeSection(startTime, endTime)\n// after\nif startTime > endTime {\n    return gerror.New(\"start_time must be less than end_time\")\n}\no.filterAndPrepareTimeSection(startTime, endTime)","handlingStrategy":"validation","validationCode":"start, _ := strconv.ParseInt(startTimeStr, 10, 64)\nend, _ := strconv.ParseInt(endTimeStr, 10, 64)\nif start <= 0 { start = time.Now().AddDate(0, -1, 0).Unix() }\nif end <= 0 { end = time.Now().Unix() }\nif start > end { return errors.New(\"start_time must be <= end_time\") }","typeGuard":null,"tryCatchPattern":"defer func() {\n    if r := recover(); r != nil {\n        if strings.Contains(fmt.Sprint(r), \"end_time must greater than start_time\") {\n            respondBadRequest(\"invalid time range\")\n        } else { panic(r) }\n    }\n}()","preventionTips":["Validate timestamp ordering in the controller before service calls","Normalize ms-vs-s timestamps at the API boundary","Fix date-range pickers so they cannot emit start > end","Prefer returning a 400 error over panicking on bad input"],"tags":["validation","time-range","panic"],"backgroundTag":"invalid-time-range","analyzedSha":"fc36c76c050c3775c5e899faf7403cf0262d2744","analyzedAt":"2026-09-05T21:28:54.019Z","contentChangedAt":"2026-09-05T21:28:54.019Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}