Skip to content

Commit 6eaf257

Browse files
robKitsonclauderyoppippi
authored
Using UTC year/month/day in data-loader (#312)
* Using UTC year/month/day in data-loader * fix: handle timezone detection in date formatting methods Refactored formatDate and formatDateCompact to use shared formatDateInternal method that properly detects timezone indicators (Z or ±HH:MM) and uses appropriate date getters (UTC vs local) based on the input format. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> * chore: format --------- Co-authored-by: Claude <[email protected]> Co-authored-by: ryoppippi <[email protected]>
1 parent b16fa46 commit 6eaf257

File tree

1 file changed

+63
-22
lines changed

1 file changed

+63
-22
lines changed

src/data-loader.ts

Lines changed: 63 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -420,17 +420,33 @@ function extractUniqueModels<T>(
420420
return uniq(entries.map(getModel).filter((m): m is string => m != null && m !== '<synthetic>'));
421421
}
422422

423+
/**
424+
* Shared method for formatting dates with proper timezone handling
425+
* @param dateStr - Input date string
426+
* @param twoLine - Whether to format as two lines (true) or single line (false)
427+
* @returns Formatted date string
428+
*/
429+
function formatDateInternal(dateStr: string, twoLine: boolean): string {
430+
const date = new Date(dateStr);
431+
432+
// Detect if the string includes UTC indicator (Z) or timezone offset (±HH:MM)
433+
const hasTimezone = /Z|[+-]\d{2}:\d{2}/.test(dateStr);
434+
435+
// Use UTC getters if timezone is specified, otherwise use local getters
436+
const year = hasTimezone ? date.getUTCFullYear() : date.getFullYear();
437+
const month = String(hasTimezone ? date.getUTCMonth() + 1 : date.getMonth() + 1).padStart(2, '0');
438+
const day = String(hasTimezone ? date.getUTCDate() : date.getDate()).padStart(2, '0');
439+
440+
return twoLine ? `${year}\n${month}-${day}` : `${year}-${month}-${day}`;
441+
}
442+
423443
/**
424444
* Formats a date string to YYYY-MM-DD format
425445
* @param dateStr - Input date string
426446
* @returns Formatted date string in YYYY-MM-DD format
427447
*/
428448
export function formatDate(dateStr: string): string {
429-
const date = new Date(dateStr);
430-
const year = date.getFullYear();
431-
const month = String(date.getMonth() + 1).padStart(2, '0');
432-
const day = String(date.getDate()).padStart(2, '0');
433-
return `${year}-${month}-${day}`;
449+
return formatDateInternal(dateStr, false);
434450
}
435451

436452
/**
@@ -439,11 +455,7 @@ export function formatDate(dateStr: string): string {
439455
* @returns Formatted date string with newline separator (YYYY\nMM-DD)
440456
*/
441457
export function formatDateCompact(dateStr: string): string {
442-
const date = new Date(dateStr);
443-
const year = date.getFullYear();
444-
const month = String(date.getMonth() + 1).padStart(2, '0');
445-
const day = String(date.getDate()).padStart(2, '0');
446-
return `${year}\n${month}-${day}`;
458+
return formatDateInternal(dateStr, true);
447459
}
448460

449461
/**
@@ -1142,16 +1154,30 @@ export async function loadSessionBlockData(
11421154

11431155
if (import.meta.vitest != null) {
11441156
describe('formatDate', () => {
1145-
it('formats UTC timestamp to local date', () => {
1146-
// Test with UTC timestamps - results depend on local timezone
1157+
it('formats UTC timestamps using UTC date', () => {
1158+
// UTC timestamps should always use UTC date regardless of local timezone
11471159
expect(formatDate('2024-01-01T00:00:00Z')).toBe('2024-01-01');
11481160
expect(formatDate('2024-12-31T23:59:59Z')).toBe('2024-12-31');
1161+
expect(formatDate('2024-01-01T12:00:00.000Z')).toBe('2024-01-01');
11491162
});
11501163

1151-
it('handles various date formats', () => {
1152-
expect(formatDate('2024-01-01')).toBe('2024-01-01');
1153-
expect(formatDate('2024-01-01T12:00:00')).toBe('2024-01-01');
1154-
expect(formatDate('2024-01-01T12:00:00.000Z')).toBe('2024-01-01');
1164+
it('formats timezone offset strings using UTC date', () => {
1165+
// Strings with timezone offsets should use UTC date
1166+
expect(formatDate('2024-01-01T00:00:00+00:00')).toBe('2024-01-01');
1167+
expect(formatDate('2024-01-01T00:00:00-05:00')).toBe('2024-01-01');
1168+
expect(formatDate('2024-12-31T23:59:59+08:00')).toBe('2024-12-31');
1169+
});
1170+
1171+
it('formats local date strings using local date', () => {
1172+
// Without timezone indicator, should use local date interpretation
1173+
const localDate = new Date('2024-01-01T12:00:00');
1174+
const expectedYear = localDate.getFullYear();
1175+
const expectedMonth = String(localDate.getMonth() + 1).padStart(2, '0');
1176+
const expectedDay = String(localDate.getDate()).padStart(2, '0');
1177+
const expected = `${expectedYear}-${expectedMonth}-${expectedDay}`;
1178+
1179+
expect(formatDate('2024-01-01')).toBe(expected);
1180+
expect(formatDate('2024-01-01T12:00:00')).toBe(expected);
11551181
});
11561182

11571183
it('pads single digit months and days', () => {
@@ -1161,17 +1187,32 @@ if (import.meta.vitest != null) {
11611187
});
11621188

11631189
describe('formatDateCompact', () => {
1164-
it('formats UTC timestamp to local date with line break', () => {
1190+
it('formats UTC timestamps using UTC date with line break', () => {
1191+
// UTC timestamps should always use UTC date regardless of local timezone
11651192
expect(formatDateCompact('2024-01-01T00:00:00Z')).toBe('2024\n01-01');
1166-
});
1167-
1168-
it('handles various date formats', () => {
11691193
expect(formatDateCompact('2024-12-31T23:59:59Z')).toBe('2024\n12-31');
1170-
expect(formatDateCompact('2024-01-01')).toBe('2024\n01-01');
1171-
expect(formatDateCompact('2024-01-01T12:00:00')).toBe('2024\n01-01');
11721194
expect(formatDateCompact('2024-01-01T12:00:00.000Z')).toBe('2024\n01-01');
11731195
});
11741196

1197+
it('formats timezone offset strings using UTC date with line break', () => {
1198+
// Strings with timezone offsets should use UTC date
1199+
expect(formatDateCompact('2024-01-01T00:00:00+00:00')).toBe('2024\n01-01');
1200+
expect(formatDateCompact('2024-01-01T00:00:00-05:00')).toBe('2024\n01-01');
1201+
expect(formatDateCompact('2024-12-31T23:59:59+08:00')).toBe('2024\n12-31');
1202+
});
1203+
1204+
it('formats local date strings using local date with line break', () => {
1205+
// Without timezone indicator, should use local date interpretation
1206+
const localDate = new Date('2024-01-01T12:00:00');
1207+
const expectedYear = localDate.getFullYear();
1208+
const expectedMonth = String(localDate.getMonth() + 1).padStart(2, '0');
1209+
const expectedDay = String(localDate.getDate()).padStart(2, '0');
1210+
const expected = `${expectedYear}\n${expectedMonth}-${expectedDay}`;
1211+
1212+
expect(formatDateCompact('2024-01-01')).toBe(expected);
1213+
expect(formatDateCompact('2024-01-01T12:00:00')).toBe(expected);
1214+
});
1215+
11751216
it('pads single digit months and days', () => {
11761217
expect(formatDateCompact('2024-01-05T00:00:00Z')).toBe('2024\n01-05');
11771218
expect(formatDateCompact('2024-10-01T00:00:00Z')).toBe('2024\n10-01');

0 commit comments

Comments
 (0)