Skip to content

Commit

Permalink
feat: diff functions
Browse files Browse the repository at this point in the history
* differenceInMilliseconds

* created the other constant difference functions, but will need to add the option 'ignoreTime' for day/week version

* created the other constant difference functions

* completing tests

* adding differenceInX exports in index

* make sure that rounding doesn't give -0

* added difference In Months & years

* shortened the all difference functions to diffX
moved using monthDays at diffMonths after `if (ld < rd)`

* raname type DifferenceRoundingMethod -> DiffRoundingMethod
  • Loading branch information
WilcoSp authored Apr 17, 2024
1 parent c1fd5ab commit 809d4c6
Show file tree
Hide file tree
Showing 18 changed files with 334 additions and 0 deletions.
20 changes: 20 additions & 0 deletions src/__tests__/diffDays.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { describe, expect, it } from "vitest"
import { diffDays } from "../diffDays"

describe("differenceInDays", () => {
it("difference is 3 days", () => {
expect(diffDays("2024-04-10", "2024-04-07")).toBe(3)
})

it("difference is 2 days", () => {
expect(
diffDays("2024-04-10T09:50:00.000Z", "2024-04-07T15:28:00.000Z")
).toBe(2)
})

it("difference is 3 days by using round", () => {
expect(
diffDays("2024-04-10T09:50:00.000Z", "2024-04-07T15:28:00.000Z", "round")
).toBe(3)
})
})
10 changes: 10 additions & 0 deletions src/__tests__/diffHours.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { describe, expect, it } from "vitest"
import { diffHours } from "../diffHours"

describe("differenceInHours", () => {
it("difference is 5 hours", () => {
expect(
diffHours("2024-04-07T15:28:00.000Z", "2024-04-07T09:50:00.000Z")
).toBe(5)
})
})
10 changes: 10 additions & 0 deletions src/__tests__/diffMilliseconds.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { describe, it, expect } from "vitest"
import { diffMilliseconds } from "../diffMilliseconds"

describe("differenceInMilliseconds", () => {
it("difference is 257 milliseconds", () => {
expect(
diffMilliseconds("2024-04-07T09:10:48.257Z", "2024-04-07T09:10:48.000Z")
).toBe(257)
})
})
19 changes: 19 additions & 0 deletions src/__tests__/diffMinutes.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { describe, it, expect } from "vitest"
import { diffMinutes } from "../diffMinutes"

describe("differenceInMinutes", () => {
it("difference is 18 minutes", () => {
expect(
diffMinutes("2024-04-07T09:28:30.050Z", "2024-04-07T09:10:00.000Z")
).toBe(18)
})
it("difference is 19 minutes by using ceil", () => {
expect(
diffMinutes(
"2024-04-07T09:28:01.050Z",
"2024-04-07T09:10:00.000Z",
"ceil"
)
).toBe(19)
})
})
32 changes: 32 additions & 0 deletions src/__tests__/diffMonths.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { describe, it, expect } from "vitest"
import { diffMonths } from "../diffMonths"

describe("differenceInMonths", () => {
it("should give 11 months", () => {
expect(diffMonths("2025-04-13", "2024-04-14")).toBe(11)
})

it("should give 12 months", () => {
expect(diffMonths("2025-04-14", "2024-04-14")).toBe(12)
})

it("should give a negative amount when the right side is in the future", () => {
expect(diffMonths("2024-04-14", "2025-04-15")).toBe(-12)
})

it("should give 8 months" /* till it's xmas */, () => {
expect(diffMonths("2024-12-25", "2024-04-13")).toBe(8)
})

it("should give 3 full months", () => {
expect(diffMonths("2024-04-13", "2023-12-25")).toBe(3) // not yet full 4 months
})

it("should still be a full month even if the left one is shorter", () => {
expect(diffMonths("2024-02-29", "2024-01-31")).toBe(1)
})

it("should also be a negative full month when swapped", () => {
expect(diffMonths("2024-01-31", "2024-02-29")).toBe(-1)
})
})
10 changes: 10 additions & 0 deletions src/__tests__/diffSeconds.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { describe, it, expect } from "vitest"
import { diffSeconds } from "../diffSeconds"

describe("differenceInSeconds", () => {
it("difference is 28 seconds", () => {
expect(
diffSeconds("2024-04-07T09:10:28.900Z", "2024-04-07T09:10:00.000Z")
).toBe(28)
})
})
8 changes: 8 additions & 0 deletions src/__tests__/diffWeeks.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { describe, expect, it } from "vitest"
import { diffWeeks } from "../diffWeeks"

describe("differenceInWeeks", () => {
it("difference is 5 hours", () => {
expect(diffWeeks("2025-06-30", "2024-04-07")).toBe(64)
})
})
48 changes: 48 additions & 0 deletions src/__tests__/diffYears.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { describe, it, expect, suite } from "vitest"
import { diffYears } from "../diffYears"

describe("differenceInYears", () => {
it("returns the amount of full years between dates", () => {
expect(diffYears("2025-04-20", "2024-03-31")).toBe(1)
})

it("returns a negative number when swapped around as the first is now smaller", () => {
expect(diffYears("2024-03-31", "2025-04-20")).toBe(-1)
})

// date-fns had a lot of tests for the leap day
// although because differenceInMonth/year doesn't depend on time it shouldn't be an issue
suite("leap days", () => {
it("supports right side dates that are after a leap day", () => {
expect(diffYears("2024-02-29", "2022-03-01")).toBe(1)
})

it("And also supports if right side date is before the leap date", () => {
expect(diffYears("2024-02-29", "2022-02-28")).toBe(2)
})

it("supports future dates", () => {
expect(diffYears("2024-02-29", "2026-03-10")).toBe(-2)
})

it("equal (leap) day give 0", () => {
expect(diffYears("2024-02-28", "2024-02-28")).toBe(0)
})

it("Futute (leap) dates supported", () => {
expect(diffYears("2028-02-29", "2024-02-29")).toBe(4)
})
})

// some of the edge cases are also tested with leap days
suite("edge cases", () => {
it("difference is less than a year because of 1 day difference", () => {
// NL kings day
expect(diffYears("2025-04-26", "2024-04-27")).toBe(0)
})

it("same but swapped", () => {
expect(diffYears("2024-04-27", "2025-04-26")).toBe(0)
})
})
})
20 changes: 20 additions & 0 deletions src/diffDays.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { diffMilliseconds } from "./diffMilliseconds"
import { DateInput } from "./types"
import { diffRound, type DiffRoundingMethod } from "./diffRound"

/**
* Returns the difference between 2 dates in days.
* @param leftDate A date to compare with the right date
* @param rightDate A date to compare with the left date
* @param roundingMethod the rounding method to use, default: trunc
*/
export function diffDays(
leftDate: DateInput,
rightDate: DateInput,
roundingMethod?: DiffRoundingMethod
) {
return diffRound(
diffMilliseconds(leftDate, rightDate) / 86_400_000, // hour * 24
roundingMethod
)
}
20 changes: 20 additions & 0 deletions src/diffHours.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { diffMilliseconds } from "./diffMilliseconds"
import { diffRound, type DiffRoundingMethod } from "./diffRound"
import { DateInput } from "./types"

/**
* Returns the difference between 2 dates in hours.
* @param leftDate A date to compare with the right date
* @param rightDate A date to compare with the left date
* @param roundingMethod the rounding method to use, default: trunc
*/
export function diffHours(
leftDate: DateInput,
rightDate: DateInput,
roundingMethod?: DiffRoundingMethod
) {
return diffRound(
diffMilliseconds(leftDate, rightDate) / 3_600_000, // 1000 * 60 * 60
roundingMethod
)
}
13 changes: 13 additions & 0 deletions src/diffMilliseconds.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { date } from "./date"
import { DateInput } from "./types"

/**
* Returns the difference between 2 dates in milliseconds.
* @param leftDate A date to compare with the right date
* @param rightDate A date to compare with the left date
*/
export function diffMilliseconds(leftDate: DateInput, rightDate: DateInput) {
const left = date(leftDate)
const right = date(rightDate)
return +left - +right
}
19 changes: 19 additions & 0 deletions src/diffMinutes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { DateInput } from "./types"
import { diffMilliseconds } from "./diffMilliseconds"
import { diffRound, type DiffRoundingMethod } from "./diffRound"

/**
* Returns the difference between 2 dates in minutes.
* @param leftDate A date to compare with the right date
* @param roundingMethod the rounding method to use, default: trunc
*/
export function diffMinutes(
leftDate: DateInput,
rightDate: DateInput,
roundingMethod?: DiffRoundingMethod
) {
return diffRound(
diffMilliseconds(leftDate, rightDate) / 60_000,
roundingMethod
)
}
36 changes: 36 additions & 0 deletions src/diffMonths.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { date } from "./date"
import { DateInput } from "./types"
import { monthDays } from "./monthDays"

/**
* Returns the difference between 2 dates in months.
* @param left A date to compare with the right date
* @param right A date to compare with the left date
*/
export function diffMonths(left: DateInput, right: DateInput): number {
const l = date(left)
const r = date(right)
// if the right one is bigger, we switch them around as it's easier to do
if (l < r) {
const rs = diffMonths(r, l)
return rs == 0 ? 0 : -rs
}

// we first get the amount of calendar months
let months =
(l.getFullYear() - r.getFullYear()) * 12 + (l.getMonth() - r.getMonth())

const ld = l.getDate()
const rd = r.getDate()

// if no full month has passed we may subtract a month from the calendar months so we get the amount of full months
if (ld < rd) {
// in case left date is the last day of the month & the right date is higher, we don't subtract as a full month did actually pass
const lm = monthDays(l)
if (!(lm == ld && lm < rd)) {
months--
}
}
//ensures we don't give back -0
return months == 0 ? 0 : months
}
11 changes: 11 additions & 0 deletions src/diffRound.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export type DiffRoundingMethod = "trunc" | "round" | "floor" | "ceil"

/**
* Return a rounded value with the given rounding method
* @param value the value to round
* @param method the rounding method
*/
export function diffRound(value: number, method: DiffRoundingMethod = "trunc") {
const r = Math[method](value)
return r == 0 ? 0 : r
}
17 changes: 17 additions & 0 deletions src/diffSeconds.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { diffMilliseconds } from "./diffMilliseconds"
import { DiffRoundingMethod, diffRound } from "./diffRound"
import { DateInput } from "./types"

/**
* Returns the difference between 2 dates in seconds.
* @param leftDate A date to compare with the right date
* @param rightDate A date to compare with the left date
* @param roundingMethod the rounding method to use, default: trunc
*/
export function diffSeconds(
leftDate: DateInput,
rightDate: DateInput,
roundingMethod?: DiffRoundingMethod
) {
return diffRound(diffMilliseconds(leftDate, rightDate) / 1000, roundingMethod)
}
20 changes: 20 additions & 0 deletions src/diffWeeks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { diffMilliseconds } from "./diffMilliseconds"
import { DateInput } from "./types"
import { diffRound, type DiffRoundingMethod } from "./diffRound"

/**
* Returns the difference between 2 dates in days.
* @param leftDate A date to compare with the right date
* @param rightDate A date to compare with the left date
* @param roundingMethod the rounding method to use, default: trunc
*/
export function diffWeeks(
leftDate: DateInput,
rightDate: DateInput,
roundingMethod?: DiffRoundingMethod
) {
return diffRound(
diffMilliseconds(leftDate, rightDate) / 604800000, // day * 7
roundingMethod
)
}
13 changes: 13 additions & 0 deletions src/diffYears.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { diffMonths } from "./diffMonths"
import { DateInput } from "./types"

/**
* Returns the difference between 2 dates in years.
* @param left A date to compare with the right date
* @param right A date to compare with the left date
*/
export function diffYears(left: DateInput, right: DateInput): number {
const r = Math.trunc(diffMonths(left, right) / 12)
//ensures we don't give back -0
return r == 0 ? 0 : r
}
8 changes: 8 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,11 @@ export { isBefore } from "./isBefore"
export { isAfter } from "./isAfter"
export { isEqual } from "./isEqual"
export * from "./types"
export { diffMilliseconds } from "./diffMilliseconds"
export { diffSeconds } from "./diffSeconds"
export { diffMinutes } from "./diffMinutes"
export { diffHours } from "./diffHours"
export { diffDays } from "./diffDays"
export { diffWeeks } from "./diffWeeks"
export { diffMonths } from "./diffMonths"
export { diffYears } from "./diffYears"

0 comments on commit 809d4c6

Please sign in to comment.