From 327f40baa8dd52da03f967aea0310351a3d6139c Mon Sep 17 00:00:00 2001 From: Dominic Baur Date: Tue, 21 Apr 2026 15:52:51 +0200 Subject: [PATCH 01/13] up --- src/lib/date.spec.ts | 6 ++++++ src/lib/date.ts | 10 ++++++++++ 2 files changed, 16 insertions(+) diff --git a/src/lib/date.spec.ts b/src/lib/date.spec.ts index 71a1393..1f46659 100644 --- a/src/lib/date.spec.ts +++ b/src/lib/date.spec.ts @@ -162,4 +162,10 @@ describe("date tests", () => { expect(getLastDayOfYear(date).getTime()).toBe(new Date(2024, 11, 31, 0, 0, 0, 0).getTime()); } }); + + test("get ", () => { + for (let date = new Date(2024, 0, 1); date < new Date(2025, 0, 1); date.setDate(date.getDate() + 1)) { + expect(getLastDayOfYear(date).getTime()).toBe(new Date(2024, 11, 31, 0, 0, 0, 0).getTime()); + } + }); }); diff --git a/src/lib/date.ts b/src/lib/date.ts index 8039339..cd40293 100644 --- a/src/lib/date.ts +++ b/src/lib/date.ts @@ -10,6 +10,7 @@ import { startOfDay, endOfDay, lastDayOfYear, + getQuarter, } from "date-fns"; /** @@ -112,3 +113,12 @@ export function getFirstDayOfYear(date: Date): Date { export function getLastDayOfYear(date: Date): Date { return dateIsValid(date) ? lastDayOfYear(date) : new Date(Number.NaN); } + +/** + * Get the current quarter + * @param date The date + * @returns the number of the current quarter + */ +export function getCurrentQuarter(date: Date): number { + return dateIsValid(date) ? getQuarter(date) : Number.NaN; +} From 9d123f72690a88b1ab44f55cab6820c65e6e2957 Mon Sep 17 00:00:00 2001 From: Dominic Baur Date: Tue, 21 Apr 2026 16:03:07 +0200 Subject: [PATCH 02/13] up --- CHANGELOG.md | 1 + src/lib/date.spec.ts | 22 ++++++++++++++++++---- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 52990f6..0c96f4f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - `parseEnum` utility function +- `getCurrentQuarter` utility function ## [2.2.0] - 2025-10-02 diff --git a/src/lib/date.spec.ts b/src/lib/date.spec.ts index 1f46659..4589111 100644 --- a/src/lib/date.spec.ts +++ b/src/lib/date.spec.ts @@ -10,6 +10,7 @@ import { getEndOfDay, getFirstDayOfYear, getLastDayOfYear, + getCurrentQuarter, } from "./date"; describe("date tests", () => { @@ -163,9 +164,22 @@ describe("date tests", () => { } }); - test("get ", () => { - for (let date = new Date(2024, 0, 1); date < new Date(2025, 0, 1); date.setDate(date.getDate() + 1)) { - expect(getLastDayOfYear(date).getTime()).toBe(new Date(2024, 11, 31, 0, 0, 0, 0).getTime()); - } + test.each([ + [new Date("2026-01-15"), 1], + [new Date("2026-04-10"), 2], + [new Date("2026-08-20"), 3], + [new Date("2026-11-05"), 4], + ])("getCurrentQuarter", (date, expected) => { + expect(getCurrentQuarter(date)).toBe(expected); + }); + + test.each([ + [null as unknown as Date, Number.NaN], + [undefined as unknown as Date, Number.NaN], + [42 as unknown as Date, Number.NaN], + ["test" as unknown as Date, Number.NaN], + [new Date("invalid-date"), Number.NaN], + ])("getCurrentQuarter", (date) => { + expect(getCurrentQuarter(date)).toBeNaN(); }); }); From bb3a08b32f2e8971cf93510f9219a9e467c5c97d Mon Sep 17 00:00:00 2001 From: Dominic Baur Date: Tue, 21 Apr 2026 17:30:16 +0200 Subject: [PATCH 03/13] fix changelog --- CHANGELOG.md | 5 ++++- src/lib/date.spec.ts | 6 +++--- src/lib/date.ts | 6 +++--- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c96f4f..cedfbb7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,12 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- `getQuarter` utility function + ## [2.3.0] - 2026-04-07 ### Added - `parseEnum` utility function -- `getCurrentQuarter` utility function ## [2.2.0] - 2025-10-02 diff --git a/src/lib/date.spec.ts b/src/lib/date.spec.ts index 4589111..45813cf 100644 --- a/src/lib/date.spec.ts +++ b/src/lib/date.spec.ts @@ -10,7 +10,7 @@ import { getEndOfDay, getFirstDayOfYear, getLastDayOfYear, - getCurrentQuarter, + getQuarter, } from "./date"; describe("date tests", () => { @@ -170,7 +170,7 @@ describe("date tests", () => { [new Date("2026-08-20"), 3], [new Date("2026-11-05"), 4], ])("getCurrentQuarter", (date, expected) => { - expect(getCurrentQuarter(date)).toBe(expected); + expect(getQuarter(date)).toBe(expected); }); test.each([ @@ -180,6 +180,6 @@ describe("date tests", () => { ["test" as unknown as Date, Number.NaN], [new Date("invalid-date"), Number.NaN], ])("getCurrentQuarter", (date) => { - expect(getCurrentQuarter(date)).toBeNaN(); + expect(getQuarter(date)).toBeNaN(); }); }); diff --git a/src/lib/date.ts b/src/lib/date.ts index cd40293..4ad6681 100644 --- a/src/lib/date.ts +++ b/src/lib/date.ts @@ -10,7 +10,7 @@ import { startOfDay, endOfDay, lastDayOfYear, - getQuarter, + getQuarter as getQuarterInternal, } from "date-fns"; /** @@ -119,6 +119,6 @@ export function getLastDayOfYear(date: Date): Date { * @param date The date * @returns the number of the current quarter */ -export function getCurrentQuarter(date: Date): number { - return dateIsValid(date) ? getQuarter(date) : Number.NaN; +export function getQuarter(date: Date): number { + return dateIsValid(date) ? getQuarterInternal(date) : Number.NaN; } From 7cf6e07546564af5161bb1fc77a4e33fbf1c686c Mon Sep 17 00:00:00 2001 From: Dominic Baur Date: Tue, 21 Apr 2026 17:32:49 +0200 Subject: [PATCH 04/13] up --- src/lib/date.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/date.spec.ts b/src/lib/date.spec.ts index 45813cf..c571614 100644 --- a/src/lib/date.spec.ts +++ b/src/lib/date.spec.ts @@ -169,7 +169,7 @@ describe("date tests", () => { [new Date("2026-04-10"), 2], [new Date("2026-08-20"), 3], [new Date("2026-11-05"), 4], - ])("getCurrentQuarter", (date, expected) => { + ])("getQuarter", (date, expected) => { expect(getQuarter(date)).toBe(expected); }); @@ -179,7 +179,7 @@ describe("date tests", () => { [42 as unknown as Date, Number.NaN], ["test" as unknown as Date, Number.NaN], [new Date("invalid-date"), Number.NaN], - ])("getCurrentQuarter", (date) => { + ])("getQuarter", (date) => { expect(getQuarter(date)).toBeNaN(); }); }); From 3a7cf45b46558b629dc66bb8b8ea6da66cbab35b Mon Sep 17 00:00:00 2001 From: Dominic Baur Date: Tue, 21 Apr 2026 17:33:31 +0200 Subject: [PATCH 05/13] up --- src/lib/date.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/date.ts b/src/lib/date.ts index 4ad6681..2567f85 100644 --- a/src/lib/date.ts +++ b/src/lib/date.ts @@ -115,9 +115,9 @@ export function getLastDayOfYear(date: Date): Date { } /** - * Get the current quarter + * Get the quarter * @param date The date - * @returns the number of the current quarter + * @returns the number of the quarter */ export function getQuarter(date: Date): number { return dateIsValid(date) ? getQuarterInternal(date) : Number.NaN; From 6436273cb4618096cd0179ce753fe23b1b726d64 Mon Sep 17 00:00:00 2001 From: dom-baur Date: Wed, 22 Apr 2026 08:15:09 +0200 Subject: [PATCH 06/13] Update src/lib/date.ts Co-authored-by: Daniele Debernardi --- src/lib/date.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/date.ts b/src/lib/date.ts index 2567f85..bb982a0 100644 --- a/src/lib/date.ts +++ b/src/lib/date.ts @@ -117,7 +117,7 @@ export function getLastDayOfYear(date: Date): Date { /** * Get the quarter * @param date The date - * @returns the number of the quarter + * @returns The year quarter */ export function getQuarter(date: Date): number { return dateIsValid(date) ? getQuarterInternal(date) : Number.NaN; From a1784a5fa74b643027602dbff32eb6a808106d5c Mon Sep 17 00:00:00 2001 From: dom-baur Date: Wed, 22 Apr 2026 08:15:22 +0200 Subject: [PATCH 07/13] Update src/lib/date.ts Co-authored-by: Daniele Debernardi --- src/lib/date.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/date.ts b/src/lib/date.ts index bb982a0..48c0476 100644 --- a/src/lib/date.ts +++ b/src/lib/date.ts @@ -115,7 +115,7 @@ export function getLastDayOfYear(date: Date): Date { } /** - * Get the quarter + * Get the year quarter for the given date * @param date The date * @returns The year quarter */ From ae321e52d52604b2c49a67a64292c62a8626ea3e Mon Sep 17 00:00:00 2001 From: Dominic Baur Date: Wed, 22 Apr 2026 08:20:07 +0200 Subject: [PATCH 08/13] Update test --- src/lib/date.spec.ts | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/lib/date.spec.ts b/src/lib/date.spec.ts index c571614..7a5072a 100644 --- a/src/lib/date.spec.ts +++ b/src/lib/date.spec.ts @@ -165,6 +165,11 @@ describe("date tests", () => { }); test.each([ + [null as unknown as Date, Number.NaN], + [undefined as unknown as Date, Number.NaN], + [42 as unknown as Date, Number.NaN], + ["test" as unknown as Date, Number.NaN], + [new Date("invalid-date"), Number.NaN], [new Date("2026-01-15"), 1], [new Date("2026-04-10"), 2], [new Date("2026-08-20"), 3], @@ -172,14 +177,4 @@ describe("date tests", () => { ])("getQuarter", (date, expected) => { expect(getQuarter(date)).toBe(expected); }); - - test.each([ - [null as unknown as Date, Number.NaN], - [undefined as unknown as Date, Number.NaN], - [42 as unknown as Date, Number.NaN], - ["test" as unknown as Date, Number.NaN], - [new Date("invalid-date"), Number.NaN], - ])("getQuarter", (date) => { - expect(getQuarter(date)).toBeNaN(); - }); }); From 3f385fa28b9d98d2e75cd789f55b2c19f4d082ed Mon Sep 17 00:00:00 2001 From: Dominic Baur Date: Wed, 22 Apr 2026 10:02:32 +0200 Subject: [PATCH 09/13] up --- CHANGELOG.md | 1 + src/lib/date.spec.ts | 24 +++++++++++++++++++++--- src/lib/date.ts | 13 +++++++++++++ 3 files changed, 35 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cedfbb7..0a0b827 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - `getQuarter` utility function +- `getPreviousQuarter` utility function ## [2.3.0] - 2026-04-07 diff --git a/src/lib/date.spec.ts b/src/lib/date.spec.ts index 7a5072a..b0dfe95 100644 --- a/src/lib/date.spec.ts +++ b/src/lib/date.spec.ts @@ -11,18 +11,16 @@ import { getFirstDayOfYear, getLastDayOfYear, getQuarter, + getPreviousQuarter, } from "./date"; describe("date tests", () => { test.each([ - // Valid dates [new Date(), true], [new Date(new Date()), true], [new Date("2014-03-15"), true], [new Date(2014, 3, 15), true], [new Date(42), true], - - // Invalid dates [new Date(Number.MAX_VALUE), false], [new Date(Number.NaN), false], [new Date("2014-03-36"), false], @@ -177,4 +175,24 @@ describe("date tests", () => { ])("getQuarter", (date, expected) => { expect(getQuarter(date)).toBe(expected); }); + + test.each([[null as unknown as Date], [undefined as unknown as Date], [42 as unknown as Date], ["test" as unknown as Date]])( + "getQuarter invalid inputs", + (date) => { + expect(getQuarter(date)).toBeNaN(); + }, + ); + + test.each([ + [new Date("2026-01-15"), 4], + [new Date("2026-04-10"), 1], + [new Date("2026-08-20"), 2], + [new Date("2026-11-05"), 3], + ])("getPreviousQuarter", (date, expected) => { + expect(getPreviousQuarter(date)).toBe(expected); + }); + + test.each([null, undefined, 42, "test", new Date("invalid-date")] as unknown as Date[])("getPreviousQuarter invalid inputs", (date) => { + expect(getPreviousQuarter(date)).toBeNaN(); + }); }); diff --git a/src/lib/date.ts b/src/lib/date.ts index 48c0476..744a019 100644 --- a/src/lib/date.ts +++ b/src/lib/date.ts @@ -122,3 +122,16 @@ export function getLastDayOfYear(date: Date): Date { export function getQuarter(date: Date): number { return dateIsValid(date) ? getQuarterInternal(date) : Number.NaN; } + +/** + * Get the previous year quarter for the given date + * @param date The date + * @returns The previous year quarter + */ +export function getPreviousQuarter(date: Date): number { + if (!dateIsValid(date)) return Number.NaN; + + const currentQuarter = getQuarter(date); + + return currentQuarter === 1 ? 4 : currentQuarter - 1; +} From 70ea670fae144a341b142c32296258c93511a019 Mon Sep 17 00:00:00 2001 From: Dominic Baur Date: Wed, 22 Apr 2026 10:19:38 +0200 Subject: [PATCH 10/13] up --- src/lib/date.spec.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/lib/date.spec.ts b/src/lib/date.spec.ts index b0dfe95..e8f5458 100644 --- a/src/lib/date.spec.ts +++ b/src/lib/date.spec.ts @@ -16,6 +16,7 @@ import { describe("date tests", () => { test.each([ + // Valid dates [new Date(), true], [new Date(new Date()), true], [new Date("2014-03-15"), true], @@ -24,6 +25,8 @@ describe("date tests", () => { [new Date(Number.MAX_VALUE), false], [new Date(Number.NaN), false], [new Date("2014-03-36"), false], + + // Invalid dates [null as unknown as Date, false], [undefined as unknown as Date, false], [42 as unknown as Date, false], @@ -176,13 +179,6 @@ describe("date tests", () => { expect(getQuarter(date)).toBe(expected); }); - test.each([[null as unknown as Date], [undefined as unknown as Date], [42 as unknown as Date], ["test" as unknown as Date]])( - "getQuarter invalid inputs", - (date) => { - expect(getQuarter(date)).toBeNaN(); - }, - ); - test.each([ [new Date("2026-01-15"), 4], [new Date("2026-04-10"), 1], @@ -192,7 +188,11 @@ describe("date tests", () => { expect(getPreviousQuarter(date)).toBe(expected); }); - test.each([null, undefined, 42, "test", new Date("invalid-date")] as unknown as Date[])("getPreviousQuarter invalid inputs", (date) => { - expect(getPreviousQuarter(date)).toBeNaN(); - }); + test.each([[null as unknown as Date], [undefined as unknown as Date], [42 as unknown as Date], ["test" as unknown as Date]])( + "getQuarter and getPreviousQuarter invalid inputs", + (date) => { + expect(getQuarter(date)).toBeNaN(); + expect(getPreviousQuarter(date)).toBeNaN(); + }, + ); }); From ca15a47f06b08d07b9ef3224dd6442d4b5d21bf1 Mon Sep 17 00:00:00 2001 From: Dominic Baur Date: Wed, 29 Apr 2026 15:29:17 +0200 Subject: [PATCH 11/13] up --- CHANGELOG.md | 2 +- src/lib/date.spec.ts | 35 +++++++++++++++++++---------------- src/lib/date.ts | 14 ++++++-------- 3 files changed, 26 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a0b827..ff51720 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - `getQuarter` utility function -- `getPreviousQuarter` utility function +- `addQuarters` utility function ## [2.3.0] - 2026-04-07 diff --git a/src/lib/date.spec.ts b/src/lib/date.spec.ts index e8f5458..563174c 100644 --- a/src/lib/date.spec.ts +++ b/src/lib/date.spec.ts @@ -1,3 +1,4 @@ +/* eslint-disable max-lines */ import { dateIsValid, dateIsFirstDayOfMonth, @@ -10,8 +11,8 @@ import { getEndOfDay, getFirstDayOfYear, getLastDayOfYear, + addQuarters, getQuarter, - getPreviousQuarter, } from "./date"; describe("date tests", () => { @@ -180,19 +181,21 @@ describe("date tests", () => { }); test.each([ - [new Date("2026-01-15"), 4], - [new Date("2026-04-10"), 1], - [new Date("2026-08-20"), 2], - [new Date("2026-11-05"), 3], - ])("getPreviousQuarter", (date, expected) => { - expect(getPreviousQuarter(date)).toBe(expected); - }); - - test.each([[null as unknown as Date], [undefined as unknown as Date], [42 as unknown as Date], ["test" as unknown as Date]])( - "getQuarter and getPreviousQuarter invalid inputs", - (date) => { - expect(getQuarter(date)).toBeNaN(); - expect(getPreviousQuarter(date)).toBeNaN(); - }, - ); + [new Date(2026, 4, 29), 1, new Date(2026, 7, 29)], + [new Date(2026, 4, 29), 4, new Date(2027, 4, 29)], + [new Date(2026, 8, 15), -1, new Date(2026, 5, 15)], + [new Date(2026, 8, 15), -4, new Date(2025, 8, 15)], + ])("addQuarters", (date, amount, expected) => { + expect(addQuarters(date, amount).getTime()).toBe(expected.getTime()); + }); + + test.each([ + [null as unknown as Date, 1], + [undefined as unknown as Date, -1], + [42 as unknown as Date, 1], + ["test" as unknown as Date, -1], + [new Date("invalid-date"), 1], + ])("addQuarters with invalid inputs", (date, amount) => { + expect(addQuarters(date, amount).getTime()).toBe(Number.NaN); + }); }); diff --git a/src/lib/date.ts b/src/lib/date.ts index 744a019..b91f628 100644 --- a/src/lib/date.ts +++ b/src/lib/date.ts @@ -11,6 +11,7 @@ import { endOfDay, lastDayOfYear, getQuarter as getQuarterInternal, + addQuarters as addQuartersInternal, } from "date-fns"; /** @@ -124,14 +125,11 @@ export function getQuarter(date: Date): number { } /** - * Get the previous year quarter for the given date + * Shifts a date by a given number of quarters. * @param date The date - * @returns The previous year quarter + * @param amount Number of quarters to shift by (negative values shift the date backwards) + * @returns A new Date shifted by the specified number of quarters */ -export function getPreviousQuarter(date: Date): number { - if (!dateIsValid(date)) return Number.NaN; - - const currentQuarter = getQuarter(date); - - return currentQuarter === 1 ? 4 : currentQuarter - 1; +export function addQuarters(date: Date, amount: number): Date { + return dateIsValid(date) ? addQuartersInternal(date, amount) : new Date(Number.NaN); } From ff52f5a9f68dcebedb9f9ea3598439bb93e2f210 Mon Sep 17 00:00:00 2001 From: Dominic Baur Date: Wed, 29 Apr 2026 15:41:32 +0200 Subject: [PATCH 12/13] up --- src/lib/date.spec.ts | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/lib/date.spec.ts b/src/lib/date.spec.ts index 563174c..7da5195 100644 --- a/src/lib/date.spec.ts +++ b/src/lib/date.spec.ts @@ -1,4 +1,3 @@ -/* eslint-disable max-lines */ import { dateIsValid, dateIsFirstDayOfMonth, @@ -181,6 +180,12 @@ describe("date tests", () => { }); test.each([ + [null as unknown as Date, 1, new Date(Number.NaN)], + [undefined as unknown as Date, -1, new Date(Number.NaN)], + [42 as unknown as Date, 1, new Date(Number.NaN)], + ["test" as unknown as Date, -1, new Date(Number.NaN)], + [new Date("invalid-date"), 1, new Date(Number.NaN)], + [new Date(Number.NaN), 1, new Date(Number.NaN)], [new Date(2026, 4, 29), 1, new Date(2026, 7, 29)], [new Date(2026, 4, 29), 4, new Date(2027, 4, 29)], [new Date(2026, 8, 15), -1, new Date(2026, 5, 15)], @@ -188,14 +193,4 @@ describe("date tests", () => { ])("addQuarters", (date, amount, expected) => { expect(addQuarters(date, amount).getTime()).toBe(expected.getTime()); }); - - test.each([ - [null as unknown as Date, 1], - [undefined as unknown as Date, -1], - [42 as unknown as Date, 1], - ["test" as unknown as Date, -1], - [new Date("invalid-date"), 1], - ])("addQuarters with invalid inputs", (date, amount) => { - expect(addQuarters(date, amount).getTime()).toBe(Number.NaN); - }); }); From 9268b4ddeb26cfa806681379532387b00b085450 Mon Sep 17 00:00:00 2001 From: Dominic Baur Date: Wed, 29 Apr 2026 15:46:35 +0200 Subject: [PATCH 13/13] up --- src/lib/date.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/date.spec.ts b/src/lib/date.spec.ts index 7da5195..b2336f1 100644 --- a/src/lib/date.spec.ts +++ b/src/lib/date.spec.ts @@ -22,11 +22,11 @@ describe("date tests", () => { [new Date("2014-03-15"), true], [new Date(2014, 3, 15), true], [new Date(42), true], + + // Invalid dates [new Date(Number.MAX_VALUE), false], [new Date(Number.NaN), false], [new Date("2014-03-36"), false], - - // Invalid dates [null as unknown as Date, false], [undefined as unknown as Date, false], [42 as unknown as Date, false],