Skip to content
5 changes: 3 additions & 2 deletions static/app/views/organizationStats/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,9 @@ export class OrganizationStatsInner extends Component<OrganizationStatsProps> {
utc,
}
: {
start: moment(start).utc().format(),
end: moment(end).utc().format(),
// Treat start/end URL params as UTC (consistent with DatePageFilter)
start: moment.utc(start).format(),
end: moment.utc(end).format(),
utc,
};
}
Expand Down
14 changes: 14 additions & 0 deletions static/app/views/organizationStats/usageChart/utils.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import moment from 'moment-timezone';

import {ConfigStore} from 'sentry/stores/configStore';
import {
getDateFromMoment,
getXAxisDates,
Expand All @@ -8,6 +9,19 @@ import {
const TS_START = 1531094400000; // 2018 July 9, 12am UTC
const TS_END = 1531180800000; // 2018 July 10, 12am UTC

// Tests that format sub-daily dates in local time expect America/New_York (-04:00 EDT).
// We explicitly set this via ConfigStore so the output is environment-independent
// (previously these tests relied on the CI runner's system timezone).
beforeEach(() => {
ConfigStore.set('user', {
...ConfigStore.get('user'),
options: {
...ConfigStore.get('user')?.options,
timezone: 'America/New_York',
},
} as any);
});

describe('getDateFromMoment', () => {
const start = moment.unix(TS_START / 1000);
// Ensure date remains in UTC
Expand Down
6 changes: 4 additions & 2 deletions static/app/views/organizationStats/usageChart/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import moment from 'moment-timezone';

import {parseStatsPeriod} from 'sentry/components/pageFilters/parse';
import type {DataCategory, IntervalPeriod} from 'sentry/types/core';
import {shouldUse24Hours} from 'sentry/utils/dates';
import {getUserTimezone, shouldUse24Hours} from 'sentry/utils/dates';
import {parsePeriodToHours} from 'sentry/utils/duration/parsePeriodToHours';
import {formatUsageWithUnits} from 'sentry/views/organizationStats/utils';

Expand Down Expand Up @@ -39,7 +39,9 @@ export function getDateFromMoment(

// For sub-daily intervals, use hourly format
const parsedInterval = parseStatsPeriod(interval);
const datetime = useUtc ? moment(m).utc() : moment(m).local();
const datetime = useUtc
? moment(m).utc()
: moment(m).tz(getUserTimezone() ?? moment.tz.guess());

const intervalFormat = use24Hours ? FORMAT_DATETIME_HOURLY_24H : FORMAT_DATETIME_HOURLY;

Expand Down
10 changes: 7 additions & 3 deletions static/app/views/organizationStats/usageStatsOrg.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import type {
import type {Organization} from 'sentry/types/organization';
import {trackAnalytics} from 'sentry/utils/analytics';
import {getApiUrl} from 'sentry/utils/api/getApiUrl';
import {shouldUse24Hours} from 'sentry/utils/dates';
import {getUserTimezone, shouldUse24Hours} from 'sentry/utils/dates';
import {parsePeriodToHours} from 'sentry/utils/duration/parsePeriodToHours';
import {hasDynamicSamplingCustomFeature} from 'sentry/utils/dynamicSampling/features';
import type {UseApiQueryResult} from 'sentry/utils/queryClient';
Expand Down Expand Up @@ -463,8 +463,12 @@ export function UsageStatsOrganization({

const xAxisStart = moment(startTime);
const xAxisEnd = moment(endTime);
const displayStart = useUtc ? moment(startTime).utc() : moment(startTime).local();
const displayEnd = useUtc ? moment(endTime).utc() : moment(endTime).local();
const displayStart = useUtc
? moment(startTime).utc()
: moment(startTime).tz(getUserTimezone() ?? moment.tz.guess());
const displayEnd = useUtc
? moment(endTime).utc()
: moment(endTime).tz(getUserTimezone() ?? moment.tz.guess());

if (intervalHours < 24) {
displayEnd.add(intervalHours, 'h');
Expand Down
Loading