Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions gottext/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ from CI.
### Fixed

- `mismatched` reads the tags a message carries, so a translation that adds, drops, renames or repeats one is named.
- `formatTime` writes a bare wall clock time, such as `09:30`, as the time it names for every reader, instead of Invalid Date.

## [0.8.0] - 2026-11-02

Expand Down
13 changes: 11 additions & 2 deletions gottext/src/format.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ let named: string | undefined
/** CALENDAR_DAY is the shape of a bare calendar day, such as 2026-09-01. */
const CALENDAR_DAY = /^\d{4}-\d{2}-\d{2}$/

/** WALL_CLOCK is the shape of a bare wall clock time, such as 09:30. */
const WALL_CLOCK = /^\d{2}:\d{2}$/

/** DAY is how a date is written unless the caller asks otherwise. */
const DAY: Intl.DateTimeFormatOptions = { day: '2-digit', month: '2-digit', year: 'numeric' }

Expand Down Expand Up @@ -68,11 +71,17 @@ export function formatDate(at: Date | string, options: Intl.DateTimeFormatOption

/**
* Returns a moment as a time of day in the format locale.
* @param at - The moment, or the text a server stored.
* @param at - The moment, the text a server stored, or a bare wall clock time.
* @returns The time, such as 09:05, empty when there is no moment.
*/
export function formatTime(at: Date | string): string {
return at === '' ? '' : new Date(at).toLocaleTimeString(formatLocale(), CLOCK)
if (at === '') {
return ''
}
if (typeof at === 'string' && WALL_CLOCK.test(at)) {
return new Date(`1970-01-01T${at}Z`).toLocaleTimeString(formatLocale(), { ...CLOCK, timeZone: 'UTC' })
}
return new Date(at).toLocaleTimeString(formatLocale(), CLOCK)
}

/**
Expand Down
27 changes: 27 additions & 0 deletions gottext/test/format.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,33 @@ test('writes a time as two digit hours and minutes on the es-ES clock', () => {
expect(formatTime('2026-09-30T09:05:00Z')).toBe('09:05')
})

test('writes a bare wall clock time as the time it names in every time zone', () => {
rememberFormatLocale('es-ES')
vi.stubEnv('TZ', 'America/Los_Angeles')

expect(formatTime('09:30')).toBe('09:30')
expect(formatTime('21:05')).toBe('21:05')
expect(formatTime('00:00')).toBe('00:00')
expect(formatTime('23:59')).toBe('23:59')
})

test('writes a bare wall clock time on the en-US clock as the time it names', () => {
rememberFormatLocale('en-US')
vi.stubEnv('TZ', 'Asia/Tokyo')

expect(formatTime('09:30')).toBe('09:30 AM')
expect(formatTime('21:05')).toBe('09:05 PM')
expect(formatTime('00:00')).toBe('12:00 AM')
})

test('writes a timestamp a server stored on the reader clock, with or without a zone', () => {
rememberFormatLocale('es-ES')
vi.stubEnv('TZ', 'America/Los_Angeles')

expect(formatTime('2026-09-30T09:05:00Z')).toBe('02:05')
expect(formatTime('2026-09-30T09:05')).toBe('09:05')
})

test('writes nothing for an absent moment', () => {
rememberFormatLocale('es-ES')

Expand Down
Loading