This is an automated email from the ASF dual-hosted git repository.

lizhimins pushed a commit to branch rocketmq-studio
in repository https://gitbox.apache.org/repos/asf/rocketmq-dashboard.git


The following commit(s) were added to refs/heads/rocketmq-studio by this push:
     new f6decc63c fix(k8s): sort certificate expiry timestamps as UTC (#7319)
f6decc63c is described below

commit f6decc63cbd398301b78449115aa5baafdf657d5
Author: coder999o <[email protected]>
AuthorDate: Mon Oct 26 20:09:23 2026 +0800

    fix(k8s): sort certificate expiry timestamps as UTC (#7319)
    
    * fix(k8s): sort certificate expiry timestamps as UTC
---
 .../pages/cluster/__tests__/K8sCertsPage.test.tsx  | 32 ++++++++++++++++++++++
 web/src/pages/cluster/certs.tsx                    |  4 +--
 2 files changed, 34 insertions(+), 2 deletions(-)

diff --git a/web/src/pages/cluster/__tests__/K8sCertsPage.test.tsx 
b/web/src/pages/cluster/__tests__/K8sCertsPage.test.tsx
index d94801d47..54954b202 100644
--- a/web/src/pages/cluster/__tests__/K8sCertsPage.test.tsx
+++ b/web/src/pages/cluster/__tests__/K8sCertsPage.test.tsx
@@ -159,6 +159,38 @@ describe('K8sCertsPage', () => {
     expect(screen.getAllByText('-').length).toBeGreaterThan(0);
   });
 
+  it.each(['America/New_York', 'Asia/Shanghai', 'UTC'])(
+    'sortsUtcCertificateExpiriesInBothDirectionsIn%sTest',
+    async (timeZone) => {
+      vi.stubEnv('TZ', timeZone);
+      try {
+        // These are UTC instants, although the wire format has no offset. In 
New York,
+        // parsing them as local times normalizes the DST gap's 02:30 to 03:30.
+        vi.mocked(listK8sCerts).mockResolvedValue([
+          { ...certs[0], id: 2, k8sId: 'later-expiry', notAfter: 
'2026-03-08T03:00:00' },
+          { ...certs[0], id: 1, k8sId: 'earlier-expiry', notAfter: 
'2026-03-08T02:30:00' },
+        ]);
+        const user = userEvent.setup();
+        renderPage();
+        await screen.findByText('earlier-expiry');
+
+        const rowIds = () =>
+          screen
+            .getAllByRole('row')
+            .filter((row) => row.hasAttribute('data-row-key'))
+            .map((row) => row.getAttribute('data-row-key'));
+        const expiryHeader = screen.getByRole('columnheader', { name: /到期时间/ 
});
+        await user.click(expiryHeader);
+        await waitFor(() => expect(rowIds()).toEqual(['1', '2']));
+
+        await user.click(expiryHeader);
+        await waitFor(() => expect(rowIds()).toEqual(['2', '1']));
+      } finally {
+        vi.unstubAllEnvs();
+      }
+    },
+  );
+
   it('explains that certificate records are Studio-local metadata', async () 
=> {
     renderPage();
 
diff --git a/web/src/pages/cluster/certs.tsx b/web/src/pages/cluster/certs.tsx
index fa293a633..d4bc88821 100644
--- a/web/src/pages/cluster/certs.tsx
+++ b/web/src/pages/cluster/certs.tsx
@@ -39,7 +39,7 @@ import InfoBanner from '../../components/InfoBanner';
 import type { K8sCertInfo } from '../../api/cluster';
 import { listK8sCerts, createK8sCert, deleteK8sCert } from 
'../../services/clusterService';
 import { describeThrownMessage } from '../../utils/apiError';
-import { formatUtcDateTime } from '../../utils/format';
+import { formatUtcDateTime, parseUtcDateTime } from '../../utils/format';
 import { tableScrollX } from '../../utils/table';
 import { useLang } from '../../i18n/LangContext';
 
@@ -211,7 +211,7 @@ const K8sCertsPage = () => {
       dataIndex: 'notAfter',
       key: 'notAfter',
       width: 156,
-      sorter: (a, b) => (Date.parse(a.notAfter ?? '') || 0) - 
(Date.parse(b.notAfter ?? '') || 0),
+      sorter: (a, b) => (parseUtcDateTime(a.notAfter) || 0) - 
(parseUtcDateTime(b.notAfter) || 0),
       render: (iso: string | null) => (
         <Text type="secondary" style={{ fontSize: 14 }}>
           {formatUtcDateTime(iso)}

Reply via email to