Browse Source

Implemented tz-indifferent eachDayOfInterval method

pull/3167/head
helgehatt 1 year ago
committed by Thomas Kaul
parent
commit
978dc38a5a
  1. 24
      apps/api/src/app/benchmark/benchmark.service.ts
  2. 14
      apps/api/src/app/portfolio/portfolio-calculator.ts
  3. 76
      libs/common/src/lib/helper.spec.ts
  4. 29
      libs/common/src/lib/helper.ts

24
apps/api/src/app/benchmark/benchmark.service.ts

@ -13,6 +13,7 @@ import {
import { import {
DATE_FORMAT, DATE_FORMAT,
calculateBenchmarkTrend, calculateBenchmarkTrend,
eachDayOfInterval,
parseDate, parseDate,
resetHours resetHours
} from '@ghostfolio/common/helper'; } from '@ghostfolio/common/helper';
@ -28,13 +29,7 @@ import { BenchmarkTrend } from '@ghostfolio/common/types';
import { Injectable, Logger } from '@nestjs/common'; import { Injectable, Logger } from '@nestjs/common';
import { SymbolProfile } from '@prisma/client'; import { SymbolProfile } from '@prisma/client';
import { Big } from 'big.js'; import { Big } from 'big.js';
import { import { differenceInDays, format, isSameDay, subDays } from 'date-fns';
differenceInDays,
eachDayOfInterval,
format,
isSameDay,
subDays
} from 'date-fns';
import { isNumber, last, uniqBy } from 'lodash'; import { isNumber, last, uniqBy } from 'lodash';
import ms from 'ms'; import ms from 'ms';
@ -227,15 +222,12 @@ export class BenchmarkService {
const marketData: { date: string; value: number }[] = []; const marketData: { date: string; value: number }[] = [];
const days = differenceInDays(endDate, startDate) + 1; const days = differenceInDays(endDate, startDate) + 1;
const step = Math.round(days / Math.min(days, MAX_CHART_ITEMS)); const dates = eachDayOfInterval({
const dates = eachDayOfInterval( start: startDate,
{ start: startDate, end: endDate }, end: endDate,
{ step } step: Math.round(days / Math.min(days, MAX_CHART_ITEMS)),
); includeEnd: true
});
if (!isSameDay(last(dates), endDate)) {
dates.push(resetHours(endDate));
}
const [currentSymbolItem, marketDataItems] = await Promise.all([ const [currentSymbolItem, marketDataItems] = await Promise.all([
this.symbolService.get({ this.symbolService.get({

14
apps/api/src/app/portfolio/portfolio-calculator.ts

@ -1,7 +1,12 @@
import { getFactor } from '@ghostfolio/api/helper/portfolio.helper'; import { getFactor } from '@ghostfolio/api/helper/portfolio.helper';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service';
import { IDataGatheringItem } from '@ghostfolio/api/services/interfaces/interfaces'; import { IDataGatheringItem } from '@ghostfolio/api/services/interfaces/interfaces';
import { DATE_FORMAT, parseDate, resetHours } from '@ghostfolio/common/helper'; import {
DATE_FORMAT,
eachDayOfInterval,
parseDate,
resetHours
} from '@ghostfolio/common/helper';
import { import {
DataProviderInfo, DataProviderInfo,
HistoricalDataItem, HistoricalDataItem,
@ -18,7 +23,6 @@ import {
addDays, addDays,
addMilliseconds, addMilliseconds,
differenceInDays, differenceInDays,
eachDayOfInterval,
endOfDay, endOfDay,
format, format,
isBefore, isBefore,
@ -203,11 +207,7 @@ export class PortfolioCalculator {
const dataGatheringItems: IDataGatheringItem[] = []; const dataGatheringItems: IDataGatheringItem[] = [];
const firstIndex = transactionPointsBeforeEndDate.length; const firstIndex = transactionPointsBeforeEndDate.length;
const dates = eachDayOfInterval({ start, end }, { step }); const dates = eachDayOfInterval({ start, end, step, includeEnd: true });
if (!isSameDay(last(dates), end)) {
dates.push(resetHours(end));
}
if (transactionPointsBeforeEndDate.length > 0) { if (transactionPointsBeforeEndDate.length > 0) {
for (const item of transactionPointsBeforeEndDate[firstIndex - 1].items) { for (const item of transactionPointsBeforeEndDate[firstIndex - 1].items) {

76
libs/common/src/lib/helper.spec.ts

@ -1,4 +1,7 @@
import { extractNumberFromString } from '@ghostfolio/common/helper'; import {
eachDayOfInterval,
extractNumberFromString
} from '@ghostfolio/common/helper';
describe('Helper', () => { describe('Helper', () => {
describe('Extract number from string', () => { describe('Extract number from string', () => {
@ -36,4 +39,75 @@ describe('Helper', () => {
expect(extractNumberFromString({ value: 'X' })).toEqual(NaN); expect(extractNumberFromString({ value: 'X' })).toEqual(NaN);
}); });
}); });
describe('eachDayOfInterval', () => {
describe('start and end', () => {
const expected = [
new Date(Date.UTC(2021, 0, 1)),
new Date(Date.UTC(2021, 0, 2)),
new Date(Date.UTC(2021, 0, 3))
];
it('as strings', () => {
const [start, end] = ['2021-01-01', '2021-01-03'];
expect(eachDayOfInterval({ start, end })).toEqual(expected);
});
it('as numbers', () => {
const [start, end] = [Date.UTC(2021, 0, 1), Date.UTC(2021, 0, 3)];
expect(eachDayOfInterval({ start, end })).toEqual(expected);
});
it('as dates', () => {
const [start, end] = [
new Date(Date.UTC(2021, 0, 1)),
new Date(Date.UTC(2021, 0, 3))
];
expect(eachDayOfInterval({ start, end })).toEqual(expected);
});
});
describe('step', () => {
const [start, end] = ['2021-01-01', '2021-01-06'];
it('step size of 2', () => {
const expected = [
new Date(Date.UTC(2021, 0, 1)),
new Date(Date.UTC(2021, 0, 3)),
new Date(Date.UTC(2021, 0, 5))
];
expect(eachDayOfInterval({ start, end, step: 2 })).toEqual(expected);
});
it('step size of 4', () => {
const expected = [
new Date(Date.UTC(2021, 0, 1)),
new Date(Date.UTC(2021, 0, 5))
];
expect(eachDayOfInterval({ start, end, step: 4 })).toEqual(expected);
});
it('step size of 5', () => {
const expected = [
new Date(Date.UTC(2021, 0, 1)),
new Date(Date.UTC(2021, 0, 6))
];
expect(eachDayOfInterval({ start, end, step: 5 })).toEqual(expected);
});
});
describe('includeEnd', () => {
const [start, end] = ['2021-01-01', '2021-01-06'];
it('end is added', () => {
const expected = [
new Date(Date.UTC(2021, 0, 1)),
new Date(Date.UTC(2021, 0, 5)),
new Date(Date.UTC(2021, 0, 6))
];
expect(
eachDayOfInterval({ start, end, step: 4, includeEnd: true })
).toEqual(expected);
});
it('end is not duplicated', () => {
const expected = [
new Date(Date.UTC(2021, 0, 1)),
new Date(Date.UTC(2021, 0, 6))
];
expect(
eachDayOfInterval({ start, end, step: 5, includeEnd: true })
).toEqual(expected);
});
});
});
}); });

29
libs/common/src/lib/helper.ts

@ -375,6 +375,35 @@ export function resetHours(aDate: Date) {
return new Date(Date.UTC(year, month, day)); return new Date(Date.UTC(year, month, day));
} }
export function eachDayOfInterval({
start,
end,
step = 1,
includeEnd = false
}: {
start: Date | string | number;
end: Date | string | number;
step?: number;
includeEnd?: boolean;
}) {
[start, end] = [new Date(start), new Date(end)];
[start, end] = [resetHours(start), resetHours(end)];
[start, end] = [start.getTime(), end.getTime()];
const DAY_IN_MS = 1000 * 60 * 60 * 24;
const dates: number[] = [];
for (let date = start; date <= end; date += step * DAY_IN_MS) {
dates.push(date);
}
if (includeEnd && end !== dates?.[dates.length - 1]) {
dates.push(end);
}
return dates.map((date) => new Date(date));
}
export function resolveFearAndGreedIndex(aValue: number) { export function resolveFearAndGreedIndex(aValue: number) {
if (aValue <= 25) { if (aValue <= 25) {
return { emoji: '🥵', key: 'EXTREME_FEAR', text: 'Extreme Fear' }; return { emoji: '🥵', key: 'EXTREME_FEAR', text: 'Extreme Fear' };

Loading…
Cancel
Save