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
|
localPort = 33035
|
||||||
externalPort = 3001
|
externalPort = 3001
|
||||||
|
|
||||||
|
[[ports]]
|
||||||
|
localPort = 34027
|
||||||
|
externalPort = 6000
|
||||||
|
|
||||||
[[ports]]
|
[[ports]]
|
||||||
localPort = 41295
|
localPort = 41295
|
||||||
externalPort = 5173
|
externalPort = 5173
|
||||||
@ -47,10 +51,6 @@ externalPort = 5000
|
|||||||
localPort = 43267
|
localPort = 43267
|
||||||
externalPort = 3003
|
externalPort = 3003
|
||||||
|
|
||||||
[[ports]]
|
|
||||||
localPort = 44369
|
|
||||||
externalPort = 6000
|
|
||||||
|
|
||||||
[env]
|
[env]
|
||||||
PORT = "5000"
|
PORT = "5000"
|
||||||
|
|
||||||
|
|||||||
@ -85,14 +85,14 @@ interface GeneralPlanningResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Helper per formattare orario in formato italiano 24h (HH:MM)
|
// 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 formatTime = (dateString: string) => {
|
||||||
const date = new Date(dateString);
|
const date = new Date(dateString);
|
||||||
return date.toLocaleTimeString("it-IT", {
|
return date.toLocaleTimeString("it-IT", {
|
||||||
hour: "2-digit",
|
hour: "2-digit",
|
||||||
minute: "2-digit",
|
minute: "2-digit",
|
||||||
hour12: false,
|
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>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
dayShifts.map((shift) => {
|
dayShifts.map((shift) => {
|
||||||
// Parsing sicuro orari
|
// Parsing sicuro orari (DB in UTC → visualizza in Europe/Rome)
|
||||||
let startTime = "N/A";
|
let startTime = "N/A";
|
||||||
let endTime = "N/A";
|
let endTime = "N/A";
|
||||||
|
|
||||||
if (shift.plannedStartTime) {
|
if (shift.plannedStartTime) {
|
||||||
const parsedStart = parseISO(shift.plannedStartTime);
|
const parsedStart = new Date(shift.plannedStartTime);
|
||||||
if (isValid(parsedStart)) {
|
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) {
|
if (shift.plannedEndTime) {
|
||||||
const parsedEnd = parseISO(shift.plannedEndTime);
|
const parsedEnd = new Date(shift.plannedEndTime);
|
||||||
if (isValid(parsedEnd)) {
|
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>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
dayGuards.map((guard, index) => {
|
dayGuards.map((guard, index) => {
|
||||||
// Parsing sicuro orari
|
// Parsing sicuro orari (DB in UTC → visualizza in Europe/Rome)
|
||||||
let startTime = "N/A";
|
let startTime = "N/A";
|
||||||
let endTime = "N/A";
|
let endTime = "N/A";
|
||||||
|
|
||||||
if (guard.plannedStartTime) {
|
if (guard.plannedStartTime) {
|
||||||
const parsedStart = parseISO(guard.plannedStartTime);
|
const parsedStart = new Date(guard.plannedStartTime);
|
||||||
if (isValid(parsedStart)) {
|
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) {
|
if (guard.plannedEndTime) {
|
||||||
const parsedEnd = parseISO(guard.plannedEndTime);
|
const parsedEnd = new Date(guard.plannedEndTime);
|
||||||
if (isValid(parsedEnd)) {
|
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";
|
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)
|
* 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)
|
* @returns offset in ore (1 o 2)
|
||||||
*/
|
*/
|
||||||
function getItalyTimezoneOffsetHours(date: Date): number {
|
function getItalyTimezoneOffsetHours(year: number, month: number, day: number, hour: number, minute: number = 0): number {
|
||||||
const year = date.getFullYear();
|
// 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)
|
// Ottieni tutti i componenti in Europe/Rome timezone
|
||||||
const marchLastSunday = new Date(year, 2, 31); // 31 marzo
|
const formatter = new Intl.DateTimeFormat('en-US', {
|
||||||
marchLastSunday.setDate(31 - marchLastSunday.getDay()); // torna indietro alla domenica
|
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 parts = formatter.formatToParts(utcDate);
|
||||||
const octoberLastSunday = new Date(year, 9, 31); // 31 ottobre
|
const italyYear = parseInt(parts.find(p => p.type === 'year')?.value || '0');
|
||||||
octoberLastSunday.setDate(31 - octoberLastSunday.getDay()); // torna indietro alla domenica
|
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
|
// formatToParts restituisce hour=24 per mezzanotte (00:00 del giorno successivo)
|
||||||
// e termina alle 03:00 UTC dell'ultima domenica di ottobre
|
// Il giorno è già stato incrementato automaticamente da formatToParts
|
||||||
const isDST = date >= marchLastSunday && date < octoberLastSunday;
|
// Normalizziamo solo l'ora: 24:00 → 00:00
|
||||||
|
const normalizedHour = italyHour === 24 ? 0 : italyHour;
|
||||||
|
|
||||||
// UTC+1 (inverno) o UTC+2 (estate)
|
// Crea timestamp Italy come se fosse UTC (per calcolare differenza)
|
||||||
return isDST ? 2 : 1;
|
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
|
// 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)
|
// CRITICAL: Gli orari dal frontend sono in fuso orario Europe/Rome (UTC+1 o UTC+2)
|
||||||
// Calcola offset corretto per convertire a UTC
|
// 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:", {
|
console.log("🕐 DEBUG Timezone setup:", {
|
||||||
inputDate: date,
|
inputDate: date,
|
||||||
inputTime: `${String(hours).padStart(2,'0')}:${String(minutes).padStart(2,'0')}`,
|
inputTime: `${String(hours).padStart(2,'0')}:${String(minutes).padStart(2,'0')}`,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user