diff --git a/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts b/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts index ca440c6d2a..d172652a83 100644 --- a/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts +++ b/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts @@ -64,7 +64,6 @@ import { format, isAfter, isBefore, - isEqual, isFuture, isPast, isWithinInterval, @@ -1418,8 +1417,8 @@ export abstract class PortfolioCalculator { let netPerformanceAtStartDate: number; let netPerformanceWithCurrencyEffectAtStartDate: number; - const endDate = this.toChartDate(end); - const startDate = this.toChartDate(start); + const endDate = resetHours(end); + const startDate = resetHours(start); for (const historicalDataItem of historicalData) { const date = resetHours(parseDate(historicalDataItem.date)); @@ -1914,8 +1913,4 @@ export abstract class PortfolioCalculator { await this.initialize(attempt + 1); } } - - private toChartDate(aDate: Date) { - return isEqual(aDate, endOfDay(aDate)) ? resetHours(aDate) : aDate; - } } 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 43ff160a78..9e0fbc701f 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 @@ -226,5 +226,46 @@ describe('PortfolioCalculator', () => { }) ).toEqual(['2016-12-31', '2017-12-31']); }); + + it('with a year to date date range', async () => { + const snapshot = await portfolioCalculator.getSnapshot(); + + snapshot.historicalData = [ + getHistoricalDataItem({ + date: '2016-12-30', + investment: 100, + netPerformance: 0 + }), + getHistoricalDataItem({ + date: '2016-12-31', + investment: 100, + netPerformance: 10 + }), + getHistoricalDataItem({ + date: '2017-01-03', + investment: 100, + netPerformance: 20 + }) + ]; + + jest.useFakeTimers().setSystemTime(new Date('2017-01-04T02:30:00.000Z')); + + const { endDate, startDate } = getIntervalFromDateRange({ + dateRange: 'ytd' + }); + + const { chart } = await portfolioCalculator.getPerformance({ + end: endDate, + start: startDate + }); + + jest.useRealTimers(); + + expect( + chart.map(({ date }) => { + return date; + }) + ).toEqual(['2016-12-31', '2017-01-03']); + }); }); }); diff --git a/apps/api/src/app/portfolio/portfolio.service.spec.ts b/apps/api/src/app/portfolio/portfolio.service.spec.ts index f562966b06..c408f51934 100644 --- a/apps/api/src/app/portfolio/portfolio.service.spec.ts +++ b/apps/api/src/app/portfolio/portfolio.service.spec.ts @@ -16,7 +16,7 @@ import { TAG_ID_EXCLUDE_FROM_ANALYSIS, UNKNOWN_KEY } from '@ghostfolio/common/config'; -import { parseDate, resetHours } from '@ghostfolio/common/helper'; +import { parseDate } from '@ghostfolio/common/helper'; import { Activity, AssetProfileIdentifier, @@ -1045,7 +1045,7 @@ describe('PortfolioService', () => { expect(getPerformanceOfCalculator).toHaveBeenCalledTimes(3); expect(getPerformanceOfCalculator).toHaveBeenNthCalledWith(2, { end: endOfDay(parseDate('2024-12-31')), - start: resetHours(parseDate('2024-06-15')) + start: parseDate('2024-06-15') }); expect(getPerformanceOfCalculator).toHaveBeenNthCalledWith(3, { end: endOfDay(parseDate('2025-06-15')), @@ -1099,11 +1099,11 @@ describe('PortfolioService', () => { expect(getPerformanceOfCalculator).toHaveBeenCalledTimes(3); expect(getPerformanceOfCalculator).toHaveBeenNthCalledWith(1, { end: endOfDay(parseDate('2026-01-01')), - start: resetHours(parseDate('2025-01-01')) + start: parseDate('2025-01-01') }); expect(getPerformanceOfCalculator).toHaveBeenNthCalledWith(2, { end: endOfDay(parseDate('2025-12-31')), - start: resetHours(parseDate('2025-01-01')) + start: parseDate('2025-01-01') }); expect(getPerformanceOfCalculator).toHaveBeenNthCalledWith(3, { end: endOfDay(parseDate('2026-01-01')), diff --git a/apps/api/src/main.ts b/apps/api/src/main.ts index 3e0985cbcb..72be15b9d9 100644 --- a/apps/api/src/main.ts +++ b/apps/api/src/main.ts @@ -31,8 +31,6 @@ import { EnvHttpProxyAgent, setGlobalDispatcher } from 'undici'; import { AppModule } from './app/app.module'; import { environment } from './environments/environment'; -process.env.TZ = 'UTC'; - const logger = new Logger('Bootstrap'); const processWarningLogger = new Logger('ProcessWarning'); diff --git a/libs/common/src/lib/calculation-helper.spec.ts b/libs/common/src/lib/calculation-helper.spec.ts index 75a24edbab..a16e487431 100644 --- a/libs/common/src/lib/calculation-helper.spec.ts +++ b/libs/common/src/lib/calculation-helper.spec.ts @@ -6,6 +6,7 @@ import { getIntervalFromDateRange } from './calculation-helper'; import { DATE_FORMAT } from './helper'; +import { DateRange } from './types'; describe('CalculationHelper', () => { describe('interval from date range', () => { @@ -23,6 +24,27 @@ describe('CalculationHelper', () => { // part of the interval expect(startDate.getTime()).toEqual(new Date(2024, 0, 1).getTime() - 1); }); + + it('Get interval of a date range relative to today', () => { + jest.useFakeTimers().setSystemTime(new Date(2024, 9, 10, 22, 0)); + + expect( + (['1d', 'mtd', 'wtd', 'ytd', '1y', '5y'] as DateRange[]).map( + (dateRange) => { + return getIntervalFromDateRange({ dateRange }).startDate; + } + ) + ).toEqual([ + new Date(2024, 9, 9), + new Date(2024, 8, 30), + new Date(2024, 9, 6), + new Date(2023, 11, 31), + new Date(2023, 9, 10), + new Date(2019, 9, 10) + ]); + + jest.useRealTimers(); + }); }); describe('annualized performance percentage', () => { diff --git a/libs/common/src/lib/calculation-helper.ts b/libs/common/src/lib/calculation-helper.ts index 2cc1fa2d1c..6ee06af857 100644 --- a/libs/common/src/lib/calculation-helper.ts +++ b/libs/common/src/lib/calculation-helper.ts @@ -3,6 +3,7 @@ import { endOfDay, endOfYear, max, + startOfDay, startOfMonth, startOfWeek, startOfYear, @@ -12,7 +13,6 @@ import { } from 'date-fns'; import { isFinite, isNumber } from 'lodash-es'; -import { resetHours } from './helper'; import { DateRange } from './types'; export function getAnnualizedPerformancePercent({ @@ -48,31 +48,25 @@ export function getIntervalFromDateRange(params: { switch (dateRange) { case '1d': - startDate = max([startDate, subDays(resetHours(new Date()), 1)]); + startDate = max([startDate, subDays(startOfDay(new Date()), 1)]); break; case 'mtd': - startDate = max([ - startDate, - subDays(startOfMonth(resetHours(new Date())), 1) - ]); + startDate = max([startDate, subDays(startOfMonth(new Date()), 1)]); break; case 'wtd': startDate = max([ startDate, - subDays(startOfWeek(resetHours(new Date()), { weekStartsOn: 1 }), 1) + subDays(startOfWeek(new Date(), { weekStartsOn: 1 }), 1) ]); break; case 'ytd': - startDate = max([ - startDate, - subDays(startOfYear(resetHours(new Date())), 1) - ]); + startDate = max([startDate, subDays(startOfYear(new Date()), 1)]); break; case '1y': - startDate = max([startDate, subYears(resetHours(new Date()), 1)]); + startDate = max([startDate, subYears(startOfDay(new Date()), 1)]); break; case '5y': - startDate = max([startDate, subYears(resetHours(new Date()), 5)]); + startDate = max([startDate, subYears(startOfDay(new Date()), 5)]); break; case 'max': break;