From dc0109dd06afc05520efcb60e06ca8cc884c0338 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Thu, 8 Oct 2026 14:41:12 +0200 Subject: [PATCH] Fix end date of calendar year date ranges --- .../calculator/portfolio-calculator.ts | 9 +++- .../roai/portfolio-calculator.spec.ts | 44 +++++++++++++++++++ 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts b/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts index 7a8bbcc741..1527c4bd3b 100644 --- a/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts +++ b/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts @@ -1407,7 +1407,7 @@ export abstract class PortfolioCalculator { return this.snapshot.totalLiabilitiesWithCurrencyEffect; } - public async getPerformance({ end, start }) { + public async getPerformance({ end, start }: { end: Date; start: Date }) { await this.snapshotPromise; const { historicalData } = this.snapshot; @@ -1418,6 +1418,11 @@ export abstract class PortfolioCalculator { let netPerformanceAtStartDate: number; let netPerformanceWithCurrencyEffectAtStartDate: number; + // The historical data items are dated at midnight in UTC, hence a start + // or an end at the end of a day in the time zone of the instance is + // changed to this date + const endDate = isEqual(end, endOfDay(end)) ? resetHours(end) : end; + const startDate = isEqual(start, endOfDay(start)) ? resetHours(start) : start; @@ -1425,7 +1430,7 @@ export abstract class PortfolioCalculator { for (const historicalDataItem of historicalData) { const date = resetHours(parseDate(historicalDataItem.date)); - if (!isBefore(date, startDate) && !isAfter(date, end)) { + if (!isBefore(date, startDate) && !isAfter(date, endDate)) { // Take the values at the start date from the first day of the date // range if (historicalDataItemsOfDateRange.length === 0) { diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator.spec.ts index 5f8d9cdc44..9589132372 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator.spec.ts @@ -9,6 +9,7 @@ import { ConfigurationService } from '@ghostfolio/api/services/configuration/con import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock'; +import { getIntervalFromDateRange } from '@ghostfolio/common/calculation-helper'; import { HistoricalDataItem } from '@ghostfolio/common/interfaces'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; @@ -183,4 +184,47 @@ describe('PortfolioCalculator', () => { ).toBeCloseTo(0.23, 10); }); }); + + describe('get performance', () => { + // The chart items are dated at midnight in UTC, but a calendar year date + // range ends at the end of 31 December in the time zone of the instance. + // Hence this test must give the same result with the instance in any time + // zone. Set TEST_TZ to run it with the instance in another time zone. + it('with a calendar year date range', async () => { + const snapshot = await portfolioCalculator.getSnapshot(); + + snapshot.historicalData = [ + getHistoricalDataItem({ + date: '2016-12-31', + investment: 100, + netPerformance: 0 + }), + getHistoricalDataItem({ + date: '2017-12-31', + investment: 100, + netPerformance: 10 + }), + getHistoricalDataItem({ + date: '2018-01-01', + investment: 100, + netPerformance: 20 + }) + ]; + + const { endDate, startDate } = getIntervalFromDateRange({ + dateRange: '2017' + }); + + const { chart } = await portfolioCalculator.getPerformance({ + end: endDate, + start: startDate + }); + + expect( + chart.map(({ date }) => { + return date; + }) + ).toEqual(['2016-12-31', '2017-12-31']); + }); + }); });