Skip to content

Commit

Permalink
Allow negative and invalid intervals in intervalToDuration
Browse files Browse the repository at this point in the history
Also reduce invalid and 0 values, resulting in more compact duration
being returned.
  • Loading branch information
kossnocorp committed Nov 11, 2023
1 parent e79ddbf commit 98fbbcc
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 90 deletions.
37 changes: 19 additions & 18 deletions src/intervalToDuration/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,6 @@ import type { Duration, Interval } from '../types'
*
* @returns The duration object
*
* @throws {RangeError} `start` must not be Invalid Date
* @throws {RangeError} `end` must not be Invalid Date
* @throws {RangeError} The start of an interval cannot be after its end
*
* @example
* // Get the duration between January 15, 1929 and April 4, 1968.
* intervalToDuration({
Expand All @@ -34,36 +30,41 @@ import type { Duration, Interval } from '../types'
* })
* // => { years: 39, months: 2, days: 20, hours: 7, minutes: 5, seconds: 0 }
*/
export default function interval<DateType extends Date>(
export default function intervalToDuration<DateType extends Date>(
interval: Interval<DateType>
): Duration {
const start = toDate(interval.start)
const end = toDate(interval.end)

if (isNaN(start.getTime())) throw new RangeError('Start Date is invalid')
if (isNaN(end.getTime())) throw new RangeError('End Date is invalid')
if (start > end) {
throw new RangeError('The start of an interval cannot be after its end')
}
const duration: Duration = {}

const duration: Duration = {
years: differenceInYears(end, start),
}
const years = differenceInYears(end, start)
if (years) duration.years = years

const remainingMonths = add(start, { years: duration.years })
duration.months = differenceInMonths(end, remainingMonths)

const months = differenceInMonths(end, remainingMonths)
if (months) duration.months = months

const remainingDays = add(remainingMonths, { months: duration.months })
duration.days = differenceInDays(end, remainingDays)

const days = differenceInDays(end, remainingDays)
if (days) duration.days = days

const remainingHours = add(remainingDays, { days: duration.days })
duration.hours = differenceInHours(end, remainingHours)

const hours = differenceInHours(end, remainingHours)
if (hours) duration.hours = hours

const remainingMinutes = add(remainingHours, { hours: duration.hours })
duration.minutes = differenceInMinutes(end, remainingMinutes)

const minutes = differenceInMinutes(end, remainingMinutes)
if (minutes) duration.minutes = minutes

const remainingSeconds = add(remainingMinutes, { minutes: duration.minutes })
duration.seconds = differenceInSeconds(end, remainingSeconds)

const seconds = differenceInSeconds(end, remainingSeconds)
if (seconds) duration.seconds = seconds

return duration
}
82 changes: 10 additions & 72 deletions src/intervalToDuration/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ describe('intervalToDuration', () => {
days: 20,
hours: 7,
minutes: 5,
seconds: 0,
})
})

Expand All @@ -41,41 +40,37 @@ describe('intervalToDuration', () => {
const end = new Date(2020, 2, 1, 12, 0, 0)
const result = intervalToDuration({ start, end })

assert.deepStrictEqual(result, {
years: 0,
months: 0,
days: 0,
hours: 0,
minutes: 0,
seconds: 0,
})
assert.deepStrictEqual(result, {})
})

it("throws a RangeError if interval's start date is greater than its end date", () => {
it("returns a negative duration if interval's start date is greater than its end date", () => {
const interval = {
start: new Date(2020, 3, 1),
end: new Date(2020, 2, 1),
}
const result = intervalToDuration(interval)

assert.throws(intervalToDuration.bind(null, interval), RangeError)
assert.deepStrictEqual(result, { months: -1 })
})

it("throws a RangeError if interval's start date invalid", () => {
it("returns an empty object if interval's start date invalid", () => {
const interval = {
start: new Date(NaN),
end: new Date(2020, 0, 1),
}
const result = intervalToDuration(interval)

assert.throws(intervalToDuration.bind(null, interval), RangeError)
assert.deepStrictEqual(result, {})
})

it("throws a RangeError if interval's end date invalid", () => {
it("returns an empty object if interval's end date invalid", () => {
const interval = {
start: new Date(2020, 0, 1),
end: new Date(NaN),
}
const result = intervalToDuration(interval)

assert.throws(intervalToDuration.bind(null, interval), RangeError)
assert.deepStrictEqual(result, {})
})

describe('edge cases', () => {
Expand All @@ -86,12 +81,8 @@ describe('intervalToDuration', () => {
end: new Date(2012, 1 /* Feb */, 29, 10, 0, 0),
}),
{
years: 0,
months: 0,
days: 1,
hours: 1,
minutes: 0,
seconds: 0,
}
)

Expand All @@ -101,12 +92,7 @@ describe('intervalToDuration', () => {
end: new Date(2012, 1 /* Feb */, 29, 10, 0, 0),
}),
{
years: 0,
months: 0,
days: 0,
hours: 1,
minutes: 0,
seconds: 0,
}
)

Expand All @@ -116,12 +102,7 @@ describe('intervalToDuration', () => {
end: new Date(2012, 1 /* Feb */, 28, 10, 0, 0),
}),
{
years: 0,
months: 0,
days: 0,
hours: 1,
minutes: 0,
seconds: 0,
}
)

Expand All @@ -132,10 +113,6 @@ describe('intervalToDuration', () => {
end: new Date(2021, 1 /* Feb */, 28, 7, 38, 18),
}),
{
years: 0,
months: 0,
days: 0,
hours: 0,
minutes: 15,
seconds: 11,
}
Expand All @@ -150,12 +127,7 @@ describe('intervalToDuration', () => {

const duration = intervalToDuration({ start, end })
const expectedDuration = {
years: 0,
months: 1,
days: 0,
hours: 0,
minutes: 0,
seconds: 0,
}

assert.deepStrictEqual(duration, expectedDuration)
Expand All @@ -167,12 +139,7 @@ describe('intervalToDuration', () => {
end: new Date(2020, 2, 29),
})
const expectedDuration = {
years: 0,
months: 1,
days: 0,
hours: 0,
minutes: 0,
seconds: 0,
}

assert.deepStrictEqual(duration, expectedDuration)
Expand All @@ -184,12 +151,8 @@ describe('intervalToDuration', () => {
end: new Date(2022, 3, 30),
})
const expectedDuration = {
years: 0,
months: 2,
days: 2,
hours: 0,
minutes: 0,
seconds: 0,
}

assert.deepStrictEqual(duration, expectedDuration)
Expand All @@ -202,12 +165,8 @@ describe('intervalToDuration', () => {
end: new Date(2021, 7, 31),
})
const expectedDuration = {
years: 0,
months: 6,
days: 3,
hours: 0,
minutes: 0,
seconds: 0,
}

assert.deepStrictEqual(duration, expectedDuration)
Expand All @@ -219,12 +178,8 @@ describe('intervalToDuration', () => {
end: new Date(2021, 7, 30),
})
const expectedDuration = {
years: 0,
months: 6,
days: 2,
hours: 0,
minutes: 0,
seconds: 0,
}

assert.deepStrictEqual(duration, expectedDuration)
Expand All @@ -236,12 +191,8 @@ describe('intervalToDuration', () => {
end: new Date(2021, 7, 29),
})
const expectedDuration = {
years: 0,
months: 6,
days: 1,
hours: 0,
minutes: 0,
seconds: 0,
}

assert.deepStrictEqual(duration, expectedDuration)
Expand All @@ -253,12 +204,7 @@ describe('intervalToDuration', () => {
end: new Date(2021, 7, 28),
})
const expectedDuration = {
years: 0,
months: 6,
days: 0,
hours: 0,
minutes: 0,
seconds: 0,
}

assert.deepStrictEqual(duration, expectedDuration)
Expand All @@ -272,12 +218,8 @@ describe('intervalToDuration', () => {
end: new Date(2021, 7, 27),
})
const expectedDuration = {
years: 0,
months: 5,
days: 30,
hours: 0,
minutes: 0,
seconds: 0,
}

assert.deepStrictEqual(duration, expectedDuration)
Expand All @@ -289,12 +231,8 @@ describe('intervalToDuration', () => {
end: new Date(2021, 4, 31),
})
const expectedDuration = {
years: 0,
months: 1,
days: 1,
hours: 0,
minutes: 0,
seconds: 0,
}

assert.deepStrictEqual(duration, expectedDuration)
Expand Down

0 comments on commit 98fbbcc

Please sign in to comment.