-
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
16bf961
commit e4900d5
Showing
8 changed files
with
164 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,2 @@ | ||
const isWorkingDay = (date: Date) => date.getDay() >= 1 && date.getDay() <= 5; | ||
|
||
export const isOfficeHours = (date: Date): boolean => | ||
isWorkingDay(date) && date.getHours() >= 9 && date.getHours() < 17; | ||
export { isOfficeHours } from './is-office-hours'; | ||
export { outsideOfficeHours } from './outside-office-hours'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
const isWorkingDay = (day: number) => day >= 1 && day <= 5; | ||
|
||
/** @member hour @member minute @member second */ | ||
export type Time = [number, number, number]; | ||
|
||
export const stintStart: Time = [9, 0, 0]; | ||
export const stintEnd: Time = [16, 59, 59]; | ||
|
||
/** | ||
* Returns `true` if the provided `Date` falls within Monday to Friday 9:00am to | ||
* 4:59pm. | ||
*/ | ||
export const isOfficeHours = (date: Date): boolean => | ||
isWorkingDay(date.getDay()) && | ||
date.getHours() >= stintStart[0] && | ||
date.getHours() <= stintEnd[0]; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export const pad = (n: number) => (n < 10 ? `0${n}` : `${n}`); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
export const range = (floor: number, ceiling: number) => { | ||
const array = []; | ||
while (floor <= ceiling) { | ||
array.push(floor++); | ||
} | ||
return array; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import 'expect-more-jest'; | ||
import { outsideOfficeHours } from './'; | ||
import { pad } from './lib/pad'; | ||
import { range } from './lib/range'; | ||
|
||
describe('Dates during office hours', () => { | ||
range(9, 16).forEach((hour) => { | ||
range(0, 59).forEach((minute) => { | ||
const eveStart = new Date('2019-03-04T18:00:00.000Z'); | ||
const eveEnd = new Date('2019-03-04T22:59:59.000Z'); | ||
const iso8601 = `2019-03-04T${pad(hour)}:${pad(minute)}:00.000Z`; | ||
const date = new Date(iso8601); | ||
const epoch = date.getTime(); | ||
const nextDate = outsideOfficeHours(date); | ||
|
||
it(`Moves ${iso8601}`, () => { | ||
expect(nextDate.getTime()).toBeLessThanOrEqual(eveEnd.getTime()); | ||
expect(nextDate.getTime()).toBeGreaterThanOrEqual(eveStart.getTime()); | ||
}); | ||
|
||
it('Does not mutate the original Date', () => { | ||
expect(date.getTime()).toEqual(epoch); | ||
}); | ||
}); | ||
}); | ||
}); | ||
|
||
describe('Dates outside office hours', () => { | ||
range(0, 8) | ||
.concat(range(17, 23)) | ||
.forEach((hour) => { | ||
range(0, 59).forEach((minute) => { | ||
const iso8601 = `2019-03-04T${pad(hour)}:${pad(minute)}:00.000Z`; | ||
const date = new Date(iso8601); | ||
|
||
it(`Leaves ${iso8601} untouched`, () => { | ||
expect(outsideOfficeHours(date)).toBe(date); | ||
}); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import { isOfficeHours, stintEnd, stintStart, Time } from './is-office-hours'; | ||
|
||
const getPercentOfPart = (part: number, whole: number) => (part / whole) * 100; | ||
|
||
const getPartFromPercent = (percent: number, whole: number) => | ||
whole * (percent / 100); | ||
|
||
const getMsSinceMidnight = ([hours, minutes, seconds]: Time) => { | ||
const date = new Date(0); | ||
date.setHours(hours); | ||
date.setMinutes(minutes); | ||
date.setSeconds(seconds); | ||
return date.getTime(); | ||
}; | ||
|
||
const getTimeFromDate = (date: Date): Time => [ | ||
date.getHours(), | ||
date.getMinutes(), | ||
date.getSeconds() | ||
]; | ||
|
||
const getDateWithTime = (date: Date, [hours, minutes, seconds]: Time) => { | ||
const nextDate = new Date(date.getTime()); | ||
nextDate.setHours(hours); | ||
nextDate.setMinutes(minutes); | ||
nextDate.setSeconds(seconds); | ||
return nextDate; | ||
}; | ||
|
||
const getMsBetween = (start: Time, end: Time) => | ||
getMsSinceMidnight(end) - getMsSinceMidnight(start); | ||
|
||
const eveningStart: Time = [18, 0, 0]; | ||
const eveningEnd: Time = [22, 59, 59]; | ||
const eveningLength = getMsBetween(eveningStart, eveningEnd); | ||
const stintLength = getMsBetween(stintStart, stintEnd); | ||
const eveningStartMs = getMsSinceMidnight(eveningStart); | ||
const stintStartMs = getMsSinceMidnight(stintStart); | ||
|
||
/** | ||
* If the provided `Date` falls within Office Hours, a new `Date` is returned | ||
* with the time adjusted to fall outside Office Hours. If the provided `Date` | ||
* falls outside Office Hours, it is returned unchanged. | ||
* | ||
* The provided `Date` is never mutated. | ||
*/ | ||
export const outsideOfficeHours = (date: Date): Date => { | ||
if (isOfficeHours(date)) { | ||
const eventTime = getTimeFromDate(date); | ||
const eventStartMs = getMsSinceMidnight(eventTime); | ||
const eventDistance = eventStartMs - stintStartMs; | ||
if (eventDistance === 0) { | ||
return getDateWithTime(date, eveningStart); | ||
} | ||
const eventPercent = getPercentOfPart(eventDistance, stintLength); | ||
const eveningDistance = getPartFromPercent(eventPercent, eveningLength); | ||
const nextDate = new Date(eveningDistance + eveningStartMs); | ||
return getDateWithTime(date, getTimeFromDate(nextDate)); | ||
} | ||
return date; | ||
}; |