diff --git a/STATUS.md b/STATUS.md index 5fcd1bf..f87c697 100644 --- a/STATUS.md +++ b/STATUS.md @@ -68,7 +68,7 @@ - [x] `addBusinessDays` - [x] `addDays` - [x] `subDays` -- [ ] `differenceInBusinessDays` +- [x] `differenceInBusinessDays` - [x] `differenceInDays` - [x] `differenceInCalendarDays` - [x] `startOfDay` diff --git a/__tests__/differenceInBusinessDays_test.re b/__tests__/differenceInBusinessDays_test.re new file mode 100644 index 0000000..f6d43aa --- /dev/null +++ b/__tests__/differenceInBusinessDays_test.re @@ -0,0 +1,72 @@ +open Jest; + +open Js.Date; + +describe("differenceInBusinessDays", () => { + open ExpectJs; + + test("returns 0 if the given dates are the same", () => { + let fstDate = makeWithYMD(~year=2018., ~month=0., ~date=1., ()); + let sndDate = makeWithYMD(~year=2018., ~month=0., ~date=1., ()); + + fstDate + |> ReDate.differenceInBusinessDays(sndDate) + |> expect + |> toEqual(0); + }); + + test( + "returns the number of business days between the given dates, excluding weekends", + () => { + let fstDate = makeWithYMD(~year=2018., ~month=0., ~date=10., ()); + let sndDate = makeWithYMD(~year=2018., ~month=0., ~date=2., ()); + + fstDate + |> ReDate.differenceInBusinessDays(sndDate) + |> expect + |> toEqual(6); + }, + ); + + test( + "returns a negative number if the time value of the first date is smaller", + () => { + let fstDate = makeWithYMD(~year=2018., ~month=0., ~date=2., ()); + let sndDate = makeWithYMD(~year=2018., ~month=0., ~date=10., ()); + + fstDate + |> ReDate.differenceInBusinessDays(sndDate) + |> expect + |> toEqual(-6); + }); + + test("returns a correct number when the first date falls on a weekend", () => { + let fstDate = makeWithYMD(~year=2019., ~month=6., ~date=20., ()); + let sndDate = makeWithYMD(~year=2019., ~month=6., ~date=18., ()); + + fstDate + |> ReDate.differenceInBusinessDays(sndDate) + |> expect + |> toEqual(2); + }); + + test("returns a correct number when the second date falls on a weekend", () => { + let fstDate = makeWithYMD(~year=2019., ~month=6., ~date=23., ()); + let sndDate = makeWithYMD(~year=2019., ~month=6., ~date=20., ()); + + fstDate + |> ReDate.differenceInBusinessDays(sndDate) + |> expect + |> toEqual(1); + }); + + test("returns a correct number when both dates fall on a weekend", () => { + let fstDate = makeWithYMD(~year=2019., ~month=6., ~date=28., ()); + let sndDate = makeWithYMD(~year=2019., ~month=6., ~date=20., ()); + + fstDate + |> ReDate.differenceInBusinessDays(sndDate) + |> expect + |> toEqual(5); + }); +}); diff --git a/docs/day.md b/docs/day.md index a3246a0..8d38707 100644 --- a/docs/day.md +++ b/docs/day.md @@ -86,6 +86,19 @@ let sndDate = Js.Date.makeWithYMDHMS(~year=2018., ~month=0., ~date=2., ~hours=20 fstDate |> ReDate.differenceInDays(sndDate); ``` +#### differenceInBusinessDays + +> Get the number of full day periods between the given dates. + +`let differenceInBusinessDays: (Js.Date.t, Js.Date.t) => int` + +```reason +let fstDate = Js.Date.makeWithYMD(~year=2018., ~month=0., ~date=10., ()); +let sndDate = Js.Date.makeWithYMD(~year=2018., ~month=0., ~date=2., ()); + +fstDate |> ReDate.differenceInBusinessDays(sndDate); +``` + #### getDayOfYear > Get the day of the year of the given date. diff --git a/src/ReDate.re b/src/ReDate.re index 21f6498..3868132 100644 --- a/src/ReDate.re +++ b/src/ReDate.re @@ -23,6 +23,7 @@ type differenceIn = | Hours | Minutes | Days + | BusinessDays(helper) | CalendarDays(helper) | Weeks | CalendarWeeks(helper) @@ -151,6 +152,11 @@ module Internal = { | _ => failwith("error") ); + let is = (day, date) => + date |> Js.Date.getDay === (day |> dayToJs |> float_of_int); + + let isWeekend = date => date |> is(Saturday) || date |> is(Sunday); + let rec differenceIn = (differenceType, snd, fst) => Js.Date.( switch (differenceType) { @@ -163,6 +169,17 @@ module Internal = { ((fst |> getTime) -. (snd |> getTime)) /. (differenceType |> getMillisecondsOf |> float_of_int); diff > 0. ? diff |> Js.Math.floor_int : diff |> Js.Math.ceil_int; + | BusinessDays(startOf) => + let diff = differenceIn(CalendarDays(startOf), snd, fst); + let date = diff < 0 ? fst : snd; + let result = ref(0); + + for (index in 0 to diff |> Js.Math.abs_int |> pred) { + let day = date |> addDays(index); + result := isWeekend(day) ? result^ : result^ |> succ; + }; + + diff < 0 ? - result^ : result^; | Months => let diff = (fst |> getMonth) @@ -256,9 +273,6 @@ module Internal = { ) |> succ; - let is = (day, date) => - date |> Js.Date.getDay === (day |> dayToJs |> float_of_int); - let minOrMaxOfArray = (fn, dates) => Belt.Array.reduce(dates, None, fn |> reduceMinOrMax) |> retrieveMinOrMax; @@ -408,14 +422,14 @@ let roundToNearestMinute = (~nearestTo=1, date) => { let closestTo = (date |> Js.Date.getSeconds) /. 60. |> Js.Math.round; let closestMinute = (date |> Js.Date.getMinutes) +. closestTo; let nearestRoundedMinute = - nearestTo !== 1 ? - ( - (date |> Js.Date.getMinutes) - /. (nearestTo |> float_of_int) - |> Js.Math.round - ) - *. (nearestTo |> float_of_int) : - closestMinute; + nearestTo !== 1 + ? ( + (date |> Js.Date.getMinutes) + /. (nearestTo |> float_of_int) + |> Js.Math.round + ) + *. (nearestTo |> float_of_int) + : closestMinute; Js.Date.( setMinutes(date |> Internal.makeDate, nearestRoundedMinute) @@ -494,6 +508,9 @@ let differenceInCalendarDays = let differenceInDays = Internal.differenceIn(Days); +let differenceInBusinessDays = + Internal.differenceIn(BusinessDays(startOfDay)); + let getDayOfYear = date => date |> differenceInCalendarDays(date |> Internal.startOfYear) |> succ; @@ -547,8 +564,8 @@ let getWeekOfMonth = (~weekStartsOn=Sunday, date) => { let startWeekDay = date |> Internal.startOfMonth |> Js.Date.getDay; let weekStartsOn' = weekStartsOn |> Internal.dayToJs |> float_of_int; let diff = - startWeekDay < weekStartsOn' ? - 7. -. weekStartsOn' +. startWeekDay : startWeekDay -. weekStartsOn'; + startWeekDay < weekStartsOn' + ? 7. -. weekStartsOn' +. startWeekDay : startWeekDay -. weekStartsOn'; ((date |> Js.Date.getDate) +. diff) /. 7. |> Js.Math.ceil_int; }; @@ -579,7 +596,7 @@ let isFriday = Internal.is(Friday); let isSaturday = Internal.is(Saturday); -let isWeekend = date => date |> isSaturday || date |> isSunday; +let isWeekend = Internal.isWeekend; /* ——[Month helpers]——————————— */ diff --git a/src/ReDate.rei b/src/ReDate.rei index 7e293f2..ce8b377 100644 --- a/src/ReDate.rei +++ b/src/ReDate.rei @@ -112,6 +112,8 @@ let differenceInCalendarDays: (Js.Date.t, Js.Date.t) => int; let differenceInDays: (Js.Date.t, Js.Date.t) => int; +let differenceInBusinessDays: (Js.Date.t, Js.Date.t) => int; + let getDayOfYear: Js.Date.t => int; let isSameDay: (Js.Date.t, Js.Date.t) => bool;