Add WorkingHoursHelper
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
const organizeByDay = workingHoursData => {
|
||||
return workingHoursData.reduce((acc, schedule) => {
|
||||
acc[schedule.dayOfWeek] = schedule;
|
||||
return acc;
|
||||
}, {});
|
||||
};
|
||||
|
||||
const toMinutes = (hours, minutes = 0) => {
|
||||
return hours * 60 + (minutes || 0);
|
||||
};
|
||||
|
||||
const calculateMinutesUntilOpen = (
|
||||
schedule,
|
||||
currentTimeInMinutes,
|
||||
dayOffset
|
||||
) => {
|
||||
if (!schedule || schedule.closedAllDay) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let openHour = schedule.openHour;
|
||||
let openMinutes = schedule.openMinutes;
|
||||
|
||||
if (schedule.openAllDay) {
|
||||
openHour = 0;
|
||||
openMinutes = 0;
|
||||
}
|
||||
|
||||
const openTimeInMinutes = toMinutes(openHour, openMinutes);
|
||||
|
||||
if (dayOffset === 0) {
|
||||
if (currentTimeInMinutes < openTimeInMinutes) {
|
||||
return openTimeInMinutes - currentTimeInMinutes;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
toMinutes(24, 0) * dayOffset + openTimeInMinutes - currentTimeInMinutes
|
||||
);
|
||||
};
|
||||
|
||||
class WorkingHours {
|
||||
constructor(workingHoursData) {
|
||||
this.workingHoursByDay = organizeByDay(workingHoursData);
|
||||
}
|
||||
|
||||
isOpen(date) {
|
||||
const schedule = this.workingHoursByDay[date.getDay()];
|
||||
|
||||
if (!schedule || schedule.closedAllDay) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (schedule.openAllDay) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const currentMinutes = toMinutes(date.getHours(), date.getMinutes());
|
||||
const openMinutes = toMinutes(schedule.openHour, schedule.openMinutes);
|
||||
const closeMinutes = toMinutes(schedule.closeHour, schedule.closeMinutes);
|
||||
|
||||
return currentMinutes >= openMinutes && currentMinutes <= closeMinutes;
|
||||
}
|
||||
|
||||
openInMinutes(date) {
|
||||
if (this.isOpen(date)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const currentMinutes = toMinutes(date.getHours(), date.getMinutes());
|
||||
|
||||
for (let i = 0; i < 7; i += 1) {
|
||||
const checkDate = new Date(date);
|
||||
checkDate.setDate(date.getDate() + i);
|
||||
|
||||
const schedule = this.workingHoursByDay[checkDate.getDay()];
|
||||
const minutesUntilOpen = calculateMinutesUntilOpen(
|
||||
schedule,
|
||||
currentMinutes,
|
||||
i
|
||||
);
|
||||
|
||||
if (minutesUntilOpen !== null) {
|
||||
return minutesUntilOpen;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
export default WorkingHours;
|
||||
@@ -0,0 +1,57 @@
|
||||
export default [
|
||||
{
|
||||
dayOfWeek: 0,
|
||||
openHour: 10,
|
||||
openMinutes: 0,
|
||||
closeHour: 16,
|
||||
closeMinutes: 0,
|
||||
closedAllDay: false,
|
||||
openAllDay: false,
|
||||
}, // Sunday
|
||||
{
|
||||
dayOfWeek: 1,
|
||||
openHour: 9,
|
||||
openMinutes: 0,
|
||||
closeHour: 17,
|
||||
closeMinutes: 0,
|
||||
closedAllDay: false,
|
||||
openAllDay: false,
|
||||
}, // Monday
|
||||
{
|
||||
dayOfWeek: 2,
|
||||
openHour: 9,
|
||||
openMinutes: 0,
|
||||
closeHour: 17,
|
||||
closeMinutes: 0,
|
||||
closedAllDay: false,
|
||||
openAllDay: false,
|
||||
}, // Tuesday
|
||||
{
|
||||
dayOfWeek: 3,
|
||||
openHour: 9,
|
||||
openMinutes: 0,
|
||||
closeHour: 17,
|
||||
closeMinutes: 0,
|
||||
closedAllDay: false,
|
||||
openAllDay: false,
|
||||
}, // Wednesday
|
||||
{
|
||||
dayOfWeek: 4,
|
||||
openHour: 9,
|
||||
openMinutes: 0,
|
||||
closeHour: 17,
|
||||
closeMinutes: 0,
|
||||
closedAllDay: false,
|
||||
openAllDay: false,
|
||||
}, // Thursday
|
||||
{
|
||||
dayOfWeek: 5,
|
||||
openHour: 9,
|
||||
openMinutes: 0,
|
||||
closeHour: 17,
|
||||
closeMinutes: 0,
|
||||
closedAllDay: false,
|
||||
openAllDay: false,
|
||||
}, // Friday
|
||||
{ dayOfWeek: 6, closedAllDay: true, openAllDay: false }, // Saturday
|
||||
];
|
||||
@@ -0,0 +1,95 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import WorkingHours from '../WorkingHoursHelper';
|
||||
import workingHoursData from './WorkingHourFixtures';
|
||||
|
||||
describe('WorkingHours', () => {
|
||||
describe('isOpen', () => {
|
||||
it('returns true during business hours', () => {
|
||||
const workingHours = new WorkingHours(workingHoursData);
|
||||
const date = new Date(2025, 0, 6); // Monday
|
||||
date.setHours(12, 0, 0); // 12:00
|
||||
expect(workingHours.isOpen(date)).toBe(true);
|
||||
});
|
||||
|
||||
it('returns false before opening hours', () => {
|
||||
const workingHours = new WorkingHours(workingHoursData);
|
||||
const date = new Date(2025, 0, 6); // Monday
|
||||
date.setHours(8, 0, 0); // 8:00
|
||||
expect(workingHours.isOpen(date)).toBe(false);
|
||||
});
|
||||
|
||||
it('returns false after closing hours', () => {
|
||||
const workingHours = new WorkingHours(workingHoursData);
|
||||
const date = new Date(2025, 0, 6); // Monday
|
||||
date.setHours(18, 0, 0); // 18:00
|
||||
expect(workingHours.isOpen(date)).toBe(false);
|
||||
});
|
||||
|
||||
it('returns false on closed days', () => {
|
||||
const workingHours = new WorkingHours(workingHoursData);
|
||||
const date = new Date(2025, 0, 4); // Saturday
|
||||
date.setHours(12, 0, 0); // 12:00
|
||||
expect(workingHours.isOpen(date)).toBe(false);
|
||||
});
|
||||
|
||||
it('returns true for openAllDay', () => {
|
||||
const customData = [...workingHoursData];
|
||||
customData[1] = { ...customData[1], openAllDay: true };
|
||||
const workingHours = new WorkingHours(customData);
|
||||
const date = new Date(2025, 0, 6); // Monday
|
||||
date.setHours(22, 0, 0);
|
||||
expect(workingHours.isOpen(date)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('openInMinutes', () => {
|
||||
it('returns 0 when currently open', () => {
|
||||
const workingHours = new WorkingHours(workingHoursData);
|
||||
const date = new Date(2025, 0, 6); // Monday
|
||||
date.setHours(12, 0, 0); // 12:00
|
||||
expect(workingHours.openInMinutes(date)).toBe(0);
|
||||
});
|
||||
|
||||
it('calculates minutes until opening same day', () => {
|
||||
const workingHours = new WorkingHours(workingHoursData);
|
||||
const date = new Date(2025, 0, 6); // Monday
|
||||
date.setHours(8, 0, 0); // 8:00
|
||||
expect(workingHours.openInMinutes(date)).toBe(60); // 60 minutes until 9:00
|
||||
});
|
||||
|
||||
it('calculates minutes until opening next day', () => {
|
||||
const workingHours = new WorkingHours(workingHoursData);
|
||||
const date = new Date(2025, 0, 6); // Monday
|
||||
date.setHours(23, 0, 0); // 23:00
|
||||
// Tuesday opens at 9:00, so it's 10 hours (600 minutes) away
|
||||
expect(workingHours.openInMinutes(date)).toBe(600);
|
||||
});
|
||||
|
||||
it('handles closed days correctly', () => {
|
||||
const workingHours = new WorkingHours(workingHoursData);
|
||||
const date = new Date(2025, 0, 4); // Saturday
|
||||
date.setHours(12, 0, 0); // 12:00
|
||||
// Should find Sunday's opening at 10:00
|
||||
const expectedMinutes = (24 - 12) * 60 + 10 * 60; // Remaining hours today + hours until opening tomorrow
|
||||
expect(workingHours.openInMinutes(date)).toBe(expectedMinutes);
|
||||
});
|
||||
|
||||
it('returns -1 if no opening time found within a week', () => {
|
||||
const closedWeek = workingHoursData.map(day => ({
|
||||
...day,
|
||||
closedAllDay: true,
|
||||
}));
|
||||
const workingHours = new WorkingHours(closedWeek);
|
||||
const date = new Date(2025, 0, 6); // Monday
|
||||
date.setHours(12, 0, 0); // 12:00
|
||||
expect(workingHours.openInMinutes(date)).toBe(-1);
|
||||
});
|
||||
|
||||
it('returns -1 if no working hours are provided', () => {
|
||||
const workingHours = new WorkingHours([]);
|
||||
const date = new Date(2025, 0, 6); // Monday
|
||||
date.setHours(12, 0, 0); // 12:00
|
||||
expect(workingHours.openInMinutes(date)).toBe(-1);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user