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:
parent
580fbfcaab
commit
74bd542309
4
.replit
4
.replit
@ -47,6 +47,10 @@ externalPort = 5000
|
||||
localPort = 43267
|
||||
externalPort = 3003
|
||||
|
||||
[[ports]]
|
||||
localPort = 44369
|
||||
externalPort = 6000
|
||||
|
||||
[env]
|
||||
PORT = "5000"
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user