From 67334a65d829b5ceccdb02bf0596c3f725f7419d Mon Sep 17 00:00:00 2001 From: Bailey Taylor Date: Wed, 8 Oct 2025 08:03:54 +0800 Subject: [PATCH] Adjusted context to hopefully reflect accurate timezones relative to browser. --- package-lock.json | 17 +++++++++++++++++ src/components/DeviceListSidebar.tsx | 16 +++++++++++++--- src/components/admin/DeviceTableSection.tsx | 7 ++++--- src/components/client/DevicesClient.tsx | 11 ++++++++++- src/context/DeviceContext.tsx | 9 ++++++++- 5 files changed, 52 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index c15b541..89675d6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -310,6 +310,7 @@ "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz", "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==", "license": "MIT", + "peer": true, "dependencies": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.13.5", @@ -353,6 +354,7 @@ "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.0.tgz", "integrity": "sha512-XxfOnXFffatap2IyCeJyNov3kiDQWoR08gPUQxvbL7fxKryGBKUZUkG6Hz48DZwVrJSVh9sJboyV1Ds4OW6SgA==", "license": "MIT", + "peer": true, "dependencies": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.13.5", @@ -1201,6 +1203,7 @@ "resolved": "https://registry.npmjs.org/@mui/material/-/material-7.0.2.tgz", "integrity": "sha512-rjJlJ13+3LdLfobRplkXbjIFEIkn6LgpetgU/Cs3Xd8qINCCQK9qXQIjjQ6P0FXFTPFzEVMj0VgBR1mN+FhOcA==", "license": "MIT", + "peer": true, "dependencies": { "@babel/runtime": "^7.27.0", "@mui/core-downloads-tracker": "^7.0.2", @@ -1980,6 +1983,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.13.0.tgz", "integrity": "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==", "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "json-schema-traverse": "^1.0.0", @@ -2487,6 +2491,7 @@ "integrity": "sha512-7zf4YyHA+jvBNfVrk2Gtvs6x7E8V+YDW05bNfG2XkWDJfYRXrTiP/DsB2zSYTaHX0bGIujTBQdMVAhb+j7mwpg==", "devOptional": true, "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~6.19.2" } @@ -2508,6 +2513,7 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.12.tgz", "integrity": "sha512-V6Ar115dBDrjbtXSrS+/Oruobc+qVbbUxDFC1RSbRqLt5SYvxxyIDrSC85RWml54g+jfNeEMZhEj7wW07ONQhA==", "license": "MIT", + "peer": true, "dependencies": { "csstype": "^3.0.2" } @@ -2517,6 +2523,7 @@ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.0.4.tgz", "integrity": "sha512-4fSQ8vWFkg+TGhePfUzVmat3eC14TXYSsiiDSLI0dVLsrm9gZFABjPy/Qu6TKgl1tq1Bu1yDsuQgY3A3DOjCcg==", "license": "MIT", + "peer": true, "peerDependencies": { "@types/react": "^19.0.0" } @@ -2575,6 +2582,7 @@ "integrity": "sha512-8C0+jlNJOwQso2GapCVWWfW/rzaq7Lbme+vGUFKE31djwNncIpgXD7Cd4weEsDdkoZDjH0lwwr3QDQFuyrMg9g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.29.0", "@typescript-eslint/types": "8.29.0", @@ -3106,6 +3114,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -3576,6 +3585,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001688", "electron-to-chromium": "^1.5.73", @@ -4353,6 +4363,7 @@ "integrity": "sha512-jV7AbNoFPAY1EkFYpLq5bslU9NLNO8xnEeQXwErNibVryjk67wHVmddTBilc5srIttJDBrB0eMHKZBFbSIABCw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", @@ -4527,6 +4538,7 @@ "integrity": "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.8", @@ -6977,6 +6989,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.8", "picocolors": "^1.1.1", @@ -7077,6 +7090,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -7086,6 +7100,7 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", "license": "MIT", + "peer": true, "dependencies": { "scheduler": "^0.26.0" }, @@ -7966,6 +7981,7 @@ "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -8225,6 +8241,7 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" diff --git a/src/components/DeviceListSidebar.tsx b/src/components/DeviceListSidebar.tsx index acbc01a..be7aa92 100644 --- a/src/components/DeviceListSidebar.tsx +++ b/src/components/DeviceListSidebar.tsx @@ -32,14 +32,24 @@ const ONLINE_THRESHOLD_MINUTES = 1440; function getStatusColor(lastCheckedIn: string) { - const lastSeen = parseISO(lastCheckedIn); + // Parse UTC timestamp and treat it correctly as UTC + const lastSeen = new Date(lastCheckedIn + 'Z'); // Add Z to indicate UTC const minutesAgo = (Date.now() - lastSeen.getTime()) / 1000 / 60; return minutesAgo < ONLINE_THRESHOLD_MINUTES ? 'green' : 'grey'; } function formatRelativeTime(lastCheckedIn: string) { try { - return formatDistanceToNowStrict(parseISO(lastCheckedIn), { addSuffix: true }); + // Parse UTC timestamp and treat it correctly as UTC + const lastSeen = new Date(lastCheckedIn + 'Z'); // Add Z to indicate UTC + + // Debug: Log what we're getting vs what we should get + //const wrongWay = new Date(lastCheckedIn); // Without Z - treats as local + //console.log(`🕒 DEBUG: "${lastCheckedIn}"`); + //console.log(`🕒 Wrong way (as local): ${wrongWay.toLocaleString()} -> ${formatDistanceToNowStrict(wrongWay, { addSuffix: true })}`); + //console.log(`🕒 Right way (as UTC): ${lastSeen.toLocaleString()} -> ${formatDistanceToNowStrict(lastSeen, { addSuffix: true })}`); + + return formatDistanceToNowStrict(lastSeen, { addSuffix: true }); } catch { return 'Unknown'; } @@ -56,7 +66,7 @@ const DeviceListSidebar: React.FC = ({ onTogglePin, }) => { const [sortAsc, setSortAsc] = React.useState(true); - const [searchQuery, setSearchQuery] = React.useState(''); + const [searchQuery, setSearchQuery] = React.useState(''); diff --git a/src/components/admin/DeviceTableSection.tsx b/src/components/admin/DeviceTableSection.tsx index 1c1ed55..fc3a66e 100644 --- a/src/components/admin/DeviceTableSection.tsx +++ b/src/components/admin/DeviceTableSection.tsx @@ -81,11 +81,12 @@ export default function DeviceTableSection({ initialDevices }: Props) { const formatLastCheckedIn = (isoString: string): string => { - const date = new Date(isoString); + // Parse as UTC by adding 'Z' if not present + const date = new Date(isoString + (isoString.includes('Z') || isoString.includes('+') ? '' : 'Z')); const now = new Date(); const diffMs = now.getTime() - date.getTime(); const diffHours = Math.floor(diffMs / (1000 * 60 * 60)); - + const formatted = date.toLocaleString('en-AU', { day: '2-digit', month: '2-digit', @@ -94,7 +95,7 @@ export default function DeviceTableSection({ initialDevices }: Props) { minute: '2-digit', hour12: true, }); - + return `${formatted} (${diffHours} hours ago)`; }; diff --git a/src/components/client/DevicesClient.tsx b/src/components/client/DevicesClient.tsx index 6eaa653..42ae05d 100644 --- a/src/components/client/DevicesClient.tsx +++ b/src/components/client/DevicesClient.tsx @@ -127,7 +127,16 @@ export default function DevicesClient() { const formatBootTime = (timestamp: string) => { try { - const parsed = parseISO(timestamp); + // Handle timestamp - could be UTC or already have timezone info + let parsed: Date; + if (timestamp.includes('+') || timestamp.endsWith('Z')) { + // Already has timezone info, parse directly + parsed = new Date(timestamp); + } else { + // Assume UTC, add Z to indicate UTC + parsed = new Date(timestamp + 'Z'); + } + const formattedDate = format(parsed, 'EEEE do MMMM yyyy'); const relative = formatDistanceToNowStrict(parsed, { addSuffix: true }); return `${formattedDate} (${relative})`; diff --git a/src/context/DeviceContext.tsx b/src/context/DeviceContext.tsx index 5d55552..c3c4413 100644 --- a/src/context/DeviceContext.tsx +++ b/src/context/DeviceContext.tsx @@ -7,6 +7,13 @@ import { DetailedDevice } from '@/types/devices'; import { disableConsoleInProd } from '@/lib/disableConsole'; disableConsoleInProd(); +// Helper function to convert UTC timestamps to local time +const convertUtcToLocal = (utcString: string | null): string | null => { + if (!utcString) return null; + // Add 'Z' to indicate it's UTC, then convert to local time + return new Date(utcString + 'Z').toLocaleString(); +}; + interface DriveInfo { @@ -125,7 +132,7 @@ export const DeviceProvider = ({ console.log('✅ Software fetched:', softwareRes.data); console.groupEnd(); - // STEP 1: Set basic data + // STEP 1: Set basic data (keep original UTC timestamps for calculations) setDevices(devicesRes.data.devices); setDeviceVulns(devicesRes.data.vulnerabilitiesByDevice); setCachedSoftware(softwareRes.data);