From 76945e043459ba5626c829d9a68c0a84498424f3 Mon Sep 17 00:00:00 2001 From: Tan75 Date: Thu, 8 Apr 2021 04:43:37 +0100 Subject: [PATCH] New eachMinuteOfInterval function (#2382) Added new `eachMinuteOfInterval` function. Co-authored-by: Vitor Ferreira --- src/eachMinuteOfInterval/benchmark.js | 20 ++++++ src/eachMinuteOfInterval/index.ts | 70 +++++++++++++++++++ src/eachMinuteOfInterval/test.ts | 99 +++++++++++++++++++++++++++ 3 files changed, 189 insertions(+) create mode 100644 src/eachMinuteOfInterval/benchmark.js create mode 100644 src/eachMinuteOfInterval/index.ts create mode 100644 src/eachMinuteOfInterval/test.ts diff --git a/src/eachMinuteOfInterval/benchmark.js b/src/eachMinuteOfInterval/benchmark.js new file mode 100644 index 0000000000..02772339ce --- /dev/null +++ b/src/eachMinuteOfInterval/benchmark.js @@ -0,0 +1,20 @@ +// @flow +/* eslint-env mocha */ +/* global suite, benchmark */ + +import eachMinuteOfInterval from '.' + +suite( + 'eachMinuteOfInterval', + function() { + benchmark('date-fns', function() { + return eachMinuteOfInterval({ start: this.dateA, end: this.dateB }) + }) + }, + { + setup: function() { + this.dateA = new Date() + this.dateB = new Date(this.dateA.getTime() + 300000) + } + } +) diff --git a/src/eachMinuteOfInterval/index.ts b/src/eachMinuteOfInterval/index.ts new file mode 100644 index 0000000000..682b6c026f --- /dev/null +++ b/src/eachMinuteOfInterval/index.ts @@ -0,0 +1,70 @@ +import addMinutes from '../addMinutes/index' +import toDate from '../toDate/index' +import startOfMinute from '../startOfMinute/index' +import requiredArgs from '../_lib/requiredArgs/index' + +import { Interval, StepOptions } from '../types' + +/** + * @name eachMinuteOfInterval + * @category Interval Helpers + * @summary Return the array of minutes within the specified time interval. + * + * @description + * Returns the array of minutes within the specified time interval. + * + * @param {Interval} interval - the interval. See [Interval]{@link https://date-fns.org/docs/Interval} + * @param {Object} [options] - an object with options. + * @param {Number} [options.step=1] - the step to increment by. The starts of minutes from the hour of the interval start to the hour of the interval end + * @throws {TypeError} 1 argument requie value should be more than 1. + * @returns {Date[]} the array withred + * @throws {RangeError} `options.step` must be a number equal or greater than 1 + * @throws {RangeError} The start of an interval cannot be after its end + * @throws {RangeError} Date in interval cannot be `Invalid Date` + * + * @example + * // Each minute between 14 October 2020, 13:00 and 14 October 2020, 13:03 + * const result = eachMinuteOfInterval({ + * start: new Date(2014, 9, 14, 13), + * end: new Date(2014, 9, 14, 13, 3) + * }) + * //=> [ + * // Wed Oct 14 2014 13:00:00, + * // Wed Oct 14 2014 13:01:00, + * // Wed Oct 14 2014 13:02:00, + * // Wed Oct 14 2014 13:03:00 + * // ] + */ +export default function eachMinuteOfInterval( + interval: Interval, + options?: StepOptions +): Date[] { + requiredArgs(1, arguments) + + const startDate = startOfMinute(toDate(interval.start)) + const endDate = startOfMinute(toDate(interval.end)) + + const startTime = startDate.getTime() + const endTime = endDate.getTime() + + if (startTime >= endTime) { + throw new RangeError('Invalid interval') + } + + const dates = [] + + let currentDate = startDate + + const step = options && 'step' in options ? Number(options.step) : 1 + if (step < 1 || isNaN(step)) + throw new RangeError( + '`options.step` must be a number equal or greater than 1' + ) + + while (currentDate.getTime() <= endTime) { + dates.push(toDate(currentDate)) + currentDate = addMinutes(currentDate, step) + } + + return dates +} diff --git a/src/eachMinuteOfInterval/test.ts b/src/eachMinuteOfInterval/test.ts new file mode 100644 index 0000000000..c4dab70e65 --- /dev/null +++ b/src/eachMinuteOfInterval/test.ts @@ -0,0 +1,99 @@ +// @flow +/* eslint-env mocha */ + +import assert from 'power-assert' + +import eachMinuteOfInterval from '.' + +describe('eachMinuteOfInterval', () => { + it('should return an array of Date objects containing a Date for each minute between the interval', () => { + const result = eachMinuteOfInterval({ + start: new Date(2020, 10, 14, 13, 0), + end: new Date(2020, 10, 14, 13, 5) + }) + + assert.deepEqual(result, [ + new Date(2020, 10, 14, 13, 0), + new Date(2020, 10, 14, 13, 1), + new Date(2020, 10, 14, 13, 2), + new Date(2020, 10, 14, 13, 3), + new Date(2020, 10, 14, 13, 4), + new Date(2020, 10, 14, 13, 5) + ]) + }) + + it('should handle all the minutes that are not in the begining', () => { + const result = eachMinuteOfInterval({ + start: new Date(2020, 10, 14, 13, 0, 33), + end: new Date(2020, 10, 14, 13, 2) + }) + + assert.deepEqual(result[0], new Date(2020, 10, 14, 13)) + assert.deepEqual(result[2], new Date(2020, 10, 14, 13, 2)) + }) + + it('should accept timestamps', () => { + const start = new Date(2020, 10, 14, 13, 0).getTime() + const end = new Date(2020, 10, 14, 13, 2).getTime() + + const result = eachMinuteOfInterval({ + start, + end + }) + + assert.deepEqual(result, [ + new Date(2020, 10, 14, 13, 0), + new Date(2020, 10, 14, 13, 1), + new Date(2020, 10, 14, 13, 2) + ]) + }) + + it('throws an exception if the start date is after the end date', () => { + const block = eachMinuteOfInterval.bind(null, { + start: new Date(2014, 10, 14, 10), + end: new Date(2014, 10, 14, 5) + }) + assert.throws(block, RangeError) + }) + + describe('options.step', () => { + const interval = { + start: new Date(2020, 9, 14, 13, 1), + end: new Date(2020, 9, 14, 13, 7) + } + + const stepError = /^RangeError: `options.step` must be a number equal or greater than 1$/ + + it('returns an array with starts of hours from the hour of the start date to the hour of the end date with the given step', () => { + const result = eachMinuteOfInterval(interval, { step: 3 }) + assert.deepEqual(result, [ + new Date(2020, 9, 14, 13, 1), + new Date(2020, 9, 14, 13, 4), + new Date(2020, 9, 14, 13, 7) + ]) + }) + + it('throws TypeError error if `options.step` is less than 1', () => { + assert.throws( + () => eachMinuteOfInterval(interval, { step: 0 }), + stepError + ) + assert.throws( + () => eachMinuteOfInterval(interval, { step: -3 }), + stepError + ) + }) + + it('throws TypeError error if `options.step` is NaN', () => { + // $ExpectedMistake + assert.throws( + () => eachMinuteOfInterval(interval, { step: 'w' }), + stepError + ) + assert.throws( + () => eachMinuteOfInterval(interval, { step: NaN }), + stepError + ) + }) + }) +})