Fix incorrect shift assignments due to timezone conversion errors

Address timezone discrepancies by implementing a function to calculate the correct offset for Europe/Rome, ensuring accurate conversion of shift start and end times from local time to UTC for assignment processing.

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/7VmWFMu
This commit is contained in:
marco370 2025-10-24 13:50:00 +00:00
parent 580fbfcaab
commit 74bd542309
2 changed files with 62 additions and 3 deletions

View File

@ -47,6 +47,10 @@ externalPort = 5000
localPort = 43267
externalPort = 3003
[[ports]]
localPort = 44369
externalPort = 6000
[env]
PORT = "5000"

View File

@ -10,6 +10,31 @@ import { differenceInDays, differenceInHours, differenceInMinutes, startOfWeek,
import { z } from "zod";
import { fromZodError } from "zod-validation-error";
/**
* Calcola l'offset di Europe/Rome rispetto a UTC per una data specifica.
* 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
* @returns offset in ore (1 o 2)
*/
function getItalyTimezoneOffsetHours(date: Date): number {
const year = date.getFullYear();
// 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
// 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
// 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;
// UTC+1 (inverno) o UTC+2 (estate)
return isDST ? 2 : 1;
}
// Determina quale sistema auth usare basandosi sull'ambiente
const USE_LOCAL_AUTH = process.env.DOMAIN === "vt.alfacom.it" || !process.env.REPLIT_DOMAINS;
@ -1283,6 +1308,15 @@ export async function registerRoutes(app: Express): Promise<Server> {
try {
const { siteId, date, guardId, startTime, durationHours, consecutiveDays = 1, vehicleId, force = false } = req.body;
// DEBUG: Log per capire il problema timezone
console.log("🔍 DEBUG assign-guard - Input ricevuto:", {
date,
startTime,
durationHours,
serverTimezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
serverOffset: new Date().getTimezoneOffset()
});
if (!siteId || !date || !guardId || !startTime || !durationHours) {
return res.status(400).json({
message: "Missing required fields: siteId, date, guardId, startTime, durationHours"
@ -1312,6 +1346,16 @@ export async function registerRoutes(app: Express): Promise<Server> {
}
const [hours, minutes] = startTime.split(":").map(Number);
// 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));
console.log("🕐 DEBUG Timezone setup:", {
inputDate: date,
inputTime: `${String(hours).padStart(2,'0')}:${String(minutes).padStart(2,'0')}`,
italyOffsetHours,
isDST: italyOffsetHours === 2
});
// Atomic transaction: create assignments for all consecutive days
const result = await db.transaction(async (tx) => {
const createdAssignments = [];
@ -1342,9 +1386,20 @@ export async function registerRoutes(app: Express): Promise<Server> {
}
}
// Calculate planned start and end times in LOCAL timezone
const plannedStart = new Date(actualYear, actualMonth, actualDay, hours, minutes, 0, 0);
const plannedEnd = new Date(actualYear, actualMonth, actualDay, hours + durationHours, minutes, 0, 0);
// Calculate planned start and end times converting from Europe/Rome to UTC
// IMPORTANTE: l'utente inserisce orari in fuso orario Italia (Europe/Rome)
// Il server è in UTC, quindi dobbiamo convertire Italy time → UTC
// Formula: UTC time = Italy time - offset
// Esempio: 09:00 Italy (UTC+2) = 07:00 UTC
const plannedStart = new Date(Date.UTC(actualYear, actualMonth, actualDay, hours - italyOffsetHours, minutes, 0, 0));
const plannedEnd = new Date(Date.UTC(actualYear, actualMonth, actualDay, hours + durationHours - italyOffsetHours, minutes, 0, 0));
console.log("🕐 DEBUG Timestamp conversion:", {
inputTime: `${String(hours).padStart(2,'0')}:${String(minutes).padStart(2,'0')}`,
italyOffset: `UTC+${italyOffsetHours}`,
plannedStartUTC: plannedStart.toISOString(),
plannedEndUTC: plannedEnd.toISOString()
});
// Find or create shift for this site/date (full day boundaries in LOCAL timezone)
const dayStart = new Date(actualYear, actualMonth, actualDay, 0, 0, 0, 0);