Skip to content

Commit 09bf536

Browse files
authored
fix(amp,opencode,codex,pi): add dateFormatter to prevent date truncation (#788)
1 parent 0fae75a commit 09bf536

File tree

15 files changed

+107
-72
lines changed

15 files changed

+107
-72
lines changed

apps/amp/src/commands/daily.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { TokenUsageEvent } from '../_types.ts';
22
import {
33
addEmptySeparatorRow,
44
formatCurrency,
5+
formatDateCompact,
56
formatModelsDisplayMultiline,
67
formatNumber,
78
ResponsiveTable,
@@ -160,6 +161,7 @@ export const dailyCommand = define({
160161
compactThreshold: 100,
161162
forceCompact: Boolean(ctx.values.compact),
162163
style: { head: ['cyan'] },
164+
dateFormatter: (dateStr: string) => formatDateCompact(dateStr),
163165
});
164166

165167
for (const data of dailyData) {

apps/amp/src/commands/monthly.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { TokenUsageEvent } from '../_types.ts';
22
import {
33
addEmptySeparatorRow,
44
formatCurrency,
5+
formatDateCompact,
56
formatModelsDisplayMultiline,
67
formatNumber,
78
ResponsiveTable,
@@ -160,6 +161,7 @@ export const monthlyCommand = define({
160161
compactThreshold: 100,
161162
forceCompact: Boolean(ctx.values.compact),
162163
style: { head: ['cyan'] },
164+
dateFormatter: (dateStr: string) => formatDateCompact(dateStr),
163165
});
164166

165167
for (const data of monthlyData) {

apps/amp/src/commands/session.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { TokenUsageEvent } from '../_types.ts';
22
import {
33
addEmptySeparatorRow,
44
formatCurrency,
5+
formatDateCompact,
56
formatModelsDisplayMultiline,
67
formatNumber,
78
ResponsiveTable,
@@ -169,6 +170,7 @@ export const sessionCommand = define({
169170
compactThreshold: 100,
170171
forceCompact: Boolean(ctx.values.compact),
171172
style: { head: ['cyan'] },
173+
dateFormatter: (dateStr: string) => formatDateCompact(dateStr),
172174
});
173175

174176
for (const data of sessionData) {

apps/ccusage/src/_date-utils.ts

Lines changed: 5 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@
66
import type { DayOfWeek, WeekDay } from './_consts.ts';
77
import type { WeeklyDate } from './_types.ts';
88
import { sort } from 'fast-sort';
9-
import * as v from 'valibot';
109
import { DEFAULT_LOCALE } from './_consts.ts';
11-
import { createWeeklyDate, dailyDateSchema } from './_types.ts';
10+
import { createWeeklyDate } from './_types.ts';
1211
import { unreachable } from './_utils.ts';
1312

13+
// Re-export formatDateCompact from shared package
14+
export { formatDateCompact } from '@ccusage/terminal/table';
15+
1416
/**
1517
* Sort order for date-based sorting
1618
*/
@@ -31,24 +33,6 @@ function createDateFormatter(timezone: string | undefined, locale: string): Intl
3133
});
3234
}
3335

34-
/**
35-
* Creates a date parts formatter with the specified timezone and locale
36-
* @param timezone - Timezone to use
37-
* @param locale - Locale to use for formatting
38-
* @returns Intl.DateTimeFormat instance
39-
*/
40-
function createDatePartsFormatter(
41-
timezone: string | undefined,
42-
locale: string,
43-
): Intl.DateTimeFormat {
44-
return new Intl.DateTimeFormat(locale, {
45-
year: 'numeric',
46-
month: '2-digit',
47-
day: '2-digit',
48-
timeZone: timezone,
49-
});
50-
}
51-
5236
/**
5337
* Formats a date string to YYYY-MM-DD format
5438
* @param dateStr - Input date string
@@ -63,34 +47,6 @@ export function formatDate(dateStr: string, timezone?: string, locale?: string):
6347
return formatter.format(date);
6448
}
6549

66-
/**
67-
* Formats a date string to compact format with year on first line and month-day on second
68-
* @param dateStr - Input date string
69-
* @param timezone - Timezone to use for formatting (pass undefined to use system timezone)
70-
* @param locale - Locale to use for formatting
71-
* @returns Formatted date string with newline separator (YYYY\nMM-DD)
72-
*/
73-
export function formatDateCompact(
74-
dateStr: string,
75-
timezone: string | undefined,
76-
locale: string,
77-
): string {
78-
// For YYYY-MM-DD format, append T00:00:00 to parse as local date
79-
// Without this, new Date('YYYY-MM-DD') interprets as UTC midnight
80-
const parseResult = v.safeParse(dailyDateSchema, dateStr);
81-
const date = parseResult.success
82-
? timezone != null
83-
? new Date(`${dateStr}T00:00:00Z`)
84-
: new Date(`${dateStr}T00:00:00`)
85-
: new Date(dateStr);
86-
const formatter = createDatePartsFormatter(timezone, locale);
87-
const parts = formatter.formatToParts(date);
88-
const year = parts.find((p) => p.type === 'year')?.value ?? '';
89-
const month = parts.find((p) => p.type === 'month')?.value ?? '';
90-
const day = parts.find((p) => p.type === 'day')?.value ?? '';
91-
return `${year}\n${month}-${day}`;
92-
}
93-
9450
/**
9551
* Generic function to sort items by date based on sort order
9652
* @param items - Array of items to sort
@@ -200,27 +156,7 @@ if (import.meta.vitest != null) {
200156
});
201157
});
202158

203-
describe('formatDateCompact', () => {
204-
it('should format date to compact format with newline', () => {
205-
const result = formatDateCompact('2024-08-04', undefined, 'en-US');
206-
expect(result).toBe('2024\n08-04');
207-
});
208-
209-
it('should handle timezone parameter', () => {
210-
const result = formatDateCompact('2024-08-04T12:00:00Z', 'UTC', 'en-US');
211-
expect(result).toBe('2024\n08-04');
212-
});
213-
214-
it('should handle YYYY-MM-DD format dates', () => {
215-
const result = formatDateCompact('2024-08-04', undefined, 'en-US');
216-
expect(result).toBe('2024\n08-04');
217-
});
218-
219-
it('should handle timezone with YYYY-MM-DD format', () => {
220-
const result = formatDateCompact('2024-08-04', 'UTC', 'en-US');
221-
expect(result).toBe('2024\n08-04');
222-
});
223-
});
159+
// formatDateCompact tests are in @ccusage/terminal/table.ts
224160

225161
describe('sortByDate', () => {
226162
const testData = [

apps/codex/src/commands/daily.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import process from 'node:process';
22
import {
33
addEmptySeparatorRow,
44
formatCurrency,
5+
formatDateCompact,
56
formatModelsDisplayMultiline,
67
formatNumber,
78
ResponsiveTable,
@@ -127,6 +128,7 @@ export const dailyCommand = define({
127128
compactThreshold: 100,
128129
forceCompact: ctx.values.compact,
129130
style: { head: ['cyan'] },
131+
dateFormatter: (dateStr: string) => formatDateCompact(dateStr),
130132
});
131133

132134
const totalsForDisplay = {

apps/codex/src/commands/monthly.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import process from 'node:process';
22
import {
33
addEmptySeparatorRow,
44
formatCurrency,
5+
formatDateCompact,
56
formatModelsDisplayMultiline,
67
formatNumber,
78
ResponsiveTable,
@@ -129,6 +130,7 @@ export const monthlyCommand = define({
129130
compactThreshold: 100,
130131
forceCompact: ctx.values.compact,
131132
style: { head: ['cyan'] },
133+
dateFormatter: (dateStr: string) => formatDateCompact(dateStr),
132134
});
133135

134136
const totalsForDisplay = {

apps/codex/src/commands/session.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import process from 'node:process';
22
import {
33
addEmptySeparatorRow,
44
formatCurrency,
5+
formatDateCompact,
56
formatModelsDisplayMultiline,
67
formatNumber,
78
ResponsiveTable,
@@ -149,6 +150,7 @@ export const sessionCommand = define({
149150
compactThreshold: 100,
150151
forceCompact: ctx.values.compact,
151152
style: { head: ['cyan'] },
153+
dateFormatter: (dateStr: string) => formatDateCompact(dateStr),
152154
});
153155

154156
const totalsForDisplay = {

apps/opencode/src/commands/daily.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { LiteLLMPricingFetcher } from '@ccusage/internal/pricing';
22
import {
33
addEmptySeparatorRow,
44
formatCurrency,
5+
formatDateCompact,
56
formatModelsDisplayMultiline,
67
formatNumber,
78
ResponsiveTable,
@@ -135,6 +136,7 @@ export const dailyCommand = define({
135136
compactThreshold: 100,
136137
forceCompact: Boolean(ctx.values.compact),
137138
style: { head: ['cyan'] },
139+
dateFormatter: (dateStr: string) => formatDateCompact(dateStr),
138140
});
139141

140142
for (const data of dailyData) {

apps/opencode/src/commands/monthly.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { LiteLLMPricingFetcher } from '@ccusage/internal/pricing';
22
import {
33
addEmptySeparatorRow,
44
formatCurrency,
5+
formatDateCompact,
56
formatModelsDisplayMultiline,
67
formatNumber,
78
ResponsiveTable,
@@ -135,6 +136,7 @@ export const monthlyCommand = define({
135136
compactThreshold: 100,
136137
forceCompact: Boolean(ctx.values.compact),
137138
style: { head: ['cyan'] },
139+
dateFormatter: (dateStr: string) => formatDateCompact(dateStr),
138140
});
139141

140142
for (const data of monthlyData) {

apps/opencode/src/commands/session.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { LiteLLMPricingFetcher } from '@ccusage/internal/pricing';
22
import {
33
addEmptySeparatorRow,
44
formatCurrency,
5+
formatDateCompact,
56
formatModelsDisplayMultiline,
67
formatNumber,
78
ResponsiveTable,
@@ -153,6 +154,7 @@ export const sessionCommand = define({
153154
compactThreshold: 100,
154155
forceCompact: Boolean(ctx.values.compact),
155156
style: { head: ['cyan'] },
157+
dateFormatter: (dateStr: string) => formatDateCompact(dateStr),
156158
});
157159

158160
const sessionsByParent = groupBy(sessionData, (s) => s.parentID ?? 'root');

0 commit comments

Comments
 (0)