From 5304dcdc56fbe225d8a7c46b95845d2dfb9b2031 Mon Sep 17 00:00:00 2001 From: Liam Tait Date: Fri, 23 Jul 2021 17:50:33 +1200 Subject: [PATCH] Add clamp function (#2498) Added a new `clamp` function that allows to bound a date to an interval. --- src/clamp/index.ts | 37 +++++++++++++++++++++++++++++++++++++ src/clamp/test.ts | 43 +++++++++++++++++++++++++++++++++++++++++++ src/index.js | 1 + 3 files changed, 81 insertions(+) create mode 100644 src/clamp/index.ts create mode 100644 src/clamp/test.ts diff --git a/src/clamp/index.ts b/src/clamp/index.ts new file mode 100644 index 0000000000..dec410b8fc --- /dev/null +++ b/src/clamp/index.ts @@ -0,0 +1,37 @@ +import max from '../max' +import min from '../min' +import requiredArgs from '../_lib/requiredArgs/index' + +/** + * @name clamp + * @category Interval Helpers + * @summary Return a date bounded by the start and the end of the given interval + * + * @description + * Clamps a date to the lower bound with the start of the interval and the upper + * bound with the end of the interval. + * + * - When the date is less than the start of the interval, the start is returned. + * - When the date is greater than the end of the interval, the end is returned. + * - Otherwise the date is returned. + * + * @example + * // What is Mar, 21, 2021 bounded to an interval starting at Mar, 22, 2021 and ending at Apr, 01, 2021 + * const result = clamp(new Date(2021, 2, 21), { + * start: new Date(2021, 2, 22), + * end: new Date(2021, 3, 1), + * }) + * //=> Mon Mar 22 2021 00:00:00 + * + * @param {Date | Number} date - the date to be bounded + * @param {Interval} interval - the interval to bound to + * @returns {Date} the date bounded by the start and the end of the interval + * @throws {TypeError} 2 arguments required + */ +export default function clamp( + date: Date | number, + { start, end }: Interval +): Date { + requiredArgs(2, arguments) + return min([max([date, start]), end]) +} diff --git a/src/clamp/test.ts b/src/clamp/test.ts new file mode 100644 index 0000000000..66376c1bb4 --- /dev/null +++ b/src/clamp/test.ts @@ -0,0 +1,43 @@ +import assert from 'assert' +import clamp from '.' + +describe('clamp', () => { + it('accepts timestamps', () => { + const start = new Date(2000, 1, 1).getTime() + const date = new Date(2000, 1, 2).getTime() + const end = new Date(2000, 1, 3).getTime() + const result = clamp(date, { start, end }) + assert.deepStrictEqual(result, new Date(2000, 1, 2)) + }) + + it('returns the start date when the date is less than start', () => { + const start = new Date(2001, 1, 1) + const date = new Date(2000, 1, 1) + const end = new Date(2020, 1, 1) + const result = clamp(date, { start, end }) + assert.deepStrictEqual(result, new Date(2001, 1, 1)) + }) + + it('returns the end date when the date is greater than the end date', () => { + const start = new Date(2000, 1, 1) + const date = new Date(2003, 1, 1) + const end = new Date(2001, 1, 1) + const result = clamp(date, { start, end }) + assert.deepStrictEqual(result, new Date(2001, 1, 1)) + }) + + it('returns the date when the date is within the bound of start and end', () => { + const start = new Date(2000, 1, 1) + const date = new Date(2001, 1, 1) + const end = new Date(2003, 1, 1) + const result = clamp(date, { start, end }) + assert.deepStrictEqual(result, new Date(2001, 1, 1)) + }) + + it('throws TypeError exception if passed less than 2 arguments', () => { + // @ts-expect-error + assert.throws(clamp.bind(null), TypeError) + // @ts-expect-error + assert.throws(clamp.bind(null, 1), TypeError) + }) +}) diff --git a/src/index.js b/src/index.js index 45095f8961..bbf7fb1c4f 100644 --- a/src/index.js +++ b/src/index.js @@ -13,6 +13,7 @@ export { default as addSeconds } from './addSeconds/index' export { default as addWeeks } from './addWeeks/index' export { default as addYears } from './addYears/index' export { default as areIntervalsOverlapping } from './areIntervalsOverlapping/index' +export { default as clamp } from './clamp/index' export { default as closestIndexTo } from './closestIndexTo/index' export { default as closestTo } from './closestTo/index' export { default as compareAsc } from './compareAsc/index'