Fix date shifting issues for shift assignments
Update date handling logic in `server/routes.ts` to prevent timezone-related shifts when assigning shifts, by parsing dates using components instead of ISO strings. Documentation in `replit.md` has also been updated with new rules to avoid this recurring problem. Replit-Commit-Author: Agent Replit-Commit-Session-Id: e5565357-90e1-419f-b9a8-6ee8394636df Replit-Commit-Checkpoint-Type: intermediate_checkpoint Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/6d543d2c-20b9-4ea6-93fe-70fe9b1d9f80/e5565357-90e1-419f-b9a8-6ee8394636df/2w7P7NW
This commit is contained in:
parent
1598eb208b
commit
983adcfbe1
4
.replit
4
.replit
@ -19,6 +19,10 @@ externalPort = 80
|
||||
localPort = 33035
|
||||
externalPort = 3001
|
||||
|
||||
[[ports]]
|
||||
localPort = 37831
|
||||
externalPort = 5173
|
||||
|
||||
[[ports]]
|
||||
localPort = 41343
|
||||
externalPort = 3000
|
||||
|
||||
37
replit.md
37
replit.md
@ -10,6 +10,43 @@ VigilanzaTurni is a professional 24/7 shift management system designed for secur
|
||||
- Focus su efficienza e densità informativa
|
||||
- **Testing**: Tutti i test vengono eseguiti ESCLUSIVAMENTE sul server esterno (vt.alfacom.it) con autenticazione locale (non Replit Auth)
|
||||
|
||||
## ⚠️ CRITICAL: Date/Timezone Handling Rules
|
||||
**PROBLEMA RICORRENTE**: Quando si assegna una guardia per il giorno X, appare assegnata al giorno X±1 a causa di conversioni timezone.
|
||||
|
||||
**REGOLE OBBLIGATORIE** per evitare questo bug:
|
||||
|
||||
1. **MAI usare `parseISO()` su date YYYY-MM-DD**
|
||||
- ❌ SBAGLIATO: `const date = parseISO("2025-10-20")` → converte in UTC causando shift
|
||||
- ✅ CORRETTO: `const [y, m, d] = "2025-10-20".split("-").map(Number); const date = new Date(y, m-1, d)`
|
||||
|
||||
2. **Costruire Date da componenti, NON da stringhe ISO**
|
||||
```typescript
|
||||
// ✅ CORRETTO - date components (no timezone conversion)
|
||||
const [year, month, day] = startDate.split("-").map(Number);
|
||||
const shiftDate = new Date(year, month - 1, day);
|
||||
const shiftStart = new Date(year, month - 1, day, startHour, startMin, 0, 0);
|
||||
|
||||
// ❌ SBAGLIATO - parseISO o new Date(string ISO)
|
||||
const date = parseISO(startDate); // converte in UTC!
|
||||
const date = new Date("2025-10-20"); // timezone-dependent!
|
||||
```
|
||||
|
||||
3. **Validazione date: usare regex, NON parseISO**
|
||||
```typescript
|
||||
// ✅ CORRETTO
|
||||
const dateRegex = /^\d{4}-\d{2}-\d{2}$/;
|
||||
if (!dateRegex.test(dateStr)) { /* invalid */ }
|
||||
|
||||
// ❌ SBAGLIATO
|
||||
const parsed = parseISO(dateStr);
|
||||
if (!isValid(parsed)) { /* invalid */ }
|
||||
```
|
||||
|
||||
4. **File da verificare sempre**: `server/routes.ts` - tutte le route che ricevono date dal frontend
|
||||
5. **Testare sempre**: Assegnare guardia giorno X → verificare appaia nel giorno X (non X±1)
|
||||
|
||||
**RIFERIMENTI FIX**: Vedere commit "Fix timezone bug in shift creation" - linee 1148-1184, 615-621, 753-759 in server/routes.ts
|
||||
|
||||
## System Architecture
|
||||
|
||||
### Stack Tecnologico
|
||||
|
||||
@ -609,13 +609,13 @@ export async function registerRoutes(app: Express): Promise<Server> {
|
||||
const rawDateStr = req.query.date as string || format(new Date(), "yyyy-MM-dd");
|
||||
const normalizedDateStr = rawDateStr.split("/")[0]; // Prende solo la prima parte se c'è uno slash
|
||||
|
||||
// Valida la data
|
||||
const parsedDate = parseISO(normalizedDateStr);
|
||||
if (!isValid(parsedDate)) {
|
||||
// TIMEZONE FIX: Valida formato senza parseISO per evitare shift timezone
|
||||
const dateRegex = /^\d{4}-\d{2}-\d{2}$/;
|
||||
if (!dateRegex.test(normalizedDateStr)) {
|
||||
return res.status(400).json({ message: "Invalid date format. Use yyyy-MM-dd" });
|
||||
}
|
||||
|
||||
const dateStr = format(parsedDate, "yyyy-MM-dd");
|
||||
const dateStr = normalizedDateStr;
|
||||
|
||||
// Ottieni location dalla query (default: roccapiemonte)
|
||||
const location = req.query.location as string || "roccapiemonte";
|
||||
@ -748,13 +748,13 @@ export async function registerRoutes(app: Express): Promise<Server> {
|
||||
const rawDateStr = req.query.date as string || format(new Date(), "yyyy-MM-dd");
|
||||
const normalizedDateStr = rawDateStr.split("/")[0]; // Prende solo la prima parte se c'è uno slash
|
||||
|
||||
// Valida la data
|
||||
const parsedDate = parseISO(normalizedDateStr);
|
||||
if (!isValid(parsedDate)) {
|
||||
// TIMEZONE FIX: Valida formato senza parseISO per evitare shift timezone
|
||||
const dateRegex = /^\d{4}-\d{2}-\d{2}$/;
|
||||
if (!dateRegex.test(normalizedDateStr)) {
|
||||
return res.status(400).json({ message: "Invalid date format. Use yyyy-MM-dd" });
|
||||
}
|
||||
|
||||
const dateStr = format(parsedDate, "yyyy-MM-dd");
|
||||
const dateStr = normalizedDateStr;
|
||||
|
||||
// Ottieni location dalla query (default: roccapiemonte)
|
||||
const location = req.query.location as string || "roccapiemonte";
|
||||
@ -1142,9 +1142,12 @@ export async function registerRoutes(app: Express): Promise<Server> {
|
||||
}
|
||||
|
||||
// Pre-validate all dates are within contract period
|
||||
const startDateParsed = parseISO(startDate);
|
||||
// TIMEZONE FIX: Parse date as YYYY-MM-DD components to avoid timezone shifts
|
||||
const [year, month, day] = startDate.split("-").map(Number);
|
||||
|
||||
for (let dayOffset = 0; dayOffset < days; dayOffset++) {
|
||||
const shiftDate = addDays(startDateParsed, dayOffset);
|
||||
// Create date using local timezone components (no UTC conversion)
|
||||
const shiftDate = new Date(year, month - 1, day + dayOffset);
|
||||
const shiftDateStr = format(shiftDate, "yyyy-MM-dd");
|
||||
|
||||
if (site.contractStartDate && site.contractEndDate) {
|
||||
@ -1163,7 +1166,8 @@ export async function registerRoutes(app: Express): Promise<Server> {
|
||||
const createdShiftsInTx = [];
|
||||
|
||||
for (let dayOffset = 0; dayOffset < days; dayOffset++) {
|
||||
const shiftDate = addDays(startDateParsed, dayOffset);
|
||||
// TIMEZONE FIX: Build date from components to maintain correct day
|
||||
const shiftDate = new Date(year, month - 1, day + dayOffset);
|
||||
|
||||
// Use site service schedule or default 24h
|
||||
const serviceStart = site.serviceStartTime || "00:00";
|
||||
@ -1172,11 +1176,9 @@ export async function registerRoutes(app: Express): Promise<Server> {
|
||||
const [startHour, startMin] = serviceStart.split(":").map(Number);
|
||||
const [endHour, endMin] = serviceEnd.split(":").map(Number);
|
||||
|
||||
const shiftStart = new Date(shiftDate);
|
||||
shiftStart.setHours(startHour, startMin, 0, 0);
|
||||
|
||||
const shiftEnd = new Date(shiftDate);
|
||||
shiftEnd.setHours(endHour, endMin, 0, 0);
|
||||
// Build timestamps using date components (no timezone conversion)
|
||||
const shiftStart = new Date(year, month - 1, day + dayOffset, startHour, startMin, 0, 0);
|
||||
const shiftEnd = new Date(year, month - 1, day + dayOffset, endHour, endMin, 0, 0);
|
||||
|
||||
// If service ends before it starts, it spans midnight (add 1 day to end)
|
||||
if (shiftEnd <= shiftStart) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user