mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-07-24 21:41:08 +02:00
fix(x): show chat timestamps in the user's local timezone (#779)
* fix(x): show chat timestamps in the user's local timezone The per-message "Current date and time" context was built with toISOString(), giving the model a UTC time frame with no zone. It then quoted email Date: headers (sent in +0000) in UTC — an email shown as 1:06 PM IST in the inbox was described as 7:36 AM in chat. Use local wall-clock via toLocaleString with an explicit timeZoneName and the IANA zone (matching what the legacy engine already did) so the model converts offset-bearing timestamps to local time. * fix(x): convert email timestamps to local time for the assistant The "now" fix gave the model a local time frame, but email dates still reached it as raw RFC 2822 Date: headers (marketing mail is sent in +0000), so it kept quoting them in UTC — a 1:06 PM IST email described as 7:36 AM in chat. Add formatEmailDateLocal() and apply it at the three model-facing serialization points: the read-view email tool, the thread-summary line, and the gmail-sync knowledge markdown (**Date:**). The structured date field on snapshots/messages is left raw, since the renderer parses it with new Date() and formats it client-side. * fix(x): generalize local-time formatting for model-facing timestamps Move formatEmailDateLocal out of sync_gmail into a shared formatTimestampForModel() util — email is just the first source of external timestamps; Slack/calendar serialization can now reuse the same one-line call instead of growing per-source formatters. Also add a system-prompt instruction to always express times in the user's local timezone, as a fallback for raw dates inside email bodies, web content, and third-party tool output that pre-conversion can't reach. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
f8f5c8614f
commit
15d484980d
7 changed files with 67 additions and 17 deletions
|
|
@ -22,5 +22,6 @@ export * as notificationSettings from './notification-settings.js';
|
|||
export * as turnLimits from './turn-limits.js';
|
||||
export * as codeSessions from './code-sessions.js';
|
||||
export * as channels from './channels.js';
|
||||
export * as time from './time.js';
|
||||
export * as rowboatApp from './rowboat-app.js';
|
||||
export { PrefixLogger };
|
||||
|
|
|
|||
34
apps/x/packages/shared/src/time.ts
Normal file
34
apps/x/packages/shared/src/time.ts
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
/**
|
||||
* Format a timestamp into the user's local timezone for any text shown to the
|
||||
* LLM (tool output, knowledge markdown, thread summaries). External sources
|
||||
* carry their own offsets — email `Date:` headers are typically `+0000`, Slack
|
||||
* uses epoch seconds, calendar APIs return UTC ISO strings. Handed to the model
|
||||
* raw, it quotes them verbatim instead of converting — e.g. a 1:06 PM IST email
|
||||
* described as "7:36 AM" in chat. Call this at every model-facing serialization
|
||||
* boundary; the model's tendency to quote timestamps verbatim then produces
|
||||
* correct output. The weekday is included because models are unreliable at
|
||||
* day-of-week arithmetic. Uses the system timezone of the process it runs in.
|
||||
*
|
||||
* NOT for structured `date` fields that code later parses with `new Date(...)`
|
||||
* — those must stay raw parseable values.
|
||||
*
|
||||
* Accepts a raw date string, epoch milliseconds, or a Date. Unparseable
|
||||
* strings (e.g. 'Unknown') are returned unchanged; null/undefined and invalid
|
||||
* Date/number inputs return ''.
|
||||
*/
|
||||
export function formatTimestampForModel(raw: string | number | Date | undefined | null): string {
|
||||
if (raw === undefined || raw === null || raw === '') return '';
|
||||
const parsed = raw instanceof Date ? raw : new Date(raw);
|
||||
if (Number.isNaN(parsed.getTime())) {
|
||||
return typeof raw === 'string' ? raw : '';
|
||||
}
|
||||
return parsed.toLocaleString('en-US', {
|
||||
weekday: 'short',
|
||||
year: 'numeric',
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
hour: 'numeric',
|
||||
minute: '2-digit',
|
||||
timeZoneName: 'short',
|
||||
});
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue