From 3f687cdddf3e56777ae0db52ea7bba8f31411c27 Mon Sep 17 00:00:00 2001 From: David Requeno Date: Mon, 10 Aug 2026 21:28:53 -0600 Subject: [PATCH] Fix timezone handling of stock split dates --- .../asset-profile-split.helper.spec.ts | 20 ++++++++++++++++++- .../asset-profile-split.helper.ts | 2 +- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/apps/api/src/services/asset-profile-split/asset-profile-split.helper.spec.ts b/apps/api/src/services/asset-profile-split/asset-profile-split.helper.spec.ts index a979033b1..9b503fd67 100644 --- a/apps/api/src/services/asset-profile-split/asset-profile-split.helper.spec.ts +++ b/apps/api/src/services/asset-profile-split/asset-profile-split.helper.spec.ts @@ -2,6 +2,7 @@ import { activityDummyData, assetProfileDummyData } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator-test-utils'; +import * as commonHelper from '@ghostfolio/common/helper'; import { parseDate } from '@ghostfolio/common/helper'; import { Activity } from '@ghostfolio/common/interfaces'; @@ -39,7 +40,7 @@ describe('adjustActivityBySplits', () => { }); it('only adjusts activities before the split calendar date', () => { - const split = createSplit('2024-06-15T12:00:00Z', 2, 1); + const split = createSplit('2024-06-15T00:00:00Z', 2, 1); const activityOnSplitDate = createActivity('2024-06-15T18:00:00Z'); const activityAfterSplit = createActivity('2024-06-16T00:00:00Z'); @@ -57,6 +58,23 @@ describe('adjustActivityBySplits', () => { expect(adjustedActivityAfterSplit).toEqual(activityAfterSplit); expect(adjustedActivityAfterSplit).not.toBe(activityAfterSplit); }); + + it('compares stored UTC split dates without normalizing them locally', () => { + const resetHoursSpy = jest + .spyOn(commonHelper, 'resetHours') + .mockReturnValue(new Date('2024-06-14T00:00:00Z')); + + try { + const activity = createActivity('2024-06-14T12:00:00Z'); + const split = createSplit('2024-06-15T00:00:00Z', 2, 1); + + const adjustedActivity = adjustActivityBySplits(activity, [split]); + + expect(adjustedActivity.quantity).toBe(20); + } finally { + resetHoursSpy.mockRestore(); + } + }); }); function createActivity(date: string): Activity { diff --git a/apps/api/src/services/asset-profile-split/asset-profile-split.helper.ts b/apps/api/src/services/asset-profile-split/asset-profile-split.helper.ts index 994d9dc55..0b38092ec 100644 --- a/apps/api/src/services/asset-profile-split/asset-profile-split.helper.ts +++ b/apps/api/src/services/asset-profile-split/asset-profile-split.helper.ts @@ -13,7 +13,7 @@ export function adjustActivityBySplits( let splitFactor = new Big(1); for (const split of splits) { - if (isBefore(activityDate, resetHours(split.date))) { + if (isBefore(activityDate, split.date)) { splitFactor = splitFactor.mul(split.numerator).div(split.denominator); } }