UNPKG

business-time-calculator

Version:

Business Time Calculator is a simple tool to calculate the business time between two dates.

619 lines (618 loc) 22.7 kB
import { DateTime } from 'luxon'; import { BusinessTime } from './BusinessTime.js'; import { describe, test } from 'node:test'; import * as assert from 'node:assert/strict'; const testEachComputeTime = (testCases, businessTimeFunctionName) => { for (const { start, end, businessTimezone, businessHours, businessDays, holidays, expected, } of testCases) { const startDatetime = DateTime.fromISO(start); if (!startDatetime.isValid) { throw new Error(`Invalid start datetime: ${start}`); } const endDatetime = DateTime.fromISO(end); if (!endDatetime.isValid) { throw new Error(`Invalid end datetime: ${end}`); } const businessTime = new BusinessTime({ businessTimezone, businessHours, businessDays, holidays, }); assert.deepStrictEqual( // @ts-ignore businessTime[businessTimeFunctionName]({ start: startDatetime, end: endDatetime, }), expected); } }; const testEachMoveDateInBusinessTime = (testCases) => { for (const { businessTimezone, businessHours, businessDays, holidays, datetime, moveBehind, expected, } of testCases) { const businessTime = new BusinessTime({ businessTimezone, businessHours, businessDays, holidays, }); assert.deepStrictEqual(businessTime ._moveDateInBusinessTime({ datetime: DateTime.fromISO(datetime), moveBehind, }) .toISO(), expected); } }; const testEachIsBusinessDay = (testCases) => { for (const { datetime, businessTimezone, businessHours, businessDays, holidays, expected, } of testCases) { const datetimeObj = DateTime.fromISO(datetime); if (!datetimeObj.isValid) { throw new Error(`Invalid datetime: ${datetime}`); } const businessTime = new BusinessTime({ businessTimezone, businessHours, businessDays, holidays, }); assert.deepStrictEqual(businessTime.isBusinessDay(datetimeObj), expected); } }; const testEachAddBusinessSecondsToDate = (testCases) => { for (const { seconds, businessTimezone, businessHours, businessDays, holidays, datetime, expected, } of testCases) { const datetimeObj = DateTime.fromISO(datetime); if (!datetimeObj.isValid) { throw new Error(`Invalid datetime: ${datetime}`); } const businessTime = new BusinessTime({ businessTimezone, businessHours, businessDays, holidays, }); assert.deepStrictEqual(businessTime .addBusinessSecondsToDate({ datetime: datetimeObj, seconds }) .toISO(), expected); } }; const testEachRemoveBusinessSecondsToDate = (testCases) => { for (const { seconds, businessTimezone, businessHours, businessDays, holidays, datetime, expected, } of testCases) { const datetimeObj = DateTime.fromISO(datetime); if (!datetimeObj.isValid) { throw new Error(`Invalid datetime: ${datetime}`); } const businessTime = new BusinessTime({ businessTimezone, businessHours, businessDays, holidays, }); assert.deepStrictEqual(businessTime .removeBusinessSecondsFromDate({ datetime: datetimeObj, seconds }) .toISO(), expected); } }; describe('BusinessTime', () => { test('compute business days', () => { testEachComputeTime([ { businessTimezone: 'Europe/Rome', businessDays: [ 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', ], businessHours: [10, 19], holidays: [], start: '2020-12-28T09:00:00.000+01:00', end: '2020-12-29T23:00:00.000+01:00', expected: 2, }, ], 'computeBusinessDaysInInterval'); }); test('compute business hours', () => { testEachComputeTime([ { businessTimezone: 'Europe/Rome', businessDays: [ 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', ], businessHours: [10, 19], holidays: [], start: '2020-12-28T13:45:00.000+01:00', end: '2020-12-28T14:00:00.000+01:00', expected: 0.25, }, // same hour { businessTimezone: 'Europe/Rome', businessDays: [ 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', ], businessHours: [10, 19], holidays: ['25/12', '26/12'], start: '2020-12-25T10:45:00.000+01:00', end: '2020-12-27T10:00:00.000+01:00', expected: 0, }, // holidays { businessTimezone: 'Europe/Rome', businessDays: [ 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', ], businessHours: [10, 19], holidays: [], start: '2020-12-28T14:00:00.000+01:00', end: '2020-12-28T18:30:00.000+01:00', expected: 4.5, }, // same day { businessTimezone: 'Europe/Rome', businessDays: [ 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', ], businessHours: [10, 19], holidays: [], start: '2020-12-18T14:00:00.000+01:00', end: '2020-12-21T14:30:00.000+01:00', expected: 9.5, }, // cross weekend { businessTimezone: 'Europe/Rome', businessDays: [ 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', ], businessHours: [10, 19], holidays: [], start: '2020-12-28T15:00:00.000+01:00', end: '2020-12-28T20:00:00.000+01:00', expected: 4, }, // 4 hours in Rome { businessTimezone: 'America/Los_Angeles', businessDays: [ 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', ], businessHours: [10, 19], holidays: [], start: '2020-12-28T15:00:00.000+01:00', end: '2020-12-28T20:00:00.000+01:00', expected: 1, }, // 1 hour in San Francisco { businessTimezone: 'Europe/Rome', businessDays: [ 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', ], businessHours: [10, 19], holidays: [], start: '2021-01-04T10:00:00.000+01:00', end: '2021-03-01T10:00:00.000+01:00', expected: 360, }, // 8 weeks, 45 hours / week => 360 ], 'computeBusinessHoursInInterval'); }); test('compute business minutes', () => { testEachComputeTime([ { businessTimezone: 'Europe/Rome', businessDays: [ 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', ], businessHours: [10, 19], holidays: [], start: '2020-12-28T13:45:00.000+01:00', end: '2020-12-28T14:00:00.000+01:00', expected: 15, }, { businessTimezone: 'Europe/Rome', businessDays: [ 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', ], businessHours: [10, 19], holidays: ['01/01'], start: '2020-12-31T13:45:00.000+01:00', end: '2021-01-04T19:00:00.000+01:00', expected: 855, }, ], 'computeBusinessMinutesInInterval'); }); test('compute business seconds', () => { testEachComputeTime([ { businessTimezone: 'America/Los_Angeles', businessDays: [ 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', ], businessHours: [10, 19], holidays: [], start: '2020-12-28T15:00:00.000+01:00', end: '2020-12-28T20:00:00.000+01:00', expected: 3600, }, // 1 hour in San Francisco ], 'computeBusinessSecondsInInterval'); }); test('compute isBusinessDay', () => { testEachIsBusinessDay([ { businessTimezone: 'Europe/Rome', businessDays: ['monday'], businessHours: [10, 19], holidays: ['25/12', '26/12'], datetime: '2020-12-28T14:00:00.000+01:00', expected: true, }, // monday { businessTimezone: 'Europe/Rome', businessDays: ['monday', 'friday'], businessHours: [10, 19], holidays: ['26/12'], datetime: '2020-12-25T14:00:00.000+01:00', expected: true, }, // Christmas 2020 (friday) configured as business day { businessTimezone: 'Europe/Rome', businessDays: ['monday'], holidays: ['25/12', '26/12'], businessHours: [10, 19], datetime: '2020-12-27T14:00:00.000+01:00', expected: false, }, // tuesday configured as rest day { businessTimezone: 'America/Los_Angeles', businessDays: ['monday'], businessHours: [10, 19], holidays: ['25/12', '26/12'], datetime: '2020-12-28T01:00:00.000+01:00', expected: false, }, // monday in Rome, sunday in San Francisco ]); }); test('compute moveDateInBusinessTime', () => { testEachMoveDateInBusinessTime([ { businessTimezone: 'Europe/Rome', businessDays: ['monday'], businessHours: [13, 15], holidays: [], moveBehind: false, datetime: '2020-12-28T11:00:00.000+01:00', expected: '2020-12-28T13:00:00.000+01:00', }, { businessTimezone: 'Europe/Rome', businessDays: ['monday'], businessHours: [13, 15], holidays: [], moveBehind: false, datetime: '2020-12-28T14:00:00.000+01:00', expected: '2020-12-28T14:00:00.000+01:00', }, { businessTimezone: 'Europe/Rome', businessDays: ['monday'], businessHours: [13, 15], holidays: ['01/01'], moveBehind: false, datetime: '2020-12-28T16:00:00.000+01:00', expected: '2021-01-04T13:00:00.000+01:00', }, { businessTimezone: 'America/Los_Angeles', businessDays: ['monday', 'tuesday', 'wednesday', 'thursday', 'friday'], businessHours: [10, 19], holidays: [], moveBehind: false, datetime: '2021-06-15T00:00:00.000+02:00', // tuesday expected: '2021-06-14T15:00:00.000-07:00', }, { businessTimezone: 'Europe/Rome', businessDays: ['monday', 'tuesday'], businessHours: [13, 15], holidays: [], moveBehind: true, datetime: '2022-04-12T11:00:00.000+02:00', expected: '2022-04-11T15:00:00.000+02:00', }, { businessTimezone: 'Europe/Rome', businessDays: ['monday'], businessHours: [13, 15], holidays: [], moveBehind: true, datetime: '2020-12-28T14:00:00.000+01:00', expected: '2020-12-28T14:00:00.000+01:00', }, { businessTimezone: 'Europe/Rome', businessDays: ['monday'], businessHours: [13, 15], holidays: ['01/01'], moveBehind: true, datetime: '2022-04-11T11:00:00.000+02:00', expected: '2022-04-04T15:00:00.000+02:00', }, { businessTimezone: 'America/Los_Angeles', businessDays: ['monday', 'tuesday', 'wednesday', 'thursday', 'friday'], businessHours: [10, 19], holidays: [], moveBehind: true, datetime: '2021-06-15T00:00:00.000+02:00', // tuesday expected: '2021-06-14T15:00:00.000-07:00', }, ]); }); test('add business seconds to date', () => { testEachAddBusinessSecondsToDate([ { businessTimezone: 'Europe/Rome', businessDays: ['monday', 'tuesday', 'wednesday', 'thursday', 'friday'], businessHours: [10, 19], holidays: [], datetime: '2020-12-28T10:45:00.000+01:00', seconds: 3600 * 10, expected: '2020-12-29T11:45:00.000+01:00', }, { businessTimezone: 'Europe/Rome', businessDays: ['monday', 'tuesday', 'wednesday', 'thursday', 'friday'], businessHours: [10, 19], holidays: [], datetime: '2022-04-04T19:45:00.000+02:00', seconds: 3600 * 10, expected: '2022-04-06T11:00:00.000+02:00', }, { businessTimezone: 'Europe/Rome', businessDays: [ 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday', ], businessHours: [0, 24], holidays: ['01/01'], datetime: '2020-12-28T10:45:00.000+01:00', seconds: 3600 * 96, expected: '2021-01-02T10:45:00.000+01:00', }, { businessTimezone: 'Europe/Rome', businessDays: [ 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday', ], businessHours: [0, 24], holidays: [], datetime: '2020-12-28T10:45:00.000+01:00', seconds: 3600 * 96, expected: '2021-01-01T10:45:00.000+01:00', }, { businessTimezone: 'Europe/Rome', businessDays: [ 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday', ], businessHours: [0, 12], holidays: [], datetime: '2020-12-28T10:45:00.000+01:00', seconds: 3600 * 24, expected: '2020-12-30T10:45:00.000+01:00', }, { businessTimezone: 'Europe/Rome', businessDays: [ 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday', ], businessHours: [0, 12], holidays: [], datetime: '2022-04-11T18:00:00.000+02:00', seconds: 3600 * 24, expected: '2022-04-13T12:00:00.000+02:00', }, { businessTimezone: 'Europe/Rome', businessDays: [ 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday', ], businessHours: [12, 24], holidays: [], datetime: '2022-04-04T10:00:00.000+02:00', seconds: 3600 * 24, expected: '2022-04-06T00:00:00.000+02:00', }, ]); }); test('remove business seconds from date', () => { testEachRemoveBusinessSecondsToDate([ { businessTimezone: 'Europe/Rome', businessDays: ['monday', 'tuesday', 'wednesday', 'thursday', 'friday'], businessHours: [10, 19], holidays: [], datetime: '2020-12-28T10:45:00.000+01:00', seconds: 3600 * 10, expected: '2020-12-24T18:45:00.000+01:00', }, { businessTimezone: 'Europe/Rome', businessDays: ['monday', 'tuesday', 'wednesday', 'thursday', 'friday'], businessHours: [10, 19], holidays: [], datetime: '2022-04-08T19:45:00.000+02:00', seconds: 3600 * 10, expected: '2022-04-07T18:00:00.000+02:00', }, { businessTimezone: 'Europe/Rome', businessDays: [ 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday', ], businessHours: [0, 24], holidays: ['25/12'], datetime: '2020-12-28T10:11:11.111+01:00', seconds: 3600 * 96, // 4 days expected: '2020-12-23T10:11:00.000+01:00', }, { businessTimezone: 'Europe/Rome', businessDays: ['monday', 'tuesday', 'wednesday', 'thursday', 'friday'], // handle weekend businessHours: [0, 24], holidays: [], datetime: '2022-04-11T12:00:00.000+02:00', seconds: 3600 * 48, // 2 days expected: '2022-04-07T12:00:00.000+02:00', }, { businessTimezone: 'Europe/Rome', businessDays: ['monday', 'tuesday', 'wednesday', 'thursday', 'friday'], // handle weekend businessHours: [1, 24], holidays: [], datetime: '2022-04-11T12:00:00.000+02:00', seconds: 3600 * 48, // 2 days expected: '2022-04-07T10:00:00.000+02:00', }, { businessTimezone: 'Europe/Rome', businessDays: [ 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday', ], businessHours: [0, 12], holidays: [], datetime: '2022-04-08T10:45:00.000+02:00', seconds: 3600 * 24, expected: '2022-04-06T10:45:00.000+02:00', }, { businessTimezone: 'Europe/Rome', businessDays: [ 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday', ], businessHours: [0, 12], holidays: [], datetime: '2022-04-08T18:00:00.000+02:00', seconds: 3600 * 24, expected: '2022-04-07T00:00:00.000+02:00', }, { businessTimezone: 'Europe/Rome', businessDays: [ 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday', ], businessHours: [12, 24], holidays: [], datetime: '2022-04-08T10:00:00.000+02:00', seconds: 3600 * 24, expected: '2022-04-06T12:00:00.000+02:00', }, { businessTimezone: 'Europe/Rome', businessDays: ['monday', 'tuesday', 'wednesday', 'thursday', 'friday'], // handle weekend businessHours: [12, 24], holidays: [], datetime: '2022-04-11T10:00:00.000+02:00', seconds: 3600 * 24, expected: '2022-04-07T12:00:00.000+02:00', }, ]); }); test('compute working hours', () => { assert.strictEqual(BusinessTime.computeWorkingHours(10, 19), 9); assert.strictEqual(BusinessTime.computeWorkingHours(0, 24), 24); assert.strictEqual(BusinessTime.computeWorkingHours(18, 3), 9); assert.strictEqual(BusinessTime.computeWorkingHours(22, 0), 2); }); });