mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-10-19 15:54:27 +02:00
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:
@@ -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>
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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));
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ import { ModelViewerModal } from '../components/ModelViewerModal';
|
||||
import { useToast } from '../contexts/ToastContext';
|
||||
import { useIsMobile } from '../hooks/useIsMobile';
|
||||
import { useAuth } from '../contexts/AuthContext';
|
||||
import { formatDuration } from '../utils/date';
|
||||
import { formatDuration, parseUTCDate } from '../utils/date';
|
||||
import { formatFileSize } from '../utils/file';
|
||||
|
||||
type SortField = 'name' | 'date' | 'size' | 'type' | 'prints';
|
||||
@@ -1260,7 +1260,7 @@ export function FileManagerPage() {
|
||||
comparison = (a.print_name || a.filename).localeCompare(b.print_name || b.filename);
|
||||
break;
|
||||
case 'date':
|
||||
comparison = new Date(a.created_at).getTime() - new Date(b.created_at).getTime();
|
||||
comparison = (parseUTCDate(a.created_at)?.getTime() ?? 0) - (parseUTCDate(b.created_at)?.getTime() ?? 0);
|
||||
break;
|
||||
case 'size':
|
||||
comparison = a.file_size - b.file_size;
|
||||
|
||||
@@ -46,7 +46,7 @@ import {
|
||||
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { api, discoveryApi, firmwareApi } from '../api/client';
|
||||
import { formatDateOnly, formatETA, formatDuration } from '../utils/date';
|
||||
import { formatDateOnly, formatETA, formatDuration, parseUTCDate } from '../utils/date';
|
||||
import type { Printer, PrinterCreate, AMSUnit, DiscoveredPrinter, FirmwareUpdateInfo, FirmwareUploadStatus, LinkedSpoolInfo, SpoolAssignment } from '../api/client';
|
||||
import { Card, CardContent } from '../components/Card';
|
||||
import { Button } from '../components/Button';
|
||||
@@ -3739,7 +3739,7 @@ function PrinterCard({
|
||||
)}
|
||||
{/* Timestamp */}
|
||||
<p className="text-[10px] text-bambu-gray/60">
|
||||
{ref.timestamp ? new Date(ref.timestamp).toLocaleDateString() : ''}
|
||||
{ref.timestamp ? parseUTCDate(ref.timestamp)?.toLocaleDateString() ?? '' : ''}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
|
||||
@@ -50,7 +50,7 @@ import {
|
||||
Weight,
|
||||
} from 'lucide-react';
|
||||
import { api } from '../api/client';
|
||||
import { type TimeFormat, formatETA, formatDuration, formatRelativeTime } from '../utils/date';
|
||||
import { type TimeFormat, formatETA, formatDuration, formatRelativeTime, parseUTCDate } from '../utils/date';
|
||||
import type { PrintQueueItem, PrintQueueBulkUpdate, Permission } from '../api/client';
|
||||
import { Card, CardContent } from '../components/Card';
|
||||
import { Button } from '../components/Button';
|
||||
@@ -501,7 +501,7 @@ function SortableQueueItem({
|
||||
<span className="flex items-center gap-1.5">
|
||||
<Clock className="w-3.5 h-3.5" />
|
||||
{item.scheduled_time
|
||||
? (new Date(item.scheduled_time).getTime() - Date.now() < -60000
|
||||
? ((parseUTCDate(item.scheduled_time)?.getTime() ?? 0) - Date.now() < -60000
|
||||
? t?.('queue.time.overdue') ?? 'Overdue'
|
||||
: formatRelativeTime(item.scheduled_time, timeFormat, t))
|
||||
: t?.('queue.time.asap') ?? 'ASAP'}
|
||||
@@ -869,7 +869,7 @@ export function QueuePage() {
|
||||
// Helper to get scheduled time as timestamp (ASAP/placeholder = 0 for earliest)
|
||||
const getScheduledTime = (item: PrintQueueItem): number => {
|
||||
if (!item.scheduled_time) return 0;
|
||||
const time = new Date(item.scheduled_time).getTime();
|
||||
const time = parseUTCDate(item.scheduled_time)?.getTime() ?? 0;
|
||||
// Placeholder dates (> 6 months out) are treated as ASAP
|
||||
const sixMonthsFromNow = Date.now() + (180 * 24 * 60 * 60 * 1000);
|
||||
return time > sixMonthsFromNow ? 0 : time;
|
||||
@@ -955,7 +955,7 @@ export function QueuePage() {
|
||||
cmp = (a.printer_name || '').localeCompare(b.printer_name || '');
|
||||
} else {
|
||||
// Default: by date - most recent first (desc) is the natural order
|
||||
cmp = new Date(b.completed_at || b.created_at).getTime() - new Date(a.completed_at || a.created_at).getTime();
|
||||
cmp = (parseUTCDate(b.completed_at || b.created_at)?.getTime() ?? 0) - (parseUTCDate(a.completed_at || a.created_at)?.getTime() ?? 0);
|
||||
}
|
||||
return historySortAsc ? -cmp : cmp;
|
||||
});
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* These functions handle color normalization, slot labeling, and tray ID calculations
|
||||
* for AMS, AMS-HT, and external spool configurations.
|
||||
*/
|
||||
import { parseUTCDate } from './date';
|
||||
|
||||
/**
|
||||
* Normalize color format from various sources.
|
||||
@@ -111,5 +112,5 @@ export function getMinDateTime(): string {
|
||||
export function isPlaceholderDate(scheduledTime: string | null | undefined): boolean {
|
||||
if (!scheduledTime) return false;
|
||||
const sixMonthsFromNow = Date.now() + 180 * 24 * 60 * 60 * 1000;
|
||||
return new Date(scheduledTime).getTime() > sixMonthsFromNow;
|
||||
return (parseUTCDate(scheduledTime)?.getTime() ?? 0) > sixMonthsFromNow;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user