{"record":{"id":"1bc52765c68c5346","repo":"apache/incubator-seata","slug":"endtime-must-not-be-earlier-than-starttime","errorCode":null,"errorMessage":"endTime must not be earlier than startTime","messagePattern":"endTime must not be earlier than startTime","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"console/src/main/java/org/apache/seata/mcp/core/utils/DateUtils.java","lineNumber":73,"sourceCode":"            throw new DateTimeException(\"The time format does not match yyyy-MM-dd HH:mm:ss\", e);\n        }\n    }\n\n    public static String convertToDateTimeFromTimestamp(Long timestamp) {\n        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(\"yyyy-MM-dd HH:mm:ss\");\n        LocalDateTime dateTime;\n        try {\n            dateTime = Instant.ofEpochMilli(timestamp)\n                    .atZone(ZoneId.systemDefault())\n                    .toLocalDateTime();\n        } catch (DateTimeException | ArithmeticException e) {\n            return \"Parse Failed, please check that the timestamp is correct\";\n        }\n        return dateTime.format(formatter);\n    }\n\n    public static boolean judgeExceedTimeDuration(Long startTime, Long endTime, Long maxDuration) {\n        if (endTime < startTime) throw new IllegalArgumentException(\"endTime must not be earlier than startTime\");\n        return endTime - startTime > maxDuration;\n    }\n\n    public static Long convertToHourFromTimeStamp(Long timestamp) {\n        return timestamp / (60 * 60 * 1000);\n    }\n}\n","sourceCodeStart":55,"sourceCodeEnd":81,"githubUrl":"https://github.com/apache/incubator-seata/blob/e01f97c6db397165050caa6764020410c2c8199a/console/src/main/java/org/apache/seata/mcp/core/utils/DateUtils.java#L55-L81","documentation":"DateUtils.judgeExceedTimeDuration throws IllegalArgumentException when endTime < startTime, before even evaluating whether the range exceeds maxDuration. Console MCP query tools use it to bound time-range queries, so an inverted range is rejected immediately rather than producing a negative duration.","triggerScenarios":"MCP tool invocations for history/metrics where the endTime parameter is chronologically before startTime — e.g. passing startTime=2026-08-14 and endTime=2026-08-01, or mixing up the two converted timestamps.","commonSituations":"Swapping start/end arguments in tool calls, client-side sorting bugs, or timezone conversion of the two endpoints by different rules so the computed end millisecond value lands before the start.","solutions":["Swap the arguments so startTime <= endTime","Verify both timestamps were converted with the same timezone/zone rule before comparison","Add a client-side guard: if (end < start) throw early with a clear message before calling the tool","For date-string inputs, compare them lexicographically first — 'yyyy-MM-dd' strings sort chronologically"],"exampleFix":"// before\nboolean exceeded = DateUtils.judgeExceedTimeDuration(endTs, startTs, MAX); // inverted -> throws\n\n// after\nboolean exceeded = DateUtils.judgeExceedTimeDuration(startTs, endTs, MAX);","handlingStrategy":"validation","validationCode":"if (endTime == null || startTime == null || endTime < startTime) throw new IllegalArgumentException(\"startTime must be <= endTime\");","typeGuard":"boolean isValidRange(long start, long end) { return end >= start; }","tryCatchPattern":"try { DateUtils.judgeExceedTimeDuration(start, end, max); } catch (IllegalArgumentException e) { return toolError(\"endTime precedes startTime\"); }","preventionTips":["Compare yyyy-MM-dd strings lexicographically before converting","Name parameters explicitly at call sites to avoid start/end swaps","Convert both endpoints in one timezone"],"tags":["seata","mcp","validation","time-range"],"backgroundTag":null,"analyzedSha":"e01f97c6db397165050caa6764020410c2c8199a","analyzedAt":"2026-08-14T10:23:53.097Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}