import { ExceptionBase } from '@mobicoop/ddd-library'; import { DateInterval } from './candidate.types'; export class CalendarTools { /** * Returns the first date corresponding to a week day (0 based monday) * within a date range */ static firstDate = (weekDay: number, dateInterval: DateInterval): Date => { if (weekDay < 0 || weekDay > 6) throw new CalendarToolsException( new Error('weekDay must be between 0 and 6'), ); const lowerDateAsDate: Date = new Date(dateInterval.lowerDate); const higherDateAsDate: Date = new Date(dateInterval.higherDate); if (lowerDateAsDate.getUTCDay() == weekDay) return lowerDateAsDate; const nextDate: Date = new Date(lowerDateAsDate); nextDate.setUTCDate( lowerDateAsDate.getUTCDate() + (7 - (lowerDateAsDate.getUTCDay() - weekDay)), ); if (lowerDateAsDate.getUTCDay() < weekDay) { nextDate.setUTCMonth(lowerDateAsDate.getUTCMonth()); nextDate.setUTCFullYear(lowerDateAsDate.getUTCFullYear()); nextDate.setUTCDate( lowerDateAsDate.getUTCDate() + (weekDay - lowerDateAsDate.getUTCDay()), ); } if (nextDate <= higherDateAsDate) return nextDate; throw new CalendarToolsException( new Error('no available day for the given date range'), ); }; /** * Returns the last date corresponding to a week day (0 based monday) * within a date range */ static lastDate = (weekDay: number, dateInterval: DateInterval): Date => { if (weekDay < 0 || weekDay > 6) throw new CalendarToolsException( new Error('weekDay must be between 0 and 6'), ); const lowerDateAsDate: Date = new Date(dateInterval.lowerDate); const higherDateAsDate: Date = new Date(dateInterval.higherDate); if (higherDateAsDate.getUTCDay() == weekDay) return higherDateAsDate; const previousDate: Date = new Date(higherDateAsDate); previousDate.setUTCDate( higherDateAsDate.getUTCDate() - (higherDateAsDate.getUTCDay() - weekDay), ); if (higherDateAsDate.getUTCDay() < weekDay) { previousDate.setUTCMonth(higherDateAsDate.getUTCMonth()); previousDate.setUTCFullYear(higherDateAsDate.getUTCFullYear()); previousDate.setUTCDate( higherDateAsDate.getUTCDate() - (7 + (higherDateAsDate.getUTCDay() - weekDay)), ); } if (previousDate >= lowerDateAsDate) return previousDate; throw new CalendarToolsException( new Error('no available day for the given date range'), ); }; /** * Returns a date from a date (as a date) and a time (as a string), adding optional seconds */ static datetimeWithSeconds = ( date: Date, time: string, additionalSeconds = 0, ): Date => { const datetime: Date = new Date(date); datetime.setUTCHours(parseInt(time.split(':')[0])); datetime.setUTCMinutes(parseInt(time.split(':')[1])); datetime.setUTCSeconds(additionalSeconds); return datetime; }; /** * Returns dates from a day and time based on unix epoch day * (1970-01-01 is day 4) * The method returns an array of dates because for edges (day 0 and 6) * we need to return 2 possibilities : one for the previous week, one for the next week */ static epochDaysFromTime = (weekDay: number, time: string): Date[] => { if (weekDay < 0 || weekDay > 6) throw new CalendarToolsException( new Error('weekDay must be between 0 and 6'), ); switch (weekDay) { case 0: return [ new Date(`1969-12-28T${time}:00Z`), new Date(`1970-01-04T${time}:00Z`), ]; case 1: return [new Date(`1969-12-29T${time}:00Z`)]; case 2: return [new Date(`1969-12-30T${time}:00Z`)]; case 3: return [new Date(`1969-12-31T${time}:00Z`)]; case 5: return [new Date(`1970-01-02T${time}:00Z`)]; case 6: return [ new Date(`1969-12-27T${time}:00Z`), new Date(`1970-01-03T${time}:00Z`), ]; case 4: default: return [new Date(`1970-01-01T${time}:00Z`)]; } }; } export class CalendarToolsException extends ExceptionBase { static readonly message = 'Calendar tools error'; public readonly code = 'CALENDAR.TOOLS'; constructor(cause?: Error, metadata?: unknown) { super(CalendarToolsException.message, cause, metadata); } }