mirror of
https://github.com/Unleash/unleash.git
synced 2025-03-18 00:19:49 +01:00
fix: Data usage graphs don't work in UTC-n time zones (#9530)
Fixes a number of issues that would surface in UTC-n (where n > 1) timezones. I've not found a way to check this with tests (and it looks like [we weren't able to last time either](https://github.com/Unleash/unleash/pull/9110/files#r1919746328)), so all the testing's been done manually by adjusting my system time and zone. (From what I understand, you can't generate a Date with a specific TZ offset in JS: it's only utc or local time) Resolved: - [x] Selecting "Jan" in the dropdown results in the selection being "December" (off by one in the selector) - [x] Selecting a month view only gives you one data point (and it's probably empty). Wrong date parsing on the way out resulted in sending `{ from: "2025-02-28", to: "2025-02-28"}` instead of `{ from: "2025-03-01", to: "2025-03-31"}` - [x] The dates we create when making "daysRec" need to be adjusted. They showed the wrong month, so the dates were off. - [x] Make sure the labels are correct when hovering over. Again: we used the wrong month for generating these. - [x] The available months are wrong. Incorrect month parsing again. - [x] The request summary month is wrong. You guessed it: incorrect month parsing
This commit is contained in:
parent
0aae3bac0a
commit
dadda7b648
@ -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<Props> = ({ 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<Props> = ({ selectedPeriod, setPeriod }) => {
|
||||
<p>Last 12 months</p>
|
||||
</MonthSelectorHeaderGroup>
|
||||
<MonthGrid>
|
||||
{selectablePeriods.map((period, index) => (
|
||||
{selectablePeriods.map((period) => (
|
||||
<li key={period.label}>
|
||||
<GridButton
|
||||
selected={
|
||||
|
@ -4,6 +4,7 @@ import { subMonths } from 'date-fns';
|
||||
import { useLocationSettings } from 'hooks/useLocationSettings';
|
||||
import type { FC } from 'react';
|
||||
import type { ChartDataSelection } from './chart-data-selection';
|
||||
import { parseMonthString } from './dates';
|
||||
|
||||
type Props = {
|
||||
period: ChartDataSelection;
|
||||
@ -62,7 +63,7 @@ const incomingRequestsText = (period: ChartDataSelection): string => {
|
||||
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<Props> = ({
|
||||
|
@ -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 };
|
||||
|
@ -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++) {
|
||||
|
@ -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());
|
||||
};
|
||||
|
@ -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',
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -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',
|
||||
|
Loading…
Reference in New Issue
Block a user