Wang1rrr opened a new pull request, #4829:
URL: https://github.com/apache/rocketmq-dashboard/pull/4829
<!-- Make sure the base branch is `master`: that is the RocketMQ Studio
trunk. -->
### Which Issue(s) This PR Fixes
Trivial fix, no issue.
### Brief Description
Two private copies of `formatTimeMs` formatted the same values -
`MessageRecord.storeTime` (`number | string`) and trace node `timestamp`
(`number | string`) - and disagreed with each other and with the rest of the
console.
`web/src/components/QueueBrowser.tsx`:
```ts
export const formatTimeMs = (value: number | string) => {
const ts = typeof value === 'string' ? Date.parse(value) : value;
if (!Number.isFinite(ts)) return '-';
return new Date(ts).toLocaleString('zh-CN', { hour12: false });
};
```
The hardcoded `'zh-CN'` locale produces `2026/9/22 08:36:08`, while every
other timestamp in the console goes through `utils/format.ts` and renders the
locale-stable `2026-09-22 08:36:08`. The queue browser is reachable from the
topic page, so the same message store time is shown in two different formats a
click apart, and the English console gets a Chinese date layout.
`web/src/pages/instance/message.tsx`:
```ts
const formatTimeMs = (value: number | string): string => {
if (!value) return '-';
const d = new Date(value);
...
};
```
Two problems here:
1. `if (!value)` treats `0` as missing, so the Unix epoch renders as `-`.
The QueueBrowser test suite already asserts the opposite contract (`preserves
the Unix epoch timestamp`) - the two copies encoded contradictory invariants
about the same field.
2. `new Date(value)` is not validated, so an unparseable string (a provider
timestamp in a format this browser does not accept) renders as `NaN-NaN-NaN
NaN:NaN:NaN.NaN` in the message table, the detail drawer and the trace timeline
instead of the placeholder.
This adds one `formatTimeMs` to `web/src/utils/format.ts`, built on the
existing `formatDateTime` so it inherits the console-wide `YYYY-MM-DD HH:mm:ss`
layout and appends `.SSS`, and points both call sites at it. Zero is a real
timestamp; only a nullish, blank or unparseable value yields `-`. The message
page keeps its millisecond precision, and the queue browser's timestamp now
matches the message list it links to.
### How Did You Test This Change?
```
cd web
npx vitest run src/utils/format.test.ts
src/components/__tests__/QueueBrowser.test.tsx
src/pages/instance/__tests__/MessagePage.test.tsx
Test Files 3 passed (3)
Tests 38 passed (38)
npx tsc -b (exit 0)
npx eslint src/utils/format.ts src/utils/format.test.ts
src/components/QueueBrowser.tsx src/components/__tests__/QueueBrowser.test.tsx
src/pages/instance/message.tsx
(exit 0; one pre-existing react-refresh/only-export-components warning on
useQueueBrowser, untouched here)
npx prettier --check --end-of-line auto <same files>
All matched files use Prettier code style!
```
(`--end-of-line auto` because this checkout is CRLF while `.prettierrc` pins
`endOfLine: lf`; untouched trunk files fail a plain `--check` here for the same
reason.)
The `describe('formatTimeMs')` block moved from `QueueBrowser.test.tsx` to
`web/src/utils/format.test.ts` next to the function, keeping both original
cases and adding the ones the message-page copy violated:
- epoch zero is preserved and matches `/^\d{4}-\d{2}-\d{2}
\d{2}:\d{2}:\d{2}\.\d{3}$/`
- `'not-a-date'`, `NaN`, `Infinity`, `null`, `undefined` and `''` all yield
`-`
- epoch milliseconds render as `formatDateTime(date) + '.123'`, tying the
output to the shared formatter
- a formatted string (`2026-07-31T00:00:00Z`, the shape cloud providers
return) is accepted
Mutation-checked against **both** removed implementations:
- restoring the message-page body fails 3 tests (`preserves the Unix epoch
timestamp`, plus the `not-a-date` and `Infinity` placeholder cases);
- restoring the QueueBrowser `zh-CN` body fails the other 3 (the epoch shape
assertion and both format assertions).
Each mutation leaves the remaining 15 tests green, so the new cases are the
ones pinning the behaviour.
### Checklist
- [x] One coherent change; unrelated modifications are not bundled in
- [x] Commit subject follows Conventional Commits (`feat:` / `fix:` /
`refactor:` / `chore:` / `docs:` / `perf:`)
- [x] Tests added or updated for non-trivial changes, test methods named
`...Test`
- [x] New UI text has both Chinese and English entries under `web/src/i18n/`
(no UI text; the format is now locale-independent)
- [x] Architecture constraints stay green (`mvn test` runs the ArchUnit
checks) - no backend change
- [x] New source files carry the ASF license header (no new source files)
- [x] Documentation touched where behaviour changed (README / `docs/` /
in-app help) - none needed
--
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]