@@ -3,7 +3,8 @@ use std::env;
33
44use chrono:: {
55 DateTime ,
6- Utc ,
6+ Datelike ,
7+ FixedOffset ,
78} ;
89use serde:: {
910 Deserialize ,
@@ -54,7 +55,7 @@ pub struct UserMessage {
5455 pub additional_context : String ,
5556 pub env_context : UserEnvContext ,
5657 pub content : UserMessageContent ,
57- pub timestamp : Option < DateTime < Utc > > ,
58+ pub timestamp : Option < DateTime < FixedOffset > > ,
5859 pub images : Option < Vec < ImageBlock > > ,
5960}
6061
@@ -107,7 +108,7 @@ impl UserMessageContent {
107108impl UserMessage {
108109 /// Creates a new [UserMessage::Prompt], automatically detecting and adding the user's
109110 /// environment [UserEnvContext].
110- pub fn new_prompt ( prompt : String , timestamp : Option < DateTime < Utc > > ) -> Self {
111+ pub fn new_prompt ( prompt : String , timestamp : Option < DateTime < FixedOffset > > ) -> Self {
111112 Self {
112113 images : None ,
113114 timestamp,
@@ -120,7 +121,7 @@ impl UserMessage {
120121 pub fn new_cancelled_tool_uses < ' a > (
121122 prompt : Option < String > ,
122123 tool_use_ids : impl Iterator < Item = & ' a str > ,
123- timestamp : Option < DateTime < Utc > > ,
124+ timestamp : Option < DateTime < FixedOffset > > ,
124125 ) -> Self {
125126 Self {
126127 images : None ,
@@ -157,7 +158,7 @@ impl UserMessage {
157158 pub fn new_tool_use_results_with_images (
158159 results : Vec < ToolUseResult > ,
159160 images : Vec < ImageBlock > ,
160- timestamp : Option < DateTime < Utc > > ,
161+ timestamp : Option < DateTime < FixedOffset > > ,
161162 ) -> Self {
162163 Self {
163164 additional_context : String :: new ( ) ,
@@ -292,12 +293,20 @@ impl UserMessage {
292293 let mut content = String :: new ( ) ;
293294
294295 if let Some ( ts) = self . timestamp {
296+ let weekday = match ts. weekday ( ) {
297+ chrono:: Weekday :: Mon => "Monday" ,
298+ chrono:: Weekday :: Tue => "Tuesday" ,
299+ chrono:: Weekday :: Wed => "Wednesday" ,
300+ chrono:: Weekday :: Thu => "Thursday" ,
301+ chrono:: Weekday :: Fri => "Friday" ,
302+ chrono:: Weekday :: Sat => "Saturday" ,
303+ chrono:: Weekday :: Sun => "Sunday" ,
304+ } ;
305+ // Format the time with iso8601 format using a timezone offset.
306+ let timestamp = ts. to_rfc3339_opts ( chrono:: SecondsFormat :: Millis , false ) ;
295307 content. push_str ( & format ! (
296- "{}Current UTC time: {}{}\n " ,
297- CONTEXT_ENTRY_START_HEADER ,
298- // Format the time with iso8601 format using Z, e.g. 2025-08-08T17:43:28.672Z
299- ts. to_rfc3339_opts( chrono:: SecondsFormat :: Millis , true ) ,
300- CONTEXT_ENTRY_END_HEADER ,
308+ "{}Current time: {}, {}\n {}" ,
309+ CONTEXT_ENTRY_START_HEADER , weekday, timestamp, CONTEXT_ENTRY_END_HEADER ,
301310 ) ) ;
302311 }
303312
@@ -565,21 +574,35 @@ mod tests {
565574 fn test_user_input_message_timestamp_formatting ( ) {
566575 const USER_PROMPT : & str = "hello world" ;
567576
568- let msg = UserMessage :: new_prompt ( USER_PROMPT . to_string ( ) , Some ( Utc :: now ( ) ) ) ;
577+ // Friday, Jan 26, 2018
578+ let timestamp = DateTime :: parse_from_rfc3339 ( "2018-01-26T12:30:09.453-07:00" ) . unwrap ( ) ;
569579
570- let msgs = [
571- msg. clone ( ) . into_user_input_message ( None , & HashMap :: new ( ) ) ,
572- msg. clone ( ) . into_history_entry ( ) ,
580+ let msgs = {
581+ let msg = UserMessage :: new_prompt ( USER_PROMPT . to_string ( ) , Some ( timestamp) ) ;
582+ [
583+ msg. clone ( ) . into_user_input_message ( None , & HashMap :: new ( ) ) ,
584+ msg. clone ( ) . into_history_entry ( ) ,
585+ ]
586+ } ;
587+ let expected = [
588+ CONTEXT_ENTRY_START_HEADER ,
589+ "Current time" ,
590+ "Friday" ,
591+ CONTEXT_ENTRY_END_HEADER ,
592+ USER_ENTRY_START_HEADER ,
593+ USER_PROMPT ,
594+ USER_ENTRY_END_HEADER . trim ( ) , /* user message content is trimmed, so remove any
595+ * trailing newlines for the end header. */
573596 ] ;
574-
575597 for m in msgs {
576- println ! ( "checking {:?}" , m) ;
577- assert ! ( m. content. contains( CONTEXT_ENTRY_START_HEADER ) ) ;
578- assert ! ( m. content. contains( "Current UTC time" ) ) ;
579- assert ! ( m. content. contains( CONTEXT_ENTRY_END_HEADER ) ) ;
580- assert ! ( m. content. contains( USER_ENTRY_START_HEADER ) ) ;
581- assert ! ( m. content. contains( USER_PROMPT ) ) ;
582- assert ! ( m. content. contains( USER_ENTRY_END_HEADER . trim( ) ) ) ;
598+ for assertion in expected {
599+ assert ! (
600+ m. content. contains( assertion) ,
601+ "expected message: {} to contain: {}" ,
602+ m. content,
603+ assertion
604+ ) ;
605+ }
583606 }
584607 }
585608
0 commit comments