diff --git a/frontend/src/component/admin/network/NetworkTrafficUsage/PeriodSelector.tsx b/frontend/src/component/admin/network/NetworkTrafficUsage/PeriodSelector.tsx index 764a20a5e8..7090e93e42 100644 --- a/frontend/src/component/admin/network/NetworkTrafficUsage/PeriodSelector.tsx +++ b/frontend/src/component/admin/network/NetworkTrafficUsage/PeriodSelector.tsx @@ -5,6 +5,7 @@ import { useRef, useState, type FC } from 'react'; import { format } from 'date-fns'; import type { ChartDataSelection } from './chart-data-selection'; import { selectablePeriods } from './selectable-periods'; +import { parseMonthString } from './dates'; const dropdownWidth = '15rem'; const dropdownInlinePadding = (theme: Theme) => theme.spacing(3); @@ -148,10 +149,13 @@ export const PeriodSelector: FC = ({ selectedPeriod, setPeriod }) => { selectedPeriod.grouping === 'daily' ? selectedPeriod.month === format(new Date(), 'yyyy-MM') ? 'Current month' - : new Date(selectedPeriod.month).toLocaleDateString('en-US', { - month: 'long', - year: 'numeric', - }) + : parseMonthString(selectedPeriod.month).toLocaleDateString( + 'en-US', + { + month: 'long', + year: 'numeric', + }, + ) : `Last ${selectedPeriod.monthsBack} months`; return ( @@ -184,7 +188,7 @@ export const PeriodSelector: FC = ({ selectedPeriod, setPeriod }) => {

Last 12 months

- {selectablePeriods.map((period, index) => ( + {selectablePeriods.map((period) => (
  • { return `Average requests from ${formatMonth(fromMonth)} to ${formatMonth(toMonth)}`; } - return `Incoming requests in ${formatMonth(new Date(period.month))}`; + return `Incoming requests in ${formatMonth(parseMonthString(period.month))}`; }; export const RequestSummary: FC = ({ diff --git a/frontend/src/component/admin/network/NetworkTrafficUsage/chart-data-selection.ts b/frontend/src/component/admin/network/NetworkTrafficUsage/chart-data-selection.ts index be332ef76d..6a8df1ffab 100644 --- a/frontend/src/component/admin/network/NetworkTrafficUsage/chart-data-selection.ts +++ b/frontend/src/component/admin/network/NetworkTrafficUsage/chart-data-selection.ts @@ -1,4 +1,5 @@ import { endOfMonth, format, startOfMonth, subMonths } from 'date-fns'; +import { parseMonthString } from './dates'; export type ChartDataSelection = | { @@ -16,7 +17,7 @@ export const toDateRange = ( ): { from: string; to: string } => { const fmt = (date: Date) => format(date, 'yyyy-MM-dd'); if (selection.grouping === 'daily') { - const month = new Date(selection.month); + const month = parseMonthString(selection.month); const from = fmt(month); const to = fmt(endOfMonth(month)); return { from, to }; diff --git a/frontend/src/component/admin/network/NetworkTrafficUsage/chart-functions.ts b/frontend/src/component/admin/network/NetworkTrafficUsage/chart-functions.ts index 56af65ab67..202d5b4c07 100644 --- a/frontend/src/component/admin/network/NetworkTrafficUsage/chart-functions.ts +++ b/frontend/src/component/admin/network/NetworkTrafficUsage/chart-functions.ts @@ -10,7 +10,7 @@ import { differenceInCalendarDays, differenceInCalendarMonths, } from 'date-fns'; -import { formatDay, formatMonth } from './dates'; +import { formatDay, formatMonth, parseDateString } from './dates'; import type { ChartDataSelection } from './chart-data-selection'; export type ChartDatasetType = ChartDataset<'bar'>; @@ -80,8 +80,8 @@ const getLabelsAndRecords = ( >, ) => { if (traffic.grouping === 'monthly') { - const from = new Date(traffic.dateRange.from); - const to = new Date(traffic.dateRange.to); + const from = parseDateString(traffic.dateRange.from); + const to = parseDateString(traffic.dateRange.to); const numMonths = Math.abs(differenceInCalendarMonths(to, from)) + 1; const monthsRec: { [month: string]: number } = {}; for (let i = 0; i < numMonths; i++) { @@ -95,8 +95,8 @@ const getLabelsAndRecords = ( ); return { newRecord: () => ({ ...monthsRec }), labels }; } else { - const from = new Date(traffic.dateRange.from); - const to = new Date(traffic.dateRange.to); + const from = parseDateString(traffic.dateRange.from); + const to = parseDateString(traffic.dateRange.to); const numDays = Math.abs(differenceInCalendarDays(to, from)) + 1; const daysRec: { [day: string]: number } = {}; for (let i = 0; i < numDays; i++) { diff --git a/frontend/src/component/admin/network/NetworkTrafficUsage/dates.ts b/frontend/src/component/admin/network/NetworkTrafficUsage/dates.ts index 0976cd2a12..f44a62efcb 100644 --- a/frontend/src/component/admin/network/NetworkTrafficUsage/dates.ts +++ b/frontend/src/component/admin/network/NetworkTrafficUsage/dates.ts @@ -1,4 +1,4 @@ -import { format, getDaysInMonth } from 'date-fns'; +import { format, getDaysInMonth, parse } from 'date-fns'; export const currentDate = new Date(); @@ -11,3 +11,13 @@ export const daysInCurrentMonth = getDaysInMonth(currentDate); export const formatMonth = (date: Date) => format(date, 'yyyy-MM'); export const formatDay = (date: Date) => format(date, 'yyyy-MM-dd'); + +export const parseMonthString = (month: string): Date => { + // parses a month into a Date starting on the first day of the month, regardless of the current time zone (e.g. works in Norway and Brazil) + return parse(month, 'yyyy-MM', new Date()); +}; + +export const parseDateString = (month: string): Date => { + // parses a date string into a Date, regardless of the current time zone (e.g. works in Norway and Brazil) + return parse(month, 'yyyy-MM-dd', new Date()); +}; diff --git a/frontend/src/component/admin/network/NetworkTrafficUsage/hooks/useChartDataSelection.ts b/frontend/src/component/admin/network/NetworkTrafficUsage/hooks/useChartDataSelection.ts index 9b6618809f..6c317c8639 100644 --- a/frontend/src/component/admin/network/NetworkTrafficUsage/hooks/useChartDataSelection.ts +++ b/frontend/src/component/admin/network/NetworkTrafficUsage/hooks/useChartDataSelection.ts @@ -4,6 +4,7 @@ import { periodsRecord, selectablePeriods } from '../selectable-periods'; import { createBarChartOptions } from '../bar-chart-options'; import useTheme from '@mui/material/styles/useTheme'; import { useLocationSettings } from 'hooks/useLocationSettings'; +import { parseMonthString } from '../dates'; export const useChartDataSelection = (includedTraffic?: number) => { const theme = useTheme(); @@ -35,11 +36,11 @@ export const useChartDataSelection = (includedTraffic?: number) => { }, ); } else { - const timestamp = Date.parse(tooltipItems[0].label); - if (Number.isNaN(timestamp)) { + const month = parseMonthString(tooltipItems[0].label); + if (Number.isNaN(month.getTime())) { return 'Current month to date'; } - return new Date(timestamp).toLocaleDateString( + return month.toLocaleDateString( locationSettings?.locale ?? 'en-US', { month: 'long', diff --git a/frontend/src/component/admin/network/NetworkTrafficUsage/hooks/useStats.ts b/frontend/src/component/admin/network/NetworkTrafficUsage/hooks/useStats.ts index 36bd85e58a..805cea2fad 100644 --- a/frontend/src/component/admin/network/NetworkTrafficUsage/hooks/useStats.ts +++ b/frontend/src/component/admin/network/NetworkTrafficUsage/hooks/useStats.ts @@ -3,8 +3,8 @@ import { useTrafficSearch } from 'hooks/api/getters/useInstanceTrafficMetrics/us import { currentDate } from '../dates'; import { useMemo } from 'react'; import { - toTrafficUsageChartData as newToChartData, toConnectionChartData, + toTrafficUsageChartData, } from '../chart-functions'; import { calculateEstimatedMonthlyCost, @@ -36,7 +36,7 @@ export const useTrafficStats = ( } const traffic = result.data; - const chartData = newToChartData(traffic, filter); + const chartData = toTrafficUsageChartData(traffic, filter); const usageTotal = calculateTotalUsage(traffic); const overageCost = calculateOverageCost( usageTotal, diff --git a/frontend/src/component/admin/network/NetworkTrafficUsage/selectable-periods.ts b/frontend/src/component/admin/network/NetworkTrafficUsage/selectable-periods.ts index c70b0f5e93..f6af8442cc 100644 --- a/frontend/src/component/admin/network/NetworkTrafficUsage/selectable-periods.ts +++ b/frontend/src/component/admin/network/NetworkTrafficUsage/selectable-periods.ts @@ -1,4 +1,4 @@ -import { getDaysInMonth } from 'date-fns'; +import { getDaysInMonth, startOfMonth, subMonths } from 'date-fns'; import { currentDate, formatMonth } from './dates'; import { TRAFFIC_MEASUREMENT_START_DATE } from 'utils/traffic-calculations'; @@ -38,21 +38,18 @@ export const toSelectablePeriod = ( export const generateSelectablePeriodsFromDate = (now: Date) => { const selectablePeriods = [toSelectablePeriod(now, 'Current month')]; + const startOfCurrentMonth = startOfMonth(now); for ( let subtractMonthCount = 1; subtractMonthCount < 12; subtractMonthCount++ ) { - // this complicated calc avoids DST issues - const utcYear = now.getUTCFullYear(); - const utcMonth = now.getUTCMonth(); - const targetMonth = utcMonth - subtractMonthCount; - const targetDate = new Date(Date.UTC(utcYear, targetMonth, 1, 0, 0, 0)); + const targetMonth = subMonths(startOfCurrentMonth, subtractMonthCount); selectablePeriods.push( toSelectablePeriod( - targetDate, + targetMonth, undefined, - targetDate >= TRAFFIC_MEASUREMENT_START_DATE, + targetMonth >= TRAFFIC_MEASUREMENT_START_DATE, ), ); } diff --git a/frontend/src/utils/traffic-calculations.ts b/frontend/src/utils/traffic-calculations.ts index 464ea1801d..3a7cc5c735 100644 --- a/frontend/src/utils/traffic-calculations.ts +++ b/frontend/src/utils/traffic-calculations.ts @@ -4,10 +4,11 @@ import type { } from 'openapi'; import { getDaysInMonth } from 'date-fns'; import { format } from 'date-fns'; +import { parseDateString } from 'component/admin/network/NetworkTrafficUsage/dates'; export const DEFAULT_TRAFFIC_DATA_UNIT_COST = 5; export const DEFAULT_TRAFFIC_DATA_UNIT_SIZE = 1_000_000; -export const TRAFFIC_MEASUREMENT_START_DATE = new Date('2024-05-01'); +export const TRAFFIC_MEASUREMENT_START_DATE = parseDateString('2024-05-01'); export const METERED_TRAFFIC_ENDPOINTS = [ '/api/admin',