Skip to content

Commit 31932c5

Browse files
authored
Fixed handling for 'Today' lookback with Analytics kpis (#24555)
ref https://linear.app/ghost/issue/PROD-2243/ - formatDisplayDate was assuming UTC returned data when parsing timestamps, which was incorrectly adjusting the data returns from the API Ideally we would handle UTC data across the board, but we need a serious rethink of how we handle startDate and endDate. This appears to handle the case of 'Today' in Analytics > Overview, Analytics > Web, and Post Analytics > Overview/Web.
1 parent ac10801 commit 31932c5

File tree

2 files changed

+30
-15
lines changed

2 files changed

+30
-15
lines changed

apps/shade/src/lib/utils.ts

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -160,21 +160,40 @@ export const formatQueryDate = (date: Moment) => {
160160

161161
// Format date for UI, result is in the formate of `12 Jun 2025`
162162
export const formatDisplayDate = (dateString: string): string => {
163+
// Check if this is a datetime string (contains time) or just a date
164+
const hasTime = dateString.includes(':');
165+
163166
const date = new Date(dateString);
164167
const today = new Date();
165-
const isToday = date.toISOString().slice(0, 10) === today.toISOString().slice(0, 10);
166-
const isCurrentYear = date.getUTCFullYear() === today.getUTCFullYear();
168+
169+
let day, month, year, isToday, isCurrentYear;
170+
171+
if (hasTime && !dateString.includes('T') && !dateString.includes('Z')) {
172+
// This is a localized datetime string like "2025-07-29 19:00:00"
173+
// Use local date methods to avoid timezone conversion
174+
day = date.getDate();
175+
month = date.getMonth();
176+
year = date.getFullYear();
177+
isToday = date.toDateString() === today.toDateString();
178+
isCurrentYear = year === today.getFullYear();
179+
} else {
180+
// This is either a date-only string or an ISO format with timezone
181+
// Use UTC methods as before
182+
day = date.getUTCDate();
183+
month = date.getUTCMonth();
184+
year = date.getUTCFullYear();
185+
isToday = date.toISOString().slice(0, 10) === today.toISOString().slice(0, 10);
186+
isCurrentYear = year === today.getUTCFullYear();
187+
}
167188

168189
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
169-
const day = date.getUTCDate();
170-
const month = months[date.getUTCMonth()];
171-
const year = date.getUTCFullYear();
190+
const monthName = months[month];
172191

173192
if (isToday) {
174-
return `${day} ${month}`;
193+
return `${day} ${monthName}`;
175194
}
176195

177-
return isCurrentYear ? `${day} ${month}` : `${day} ${month} ${year}`;
196+
return isCurrentYear ? `${day} ${monthName}` : `${day} ${monthName} ${year}`;
178197
};
179198

180199
// Helper function to format timestamp

apps/stats/src/views/Stats/Growth/components/GrowthKPIs.tsx

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ const GrowthKPIs: React.FC<{
234234
const todayData = subscriptionData.find(item => item.date === today);
235235

236236
return [{
237-
date: formatDisplayDateWithRange(new Date(today), range),
237+
date: formatDisplayDateWithRange(today, range),
238238
new: todayData?.signups || 0,
239239
cancelled: -(todayData?.cancellations || 0) // Negative for the stacked bar chart
240240
}];
@@ -270,10 +270,8 @@ const GrowthKPIs: React.FC<{
270270
const filledData = fillMissingDataPoints(combinedData, range);
271271

272272
return filledData.map((item) => {
273-
const date = new Date(item.date);
274-
275273
return {
276-
date: formatDisplayDateWithRange(date, range),
274+
date: formatDisplayDateWithRange(item.date, range),
277275
new: item.signups || 0,
278276
cancelled: -(item.cancellations || 0) // Negative for the stacked bar chart
279277
};
@@ -290,7 +288,7 @@ const GrowthKPIs: React.FC<{
290288
const todayData = allChartData.find(item => item.date === today);
291289

292290
return [{
293-
date: formatDisplayDateWithRange(new Date(today), range),
291+
date: formatDisplayDateWithRange(today, range),
294292
new: todayData?.paid_subscribed || 0,
295293
cancelled: -(todayData?.paid_canceled || 0) // Negative for the stacked bar chart
296294
}];
@@ -299,10 +297,8 @@ const GrowthKPIs: React.FC<{
299297
const sanitizedData = sanitizeChartData(allChartData, range, 'paid', 'exact');
300298

301299
return sanitizedData.map((item) => {
302-
const date = new Date(item.date);
303-
304300
return {
305-
date: formatDisplayDateWithRange(date, range),
301+
date: formatDisplayDateWithRange(item.date, range),
306302
new: item.paid_subscribed || 0,
307303
cancelled: -(item.paid_canceled || 0) // Negative for the stacked bar chart
308304
};

0 commit comments

Comments
 (0)