Skip to content

Commit

Permalink
add new function roundToNearestHours
Browse files Browse the repository at this point in the history
  • Loading branch information
sakamossan committed Oct 1, 2022
1 parent b8b7e52 commit da83376
Show file tree
Hide file tree
Showing 2 changed files with 124 additions and 0 deletions.
59 changes: 59 additions & 0 deletions src/roundToNearestHours/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import constructFrom from '../constructFrom/index'
import toDate from '../toDate/index'
import type { RoundingOptions } from '../types'
import { getRoundingMethod } from '../_lib/roundingMethods/index'

/**
* The {@link roundToNearestHours} function options.
*/
export interface RoundToNearestHoursOptions extends RoundingOptions {
nearestTo?: number
}

/**
* @name roundToNearestHours
* @category Hour Helpers
* @summary Rounds the given date to the nearest hour
*
* @description
* Rounds the given date to the nearest hour (or number of hours).
* Rounds up when the given date is exactly between the nearest rounded hour.
*
* @param date - the date to round
* @param options - an object with options.
* @returns {Date} the new date rounded to the closest hour
* @throws {RangeError} `options.nearestTo` must be between 1 and 12
*
* @example
* // Round 10 July 2014 12:12:34 to nearest hour:
* var result = roundToNearestHours(new Date(2014, 6, 10, 12, 12, 34))
* //=> Thu Jul 10 2014 12:00:00
*
* @example
* // Round 10 July 2014 13:12:34 to nearest quarter of day:
* var result = roundToNearestHours(new Date(2014, 6, 10, 13, 12, 34), { nearestTo: 6 })
* // rounds up because given date is exactly between 12:00:00 and 18:00:00
* //=> Thu Jul 9 2014 12:00:00
*/
export default function roundToNearestHours(
dirtyDate: Date | number,
options?: RoundToNearestHoursOptions
): Date {
const nearestTo = options?.nearestTo ?? 1

if (nearestTo < 1 || nearestTo > 12) {
throw new RangeError('`options.nearestTo` must be between 1 and 12')
}

const date = toDate(dirtyDate)
const minutes = date.getMinutes() // relevant if nearestTo is 1, which is the default case
const hours = date.getHours() + minutes / 60
const roundingMethod = getRoundingMethod(options?.roundingMethod)
const roundedHour = roundingMethod(hours / nearestTo) * nearestTo
const remainderHour = hours % nearestTo
const addedHour = Math.round(remainderHour / nearestTo) * nearestTo

const result = constructFrom(date, date)
result.setHours(roundedHour + addedHour, 0, 0, 0)
return result
}
65 changes: 65 additions & 0 deletions src/roundToNearestHours/test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/* eslint-env mocha */

import assert from 'assert'
import roundToNearestHours from './index'

describe('roundToNearestHours', function () {
it('rounds given date to the nearest hour by default', function () {
const result = roundToNearestHours(
new Date(2014, 6 /* Jul */, 10, 12, 16, 16)
)
assert.deepStrictEqual(result, new Date(2014, 6 /* Jul */, 10, 12, 0, 0))
})

it('accepts a timestamp', function () {
const result = roundToNearestHours(
new Date(2014, 6 /* Jul */, 10, 12, 13, 16).getTime()
)
assert.deepStrictEqual(result, new Date(2014, 6 /* Jul */, 10, 12, 0, 0))
})

it('rounds to the closest x hours if nearestTo is provided', function () {
const result = roundToNearestHours(
new Date(2014, 6 /* Jul */, 10, 10, 10, 30),
{ nearestTo: 4 }
)
assert.deepStrictEqual(result, new Date(2014, 6 /* Jul */, 10, 12, 0, 0))
})

it('rounds up >=30 minutes for nearestTo=1', function () {
const result = roundToNearestHours(
new Date(2014, 6 /* Jul */, 10, 12, 30, 0)
)
assert.deepStrictEqual(result, new Date(2014, 6 /* Jul */, 10, 13, 0, 0))
})

it('rounds down <30 minutes for nearestTo=1', function () {
const result = roundToNearestHours(
new Date(2014, 6 /* Jul */, 10, 12, 13, 29, 999)
)
assert.deepStrictEqual(result, new Date(2014, 6 /* Jul */, 10, 12, 0, 0))
})

it('does not mutate the original date', function () {
const date = new Date(2014, 6 /* Jul */, 10, 12, 10, 10, 99)
roundToNearestHours(date)
assert.deepStrictEqual(date, new Date(2014, 6 /* Jul */, 10, 12, 10, 10, 99))
})

it('returns `Invalid Date` if the given date is invalid', function () {
const result = roundToNearestHours(new Date(NaN))
assert(result instanceof Date && isNaN(result.getTime()))
})

it('throws `RangeError` if nearestTo is not between 1 and 12', function () {
const date = new Date(2014, 6 /* Jul */, 10, 12, 10, 30)
assert.throws(
roundToNearestHours.bind(null, date, { nearestTo: 13 }),
RangeError
)
assert.throws(
roundToNearestHours.bind(null, date, { nearestTo: 0 }),
RangeError
)
})
})

0 comments on commit da83376

Please sign in to comment.