From df6a4c3aab45daaa1cb444f157983326f5cda526 Mon Sep 17 00:00:00 2001 From: Miguel Rocha Date: Fri, 7 Aug 2026 21:43:59 +0100 Subject: [PATCH] Bugfix/normalize date in market data update endpoint The bulk market data update endpoint parses the incoming date with parseISO, which interprets a date-only string as LOCAL midnight. The data gathering path stores market data at UTC midnight via resetHours. On a server running in any time zone other than UTC the two therefore disagree, and because MarketData is keyed on (dataSource, date, symbol) the upsert misses the existing row and a duplicate is created one offset away. Demonstration for the input "2026-08-07": UTC parseISO -> 2026-08-07T00:00:00.000Z matches Europe/Lisbon parseISO -> 2026-08-06T23:00:00.000Z off by -1h America/Sao_Paulo parseISO -> 2026-08-07T03:00:00.000Z off by +3h Asia/Tokyo parseISO -> 2026-08-06T15:00:00.000Z off by -9h Using resetHours(parseDate(date)) yields 2026-08-07T00:00:00.000Z in every zone and matches what market-data.service.ts already writes, so the endpoint now agrees with the rest of the codebase rather than only on UTC hosts. Co-Authored-By: Claude Opus 5 (1M context) --- CHANGELOG.md | 1 + .../endpoints/market-data/market-data.controller.ts | 10 +++++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 509009e6f..0df434951 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fixed the check for duplicates in the preview step of the activities import for activities without a comment - Fixed the holdings mock data in the _Storybook_ story of the portfolio filter form component +- Fixed the date normalization in the market data update endpoint to prevent duplicate entries on servers running in a non-UTC time zone ## 3.44.0 - 2026-08-07 diff --git a/apps/api/src/app/endpoints/market-data/market-data.controller.ts b/apps/api/src/app/endpoints/market-data/market-data.controller.ts index 03d50c284..8ce44780e 100644 --- a/apps/api/src/app/endpoints/market-data/market-data.controller.ts +++ b/apps/api/src/app/endpoints/market-data/market-data.controller.ts @@ -4,7 +4,12 @@ import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard' import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service'; import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service'; import { UpdateBulkMarketDataDto } from '@ghostfolio/common/dtos'; -import { getCurrencyFromSymbol, isCurrency } from '@ghostfolio/common/helper'; +import { + getCurrencyFromSymbol, + isCurrency, + parseDate, + resetHours +} from '@ghostfolio/common/helper'; import { MarketDataOfMarketsResponse } from '@ghostfolio/common/interfaces'; import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { RequestWithUser } from '@ghostfolio/common/types'; @@ -24,7 +29,6 @@ import { import { REQUEST } from '@nestjs/core'; import { AuthGuard } from '@nestjs/passport'; import { DataSource, Prisma } from '@prisma/client'; -import { parseISO } from 'date-fns'; import { getReasonPhrase, StatusCodes } from 'http-status-codes'; @Controller('market-data') @@ -99,7 +103,7 @@ export class MarketDataController { dataSource, marketPrice, symbol, - date: parseISO(date), + date: resetHours(parseDate(date)), state: 'CLOSE' }) );