Fix timestamps off by timezone offset in non-UTC containers (#504)

All backend timestamps used datetime.now() (server local time) or the
deprecated datetime.utcnow(). The frontend's parseUTCDate() assumes
timestamps without timezone indicators are UTC and appends 'Z', so
stored timestamps were off by the timezone offset when the container's
timezone wasn't UTC.

Backend: replaced datetime.now() and datetime.utcnow() with
datetime.now(timezone.utc) across 16 files (~80 call sites) for all
database fields and DB comparisons. Cosmetic timestamps (filenames,
user-facing local time formatting) intentionally left as local time.

Frontend: replaced 13 new Date(backendTimestamp) calls with
parseUTCDate() across 8 files to correctly interpret UTC timestamps.
This commit is contained in:
maziggy
2026-02-24 08:18:02 +01:00
parent 974988c4f1
commit ed36eafbec
30 changed files with 207 additions and 203 deletions
+3 -3
View File
@@ -185,7 +185,7 @@ export function CalendarView({ archives, onArchiveClick, highlightedArchiveId }:
<div>
<div className="text-2xl font-bold text-white">
{archives.filter(a => {
const d = new Date(a.completed_at || a.created_at);
const d = parseUTCDate(a.completed_at || a.created_at) || new Date();
return d.getMonth() === currentMonth && d.getFullYear() === currentYear;
}).length}
</div>
@@ -194,7 +194,7 @@ export function CalendarView({ archives, onArchiveClick, highlightedArchiveId }:
<div>
<div className="text-2xl font-bold text-green-400">
{archives.filter(a => {
const d = new Date(a.completed_at || a.created_at);
const d = parseUTCDate(a.completed_at || a.created_at) || new Date();
return d.getMonth() === currentMonth && d.getFullYear() === currentYear && a.status === 'completed';
}).length}
</div>
@@ -203,7 +203,7 @@ export function CalendarView({ archives, onArchiveClick, highlightedArchiveId }:
<div>
<div className="text-2xl font-bold text-red-400">
{archives.filter(a => {
const d = new Date(a.completed_at || a.created_at);
const d = parseUTCDate(a.completed_at || a.created_at) || new Date();
return d.getMonth() === currentMonth && d.getFullYear() === currentYear && a.status === 'failed';
}).length}
</div>
+5 -4
View File
@@ -23,6 +23,7 @@ import {
Box,
} from 'lucide-react';
import { api } from '../api/client';
import { parseUTCDate } from '../utils/date';
import { Button } from './Button';
import { ConfirmModal } from './ConfirmModal';
import { ModelViewer } from './ModelViewer';
@@ -561,13 +562,13 @@ export function FileManagerModal({ printerId, printerName, onClose }: FileManage
case 'size-desc':
return b.size - a.size;
case 'date-asc': {
const aTime = a.mtime ? new Date(a.mtime).getTime() : 0;
const bTime = b.mtime ? new Date(b.mtime).getTime() : 0;
const aTime = a.mtime ? parseUTCDate(a.mtime)?.getTime() ?? 0 : 0;
const bTime = b.mtime ? parseUTCDate(b.mtime)?.getTime() ?? 0 : 0;
return aTime - bTime;
}
case 'date-desc': {
const aTime = a.mtime ? new Date(a.mtime).getTime() : 0;
const bTime = b.mtime ? new Date(b.mtime).getTime() : 0;
const aTime = a.mtime ? parseUTCDate(a.mtime)?.getTime() ?? 0 : 0;
const bTime = b.mtime ? parseUTCDate(b.mtime)?.getTime() ?? 0 : 0;
return bTime - aTime;
}
default:
+2 -1
View File
@@ -12,6 +12,7 @@ import { useIsSidebarCompact } from '../hooks/useIsSidebarCompact';
import { useAuth } from '../contexts/AuthContext';
import { useToast } from '../contexts/ToastContext';
import { Card, CardHeader, CardContent } from './Card';
import { parseUTCDate } from '../utils/date';
import { Button } from './Button';
interface NavItem {
@@ -201,7 +202,7 @@ export function Layout() {
setDebugDuration(null);
return;
}
const enabledAt = new Date(debugLoggingState.enabled_at).getTime();
const enabledAt = parseUTCDate(debugLoggingState.enabled_at)?.getTime() ?? Date.now();
const updateDuration = () => {
setDebugDuration(Math.floor((Date.now() - enabledAt) / 1000));
};
+2 -2
View File
@@ -10,7 +10,7 @@ import { useFilamentMapping } from '../../hooks/useFilamentMapping';
import { useMultiPrinterFilamentMapping, type PerPrinterConfig } from '../../hooks/useMultiPrinterFilamentMapping';
import { isPlaceholderDate } from '../../utils/amsHelpers';
import { getCurrencySymbol } from '../../utils/currency';
import { toDateTimeLocalValue } from '../../utils/date';
import { toDateTimeLocalValue, parseUTCDate } from '../../utils/date';
import { Button } from '../Button';
import { Card, CardContent } from '../Card';
import { FilamentMapping } from './FilamentMapping';
@@ -94,7 +94,7 @@ export function PrintModal({
let scheduledTime = '';
if (queueItem.scheduled_time && !isPlaceholderDate(queueItem.scheduled_time)) {
const date = new Date(queueItem.scheduled_time);
const date = parseUTCDate(queueItem.scheduled_time) ?? new Date();
// Use toDateTimeLocalValue to convert UTC to local time for datetime-local input
scheduledTime = toDateTimeLocalValue(date);
}