diff --git a/apps/api/src/app/endpoints/benchmarks/benchmarks.service.time-zone.spec.ts b/apps/api/src/app/endpoints/benchmarks/benchmarks.service.time-zone.spec.ts new file mode 100644 index 0000000000..7d2f8b397e --- /dev/null +++ b/apps/api/src/app/endpoints/benchmarks/benchmarks.service.time-zone.spec.ts @@ -0,0 +1,62 @@ +/** + * @jest-environment /jest-environment-tz.js + * @jest-environment-options {"timeZone": "America/New_York"} + */ +import { PortfolioService } from '@ghostfolio/api/app/portfolio/portfolio.service'; +import { SymbolService } from '@ghostfolio/api/app/symbol/symbol.service'; +import { BenchmarkService } from '@ghostfolio/api/services/benchmark/benchmark.service'; +import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; +import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service'; +import { parseDate } from '@ghostfolio/common/helper'; + +import { DataSource } from '@prisma/client'; + +import { BenchmarksService } from './benchmarks.service'; + +describe('BenchmarksService in a time zone behind UTC', () => { + let benchmarksService: BenchmarksService; + + beforeEach(() => { + benchmarksService = new BenchmarksService( + new BenchmarkService(null, null, null, null, null, null), + { + getExchangeRatesByCurrency: jest.fn().mockResolvedValue({}) + } as unknown as ExchangeRateDataService, + { + getRange: jest.fn().mockResolvedValue([]), + // The market data is stored at midnight (UTC) + marketDataItems: jest.fn().mockResolvedValue([ + { date: new Date('2010-07-18T00:00:00.000Z'), marketPrice: 50 }, + { date: new Date('2010-07-19T00:00:00.000Z'), marketPrice: 100 } + ]) + } as unknown as MarketDataService, + { + getPerformance: jest.fn().mockResolvedValue({ + chart: [{ date: '2010-07-18' }, { date: '2010-07-19' }] + }) + } as unknown as PortfolioService, + { + get: jest.fn().mockResolvedValue({ currency: 'USD', marketPrice: 200 }) + } as unknown as SymbolService + ); + }); + + it('returns the dates of the market data without a shift', async () => { + await expect( + benchmarksService.getMarketDataForUser({ + dataSource: DataSource.COINGECKO, + dateRange: 'max', + endDate: parseDate('2010-07-19'), + startDate: parseDate('2010-07-18'), + symbol: 'bitcoin', + userId: 'user-id', + userSettings: { baseCurrency: 'USD' } + }) + ).resolves.toEqual({ + marketData: [ + { date: '2010-07-18', value: 0 }, + { date: '2010-07-19', value: 100 } + ] + }); + }); +}); diff --git a/apps/api/src/app/endpoints/benchmarks/benchmarks.service.ts b/apps/api/src/app/endpoints/benchmarks/benchmarks.service.ts index 5104f01f35..066516236a 100644 --- a/apps/api/src/app/endpoints/benchmarks/benchmarks.service.ts +++ b/apps/api/src/app/endpoints/benchmarks/benchmarks.service.ts @@ -12,6 +12,7 @@ import { } from '@ghostfolio/common/interfaces'; import { DateRange } from '@ghostfolio/common/types'; +import { utc } from '@date-fns/utc'; import { Injectable, Logger } from '@nestjs/common'; import { format, isSameDay, min } from 'date-fns'; import { isNumber } from 'lodash-es'; @@ -104,7 +105,8 @@ export class BenchmarksService { this.logger.error( `No historical market data has been found for ${symbol} (${dataSource}) since ${format( baselineDate, - DATE_FORMAT + DATE_FORMAT, + { in: utc } )}` ); @@ -125,7 +127,8 @@ export class BenchmarksService { this.logger.error( `No historical market data has been found for ${symbol} (${dataSource}) at ${format( baselineDate, - DATE_FORMAT + DATE_FORMAT, + { in: utc } )}` ); @@ -135,8 +138,9 @@ export class BenchmarksService { this.logger.warn( `The market data of ${symbol} (${dataSource}) starts at ${format( firstMarketDataItem.date, - DATE_FORMAT - )}, after the baseline date ${format(baselineDate, DATE_FORMAT)}` + DATE_FORMAT, + { in: utc } + )}, after the baseline date ${format(baselineDate, DATE_FORMAT, { in: utc })}` ); } @@ -147,6 +151,8 @@ export class BenchmarksService { targetCurrency: userCurrency }); + // TODO: Format the keys of the exchange rates in UTC, together with the + // market data in the portfolio calculator const exchangeRateAtStartDate = exchangeRates[`${currentSymbolItem.currency}${userCurrency}`]?.[ format(firstMarketDataItem.date, DATE_FORMAT) @@ -164,7 +170,7 @@ export class BenchmarksService { : 1; marketData.push({ - date: format(marketDataItem.date, DATE_FORMAT), + date: format(marketDataItem.date, DATE_FORMAT, { in: utc }), value: marketPriceAtStartDate === 0 ? 0