From 2f0982c95263aa25f658a933e5596e2bfbb753f2 Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Fri, 10 Oct 2025 17:01:05 -0400 Subject: [PATCH] refactor(shifts): modified shifts.utils to use ISO format instead of UTC --- .../shifts-upsert.types.ts | 10 +++ src/modules/shifts/utils/shifts.utils.ts | 77 ++++++++++++------- 2 files changed, 59 insertions(+), 28 deletions(-) diff --git a/src/modules/shifts/types-and-interfaces/shifts-upsert.types.ts b/src/modules/shifts/types-and-interfaces/shifts-upsert.types.ts index 99f140d..733ea72 100644 --- a/src/modules/shifts/types-and-interfaces/shifts-upsert.types.ts +++ b/src/modules/shifts/types-and-interfaces/shifts-upsert.types.ts @@ -5,3 +5,13 @@ export type DayShiftResponse = { is_remote: boolean; comment: string | null; } + +export type ShiftPayload = { + date: string; + start_time: string; + end_time: string; + type: string; + is_remote: boolean; + is_approved: boolean; + comment?: string | null; +} \ No newline at end of file diff --git a/src/modules/shifts/utils/shifts.utils.ts b/src/modules/shifts/utils/shifts.utils.ts index 43c569f..a0fb13e 100644 --- a/src/modules/shifts/utils/shifts.utils.ts +++ b/src/modules/shifts/utils/shifts.utils.ts @@ -3,37 +3,58 @@ import { ShiftPayloadDto } from "../dtos/upsert-shift.dto"; import { timeFromHHMM } from "../helpers/shifts-date-time-helpers"; export function overlaps( - a_start_ms: number, - a_end_ms: number, - b_start_ms: number, - b_end_ms: number, - ): boolean { - return a_start_ms < b_end_ms && b_start_ms < a_end_ms; + a_start_ms: number, + a_end_ms: number, + b_start_ms: number, + b_end_ms: number, +): boolean { + return a_start_ms < b_end_ms && b_start_ms < a_end_ms; } export function resolveBankCodeByType(type: string): Promise { - const bank = this.prisma.bankCodes.findFirst({ - where: { type }, - select: { id: true }, - }); - if (!bank) { - throw new NotFoundException({ error_code: 'SHIFT_TYPE_UNKNOWN', message: `unknown shift type: ${type}` }); - } - return bank.id; + const bank = this.prisma.bankCodes.findFirst({ + where: { type }, + select: { id: true }, + }); + if (!bank) { + throw new NotFoundException({ error_code: 'SHIFT_TYPE_UNKNOWN', message: `unknown shift type: ${type}` }); + } + return bank.id; } - export function normalizeShiftPayload(payload: ShiftPayloadDto) { - //normalize shift's infos - const date = payload.date; - const start_time = timeFromHHMM(payload.start_time); - const end_time = timeFromHHMM(payload.end_time ); - const type = (payload.type || '').trim().toUpperCase(); - const is_remote = payload.is_remote === true; - const is_approved = payload.is_approved === false; - //normalize comment - const raw_comment = payload.comment ?? null; - const trimmed = typeof raw_comment === 'string' ? raw_comment.trim() : null; - const comment = trimmed && trimmed.length > 0 ? trimmed: null; +export function normalizeShiftPayload(payload: { + date: string, + start_time: string, + end_time: string, + type: string, + is_remote: boolean, + is_approved: boolean, + comment?: string | null, +}) { + //normalize shift's infos + const date = payload.date?.trim(); + const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(date ?? ''); + if (!m) throw new Error(`Invalid date format (expected YYYY-MM-DD): "${payload.date}"`); + const year = Number(m[1]), mo = Number(m[2]), d = Number(m[3]); - return { date, start_time, end_time, type, is_remote, is_approved, comment }; - } \ No newline at end of file + const asLocalDateOn = (input: string): Date => { + // HH:mm ? + const hm = /^(\d{2}):(\d{2})$/.exec((input ?? '').trim()); + if (hm) return new Date(year, mo - 1, d, Number(hm[1]), Number(hm[2]), 0, 0); + const iso = new Date(input); + if (isNaN(iso.getTime())) throw new Error(`Invalid time: "${input}"`); + return new Date(year, mo - 1, d, iso.getHours(), iso.getMinutes(), iso.getSeconds(), iso.getMilliseconds()); + }; + + const start_time = asLocalDateOn(payload.start_time); + const end_time = asLocalDateOn(payload.end_time); + + const type = (payload.type || '').trim().toUpperCase(); + const is_remote = payload.is_remote === true; + const is_approved = payload.is_approved === false; + //normalize comment + const trimmed = typeof payload.comment === 'string' ? payload.comment.trim() : null; + const comment = trimmed && trimmed.length > 0 ? trimmed : null; + + return { date, start_time, end_time, type, is_remote, is_approved, comment }; +} \ No newline at end of file