unbridled-41 opened a new pull request, #4175:
URL: https://github.com/apache/rocketmq-dashboard/pull/4175
Fixes #4174.
## Problem / Evidence
On `/ops/alerts` (and `/ops/business-alerts`) the **最近触发时间** column renders
`lastTriggered` with `formatDateTime`, which parses offset-less ISO strings as
**browser-local** time. The backend stamps this value as UTC without a suffix:
- `NativeAlertProcessor.java:143` — `LocalDateTime eventTime =
LocalDateTime.ofInstant(collectedAt, ZoneOffset.UTC);`
- `NativeAlertProcessor.java:165` → `markRuleTriggered(rule.getId(),
eventTime.toString())` → `2026-08-23T23:30:00` in
`rmq_alert_rule.last_triggered` (`NativeAlertEvaluationService.java:92` same).
So a rule that fired at 23:30 UTC displays "23:30" in every timezone; a
UTC+8 browser shows a trigger time up to 8 hours in the future. The header
counter `近24小时触发` (`alerts.tsx:406-408`, `new Date(r.lastTriggered).getTime() >
dayAgo`) mixes the same naive parse with an epoch `dayAgo`, shifting the 24h
window.
The sibling system alerts page already established the correct contract for
these offset-less UTC values: `systemAlerts.tsx` renders `alert.time` /
`alert.acknowledgedAt` via `formatUtcDateTime`, whose doc comment states the
exact failure mode ("Alert APIs serialize UTC LocalDateTime values without an
offset, so normal Date parsing would incorrectly treat them as browser-local
timestamps").
## Root cause / Fix
`alerts.tsx` used the local-time formatter for an offset-less UTC timestamp.
Switch the column to `formatUtcDateTime` (one-line change plus comment). The
"24h triggered" filter already compares against `Date.now()`; once the value is
interpreted as UTC it is consistent with epoch math, and existing null-sort
semantics are untouched.
## Priority & scoring
PRIORITY = 84 (影响 34: wrong wall-clock time on every non-UTC browser on a
core ops page + skewed 24h counter; 波及范围 16: both alert domains, every viewer
east of UTC; 可复现性 20: any fired rule renders wrong in a non-UTC browser; 维护价值
14: aligns the page with the existing documented UTC contract). FIX_CONFIDENCE
= 92 (single formatter swap with in-repo precedent and TZ-independent tests).
## Tests
Red (before fix, `fix/alerts-last-triggered-utc` @ abef8546^, branch
`rocketmq-studio` @ 0a596661):
```
npx vitest run src/pages/ops/__tests__/AlertsPage.test.tsx
❯ src/pages/ops/__tests__/AlertsPage.test.tsx (24 tests | 2 failed)
× formats the last triggered timestamp instead of rendering the raw ISO
value
× interprets the last triggered timestamp as UTC, not the browser zone
```
Green (after fix):
```
npx vitest run src/pages/ops/__tests__/AlertsPage.test.tsx
Test Files 1 passed (1)
Tests 24 passed (24)
```
The existing "formats the last triggered timestamp" test asserted
`formatDateTime(...)` (the local-time renderer) — i.e. it pinned the buggy
behavior; it now expects `formatUtcDateTime(...)`. The new second test uses
`2026-08-23T23:30:00`, where the local-time parse and the UTC parse differ by
more than a date boundary in the reference TZ.
Full web suite (`npx vitest run`): **945 tests, 944 passed, 1 failed** —
`ConsumerPage > shows group health diagnostics from subscriptions, progress and
clients` (unrelated file). Verified flaky-under-load both ways: pristine
`rocketmq-studio` isolated run passes, isolated run with this change applied
also passes (31 tests file-level, target test green).
`tsc --noEmit` clean; `eslint` on both changed files: 0 errors (5 warnings,
all pre-existing at the same positions); `npm run build` ✓ (8.25s).
## Risk
Low. Display-only change on one column; no API/payload change. In a
UTC-configured browser the rendered string additionally gains a timezone suffix
(e.g. ` UTC`), consistent with the system alerts page. The `localeCompare`
sorter and null handling are unchanged.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]