diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a584a96..29b3f5d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -89,6 +89,8 @@ jobs: - run: yarn lint - run: yarn build - run: yarn jest + env: + TZ: Europe/Zurich - name: Code Coverage Summary Report uses: irongut/CodeCoverageSummary@v1.3.0 diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..3c03207 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +18 diff --git a/CHANGELOG.md b/CHANGELOG.md index 58adb25..6bf4f67 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- `getDateWithoutTimeZone` utility function +- `getDateAddTimeZone` utility function + ## [2.4.0] - 2026-04-29 ### Added diff --git a/eslint.config.ts b/eslint.config.ts index ba8a24f..cfd0ed0 100644 --- a/eslint.config.ts +++ b/eslint.config.ts @@ -6,4 +6,8 @@ export default [ jest: true, jsdocRequireJsdoc: true, }), + { + files: ["src/lib/*.spec.ts"], + rules: { "max-lines": ["off"] }, + }, ]; diff --git a/package.json b/package.json index 03f7a6f..8ed683b 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "prettier-check": "prettier --check .", "prettier-write": "prettier --write .", "start": "rollup -c -w", - "test": "yarn jest", + "test": "cross-env TZ=Europe/Zurich yarn jest", "start-all": "concurrently \"yarn start\" \"yarn start-yalc\"", "start-yalc": "yarn nodemon --watch dist -x \"yarn yalc push\"" }, @@ -53,6 +53,7 @@ "@types/rollup-plugin-peer-deps-external": "^2.2.5", "@types/uuid": "^9.0.7", "concurrently": "^8.0.1", + "cross-env": "^7.0.3", "eslint": "^9.24.0", "jest": "^29.6.1", "jest-localstorage-mock": "^2.4.26", diff --git a/src/lib/date.spec.ts b/src/lib/date.spec.ts index b2336f1..2913f89 100644 --- a/src/lib/date.spec.ts +++ b/src/lib/date.spec.ts @@ -12,6 +12,8 @@ import { getLastDayOfYear, addQuarters, getQuarter, + getDateAddTimeZone, + getDateWithoutTimeZone, } from "./date"; describe("date tests", () => { @@ -193,4 +195,32 @@ describe("date tests", () => { ])("addQuarters", (date, amount, expected) => { expect(addQuarters(date, amount).getTime()).toBe(expected.getTime()); }); + + test.each([ + [null as unknown as Date, new Date(Number.NaN)], + [undefined as unknown as Date, new Date(Number.NaN)], + [42 as unknown as Date, new Date(Number.NaN)], + ["2015-03-15" as unknown as Date, new Date(Number.NaN)], + [{ year: 2014, month: 2, day: 15 } as unknown as Date, new Date(Number.NaN)], + [new Date(2026, 6, 5, 0, 0, 0, 0), new Date(2026, 6, 5, 0, 0, 0, 0 + 120 * 60_000)], + [new Date(2026, 6, 5, 1, 12, 13, 1), new Date(2026, 6, 5, 1, 12, 13, 1 + 120 * 60_000)], + [new Date(2026, 12, 5, 0, 0, 0, 0), new Date(2026, 12, 5, 0, 0, 0, 0 + 60 * 60_000)], + [new Date(2026, 12, 5, 1, 12, 13, 1), new Date(2026, 12, 5, 1, 12, 13, 1 + 60 * 60_000)], + ])("getDateWithoutTimeZone", (date, expected) => { + expect(getDateWithoutTimeZone(date).getTime()).toBe(expected.getTime()); + }); + + test.each([ + [null as unknown as Date, new Date(Number.NaN)], + [undefined as unknown as Date, new Date(Number.NaN)], + [42 as unknown as Date, new Date(Number.NaN)], + ["2015-03-15" as unknown as Date, new Date(Number.NaN)], + [{ year: 2014, month: 2, day: 15 } as unknown as Date, new Date(Number.NaN)], + [new Date(2026, 6, 5, 0, 0, 0, 0), new Date(2026, 6, 5, 0, 0, 0, 0 - 120 * 60_000)], + [new Date(2026, 6, 5, 1, 12, 13, 1), new Date(2026, 6, 5, 1, 12, 13, 1 - 120 * 60_000)], + [new Date(2026, 12, 5, 0, 0, 0, 0), new Date(2026, 12, 5, 0, 0, 0, 0 - 60 * 60_000)], + [new Date(2026, 12, 5, 1, 12, 13, 1), new Date(2026, 12, 5, 1, 12, 13, 1 - 60 * 60_000)], + ])("getDateAddTimeZone", (date, expected) => { + expect(getDateAddTimeZone(date).getTime()).toBe(expected.getTime()); + }); }); diff --git a/src/lib/date.ts b/src/lib/date.ts index b91f628..63b8807 100644 --- a/src/lib/date.ts +++ b/src/lib/date.ts @@ -133,3 +133,21 @@ export function getQuarter(date: Date): number { export function addQuarters(date: Date, amount: number): Date { return dateIsValid(date) ? addQuartersInternal(date, amount) : new Date(Number.NaN); } + +/** + * Get the date removing the time-zone offset + * @param date The date + * @returns the given date removing the time-zone offset + */ +export function getDateWithoutTimeZone(date: Date): Date { + return dateIsValid(date) ? new Date(date.getTime() - date.getTimezoneOffset() * 60_000) : new Date(Number.NaN); +} + +/** + * Get the date adding the time-zone offset + * @param date The date + * @returns the given date adding the time-zone offset + */ +export function getDateAddTimeZone(date: Date): Date { + return dateIsValid(date) ? new Date(date.getTime() + date.getTimezoneOffset() * 60_000) : new Date(Number.NaN); +} diff --git a/yarn.lock b/yarn.lock index a8b93d6..060a91e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2401,7 +2401,14 @@ create-require@^1.1.0: resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== -cross-spawn@^7.0.3, cross-spawn@^7.0.6: +cross-env@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-7.0.3.tgz#865264b29677dc015ba8418918965dd232fc54cf" + integrity sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw== + dependencies: + cross-spawn "^7.0.1" + +cross-spawn@^7.0.1, cross-spawn@^7.0.3, cross-spawn@^7.0.6: version "7.0.6" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==