Skip to content

Commit

Permalink
Add roundingMethod option to differenceInX functions (date-fns#2571) (
Browse files Browse the repository at this point in the history
closes date-fns#2555)

Added `roundingMethod` option to `differenceInHours`, `differenceInMinutes`, `differenceInQuarters`, `differenceInSeconds` and `differenceInWeeks` with `trunc` as the default method. 

Co-authored-by: Lucas Silva <lucas.silva@codeminer42.com>
Co-authored-by: Tetiana <ttobin@protonmail.ch>
Co-authored-by: Sasha Koss <koss@nocorp.me>
  • Loading branch information
4 people committed Dec 27, 2021
1 parent 8b76a5d commit b884642
Show file tree
Hide file tree
Showing 17 changed files with 515 additions and 204 deletions.
18 changes: 18 additions & 0 deletions 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]
}
20 changes: 13 additions & 7 deletions 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
Expand All @@ -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)
}
79 changes: 65 additions & 14 deletions src/differenceInHours/test.ts
@@ -1,60 +1,111 @@
// @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)
)
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)
)
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()
)
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)
)
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)
)
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)
)
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)
Expand All @@ -77,28 +128,28 @@ 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)
)
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)
)
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
Expand Down
8 changes: 3 additions & 5 deletions src/differenceInMilliseconds/index.ts
Expand Up @@ -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()
}
5 changes: 3 additions & 2 deletions 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 () {
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
})
})
@@ -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
Expand All @@ -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)
}

0 comments on commit b884642

Please sign in to comment.