backend/lib/seed/helpers/Utils.dates.ts
2025-05-14 21:45:16 +02:00

311 lines
10 KiB
TypeScript

import {
AvailabilityComponent,
DateRangeComponent,
DateTimeRangeComponent,
HourComponent,
SlotAvailablity,
SlotComponent,
} from '@seed/interfaces/components.dates';
import _ from 'lodash';
import { DateTime, Interval } from 'luxon';
interface hoursRange {
startHours: string;
startHoursInNumber: number;
endHours: string;
endHoursInNumber: number;
}
/*
breakTime: [
['11:00', '14:00'],
['16:00', '18:00'],
],
*/
export function getNumberOfWeekendDays(dateBegin: DateTime, dateEnd: DateTime): number {
let weekendDays = 0;
let begin = dateBegin;
while (begin <= dateEnd) {
if (begin.weekday == 6 || begin.weekday == 7) weekendDays = weekendDays + 1;
begin = begin.plus({ day: 1 });
}
return weekendDays;
}
function isInTimeInterval(slotTime: DateTime, slotInterval: number, timeRange: hoursRange[]): boolean {
const startTime = slotTime;
const endTime = slotTime.plus({ minutes: slotInterval });
const startTimeInNumber = startTime.hour * 60 + startTime.minute;
const endTimeInNumber = endTime.hour * 60 + endTime.minute;
const isInInterval = timeRange.some((br) => {
return startTimeInNumber < br.endHoursInNumber && endTimeInNumber > br.startHoursInNumber;
// return (date.isSameOrAfter(startTime) && date.isSameOrBefore(endTime)) || (date.isSameOrAfter(startTime) && date.isSame(endTime));
});
return isInInterval;
}
export const getMinutesFromTimeInString = (str: string): number => {
const timeArray = str.split(':');
return parseInt(timeArray[0]) * 60 + parseInt(timeArray[1]);
};
export const getDateNoTZ = (old: Date): Date => {
const userTimezoneOffset = old.getTimezoneOffset() * 60000;
return new Date(old.getTime() - userTimezoneOffset);
};
export const slotToDatesRange = (slot: SlotComponent): DateRangeComponent[] => {
const dates: DateRangeComponent[] = [];
const { startDate, endDate, startTime, endTime } = slot;
const startTimes = startTime.split(':');
const endTimes = endTime.split(':');
let beginDate = DateTime.fromJSDate(startDate).startOf('day');
const stopDate = DateTime.fromJSDate(endDate).endOf('day');
while (beginDate < stopDate) {
dates.push({
startDate: beginDate
.set({ hour: parseInt(startTimes[0]) })
.set({ minute: parseInt(startTimes[1]) })
.toJSDate(),
endDate: beginDate
.set({ hour: parseInt(endTimes[0]) })
.set({ minute: parseInt(endTimes[1]) })
// Remove one seconds for overlapping
.minus({ second: 1 })
.toJSDate(),
});
beginDate = beginDate.plus({ day: 1 });
}
return dates;
};
export const getOneDaySlots = (input: {
day: DateTime;
dayUnavailability: DateTimeRangeComponent[];
slotInfo: string[][];
slotInterval: number;
slotDuration: number;
options?: {
zone?: string;
};
}): string[] => {
const { day, dayUnavailability, slotInfo, slotDuration, slotInterval, options } = input;
const zone = options?.zone || process.env.TIMEZONE || 'utc';
let currentDayMoment = day;
// Check if currentDay = today
const currentDayStart = currentDayMoment.toISODate();
const todayStart = DateTime.local().toISODate();
if (currentDayStart != todayStart) {
currentDayMoment = day.startOf('day');
}
const currentDayTimeInNumber = currentDayMoment.hour * 60 + currentDayMoment.minute;
console.log('current day', currentDayMoment.toISO(), currentDayMoment.weekday);
// Getting the slots for that day
const daySlot = slotInfo[day.weekday - 1];
const currentDaySlots: {
startTime: DateTime;
endTime: DateTime;
}[] = [];
if (daySlot && daySlot.length > 0) {
if (_.isArray(daySlot[0])) {
_.each(daySlot, (d) => {
currentDaySlots.push({
startTime: DateTime.fromFormat(d[0], 'HH:mm', { zone }),
endTime: DateTime.fromFormat(d[1], 'HH:mm', { zone }),
});
});
} else {
currentDaySlots.push({
startTime: DateTime.fromFormat(daySlot[0], 'HH:mm', { zone }),
endTime: DateTime.fromFormat(daySlot[1], 'HH:mm', { zone }),
});
}
}
const thisDaysUnavailabilities: {
startHours: string;
startHoursInNumber: number;
endHours: string;
endHoursInNumber: number;
}[] = getUnavailabilityOfThisDay(dayUnavailability, currentDayMoment);
const currentDateSlots: string[] = [];
console.log('thisDaysUnavailabilities', thisDaysUnavailabilities);
currentDaySlots.forEach((element) => {
let { startTime, endTime } = element;
while (startTime < endTime) {
if (
!(startTime.hour * 60 + startTime.minute < currentDayTimeInNumber) &&
!isInTimeInterval(startTime, slotDuration, thisDaysUnavailabilities) &&
startTime.hour * 60 + startTime.minute + slotDuration <= endTime.hour * 60 + endTime.minute
) {
currentDateSlots.push(startTime.toFormat('HH:mm'));
}
startTime = startTime.plus({ minutes: slotInterval });
}
});
return currentDateSlots;
};
export const getMultipleDaysSlots = (input: {
datesWanted: DateRangeComponent;
datesUnavailable: DateTimeRangeComponent[];
slotInterval: number;
slotInfo: string[][];
slotDuration: number;
options?: {
zone?: string;
};
}): SlotAvailablity[] => {
const returnSlots: SlotAvailablity[] = [];
const { datesWanted, datesUnavailable, slotInterval, slotInfo, slotDuration, options } = input;
const { startDate, endDate } = datesWanted;
const zone = options?.zone || process.env.TIMEZONE || 'utc';
let beginDate = DateTime.fromJSDate(startDate, { zone });
const stopDate = DateTime.fromJSDate(endDate, { zone });
// Two loops
// Looping into the wanted dates and keeping an index for the slotInfo
while (beginDate < stopDate) {
// Get the slot info from the array of array
const slots = getOneDaySlots({
day: beginDate,
dayUnavailability: datesUnavailable,
slotInfo,
slotInterval,
slotDuration,
options: { zone },
});
// Adding it to the array
returnSlots.push({
date: beginDate.toJSDate(),
slots,
});
beginDate = beginDate.plus({ day: 1 }).startOf('day');
}
return returnSlots;
};
export function getUnavailabilityOfThisDay(dayUnavailability: DateTimeRangeComponent[], currentDayMoment: DateTime) {
const thisDaysUnavailabilities: {
startHours: string;
startHoursInNumber: number;
endHours: string;
endHoursInNumber: number;
}[] = [];
dayUnavailability.forEach((element) => {
const startDate = element.startDate;
const endDate = element.endDate;
// If there are some reservations in that day or in multiple days
const isSameThanStartDate = currentDayMoment.hasSame(startDate, 'day');
const isSameThanEndDate = currentDayMoment.hasSame(endDate, 'day');
const startHours = startDate.toFormat('HH:mm');
const endHours = endDate.toFormat('HH:mm');
const starts = startHours.split(':');
const ends = endHours.split(':');
const startDateDay = startDate.startOf('day');
const endDateDay = startDate.endOf('day');
const startHoursInNumber = parseInt(starts[0]) * 60 + parseInt(starts[1]);
const endHoursInNumber = parseInt(ends[0]) * 60 + parseInt(ends[1]);
// If the reservation only last a day
if (isSameThanStartDate && isSameThanEndDate) {
thisDaysUnavailabilities.push({
startHours,
endHours,
startHoursInNumber,
endHoursInNumber,
});
}
// If the reservation last multiple days
else if (currentDayMoment >= startDateDay && currentDayMoment <= endDateDay) {
// check if start date
if (isSameThanStartDate)
thisDaysUnavailabilities.push({
startHours,
endHours: '23:59',
startHoursInNumber,
endHoursInNumber: 23 * 60 + 59,
});
else if (isSameThanEndDate)
thisDaysUnavailabilities.push({
startHours: '00:00',
endHours,
startHoursInNumber: 0,
endHoursInNumber,
});
else
thisDaysUnavailabilities.push({
startHours: '00:00',
endHours: '23:59',
startHoursInNumber: 0,
endHoursInNumber: 23 * 60 + 59,
});
}
});
return thisDaysUnavailabilities;
}
export function isTimeSlotInDates(dates: DateTimeRangeComponent[], startTime: string, endTime: string) {
const startTimesInMinutes = getMinutesFromTimeInString(startTime);
const endTimesInMinutes = getMinutesFromTimeInString(endTime);
// Check for each date if the minutes are in it
return dates.some((br) => {
const brStartTimeInMinutes = br.startDate.hour * 60 + br.startDate.minute;
const brEndTimeInMinutes = br.endDate.hour * 60 + br.endDate.minute;
return startTimesInMinutes < brEndTimeInMinutes && endTimesInMinutes > brStartTimeInMinutes;
});
}
export function converDateRangeToDateTime(
dates: DateRangeComponent[],
options?: {
zone?: string;
},
): DateTimeRangeComponent[] {
const results: DateTimeRangeComponent[] = [];
const zone = options?.zone || process.env.TIMEZONE || 'utc';
dates.forEach((element) => {
results.push({
startDate: DateTime.fromJSDate(element.startDate, { zone }),
endDate: DateTime.fromJSDate(element.endDate, { zone }),
});
});
return results;
}