From b8846422a186a37bd0befe0f18f3856632a94e41 Mon Sep 17 00:00:00 2001 From: Lucas Silva <45002716+LucasHFS@users.noreply.github.com> Date: Fri, 17 Sep 2021 02:24:58 -0300 Subject: [PATCH] Add `roundingMethod` option to differenceInX functions (#2571) (closes #2555) Added `roundingMethod` option to `differenceInHours`, `differenceInMinutes`, `differenceInQuarters`, `differenceInSeconds` and `differenceInWeeks` with `trunc` as the default method. Co-authored-by: Lucas Silva Co-authored-by: Tetiana Co-authored-by: Sasha Koss --- src/_lib/roundingMethods/index.ts | 18 +++ src/differenceInHours/index.ts | 20 ++- src/differenceInHours/test.ts | 79 +++++++-- src/differenceInMilliseconds/index.ts | 8 +- src/differenceInMilliseconds/test.ts | 5 +- .../{index.js => index.ts} | 26 +-- src/differenceInMinutes/test.js | 105 ------------ src/differenceInMinutes/test.ts | 151 ++++++++++++++++++ src/differenceInMonths/index.ts | 4 +- src/differenceInMonths/test.ts | 4 +- src/differenceInQuarters/index.ts | 19 ++- src/differenceInQuarters/test.ts | 81 ++++++++-- src/differenceInSeconds/index.ts | 13 +- src/differenceInSeconds/test.ts | 83 ++++++++-- src/differenceInWeeks/index.ts | 15 +- src/differenceInWeeks/test.ts | 82 ++++++++-- src/types.ts | 6 + 17 files changed, 515 insertions(+), 204 deletions(-) create mode 100644 src/_lib/roundingMethods/index.ts rename src/differenceInMinutes/{index.js => index.ts} (58%) delete mode 100644 src/differenceInMinutes/test.js create mode 100644 src/differenceInMinutes/test.ts diff --git a/src/_lib/roundingMethods/index.ts b/src/_lib/roundingMethods/index.ts new file mode 100644 index 0000000000..a8a8df2b1e --- /dev/null +++ b/src/_lib/roundingMethods/index.ts @@ -0,0 +1,18 @@ +import type { RoundingMethod } from '../../types' + +type RoundingFn = typeof Math.round + +type RoundingFnsMap = { [method in RoundingMethod]: RoundingFn } + +const roundingMap: RoundingFnsMap = { + ceil: Math.ceil, + round: Math.round, + floor: Math.floor, + trunc: (value: number) => (value < 0 ? Math.ceil(value) : Math.floor(value)), // Math.trunc is not supported by IE +} + +const defaultRoundingMethod: RoundingMethod = 'trunc' + +export function getRoundingMethod(method: RoundingMethod | undefined) { + return method ? roundingMap[method] : roundingMap[defaultRoundingMethod] +} diff --git a/src/differenceInHours/index.ts b/src/differenceInHours/index.ts index 3c940b1404..22e1ed3d7f 100644 --- a/src/differenceInHours/index.ts +++ b/src/differenceInHours/index.ts @@ -1,7 +1,8 @@ +import { millisecondsInHour } from '../constants/index' import differenceInMilliseconds from '../differenceInMilliseconds/index' +import type { RoundingOptions } from '../types' import requiredArgs from '../_lib/requiredArgs/index' - -const MILLISECONDS_IN_HOUR = 3600000 +import { getRoundingMethod } from '../_lib/roundingMethods/index' /** * @name differenceInHours @@ -17,22 +18,27 @@ const MILLISECONDS_IN_HOUR = 3600000 * * @param {Date|Number} dateLeft - the later date * @param {Date|Number} dateRight - the earlier date + * @param {Object} [options] - an object with options. + * @param {String} [options.roundingMethod='trunc'] - a rounding method (`ceil`, `floor`, `round` or `trunc`) * @returns {Number} the number of hours * @throws {TypeError} 2 arguments required * * @example * // How many hours are between 2 July 2014 06:50:00 and 2 July 2014 19:00:00? - * var result = differenceInHours( + * const result = differenceInHours( * new Date(2014, 6, 2, 19, 0), * new Date(2014, 6, 2, 6, 50) * ) * //=> 12 */ -export default function differenceInHours(dirtyDateLeft: Date | number, dirtyDateRight: Date | number): number { +export default function differenceInHours( + dateLeft: Date | number, + dateRight: Date | number, + options?: RoundingOptions +): number { requiredArgs(2, arguments) const diff = - differenceInMilliseconds(dirtyDateLeft, dirtyDateRight) / - MILLISECONDS_IN_HOUR - return diff > 0 ? Math.floor(diff) : Math.ceil(diff) + differenceInMilliseconds(dateLeft, dateRight) / millisecondsInHour + return getRoundingMethod(options?.roundingMethod)(diff) } diff --git a/src/differenceInHours/test.ts b/src/differenceInHours/test.ts index 303b7fa502..2ee3867a0a 100644 --- a/src/differenceInHours/test.ts +++ b/src/differenceInHours/test.ts @@ -1,11 +1,18 @@ -// @flow /* eslint-env mocha */ import assert from 'assert' import differenceInHours from '.' -describe('differenceInHours', function() { - it('returns the number of hours between the given dates', function() { +describe('differenceInHours', function () { + it('returns the number of hours between the given dates with `trunc` as a default rounding method', function () { + const result = differenceInHours( + new Date(2014, 6 /* Jul */, 2, 6, 0, 29), + new Date(2014, 6 /* Jul */, 2, 20, 0, 28.973) + ) + assert(result === -13) + }) + + it('returns the number of hours between the given dates', function () { const result = differenceInHours( new Date(2014, 6 /* Jul */, 2, 20, 0), new Date(2014, 6 /* Jul */, 2, 6, 0) @@ -13,7 +20,7 @@ describe('differenceInHours', function() { assert(result === 14) }) - it('returns a negative number if the time value of the first date is smaller', function() { + it('returns a negative number if the time value of the first date is smaller', function () { const result = differenceInHours( new Date(2014, 6 /* Jul */, 2, 6, 0), new Date(2014, 6 /* Jul */, 2, 20, 0) @@ -21,7 +28,51 @@ describe('differenceInHours', function() { assert(result === -14) }) - it('accepts timestamps', function() { + it('returns a 0, not a negative 0 - issue #2555 ', function () { + const result = differenceInHours( + new Date(2021, 6 /* Jul */, 22, 6, 1, 28.973), + new Date(2021, 6 /* Jul */, 22, 6, 1, 28.976) + ) + assert(result === 0) + }) + + it('returns 2 with a rounding method of `ceil`, not a negative 0 - issue #2555 ', function () { + const result = differenceInHours( + new Date(2021, 6 /* Jul */, 22, 7, 1, 29, 976), + new Date(2021, 6 /* Jul */, 22, 6, 1, 28, 173), + { roundingMethod: 'ceil' } + ) + assert(result === 2) + }) + + it('returns 1 with a rounding method of `floor`, not a negative 0 - issue #2555 ', function () { + const result = differenceInHours( + new Date(2021, 6 /* Jul */, 22, 7, 1, 29, 976), + new Date(2021, 6 /* Jul */, 22, 6, 1, 28, 173), + { roundingMethod: 'floor' } + ) + assert(result === 1) + }) + + it('returns 1 with a rounding method of `round`, not a negative 0 - issue #2555 ', function () { + const result = differenceInHours( + new Date(2021, 6 /* Jul */, 22, 7, 1, 29, 976), + new Date(2021, 6 /* Jul */, 22, 6, 1, 28, 173), + { roundingMethod: 'round' } + ) + assert(result === 1) + }) + + it('returns 1 with a rounding method of `trunc`, not a negative 0 - issue #2555 ', function () { + const result = differenceInHours( + new Date(2021, 6 /* Jul */, 22, 7, 1, 29, 976), + new Date(2021, 6 /* Jul */, 22, 6, 1, 28, 173), + { roundingMethod: 'trunc' } + ) + assert(result === 1) + }) + + it('accepts timestamps', function () { const result = differenceInHours( new Date(2014, 8 /* Sep */, 5, 18, 0).getTime(), new Date(2014, 8 /* Sep */, 5, 6, 0).getTime() @@ -29,8 +80,8 @@ describe('differenceInHours', function() { assert(result === 12) }) - describe('edge cases', function() { - it('the difference is less than an hour, but the given dates are in different calendar hours', function() { + describe('edge cases', function () { + it('the difference is less than an hour, but the given dates are in different calendar hours', function () { const result = differenceInHours( new Date(2014, 8 /* Sep */, 5, 12), new Date(2014, 8 /* Sep */, 5, 11, 59) @@ -38,7 +89,7 @@ describe('differenceInHours', function() { assert(result === 0) }) - it('the same for the swapped dates', function() { + it('the same for the swapped dates', function () { const result = differenceInHours( new Date(2014, 8 /* Sep */, 5, 11, 59), new Date(2014, 8 /* Sep */, 5, 12) @@ -46,7 +97,7 @@ describe('differenceInHours', function() { assert(result === 0) }) - it('the difference is an integral number of hours', function() { + it('the difference is an integral number of hours', function () { const result = differenceInHours( new Date(2014, 8 /* Sep */, 5, 13, 0), new Date(2014, 8 /* Sep */, 5, 12, 0) @@ -54,7 +105,7 @@ describe('differenceInHours', function() { assert(result === 1) }) - it('the given dates are the same', function() { + it('the given dates are the same', function () { const result = differenceInHours( new Date(2014, 8 /* Sep */, 5, 0, 0), new Date(2014, 8 /* Sep */, 5, 0, 0) @@ -77,7 +128,7 @@ describe('differenceInHours', function() { }) }) - it('returns NaN if the first date is `Invalid Date`', function() { + it('returns NaN if the first date is `Invalid Date`', function () { const result = differenceInHours( new Date(NaN), new Date(2017, 0 /* Jan */, 1) @@ -85,7 +136,7 @@ describe('differenceInHours', function() { assert(isNaN(result)) }) - it('returns NaN if the second date is `Invalid Date`', function() { + it('returns NaN if the second date is `Invalid Date`', function () { const result = differenceInHours( new Date(2017, 0 /* Jan */, 1), new Date(NaN) @@ -93,12 +144,12 @@ describe('differenceInHours', function() { assert(isNaN(result)) }) - it('returns NaN if the both dates are `Invalid Date`', function() { + it('returns NaN if the both dates are `Invalid Date`', function () { const result = differenceInHours(new Date(NaN), new Date(NaN)) assert(isNaN(result)) }) - it('throws TypeError exception if passed less than 2 arguments', function() { + it('throws TypeError exception if passed less than 2 arguments', function () { //@ts-expect-error assert.throws(differenceInHours.bind(null), TypeError) //@ts-expect-error diff --git a/src/differenceInMilliseconds/index.ts b/src/differenceInMilliseconds/index.ts index a0fc552750..0d5d8c77b5 100644 --- a/src/differenceInMilliseconds/index.ts +++ b/src/differenceInMilliseconds/index.ts @@ -28,12 +28,10 @@ import requiredArgs from '../_lib/requiredArgs/index' * //=> 1100 */ export default function differenceInMilliseconds( - dirtyDateLeft: Date | number, - dirtyDateRight: Date | number + dateLeft: Date | number, + dateRight: Date | number ): number { requiredArgs(2, arguments) - const dateLeft = toDate(dirtyDateLeft) - const dateRight = toDate(dirtyDateRight) - return dateLeft.getTime() - dateRight.getTime() + return toDate(dateLeft).getTime() - toDate(dateRight).getTime() } diff --git a/src/differenceInMilliseconds/test.ts b/src/differenceInMilliseconds/test.ts index 366c03b6c7..98bebd1e9c 100644 --- a/src/differenceInMilliseconds/test.ts +++ b/src/differenceInMilliseconds/test.ts @@ -1,6 +1,6 @@ /* eslint-env mocha */ -import assert from 'power-assert' +import assert from 'assert' import differenceInMilliseconds from '.' describe('differenceInMilliseconds', function () { @@ -32,7 +32,6 @@ describe('differenceInMilliseconds', function () { function isNegativeZero(x: number): boolean { return x === 0 && 1 / x < 0 } - const result = differenceInMilliseconds( new Date(2014, 8 /* Sep */, 5, 0, 0), new Date(2014, 8 /* Sep */, 5, 0, 0) @@ -64,7 +63,9 @@ describe('differenceInMilliseconds', function () { }) it('throws TypeError exception if passed less than 2 arguments', function () { + // @ts-expect-error assert.throws(differenceInMilliseconds.bind(null), TypeError) + // @ts-expect-error assert.throws(differenceInMilliseconds.bind(null, 1), TypeError) }) }) diff --git a/src/differenceInMinutes/index.js b/src/differenceInMinutes/index.ts similarity index 58% rename from src/differenceInMinutes/index.js rename to src/differenceInMinutes/index.ts index c1b103c398..d100fba84b 100644 --- a/src/differenceInMinutes/index.js +++ b/src/differenceInMinutes/index.ts @@ -1,7 +1,8 @@ +import { millisecondsInMinute } from '../constants/index' import differenceInMilliseconds from '../differenceInMilliseconds/index' +import type { RoundingOptions } from '../types' import requiredArgs from '../_lib/requiredArgs/index' - -var MILLISECONDS_IN_MINUTE = 60000 +import { getRoundingMethod } from '../_lib/roundingMethods/index' /** * @name differenceInMinutes @@ -17,30 +18,35 @@ var MILLISECONDS_IN_MINUTE = 60000 * * @param {Date|Number} dateLeft - the later date * @param {Date|Number} dateRight - the earlier date + * @param {Object} [options] - an object with options. + * @param {String} [options.roundingMethod='trunc'] - a rounding method (`ceil`, `floor`, `round` or `trunc`) * @returns {Number} the number of minutes * @throws {TypeError} 2 arguments required * * @example * // How many minutes are between 2 July 2014 12:07:59 and 2 July 2014 12:20:00? - * var result = differenceInMinutes( + * const result = differenceInMinutes( * new Date(2014, 6, 2, 12, 20, 0), * new Date(2014, 6, 2, 12, 7, 59) * ) * //=> 12 * * @example - * // How many minutes are from 10:01:59 to 10:00:00 - * var result = differenceInMinutes( + * // How many minutes are between 10:01:59 and 10:00:00 + * const result = differenceInMinutes( * new Date(2000, 0, 1, 10, 0, 0), * new Date(2000, 0, 1, 10, 1, 59) * ) * //=> -1 */ -export default function differenceInMinutes(dirtyDateLeft, dirtyDateRight) { +export default function differenceInMinutes( + dateLeft: Date | number, + dateRight: Date | number, + options?: RoundingOptions +): number { requiredArgs(2, arguments) - var diff = - differenceInMilliseconds(dirtyDateLeft, dirtyDateRight) / - MILLISECONDS_IN_MINUTE - return diff > 0 ? Math.floor(diff) : Math.ceil(diff) + const diff = + differenceInMilliseconds(dateLeft, dateRight) / millisecondsInMinute + return getRoundingMethod(options?.roundingMethod)(diff) } diff --git a/src/differenceInMinutes/test.js b/src/differenceInMinutes/test.js deleted file mode 100644 index ff6a6c3eb9..0000000000 --- a/src/differenceInMinutes/test.js +++ /dev/null @@ -1,105 +0,0 @@ -// @flow -/* eslint-env mocha */ - -import assert from 'power-assert' -import differenceInMinutes from '.' - -describe('differenceInMinutes', function() { - it('returns the number of minutes between the given dates', function() { - var result = differenceInMinutes( - new Date(2014, 6 /* Jul */, 2, 12, 20), - new Date(2014, 6 /* Jul */, 2, 12, 6) - ) - assert(result === 14) - }) - - it('returns a negative number if the time value of the first date is smaller', function() { - var result = differenceInMinutes( - new Date(2014, 6 /* Jul */, 2, 12, 6), - new Date(2014, 6 /* Jul */, 2, 12, 20) - ) - assert(result === -14) - }) - - it('accepts timestamps', function() { - var result = differenceInMinutes( - new Date(2014, 8 /* Sep */, 5, 18, 45).getTime(), - new Date(2014, 8 /* Sep */, 5, 18, 15).getTime() - ) - assert(result === 30) - }) - - describe('edge cases', function() { - it('the difference is less than a minute, but the given dates are in different calendar minutes', function() { - var result = differenceInMinutes( - new Date(2014, 8 /* Sep */, 5, 12, 12), - new Date(2014, 8 /* Sep */, 5, 12, 11, 59) - ) - assert(result === 0) - }) - - it('the same for the swapped dates', function() { - var result = differenceInMinutes( - new Date(2014, 8 /* Sep */, 5, 12, 11, 59), - new Date(2014, 8 /* Sep */, 5, 12, 12) - ) - assert(result === 0) - }) - - it('the difference is an integral number of minutes', function() { - var result = differenceInMinutes( - new Date(2014, 8 /* Sep */, 5, 12, 25), - new Date(2014, 8 /* Sep */, 5, 12, 15) - ) - assert(result === 10) - }) - - it('the given dates are the same', function() { - var result = differenceInMinutes( - new Date(2014, 8 /* Sep */, 5, 0, 0), - new Date(2014, 8 /* Sep */, 5, 0, 0) - ) - assert(result === 0) - }) - - it('does not return -0 when the given dates are the same', () => { - function isNegativeZero(x) { - return x === 0 && 1 / x < 0 - } - - var result = differenceInMinutes( - new Date(2014, 8 /* Sep */, 5, 0, 0), - new Date(2014, 8 /* Sep */, 5, 0, 0) - ) - - var resultIsNegative = isNegativeZero(result) - assert(resultIsNegative === false) - }) - }) - - it('returns NaN if the first date is `Invalid Date`', function() { - var result = differenceInMinutes( - new Date(NaN), - new Date(2017, 0 /* Jan */, 1) - ) - assert(isNaN(result)) - }) - - it('returns NaN if the second date is `Invalid Date`', function() { - var result = differenceInMinutes( - new Date(2017, 0 /* Jan */, 1), - new Date(NaN) - ) - assert(isNaN(result)) - }) - - it('returns NaN if the both dates are `Invalid Date`', function() { - var result = differenceInMinutes(new Date(NaN), new Date(NaN)) - assert(isNaN(result)) - }) - - it('throws TypeError exception if passed less than 2 arguments', function() { - assert.throws(differenceInMinutes.bind(null), TypeError) - assert.throws(differenceInMinutes.bind(null, 1), TypeError) - }) -}) diff --git a/src/differenceInMinutes/test.ts b/src/differenceInMinutes/test.ts new file mode 100644 index 0000000000..ab48ec0d75 --- /dev/null +++ b/src/differenceInMinutes/test.ts @@ -0,0 +1,151 @@ +/* eslint-env mocha */ + +import assert from 'assert' +import differenceInMinutes from '.' + +describe('differenceInMinutes', () => { + it('returns the number of minutes between the given dates', () => { + const result = differenceInMinutes( + new Date(2014, 6 /* Jul */, 2, 12, 20), + new Date(2014, 6 /* Jul */, 2, 12, 6) + ) + assert(result === 14) + }) + + it('returns the number of minutes between the given dates with `trunc` as a default rounding method', () => { + const result = differenceInMinutes( + new Date(2014, 6 /* Jul */, 2, 12, 6, 50), + new Date(2014, 6 /* Jul */, 2, 12, 20, 10) + ) + assert(result === -13) + }) + + it('returns the number of minutes between the given dates with `trunc` passed in as a rounding method ', () => { + const result = differenceInMinutes( + new Date(2014, 6 /* Jul */, 2, 12, 20, 50), + new Date(2014, 6 /* Jul */, 2, 12, 6, 10), + { roundingMethod: 'trunc' } + ) + assert(result === 14) + }) + + it('returns the number of minutes between the given dates with `ceil` passed in as a rounding method ', () => { + const result = differenceInMinutes( + new Date(2014, 6 /* Jul */, 2, 12, 20, 50), + new Date(2014, 6 /* Jul */, 2, 12, 6, 10), + { roundingMethod: 'ceil' } + ) + assert(result === 15) + }) + + it('returns the number of minutes between the given dates with `floor` passed in as a rounding method ', () => { + const result = differenceInMinutes( + new Date(2014, 6 /* Jul */, 2, 12, 20, 50), + new Date(2014, 6 /* Jul */, 2, 12, 6, 10), + { roundingMethod: 'floor' } + ) + assert(result === 14) + }) + + it('returns the number of minutes between the given dates with `round` passed in as a rounding method ', () => { + const result = differenceInMinutes( + new Date(2014, 6 /* Jul */, 2, 12, 20, 60), + new Date(2014, 6 /* Jul */, 2, 12, 6, 10), + { roundingMethod: 'round' } + ) + assert(result === 15) + }) + + it('returns a negative number if the time value of the first date is smaller', () => { + const result = differenceInMinutes( + new Date(2014, 6 /* Jul */, 2, 12, 6), + new Date(2014, 6 /* Jul */, 2, 12, 20) + ) + assert(result === -14) + }) + + it('accepts timestamps', () => { + const result = differenceInMinutes( + new Date(2014, 8 /* Sep */, 5, 18, 45).getTime(), + new Date(2014, 8 /* Sep */, 5, 18, 15).getTime() + ) + assert(result === 30) + }) + + describe('edge cases', () => { + it('the difference is less than a minute, but the given dates are in different calendar minutes', () => { + const result = differenceInMinutes( + new Date(2014, 8 /* Sep */, 5, 12, 12), + new Date(2014, 8 /* Sep */, 5, 12, 11, 59) + ) + assert(result === 0) + }) + + it('the same for the swapped dates', () => { + const result = differenceInMinutes( + new Date(2014, 8 /* Sep */, 5, 12, 11, 59), + new Date(2014, 8 /* Sep */, 5, 12, 12) + ) + assert(result === 0) + }) + + it('the difference is an integral number of minutes', () => { + const result = differenceInMinutes( + new Date(2014, 8 /* Sep */, 5, 12, 25), + new Date(2014, 8 /* Sep */, 5, 12, 15) + ) + assert(result === 10) + }) + + it('the given dates are the same', () => { + const result = differenceInMinutes( + new Date(2014, 8 /* Sep */, 5, 0, 0), + new Date(2014, 8 /* Sep */, 5, 0, 0) + ) + assert(result === 0) + }) + + it('does not return -0 when the given dates are the same', () => { + function isNegativeZero(x: number) { + return x === 0 && 1 / x < 0 + } + + const result = differenceInMinutes( + new Date(2014, 8 /* Sep */, 5, 0, 0), + new Date(2014, 8 /* Sep */, 5, 0, 0) + ) + + const resultIsNegative = isNegativeZero(result) + assert(resultIsNegative === false) + }) + }) + + it('returns NaN if the first date is `Invalid Date`', () => { + const result = differenceInMinutes( + new Date(NaN), + new Date(2017, 0 /* Jan */, 1) + ) + assert(isNaN(result)) + }) + + it('returns NaN if the second date is `Invalid Date`', () => { + const result = differenceInMinutes( + new Date(2017, 0 /* Jan */, 1), + new Date(NaN) + ) + assert(isNaN(result)) + }) + + it('returns NaN if the both dates are `Invalid Date`', () => { + const result = differenceInMinutes(new Date(NaN), new Date(NaN)) + assert(isNaN(result)) + }) + + it('throws TypeError exception if passed less than 2 arguments', () => { + // @ts-expect-error + assert.throws(differenceInMinutes.bind(null), TypeError) + + // @ts-expect-error + assert.throws(differenceInMinutes.bind(null, 1), TypeError) + }) +}) diff --git a/src/differenceInMonths/index.ts b/src/differenceInMonths/index.ts index c8a5c14be0..8b344504cc 100644 --- a/src/differenceInMonths/index.ts +++ b/src/differenceInMonths/index.ts @@ -10,7 +10,7 @@ import isLastDayOfMonth from '../isLastDayOfMonth/index' * @summary Get the number of full months between the given dates. * * @description - * Get the number of full months between the given dates. + * Get the number of full months between the given dates using trunc as a default rounding method. * * ### v2.0.0 breaking changes: * @@ -23,7 +23,7 @@ import isLastDayOfMonth from '../isLastDayOfMonth/index' * * @example * // How many full months are between 31 January 2014 and 1 September 2014? - * var result = differenceInMonths(new Date(2014, 8, 1), new Date(2014, 0, 31)) + * const result = differenceInMonths(new Date(2014, 8, 1), new Date(2014, 0, 31)) * //=> 7 */ export default function differenceInMonths( diff --git a/src/differenceInMonths/test.ts b/src/differenceInMonths/test.ts index edebb59b1a..22987fad41 100644 --- a/src/differenceInMonths/test.ts +++ b/src/differenceInMonths/test.ts @@ -1,6 +1,6 @@ /* eslint-env mocha */ -import assert from 'power-assert' +import assert from 'assert' import differenceInMonths from '.' describe('differenceInMonths', function () { @@ -146,7 +146,9 @@ describe('differenceInMonths', function () { }) it('throws TypeError exception if passed less than 2 arguments', function () { + // @ts-expect-error assert.throws(differenceInMonths.bind(null), TypeError) + // @ts-expect-error assert.throws(differenceInMonths.bind(null, 1), TypeError) }) diff --git a/src/differenceInQuarters/index.ts b/src/differenceInQuarters/index.ts index b53a93489f..db882595be 100644 --- a/src/differenceInQuarters/index.ts +++ b/src/differenceInQuarters/index.ts @@ -1,13 +1,15 @@ import differenceInMonths from '../differenceInMonths/index' +import type { RoundingOptions } from '../types' import requiredArgs from '../_lib/requiredArgs/index' +import { getRoundingMethod } from '../_lib/roundingMethods/index' /** * @name differenceInQuarters * @category Quarter Helpers - * @summary Get the number of full quarters between the given dates. + * @summary Get the number of quarters between the given dates. * * @description - * Get the number of full quarters between the given dates. + * Get the number of quarters between the given dates. * * ### v2.0.0 breaking changes: * @@ -15,20 +17,23 @@ import requiredArgs from '../_lib/requiredArgs/index' * * @param {Date|Number} dateLeft - the later date * @param {Date|Number} dateRight - the earlier date + * @param {Object} [options] - an object with options. + * @param {String} [options.roundingMethod='trunc'] - a rounding method (`ceil`, `floor`, `round` or `trunc`) * @returns {Number} the number of full quarters * @throws {TypeError} 2 arguments required * * @example * // How many full quarters are between 31 December 2013 and 2 July 2014? - * var result = differenceInQuarters(new Date(2014, 6, 2), new Date(2013, 11, 31)) + * const result = differenceInQuarters(new Date(2014, 6, 2), new Date(2013, 11, 31)) * //=> 2 */ export default function differenceInQuarters( - dirtyDateLeft: Date | number, - dirtyDateRight: Date | number + dateLeft: Date | number, + dateRight: Date | number, + options?: RoundingOptions ): number { requiredArgs(2, arguments) - const diff = differenceInMonths(dirtyDateLeft, dirtyDateRight) / 3 - return diff > 0 ? Math.floor(diff) : Math.ceil(diff) + const diff = differenceInMonths(dateLeft, dateRight) / 3 + return getRoundingMethod(options?.roundingMethod)(diff) } diff --git a/src/differenceInQuarters/test.ts b/src/differenceInQuarters/test.ts index 2cdb8d01d8..220f3d8e9a 100644 --- a/src/differenceInQuarters/test.ts +++ b/src/differenceInQuarters/test.ts @@ -1,10 +1,18 @@ /* eslint-env mocha */ -import assert from 'power-assert' +import assert from 'assert' import differenceInQuarters from '.' -describe('differenceInQuarters', function () { - it('returns the number of full quarters between the given dates', function () { +describe('differenceInQuarters', () => { + it('returns the number of full quarters between the given dates with `trunc` as a default rounding method', () => { + const result = differenceInQuarters( + new Date(2012, 6 /* Jul */, 2, 5, 0), + new Date(2011, 6 /* Jul */, 2, 6, 0) + ) + assert(result === 3) + }) + + it('returns the number of full quarters between the given dates', () => { const result = differenceInQuarters( new Date(2012, 6 /* Jul */, 2, 18, 0), new Date(2011, 6 /* Jul */, 2, 6, 0) @@ -12,7 +20,42 @@ describe('differenceInQuarters', function () { assert(result === 4) }) - it('returns a negative number if the time value of the first date is smaller', function () { + it('returns the number of full quarters between the given dates with `trunc` as a default rounding method', () => { + const result = differenceInQuarters( + new Date(2012, 6 /* Jul */, 2, 18, 0), + new Date(2011, 4 /* May */, 2, 6, 0) + ) + assert(result === 4) + }) + + it('returns the number of full quarters between the given dates with `ceil` as a rounding method', () => { + const result = differenceInQuarters( + new Date(2012, 6 /* Jul */, 2, 18, 0), + new Date(2011, 4 /* May */, 2, 6, 0), + { roundingMethod: 'ceil' } + ) + assert(result === 5) + }) + + it('returns the number of full quarters between the given dates with `floor` as a rounding method', () => { + const result = differenceInQuarters( + new Date(2012, 6 /* Jul */, 2, 18, 0), + new Date(2011, 4 /* May */, 2, 6, 0), + { roundingMethod: 'floor' } + ) + assert(result === 4) + }) + + it('returns the number of full quarters between the given dates with `round` as a rounding method', () => { + const result = differenceInQuarters( + new Date(2012, 6 /* Jul */, 2, 18, 0), + new Date(2011, 4 /* May */, 2, 6, 0), + { roundingMethod: 'round' } + ) + assert(result === 5) + }) + + it('returns a negative number if the time value of the first date is smaller', () => { const result = differenceInQuarters( new Date(2011, 6 /* Jul */, 2, 6, 0), new Date(2012, 6 /* Jul */, 2, 18, 0) @@ -20,7 +63,15 @@ describe('differenceInQuarters', function () { assert(result === -4) }) - it('accepts timestamps', function () { + it('returns a 0, not a negative 0 - issue #2555 ', () => { + const result = differenceInQuarters( + new Date(2021, 6 /* Jul */, 22, 6, 1, 28.973), + new Date(2021, 6 /* Jul */, 22, 6, 1, 28.976) + ) + assert(Object.is(result, 0)) + }) + + it('accepts timestamps', () => { const result = differenceInQuarters( new Date(2014, 9 /* Oct */, 2).getTime(), new Date(2010, 6 /* Jul */, 2).getTime() @@ -28,8 +79,8 @@ describe('differenceInQuarters', function () { assert(result === 17) }) - describe('edge cases', function () { - it('the difference is less than a quarter, but the given dates are in different calendar quarters', function () { + describe('edge cases', () => { + it('the difference is less than a quarter, but the given dates are in different calendar quarters', () => { const result = differenceInQuarters( new Date(2014, 6 /* Jul */, 1), new Date(2014, 5 /* Jun */, 30) @@ -37,7 +88,7 @@ describe('differenceInQuarters', function () { assert(result === 0) }) - it('the same for the swapped dates', function () { + it('the same for the swapped dates', () => { const result = differenceInQuarters( new Date(2014, 5 /* Jun */, 30), new Date(2014, 6 /* Jul */, 1) @@ -45,7 +96,7 @@ describe('differenceInQuarters', function () { assert(result === 0) }) - it('the days of months of the given dates are the same', function () { + it('the days of months of the given dates are the same', () => { const result = differenceInQuarters( new Date(2014, 3 /* Apr */, 6), new Date(2014, 0 /* Jan */, 6) @@ -53,7 +104,7 @@ describe('differenceInQuarters', function () { assert(result === 1) }) - it('the given dates are the same', function () { + it('the given dates are the same', () => { const result = differenceInQuarters( new Date(2014, 8 /* Sep */, 5, 0, 0), new Date(2014, 8 /* Sep */, 5, 0, 0) @@ -76,7 +127,7 @@ describe('differenceInQuarters', function () { }) }) - it('returns NaN if the first date is `Invalid Date`', function () { + it('returns NaN if the first date is `Invalid Date`', () => { const result = differenceInQuarters( new Date(NaN), new Date(2017, 0 /* Jan */, 1) @@ -84,7 +135,7 @@ describe('differenceInQuarters', function () { assert(isNaN(result)) }) - it('returns NaN if the second date is `Invalid Date`', function () { + it('returns NaN if the second date is `Invalid Date`', () => { const result = differenceInQuarters( new Date(2017, 0 /* Jan */, 1), new Date(NaN) @@ -92,13 +143,15 @@ describe('differenceInQuarters', function () { assert(isNaN(result)) }) - it('returns NaN if the both dates are `Invalid Date`', function () { + it('returns NaN if the both dates are `Invalid Date`', () => { const result = differenceInQuarters(new Date(NaN), new Date(NaN)) assert(isNaN(result)) }) - it('throws TypeError exception if passed less than 2 arguments', function () { + it('throws TypeError exception if passed less than 2 arguments', () => { + // @ts-expect-error assert.throws(differenceInQuarters.bind(null), TypeError) + // @ts-expect-error assert.throws(differenceInQuarters.bind(null, 1), TypeError) }) }) diff --git a/src/differenceInSeconds/index.ts b/src/differenceInSeconds/index.ts index d76b0e3093..419196b5f7 100644 --- a/src/differenceInSeconds/index.ts +++ b/src/differenceInSeconds/index.ts @@ -1,5 +1,7 @@ import differenceInMilliseconds from '../differenceInMilliseconds/index' +import type { RoundingOptions } from '../types' import requiredArgs from '../_lib/requiredArgs/index' +import { getRoundingMethod } from '../_lib/roundingMethods/index' /** * @name differenceInSeconds @@ -15,6 +17,8 @@ import requiredArgs from '../_lib/requiredArgs/index' * * @param {Date|Number} dateLeft - the later date * @param {Date|Number} dateRight - the earlier date + * @param {Object} [options] - an object with options. + * @param {String} [options.roundingMethod='trunc'] - a rounding method (`ceil`, `floor`, `round` or `trunc`) * @returns {Number} the number of seconds * @throws {TypeError} 2 arguments required * @@ -28,11 +32,12 @@ import requiredArgs from '../_lib/requiredArgs/index' * //=> 12 */ export default function differenceInSeconds( - dirtyDateLeft: Date | number, - dirtyDateRight: Date | number + dateLeft: Date | number, + dateRight: Date | number, + options?: RoundingOptions ): number { requiredArgs(2, arguments) - const diff = differenceInMilliseconds(dirtyDateLeft, dirtyDateRight) / 1000 - return diff > 0 ? Math.floor(diff) : Math.ceil(diff) + const diff = differenceInMilliseconds(dateLeft, dateRight) / 1000 + return getRoundingMethod(options?.roundingMethod)(diff) } diff --git a/src/differenceInSeconds/test.ts b/src/differenceInSeconds/test.ts index 8e40a32027..85c0c355e2 100644 --- a/src/differenceInSeconds/test.ts +++ b/src/differenceInSeconds/test.ts @@ -1,10 +1,18 @@ /* eslint-env mocha */ -import assert from 'power-assert' +import assert from 'assert' import differenceInSeconds from '.' -describe('differenceInSeconds', function () { - it('returns the number of seconds between the given dates', function () { +describe('differenceInSeconds', () => { + it('returns the number of seconds between the given dates with `trunc` as a default rounding method', () => { + const result = differenceInSeconds( + new Date(2014, 6 /* Jul */, 2, 12, 30, 6, 29), + new Date(2014, 6 /* Jul */, 2, 12, 30, 20, 28.777) + ) + assert(result === -13) + }) + + it('returns the number of seconds between the given dates', () => { const result = differenceInSeconds( new Date(2014, 6 /* Jul */, 2, 12, 30, 20), new Date(2014, 6 /* Jul */, 2, 12, 30, 6) @@ -12,7 +20,7 @@ describe('differenceInSeconds', function () { assert(result === 14) }) - it('returns a negative number if the time value of the first date is smaller', function () { + it('returns a negative number if the time value of the first date is smaller', () => { const result = differenceInSeconds( new Date(2014, 6 /* Jul */, 2, 12, 30, 6), new Date(2014, 6 /* Jul */, 2, 12, 30, 20) @@ -20,7 +28,52 @@ describe('differenceInSeconds', function () { assert(result === -14) }) - it('accepts timestamps', function () { + it('returns a 0, not a negative 0 - issue #2555 ', () => { + const result = differenceInSeconds( + new Date(2021, 6 /* Jul */, 22, 6, 1, 28.973), + new Date(2021, 6 /* Jul */, 22, 6, 1, 28.976) + ) + assert(result === 0) + }) + + it('returns 1 with `round` passed in as a rounding method', () => { + const result = differenceInSeconds( + new Date(2021, 6 /* Jul */, 22, 6, 1, 29.973), + new Date(2021, 6 /* Jul */, 22, 6, 1, 28.976), + { roundingMethod: 'round' } + ) + assert(result === 1) + }) + + it('returns a -1 with `round` passed in as a rounding method', () => { + const result = differenceInSeconds( + new Date(2021, 6 /* Jul */, 22, 6, 1, 27.976), + new Date(2021, 6 /* Jul */, 22, 6, 1, 28.973), + + { roundingMethod: 'round' } + ) + assert(result === -1) + }) + + it('returns a -2 with `ceil` passed in as a rounding method', () => { + const result = differenceInSeconds( + new Date(2021, 6 /* Jul */, 22, 6, 1, 27.976), + new Date(2021, 6 /* Jul */, 22, 6, 1, 29.973), + { roundingMethod: 'ceil' } + ) + assert(result === -2) + }) + + it('returns a 2 with `ceil` passed in as a rounding method', () => { + const result = differenceInSeconds( + new Date(2021, 6 /* Jul */, 22, 6, 1, 29.973), + new Date(2021, 6 /* Jul */, 22, 6, 1, 27.976), + { roundingMethod: 'ceil' } + ) + assert(result === 2) + }) + + it('accepts timestamps', () => { const result = differenceInSeconds( new Date(2014, 8 /* Sep */, 5, 18, 30, 45).getTime(), new Date(2014, 8 /* Sep */, 5, 18, 30, 15).getTime() @@ -28,8 +81,8 @@ describe('differenceInSeconds', function () { assert(result === 30) }) - describe('edge cases', function () { - it('the difference is less than a second, but the given dates are in different calendar seconds', function () { + describe('edge cases', () => { + it('the difference is less than a second, but the given dates are in different calendar seconds', () => { const result = differenceInSeconds( new Date(2014, 8 /* Sep */, 5, 12, 30, 12), new Date(2014, 8 /* Sep */, 5, 12, 30, 11, 999) @@ -37,7 +90,7 @@ describe('differenceInSeconds', function () { assert(result === 0) }) - it('the same for the swapped dates', function () { + it('the same for the swapped dates but a different result as a resulf of the default rounding method `trunc`', () => { const result = differenceInSeconds( new Date(2014, 8 /* Sep */, 5, 12, 30, 11, 999), new Date(2014, 8 /* Sep */, 5, 12, 30, 12) @@ -45,7 +98,7 @@ describe('differenceInSeconds', function () { assert(result === 0) }) - it('the difference is an integral number of seconds', function () { + it('the difference is an integral number of seconds', () => { const result = differenceInSeconds( new Date(2014, 8 /* Sep */, 5, 12, 30, 25), new Date(2014, 8 /* Sep */, 5, 12, 30, 15) @@ -53,7 +106,7 @@ describe('differenceInSeconds', function () { assert(result === 10) }) - it('the given dates are the same', function () { + it('the given dates are the same', () => { const result = differenceInSeconds( new Date(2014, 8 /* Sep */, 5, 0, 0), new Date(2014, 8 /* Sep */, 5, 0, 0) @@ -76,7 +129,7 @@ describe('differenceInSeconds', function () { }) }) - it('returns NaN if the first date is `Invalid Date`', function () { + it('returns NaN if the first date is `Invalid Date`', () => { const result = differenceInSeconds( new Date(NaN), new Date(2017, 0 /* Jan */, 1) @@ -84,7 +137,7 @@ describe('differenceInSeconds', function () { assert(isNaN(result)) }) - it('returns NaN if the second date is `Invalid Date`', function () { + it('returns NaN if the second date is `Invalid Date`', () => { const result = differenceInSeconds( new Date(2017, 0 /* Jan */, 1), new Date(NaN) @@ -92,13 +145,15 @@ describe('differenceInSeconds', function () { assert(isNaN(result)) }) - it('returns NaN if the both dates are `Invalid Date`', function () { + it('returns NaN if the both dates are `Invalid Date`', () => { const result = differenceInSeconds(new Date(NaN), new Date(NaN)) assert(isNaN(result)) }) - it('throws TypeError exception if passed less than 2 arguments', function () { + it('throws TypeError exception if passed less than 2 arguments', () => { + // @ts-expect-error assert.throws(differenceInSeconds.bind(null), TypeError) + // @ts-expect-error assert.throws(differenceInSeconds.bind(null, 1), TypeError) }) }) diff --git a/src/differenceInWeeks/index.ts b/src/differenceInWeeks/index.ts index 350da2f834..0aab683d92 100644 --- a/src/differenceInWeeks/index.ts +++ b/src/differenceInWeeks/index.ts @@ -1,5 +1,7 @@ import differenceInDays from '../differenceInDays/index' +import type { RoundingOptions } from '../types' import requiredArgs from '../_lib/requiredArgs/index' +import { getRoundingMethod } from '../_lib/roundingMethods/index' /** * @name differenceInWeeks @@ -8,7 +10,7 @@ import requiredArgs from '../_lib/requiredArgs/index' * * @description * Get the number of full weeks between two dates. Fractional weeks are - * truncated towards zero. + * truncated towards zero by default. * * One "full week" is the distance between a local time in one day to the same * local time 7 days earlier or later. A full week can sometimes be less than @@ -24,6 +26,8 @@ import requiredArgs from '../_lib/requiredArgs/index' * * @param {Date|Number} dateLeft - the later date * @param {Date|Number} dateRight - the earlier date + * @param {Object} [options] - an object with options. + * @param {String} [options.roundingMethod='trunc'] - a rounding method (`ceil`, `floor`, `round` or `trunc`) * @returns {Number} the number of full weeks * @throws {TypeError} 2 arguments required * @@ -45,11 +49,12 @@ import requiredArgs from '../_lib/requiredArgs/index' * //=> 8 */ export default function differenceInWeeks( - dirtyDateLeft: Date | number, - dirtyDateRight: Date | number + dateLeft: Date | number, + dateRight: Date | number, + options?: RoundingOptions ): number { requiredArgs(2, arguments) - const diff = differenceInDays(dirtyDateLeft, dirtyDateRight) / 7 - return diff > 0 ? Math.floor(diff) : Math.ceil(diff) + const diff = differenceInDays(dateLeft, dateRight) / 7 + return getRoundingMethod(options?.roundingMethod)(diff) } diff --git a/src/differenceInWeeks/test.ts b/src/differenceInWeeks/test.ts index 6e8e05e755..30937ce4a9 100644 --- a/src/differenceInWeeks/test.ts +++ b/src/differenceInWeeks/test.ts @@ -1,10 +1,10 @@ /* eslint-env mocha */ -import assert from 'power-assert' +import assert from 'assert' import differenceInWeeks from '.' -describe('differenceInWeeks', function () { - it('returns the number of full weeks between the given dates', function () { +describe('differenceInWeeks', () => { + it('returns the number of full weeks between the given dates', () => { const result = differenceInWeeks( new Date(2014, 6 /* Jul */, 8, 18, 0), new Date(2014, 5 /* Jun */, 29, 6, 0) @@ -12,7 +12,51 @@ describe('differenceInWeeks', function () { assert(result === 1) }) - it('returns a negative number if the time value of the first date is smaller', function () { + it('returns the number of weeks between the given dates with `trunc` as default a rounding method', () => { + const result = differenceInWeeks( + new Date(2014, 5 /* Jun */, 29, 6, 0), + new Date(2014, 6 /* Jul */, 13, 5, 0) + ) + assert(result === -1) + }) + + it('returns the number of weeks between the given dates with `trunc` passed in as a rounding method', () => { + const result = differenceInWeeks( + new Date(2014, 6 /* Jul */, 8, 18, 0), + new Date(2014, 5 /* Jun */, 29, 6, 0), + { roundingMethod: 'trunc' } + ) + assert(result === 1) + }) + + it('returns the number of weeks between the given dates with `ceil` passed in as a rounding method', () => { + const result = differenceInWeeks( + new Date(2014, 6 /* Jul */, 8, 18, 0), + new Date(2014, 5 /* Jun */, 29, 6, 0), + { roundingMethod: 'ceil' } + ) + assert(result === 2) + }) + + it('returns the number of weeks between the given dates with `floor` passed in as a rounding method', () => { + const result = differenceInWeeks( + new Date(2014, 6 /* Jul */, 8, 18, 0), + new Date(2014, 5 /* Jun */, 29, 6, 0), + { roundingMethod: 'floor' } + ) + assert(result === 1) + }) + + it('returns the number of weeks between the given dates with `round` passed in as a rounding method', () => { + const result = differenceInWeeks( + new Date(2014, 6 /* Jul */, 10, 18, 0), + new Date(2014, 5 /* Jun */, 29, 6, 0), + { roundingMethod: 'round' } + ) + assert(result === 2) + }) + + it('returns a negative number if the time value of the first date is smaller', () => { const result = differenceInWeeks( new Date(2014, 5 /* Jun */, 29, 6, 0), new Date(2014, 6 /* Jul */, 8, 18, 0) @@ -20,7 +64,15 @@ describe('differenceInWeeks', function () { assert(result === -1) }) - it('accepts timestamps', function () { + it('returns a 0, not a negative 0 - issue #2555 ', () => { + const result = differenceInWeeks( + new Date(2021, 6 /* Jul */, 22, 6, 1, 28.973), + new Date(2021, 6 /* Jul */, 22, 6, 1, 28.976) + ) + assert(result === 0) + }) + + it('accepts timestamps', () => { const result = differenceInWeeks( new Date(2014, 6 /* Jul */, 12).getTime(), new Date(2014, 6 /* Jul */, 2).getTime() @@ -28,8 +80,8 @@ describe('differenceInWeeks', function () { assert(result === 1) }) - describe('edge cases', function () { - it('the difference is less than a week, but the given dates are in different calendar weeks', function () { + describe('edge cases', () => { + it('the difference is less than a week, but the given dates are in different calendar weeks', () => { const result = differenceInWeeks( new Date(2014, 6 /* Jul */, 6), new Date(2014, 6 /* Jul */, 5) @@ -37,7 +89,7 @@ describe('differenceInWeeks', function () { assert(result === 0) }) - it('the same for the swapped dates', function () { + it('the same for the swapped dates', () => { const result = differenceInWeeks( new Date(2014, 6 /* Jul */, 5), new Date(2014, 6 /* Jul */, 6) @@ -45,7 +97,7 @@ describe('differenceInWeeks', function () { assert(result === 0) }) - it('days of weeks of the given dates are the same', function () { + it('days of weeks of the given dates are the same', () => { const result = differenceInWeeks( new Date(2014, 6 /* Jul */, 9), new Date(2014, 6 /* Jul */, 2) @@ -53,7 +105,7 @@ describe('differenceInWeeks', function () { assert(result === 1) }) - it('the given dates are the same', function () { + it('the given dates are the same', () => { const result = differenceInWeeks( new Date(2014, 8 /* Sep */, 5, 0, 0), new Date(2014, 8 /* Sep */, 5, 0, 0) @@ -76,7 +128,7 @@ describe('differenceInWeeks', function () { }) }) - it('returns NaN if the first date is `Invalid Date`', function () { + it('returns NaN if the first date is `Invalid Date`', () => { const result = differenceInWeeks( new Date(NaN), new Date(2017, 0 /* Jan */, 1) @@ -84,7 +136,7 @@ describe('differenceInWeeks', function () { assert(isNaN(result)) }) - it('returns NaN if the second date is `Invalid Date`', function () { + it('returns NaN if the second date is `Invalid Date`', () => { const result = differenceInWeeks( new Date(2017, 0 /* Jan */, 1), new Date(NaN) @@ -92,13 +144,15 @@ describe('differenceInWeeks', function () { assert(isNaN(result)) }) - it('returns NaN if the both dates are `Invalid Date`', function () { + it('returns NaN if the both dates are `Invalid Date`', () => { const result = differenceInWeeks(new Date(NaN), new Date(NaN)) assert(isNaN(result)) }) - it('throws TypeError exception if passed less than 2 arguments', function () { + it('throws TypeError exception if passed less than 2 arguments', () => { + // @ts-expect-error assert.throws(differenceInWeeks.bind(null), TypeError) + // @ts-expect-error assert.throws(differenceInWeeks.bind(null, 1), TypeError) }) }) diff --git a/src/types.ts b/src/types.ts index b3d9091228..93b4cf98d3 100644 --- a/src/types.ts +++ b/src/types.ts @@ -55,4 +55,10 @@ export interface DateValues { milliseconds?: number } +export type RoundingMethod = 'ceil' | 'floor' | 'round' | 'trunc' + +export interface RoundingOptions { + roundingMethod?: RoundingMethod +} + export type Unit = 'second' | 'minute' | 'hour' | 'day' | 'month' | 'year'