Adjust time display to show correct local times for users
Update time formatting logic to consistently display scheduled times in the 'Europe/Rome' timezone, resolving inconsistencies caused by UTC storage. Replit-Commit-Author: Agent Replit-Commit-Session-Id: e0b5b11c-5b75-4389-8ea9-5f3cd9332f88 Replit-Commit-Checkpoint-Type: intermediate_checkpoint Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/6d543d2c-20b9-4ea6-93fe-70fe9b1d9f80/e0b5b11c-5b75-4389-8ea9-5f3cd9332f88/HO4k2VG
This commit is contained in:
parent
74bd542309
commit
468d6477eb
8
.replit
8
.replit
@ -19,6 +19,10 @@ externalPort = 80
|
||||
localPort = 33035
|
||||
externalPort = 3001
|
||||
|
||||
[[ports]]
|
||||
localPort = 34027
|
||||
externalPort = 6000
|
||||
|
||||
[[ports]]
|
||||
localPort = 41295
|
||||
externalPort = 5173
|
||||
@ -47,10 +51,6 @@ externalPort = 5000
|
||||
localPort = 43267
|
||||
externalPort = 3003
|
||||
|
||||
[[ports]]
|
||||
localPort = 44369
|
||||
externalPort = 6000
|
||||
|
||||
[env]
|
||||
PORT = "5000"
|
||||
|
||||
|
||||
@ -85,14 +85,14 @@ interface GeneralPlanningResponse {
|
||||
}
|
||||
|
||||
// Helper per formattare orario in formato italiano 24h (HH:MM)
|
||||
// IMPORTANTE: usa timeZone UTC per evitare shift di +2 ore
|
||||
// IMPORTANTE: Gli orari nel DB sono UTC, visualizzali in timezone Europe/Rome
|
||||
const formatTime = (dateString: string) => {
|
||||
const date = new Date(dateString);
|
||||
return date.toLocaleTimeString("it-IT", {
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
hour12: false,
|
||||
timeZone: "UTC" // Evita conversione timezone locale (+2h in Italia)
|
||||
timeZone: "Europe/Rome" // Converti da UTC a Italy time
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@ -159,21 +159,31 @@ export default function MyShiftsFixed() {
|
||||
</div>
|
||||
) : (
|
||||
dayShifts.map((shift) => {
|
||||
// Parsing sicuro orari
|
||||
// Parsing sicuro orari (DB in UTC → visualizza in Europe/Rome)
|
||||
let startTime = "N/A";
|
||||
let endTime = "N/A";
|
||||
|
||||
if (shift.plannedStartTime) {
|
||||
const parsedStart = parseISO(shift.plannedStartTime);
|
||||
const parsedStart = new Date(shift.plannedStartTime);
|
||||
if (isValid(parsedStart)) {
|
||||
startTime = format(parsedStart, "HH:mm");
|
||||
startTime = parsedStart.toLocaleTimeString("it-IT", {
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
hour12: false,
|
||||
timeZone: "Europe/Rome"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (shift.plannedEndTime) {
|
||||
const parsedEnd = parseISO(shift.plannedEndTime);
|
||||
const parsedEnd = new Date(shift.plannedEndTime);
|
||||
if (isValid(parsedEnd)) {
|
||||
endTime = format(parsedEnd, "HH:mm");
|
||||
endTime = parsedEnd.toLocaleTimeString("it-IT", {
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
hour12: false,
|
||||
timeZone: "Europe/Rome"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -201,21 +201,31 @@ export default function SitePlanningView() {
|
||||
</div>
|
||||
) : (
|
||||
dayGuards.map((guard, index) => {
|
||||
// Parsing sicuro orari
|
||||
// Parsing sicuro orari (DB in UTC → visualizza in Europe/Rome)
|
||||
let startTime = "N/A";
|
||||
let endTime = "N/A";
|
||||
|
||||
if (guard.plannedStartTime) {
|
||||
const parsedStart = parseISO(guard.plannedStartTime);
|
||||
const parsedStart = new Date(guard.plannedStartTime);
|
||||
if (isValid(parsedStart)) {
|
||||
startTime = format(parsedStart, "HH:mm");
|
||||
startTime = parsedStart.toLocaleTimeString("it-IT", {
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
hour12: false,
|
||||
timeZone: "Europe/Rome"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (guard.plannedEndTime) {
|
||||
const parsedEnd = parseISO(guard.plannedEndTime);
|
||||
const parsedEnd = new Date(guard.plannedEndTime);
|
||||
if (isValid(parsedEnd)) {
|
||||
endTime = format(parsedEnd, "HH:mm");
|
||||
endTime = parsedEnd.toLocaleTimeString("it-IT", {
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
hour12: false,
|
||||
timeZone: "Europe/Rome"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -11,28 +11,62 @@ import { z } from "zod";
|
||||
import { fromZodError } from "zod-validation-error";
|
||||
|
||||
/**
|
||||
* Calcola l'offset di Europe/Rome rispetto a UTC per una data specifica.
|
||||
* Calcola l'offset di Europe/Rome rispetto a UTC per una specifica data/ora.
|
||||
* Gestisce correttamente orari serali e transizioni DST.
|
||||
* Italy: UTC+1 (ora solare inverno) / UTC+2 (ora legale estate)
|
||||
* L'ora legale va dall'ultima domenica di marzo all'ultima domenica di ottobre
|
||||
* @param year, month (0-11), day, hour, minute
|
||||
* @returns offset in ore (1 o 2)
|
||||
*/
|
||||
function getItalyTimezoneOffsetHours(date: Date): number {
|
||||
const year = date.getFullYear();
|
||||
function getItalyTimezoneOffsetHours(year: number, month: number, day: number, hour: number, minute: number = 0): number {
|
||||
// Crea timestamp UTC esatto per l'input Italy time
|
||||
// Useremo formatToParts per ottenere tutti i componenti date/time
|
||||
const utcTimestamp = Date.UTC(year, month, day, hour, minute, 0);
|
||||
const utcDate = new Date(utcTimestamp);
|
||||
|
||||
// Calcola ultima domenica di marzo (inizio ora legale)
|
||||
const marchLastSunday = new Date(year, 2, 31); // 31 marzo
|
||||
marchLastSunday.setDate(31 - marchLastSunday.getDay()); // torna indietro alla domenica
|
||||
// Ottieni tutti i componenti in Europe/Rome timezone
|
||||
const formatter = new Intl.DateTimeFormat('en-US', {
|
||||
timeZone: 'Europe/Rome',
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
hour12: false
|
||||
});
|
||||
|
||||
// Calcola ultima domenica di ottobre (fine ora legale)
|
||||
const octoberLastSunday = new Date(year, 9, 31); // 31 ottobre
|
||||
octoberLastSunday.setDate(31 - octoberLastSunday.getDay()); // torna indietro alla domenica
|
||||
const parts = formatter.formatToParts(utcDate);
|
||||
const italyYear = parseInt(parts.find(p => p.type === 'year')?.value || '0');
|
||||
const italyMonth = parseInt(parts.find(p => p.type === 'month')?.value || '0') - 1;
|
||||
const italyDay = parseInt(parts.find(p => p.type === 'day')?.value || '0');
|
||||
let italyHour = parseInt(parts.find(p => p.type === 'hour')?.value || '0');
|
||||
const italyMinute = parseInt(parts.find(p => p.type === 'minute')?.value || '0');
|
||||
|
||||
// L'ora legale inizia alle 02:00 UTC dell'ultima domenica di marzo
|
||||
// e termina alle 03:00 UTC dell'ultima domenica di ottobre
|
||||
const isDST = date >= marchLastSunday && date < octoberLastSunday;
|
||||
// formatToParts restituisce hour=24 per mezzanotte (00:00 del giorno successivo)
|
||||
// Il giorno è già stato incrementato automaticamente da formatToParts
|
||||
// Normalizziamo solo l'ora: 24:00 → 00:00
|
||||
const normalizedHour = italyHour === 24 ? 0 : italyHour;
|
||||
|
||||
// UTC+1 (inverno) o UTC+2 (estate)
|
||||
return isDST ? 2 : 1;
|
||||
// Crea timestamp Italy come se fosse UTC (per calcolare differenza)
|
||||
const italyAsUtcTimestamp = Date.UTC(italyYear, italyMonth, italyDay, normalizedHour, italyMinute, 0);
|
||||
|
||||
// Calcola differenza in millisecondi e converti in ore
|
||||
const offsetMs = italyAsUtcTimestamp - utcTimestamp;
|
||||
const offsetHours = Math.round(offsetMs / (1000 * 60 * 60));
|
||||
|
||||
console.log("🕐 Offset calculation:", {
|
||||
input: `${year}-${String(month+1).padStart(2,'0')}-${String(day).padStart(2,'0')} ${String(hour).padStart(2,'0')}:${String(minute).padStart(2,'0')}`,
|
||||
utcTimestamp: utcDate.toISOString(),
|
||||
italyComponents: { year: italyYear, month: italyMonth+1, day: italyDay, hour: italyHour, minute: italyMinute },
|
||||
offsetHours
|
||||
});
|
||||
|
||||
// Italy è sempre UTC+1 o UTC+2
|
||||
if (offsetHours !== 1 && offsetHours !== 2) {
|
||||
console.error("⚠️ Unexpected offset:", offsetHours, "- defaulting to UTC+1");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return offsetHours;
|
||||
}
|
||||
|
||||
// Determina quale sistema auth usare basandosi sull'ambiente
|
||||
@ -1348,7 +1382,7 @@ export async function registerRoutes(app: Express): Promise<Server> {
|
||||
|
||||
// CRITICAL: Gli orari dal frontend sono in fuso orario Europe/Rome (UTC+1 o UTC+2)
|
||||
// Calcola offset corretto per convertire a UTC
|
||||
const italyOffsetHours = getItalyTimezoneOffsetHours(new Date(year, month - 1, day));
|
||||
const italyOffsetHours = getItalyTimezoneOffsetHours(year, month - 1, day, hours, minutes);
|
||||
console.log("🕐 DEBUG Timezone setup:", {
|
||||
inputDate: date,
|
||||
inputTime: `${String(hours).padStart(2,'0')}:${String(minutes).padStart(2,'0')}`,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user