summaryrefslogtreecommitdiff
path: root/date-fns/src/getOverlappingDaysInIntervals
diff options
context:
space:
mode:
Diffstat (limited to 'date-fns/src/getOverlappingDaysInIntervals')
-rw-r--r--date-fns/src/getOverlappingDaysInIntervals/index.d.ts4
-rw-r--r--date-fns/src/getOverlappingDaysInIntervals/index.js.flow55
-rw-r--r--date-fns/src/getOverlappingDaysInIntervals/index.ts106
-rw-r--r--date-fns/src/getOverlappingDaysInIntervals/test.ts240
4 files changed, 405 insertions, 0 deletions
diff --git a/date-fns/src/getOverlappingDaysInIntervals/index.d.ts b/date-fns/src/getOverlappingDaysInIntervals/index.d.ts
new file mode 100644
index 0000000..e309ff9
--- /dev/null
+++ b/date-fns/src/getOverlappingDaysInIntervals/index.d.ts
@@ -0,0 +1,4 @@
+// This file is generated automatically by `scripts/build/typings.js`. Please, don't change it.
+
+import { getOverlappingDaysInIntervals } from 'date-fns'
+export default getOverlappingDaysInIntervals
diff --git a/date-fns/src/getOverlappingDaysInIntervals/index.js.flow b/date-fns/src/getOverlappingDaysInIntervals/index.js.flow
new file mode 100644
index 0000000..9c0f272
--- /dev/null
+++ b/date-fns/src/getOverlappingDaysInIntervals/index.js.flow
@@ -0,0 +1,55 @@
+// @flow
+// This file is generated automatically by `scripts/build/typings.js`. Please, don't change it.
+
+export type Interval = {
+ start: Date | number,
+ end: Date | number,
+}
+
+export type Locale = {
+ code?: string,
+ formatDistance?: (...args: Array<any>) => any,
+ formatRelative?: (...args: Array<any>) => any,
+ localize?: {
+ ordinalNumber: (...args: Array<any>) => any,
+ era: (...args: Array<any>) => any,
+ quarter: (...args: Array<any>) => any,
+ month: (...args: Array<any>) => any,
+ day: (...args: Array<any>) => any,
+ dayPeriod: (...args: Array<any>) => any,
+ },
+ formatLong?: {
+ date: (...args: Array<any>) => any,
+ time: (...args: Array<any>) => any,
+ dateTime: (...args: Array<any>) => any,
+ },
+ match?: {
+ ordinalNumber: (...args: Array<any>) => any,
+ era: (...args: Array<any>) => any,
+ quarter: (...args: Array<any>) => any,
+ month: (...args: Array<any>) => any,
+ day: (...args: Array<any>) => any,
+ dayPeriod: (...args: Array<any>) => any,
+ },
+ options?: {
+ weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6,
+ firstWeekContainsDate?: 1 | 2 | 3 | 4 | 5 | 6 | 7,
+ },
+}
+
+export type Duration = {
+ years?: number,
+ months?: number,
+ weeks?: number,
+ days?: number,
+ hours?: number,
+ minutes?: number,
+ seconds?: number,
+}
+
+export type Day = 0 | 1 | 2 | 3 | 4 | 5 | 6
+
+declare module.exports: (
+ intervalLeft: Interval,
+ intervalRight: Interval
+) => number
diff --git a/date-fns/src/getOverlappingDaysInIntervals/index.ts b/date-fns/src/getOverlappingDaysInIntervals/index.ts
new file mode 100644
index 0000000..aa5dc63
--- /dev/null
+++ b/date-fns/src/getOverlappingDaysInIntervals/index.ts
@@ -0,0 +1,106 @@
+import toDate from '../toDate/index'
+import requiredArgs from '../_lib/requiredArgs/index'
+import { Interval } from '../types'
+
+const MILLISECONDS_IN_DAY = 24 * 60 * 60 * 1000
+
+/**
+ * @name getOverlappingDaysInIntervals
+ * @category Interval Helpers
+ * @summary Get the number of days that overlap in two time intervals
+ *
+ * @description
+ * Get the number of days that overlap in two time intervals
+ *
+ * ### v2.0.0 breaking changes:
+ *
+ * - [Changes that are common for the whole library](https://github.com/date-fns/date-fns/blob/master/docs/upgradeGuide.md#Common-Changes).
+ *
+ * - The function was renamed from `getOverlappingDaysInRanges` to `getOverlappingDaysInIntervals`.
+ * This change was made to mirror the use of the word "interval" in standard ISO 8601:2004 terminology:
+ *
+ * ```
+ * 2.1.3
+ * time interval
+ * part of the time axis limited by two instants
+ * ```
+ *
+ * Also, this function now accepts an object with `start` and `end` properties
+ * instead of two arguments as an interval.
+ * This function now throws `RangeError` if the start of the interval is after its end
+ * or if any date in the interval is `Invalid Date`.
+ *
+ * ```javascript
+ * // Before v2.0.0
+ *
+ * getOverlappingDaysInRanges(
+ * new Date(2014, 0, 10), new Date(2014, 0, 20),
+ * new Date(2014, 0, 17), new Date(2014, 0, 21)
+ * )
+ *
+ * // v2.0.0 onward
+ *
+ * getOverlappingDaysInIntervals(
+ * { start: new Date(2014, 0, 10), end: new Date(2014, 0, 20) },
+ * { start: new Date(2014, 0, 17), end: new Date(2014, 0, 21) }
+ * )
+ * ```
+ *
+ * @param {Interval} intervalLeft - the first interval to compare. See [Interval]{@link docs/Interval}
+ * @param {Interval} intervalRight - the second interval to compare. See [Interval]{@link docs/Interval}
+ * @returns {Number} the number of days that overlap in two time intervals
+ * @throws {TypeError} 2 arguments required
+ * @throws {RangeError} The start of an interval cannot be after its end
+ * @throws {RangeError} Date in interval cannot be `Invalid Date`
+ *
+ * @example
+ * // For overlapping time intervals adds 1 for each started overlapping day:
+ * getOverlappingDaysInIntervals(
+ * { start: new Date(2014, 0, 10), end: new Date(2014, 0, 20) },
+ * { start: new Date(2014, 0, 17), end: new Date(2014, 0, 21) }
+ * )
+ * //=> 3
+ *
+ * @example
+ * // For non-overlapping time intervals returns 0:
+ * getOverlappingDaysInIntervals(
+ * { start: new Date(2014, 0, 10), end: new Date(2014, 0, 20) },
+ * { start: new Date(2014, 0, 21), end: new Date(2014, 0, 22) }
+ * )
+ * //=> 0
+ */
+
+export default function getOverlappingDaysInIntervals(
+ dirtyIntervalLeft: Interval,
+ dirtyIntervalRight: Interval
+): number {
+ requiredArgs(2, arguments)
+
+ const intervalLeft = dirtyIntervalLeft || {}
+ const intervalRight = dirtyIntervalRight || {}
+ const leftStartTime = toDate(intervalLeft.start).getTime()
+ const leftEndTime = toDate(intervalLeft.end).getTime()
+ const rightStartTime = toDate(intervalRight.start).getTime()
+ const rightEndTime = toDate(intervalRight.end).getTime()
+
+ // Throw an exception if start date is after end date or if any date is `Invalid Date`
+ if (!(leftStartTime <= leftEndTime && rightStartTime <= rightEndTime)) {
+ throw new RangeError('Invalid interval')
+ }
+
+ const isOverlapping =
+ leftStartTime < rightEndTime && rightStartTime < leftEndTime
+
+ if (!isOverlapping) {
+ return 0
+ }
+
+ const overlapStartDate =
+ rightStartTime < leftStartTime ? leftStartTime : rightStartTime
+
+ const overlapEndDate = rightEndTime > leftEndTime ? leftEndTime : rightEndTime
+
+ const differenceInMs = overlapEndDate - overlapStartDate
+
+ return Math.ceil(differenceInMs / MILLISECONDS_IN_DAY)
+}
diff --git a/date-fns/src/getOverlappingDaysInIntervals/test.ts b/date-fns/src/getOverlappingDaysInIntervals/test.ts
new file mode 100644
index 0000000..b39672f
--- /dev/null
+++ b/date-fns/src/getOverlappingDaysInIntervals/test.ts
@@ -0,0 +1,240 @@
+// @flow
+/* eslint-env mocha */
+
+import assert from 'power-assert'
+import getOverlappingDaysInIntervals from '.'
+
+describe('getOverlappingDaysInIntervals', function() {
+ const initialIntervalStart = new Date(2016, 10, 10, 13, 0, 0)
+ const initialIntervalEnd = new Date(2016, 11, 3, 15, 0, 0)
+
+ describe("when the time intervals don't overlap", function() {
+ it('returns 0 for a valid non overlapping interval before another interval', function() {
+ const earlierIntervalStart = new Date(2016, 9, 25)
+ const earlierIntervalEnd = new Date(2016, 10, 9)
+
+ const numOverlappingDays = getOverlappingDaysInIntervals(
+ { start: initialIntervalStart, end: initialIntervalEnd },
+ { start: earlierIntervalStart, end: earlierIntervalEnd }
+ )
+ assert(numOverlappingDays === 0)
+ })
+
+ it('returns 0 for a valid non overlapping interval after another interval', function() {
+ const laterIntervalStart = new Date(2016, 11, 4)
+ const laterIntervalEnd = new Date(2016, 11, 9)
+
+ const numOverlappingDays = getOverlappingDaysInIntervals(
+ { start: initialIntervalStart, end: initialIntervalEnd },
+ { start: laterIntervalStart, end: laterIntervalEnd }
+ )
+ assert(numOverlappingDays === 0)
+ })
+
+ it('returns 0 for a non overlapping same-day interval', function() {
+ const sameDayIntervalStart = new Date(2016, 11, 4, 9, 0, 0)
+ const sameDayIntervalEnd = new Date(2016, 11, 4, 18, 0, 0)
+
+ const numOverlappingDays = getOverlappingDaysInIntervals(
+ { start: initialIntervalStart, end: initialIntervalEnd },
+ { start: sameDayIntervalStart, end: sameDayIntervalEnd }
+ )
+ assert(numOverlappingDays === 0)
+ })
+
+ it('returns 0 for an interval differing by a few hours', function() {
+ const oneDayOverlappingIntervalStart = new Date(2016, 11, 3, 18, 0, 0)
+ const oneDayOverlappingIntervalEnd = new Date(2016, 11, 14, 13, 0, 0)
+
+ const numOverlappingDays = getOverlappingDaysInIntervals(
+ { start: initialIntervalStart, end: initialIntervalEnd },
+ {
+ start: oneDayOverlappingIntervalStart,
+ end: oneDayOverlappingIntervalEnd
+ }
+ )
+ assert(numOverlappingDays === 0)
+ })
+
+ it("returns 0 for an interval with the same startDateTime as the initial time intervals's endDateTime", function() {
+ const oneDayOverlapIntervalStart = new Date(2016, 11, 3, 15, 0, 0)
+ const oneDayOverlapIntervalEnd = new Date(2016, 11, 14, 13, 0, 0)
+
+ const numOverlappingDays = getOverlappingDaysInIntervals(
+ { start: initialIntervalStart, end: initialIntervalEnd },
+ { start: oneDayOverlapIntervalStart, end: oneDayOverlapIntervalEnd }
+ )
+ assert(numOverlappingDays === 0)
+ })
+
+ it("returns 0 for an interval with the same endDateTime as the initial time interval's startDateTime", function() {
+ const oneDayOverlapIntervalStart = new Date(2016, 10, 3, 15, 0, 0)
+ const oneDayOverlapIntervalEnd = new Date(2016, 10, 10, 13, 0, 0)
+
+ const numOverlappingDays = getOverlappingDaysInIntervals(
+ { start: initialIntervalStart, end: initialIntervalEnd },
+ { start: oneDayOverlapIntervalStart, end: oneDayOverlapIntervalEnd }
+ )
+ assert(numOverlappingDays === 0)
+ })
+ })
+
+ describe('when the time intervals overlap', function() {
+ it('rounds up the result to include each started overlapping day', function() {
+ const includedIntervalStart = new Date(2016, 10, 14, 9, 0, 0)
+ const includedIntervalEnd = new Date(2016, 10, 15, 18, 0, 0)
+
+ const numOverlappingDays = getOverlappingDaysInIntervals(
+ { start: initialIntervalStart, end: initialIntervalEnd },
+ { start: includedIntervalStart, end: includedIntervalEnd }
+ )
+ assert(numOverlappingDays === 2)
+ })
+
+ it('returns the correct value for an interval included within another interval', function() {
+ const includedIntervalStart = new Date(2016, 10, 14)
+ const includedIntervalEnd = new Date(2016, 10, 15)
+
+ const numOverlappingDays = getOverlappingDaysInIntervals(
+ { start: initialIntervalStart, end: initialIntervalEnd },
+ { start: includedIntervalStart, end: includedIntervalEnd }
+ )
+ assert(numOverlappingDays === 1)
+ })
+
+ it('returns the correct value for an interval overlapping at the end', function() {
+ const endOverlappingIntervalStart = new Date(2016, 10, 5)
+ const endOverlappingIntervalEnd = new Date(2016, 10, 14)
+
+ const numOverlappingDays = getOverlappingDaysInIntervals(
+ { start: initialIntervalStart, end: initialIntervalEnd },
+ { start: endOverlappingIntervalStart, end: endOverlappingIntervalEnd }
+ )
+ assert(numOverlappingDays === 4)
+ })
+
+ it('returns the correct value for an interval overlapping at the beginning', function() {
+ const startOverlappingIntervalStart = new Date(2016, 10, 20)
+ const startOverlappingIntervalEnd = new Date(2016, 11, 14)
+
+ const numOverlappingDays = getOverlappingDaysInIntervals(
+ { start: initialIntervalStart, end: initialIntervalEnd },
+ {
+ start: startOverlappingIntervalStart,
+ end: startOverlappingIntervalEnd
+ }
+ )
+ assert(numOverlappingDays === 14)
+ })
+
+ it('returns the correct value for an interval including another interval', function() {
+ const includingIntervalStart = new Date(2016, 10, 5)
+ const includingIntervalEnd = new Date(2016, 11, 15)
+
+ const numOverlappingDays = getOverlappingDaysInIntervals(
+ { start: initialIntervalStart, end: initialIntervalEnd },
+ { start: includingIntervalStart, end: includingIntervalEnd }
+ )
+ assert(numOverlappingDays === 24)
+ })
+ })
+
+ it('accepts a timestamp', function() {
+ const initialIntervalStart = new Date(2016, 10, 10, 13, 0, 0).getTime()
+ const initialIntervalEnd = new Date(2016, 11, 3, 15, 0, 0).getTime()
+
+ const endOverlappingIntervalStart = new Date(2016, 10, 5).getTime()
+ const endOverlappingIntervalEnd = new Date(2016, 10, 14).getTime()
+
+ const numOverlappingDays = getOverlappingDaysInIntervals(
+ { start: initialIntervalStart, end: initialIntervalEnd },
+ { start: endOverlappingIntervalStart, end: endOverlappingIntervalEnd }
+ )
+ assert(numOverlappingDays === 4)
+ })
+
+ it('throws an exception if the start date of the initial time interval is after the end date', function() {
+ const block = getOverlappingDaysInIntervals.bind(
+ null,
+ { start: new Date(2016, 10, 7), end: new Date(2016, 10, 3) },
+ { start: new Date(2016, 10, 5), end: new Date(2016, 10, 15) }
+ )
+ assert.throws(block, RangeError)
+ })
+
+ it('throws an exception if the start date of the compared time interval is after the end date', function() {
+ const block = getOverlappingDaysInIntervals.bind(
+ null,
+ { start: new Date(2016, 10, 3), end: new Date(2016, 10, 7) },
+ { start: new Date(2016, 10, 15), end: new Date(2016, 10, 5) }
+ )
+ assert.throws(block, RangeError)
+ })
+
+ it('throws an exception if the initial interval is undefined', function() {
+ const block = getOverlappingDaysInIntervals.bind(
+ null,
+ // $ExpectedMistake
+ //@ts-expect-error
+ undefined,
+ { start: new Date(2016, 10, 5), end: new Date(2016, 10, 15) }
+ )
+ assert.throws(block, RangeError)
+ })
+
+ it('throws an exception if the compared interval is undefined', function() {
+ const block = getOverlappingDaysInIntervals.bind(
+ null,
+ { start: new Date(2016, 10, 3), end: new Date(2016, 10, 7) },
+ // $ExpectedMistake
+ //@ts-expect-error
+ undefined
+ )
+ assert.throws(block, RangeError)
+ })
+
+ describe('one of the dates is `Invalid Date`', function() {
+ it('throws an exception if the start date of the initial time interval is `Invalid Date`', function() {
+ const block = getOverlappingDaysInIntervals.bind(
+ null,
+ { start: new Date(NaN), end: new Date(2016, 10, 3) },
+ { start: new Date(2016, 10, 5), end: new Date(2016, 10, 15) }
+ )
+ assert.throws(block, RangeError)
+ })
+
+ it('throws an exception if the end date of the initial time interval is `Invalid Date`', function() {
+ const block = getOverlappingDaysInIntervals.bind(
+ null,
+ { start: new Date(2016, 10, 3), end: new Date(NaN) },
+ { start: new Date(2016, 10, 5), end: new Date(2016, 10, 15) }
+ )
+ assert.throws(block, RangeError)
+ })
+
+ it('throws an exception if the start date of the compared time interval is `Invalid Date`', function() {
+ const block = getOverlappingDaysInIntervals.bind(
+ null,
+ { start: new Date(2016, 10, 3), end: new Date(2016, 10, 7) },
+ { start: new Date(NaN), end: new Date(2016, 10, 5) }
+ )
+ assert.throws(block, RangeError)
+ })
+
+ it('throws an exception if the end date of the compared time interval is `Invalid Date`', function() {
+ const block = getOverlappingDaysInIntervals.bind(
+ null,
+ { start: new Date(2016, 10, 3), end: new Date(2016, 10, 7) },
+ { start: new Date(2016, 10, 5), end: new Date(NaN) }
+ )
+ assert.throws(block, RangeError)
+ })
+ })
+
+ it('throws TypeError exception if passed less than 2 arguments', function() {
+ assert.throws(getOverlappingDaysInIntervals.bind(null), TypeError)
+ // $ExpectedMistake
+ //@ts-expect-error
+ assert.throws(getOverlappingDaysInIntervals.bind(null, 1), TypeError)
+ })
+})