@@ -53,11 +53,17 @@ describe('util.parse_run_args', function()
5353 end )
5454
5555 it (' parses multiple prefixes in order' , function ()
56- local opts , prompt = util .parse_run_args ({ ' agent=plan' , ' model=openai/gpt-4' , ' context=current_file.enabled=false' , ' prompt' , ' here' })
56+ local opts , prompt = util .parse_run_args ({
57+ ' agent=plan' ,
58+ ' model=openai/gpt-4' ,
59+ ' context=current_file.enabled=false' ,
60+ ' prompt' ,
61+ ' here' ,
62+ })
5763 assert .are .same ({
5864 agent = ' plan' ,
5965 model = ' openai/gpt-4' ,
60- context = { current_file = { enabled = false } }
66+ context = { current_file = { enabled = false } },
6167 }, opts )
6268 assert .equals (' prompt here' , prompt )
6369 end )
@@ -67,8 +73,8 @@ describe('util.parse_run_args', function()
6773 assert .are .same ({
6874 context = {
6975 current_file = { enabled = false },
70- selection = { enabled = true }
71- }
76+ selection = { enabled = true },
77+ },
7278 }, opts )
7379 assert .equals (' test' , prompt )
7480 end )
@@ -91,3 +97,155 @@ describe('util.parse_run_args', function()
9197 assert .equals (' some prompt model=openai/gpt-4' , prompt )
9298 end )
9399end )
100+
101+ describe (' util.format_time' , function ()
102+ local function make_timestamp (year , month , day , hour , min , sec )
103+ return os.time ({ year = year , month = month , day = day , hour = hour or 0 , min = min or 0 , sec = sec or 0 })
104+ end
105+
106+ local today = os.date (' *t' )
107+ local today_morning = make_timestamp (today .year , today .month , today .day , 8 , 30 , 0 )
108+ local today_afternoon = make_timestamp (today .year , today .month , today .day , 15 , 45 , 30 )
109+ local today_evening = make_timestamp (today .year , today .month , today .day , 23 , 59 , 59 )
110+
111+ local yesterday = os.time () - 86400 -- 24 hours ago
112+ local last_week = os.time () - (7 * 86400 ) -- 7 days ago
113+ local next_year = make_timestamp (today .year + 1 , 6 , 15 , 12 , 0 , 0 )
114+
115+ describe (' today timestamps' , function ()
116+ it (' formats morning time correctly' , function ()
117+ local result = util .format_time (today_morning )
118+ assert .matches (' ^%d%d?:%d%d [AP]M$' , result )
119+ assert .is_nil (result :match (' %d%d%d%d' ))
120+ end )
121+
122+ it (' formats afternoon time correctly' , function ()
123+ local result = util .format_time (today_afternoon )
124+ assert .matches (' ^%d%d?:%d%d [AP]M$' , result )
125+ assert .is_nil (result :match (' %d%d%d%d' ))
126+ end )
127+
128+ it (' formats late evening time correctly' , function ()
129+ local result = util .format_time (today_evening )
130+ assert .matches (' ^%d%d?:%d%d [AP]M$' , result )
131+ assert .is_nil (result :match (' %d%d%d%d' ))
132+ end )
133+
134+ it (' formats current time as time-only' , function ()
135+ local current_time = os.time ()
136+ local result = util .format_time (current_time )
137+ assert .matches (' ^%d%d?:%d%d [AP]M$' , result )
138+ assert .is_nil (result :match (' %d%d%d%d' ))
139+ end )
140+ end )
141+
142+ describe (' other day timestamps' , function ()
143+ it (' formats yesterday with full date' , function ()
144+ local result = util .format_time (yesterday )
145+ assert .matches (' ^%d%d? %a%a%a %d%d%d%d %d%d?:%d%d [AP]M$' , result )
146+ assert .matches (' %d%d%d%d' , result )
147+ end )
148+
149+ it (' formats last week with full date' , function ()
150+ local result = util .format_time (last_week )
151+ assert .matches (' ^%d%d? %a%a%a %d%d%d%d %d%d?:%d%d [AP]M$' , result )
152+ assert .matches (' %d%d%d%d' , result )
153+ end )
154+
155+ it (' formats future date with full date' , function ()
156+ local result = util .format_time (next_year )
157+ assert .matches (' ^%d%d? %a%a%a %d%d%d%d %d%d?:%d%d [AP]M$' , result )
158+ assert .matches (' %d%d%d%d' , result )
159+ end )
160+ end )
161+
162+ describe (' millisecond timestamp conversion' , function ()
163+ it (' converts millisecond timestamps to seconds' , function ()
164+ local seconds_timestamp = os.time ()
165+ local milliseconds_timestamp = seconds_timestamp * 1000
166+
167+ local seconds_result = util .format_time (seconds_timestamp )
168+ local milliseconds_result = util .format_time (milliseconds_timestamp )
169+
170+ assert .equals (seconds_result , milliseconds_result )
171+ end )
172+
173+ it (' handles large millisecond timestamps correctly' , function ()
174+ local ms_timestamp = 1762350000000 -- ~November 2025 in milliseconds
175+ local result = util .format_time (ms_timestamp )
176+
177+ assert .is_not_nil (result )
178+ assert .is_string (result )
179+
180+ local is_time_only = result :match (' ^%d%d?:%d%d [AP]M$' )
181+ local is_full_date = result :match (' ^%d%d? %a%a%a %d%d%d%d %d%d?:%d%d [AP]M$' )
182+ assert .is_true (is_time_only ~= nil or is_full_date ~= nil )
183+ end )
184+
185+ it (' does not convert regular second timestamps' , function ()
186+ local small_timestamp = 1000000000 -- Year 2001, definitely in seconds
187+ local result = util .format_time (small_timestamp )
188+
189+ -- Should format without error
190+ assert .is_not_nil (result )
191+ assert .is_string (result )
192+ end )
193+ end )
194+
195+ describe (' edge cases' , function ()
196+ it (' handles midnight correctly' , function ()
197+ local midnight = make_timestamp (today .year , today .month , today .day , 0 , 0 , 0 )
198+ local result = util .format_time (midnight )
199+
200+ if os.date (' %Y-%m-%d' , midnight ) == os.date (' %Y-%m-%d' ) then
201+ assert .matches (' ^%d%d?:%d%d [AP]M$' , result )
202+ assert .matches (' 12:00 AM' , result ) -- Midnight should be 12:00 AM
203+ else
204+ assert .matches (' ^%d%d? %a%a%a %d%d%d%d %d%d?:%d%d [AP]M$' , result )
205+ end
206+ end )
207+
208+ it (' handles noon correctly' , function ()
209+ local noon = make_timestamp (today .year , today .month , today .day , 12 , 0 , 0 )
210+ local result = util .format_time (noon )
211+
212+ assert .matches (' ^%d%d?:%d%d [AP]M$' , result )
213+ assert .matches (' 12:00 PM' , result ) -- Noon should be 12:00 PM
214+ end )
215+
216+ it (' handles date boundary transitions' , function ()
217+ local late_today = make_timestamp (today .year , today .month , today .day , 23 , 59 , 0 )
218+ local early_tomorrow = late_today + 120 -- 2 minutes later (next day)
219+
220+ local late_result = util .format_time (late_today )
221+ local early_result = util .format_time (early_tomorrow )
222+
223+ -- Late today should be time-only
224+ assert .matches (' ^%d%d?:%d%d [AP]M$' , late_result )
225+
226+ -- Early tomorrow behavior depends on whether it's actually tomorrow
227+ if os.date (' %Y-%m-%d' , early_tomorrow ) == os.date (' %Y-%m-%d' ) then
228+ -- Still today
229+ assert .matches (' ^%d%d?:%d%d [AP]M$' , early_result )
230+ else
231+ -- Actually tomorrow
232+ assert .matches (' ^%d%d? %a%a%a %d%d%d%d %d%d?:%d%d [AP]M$' , early_result )
233+ end
234+ end )
235+ end )
236+
237+ describe (' timezone consistency' , function ()
238+ it (' uses consistent timezone for date comparison' , function ()
239+ local now = os.time ()
240+
241+ -- Both should use the same local timezone
242+ local timestamp_date = os.date (' %Y-%m-%d' , now )
243+ local current_date = os.date (' %Y-%m-%d' )
244+
245+ assert .equals (timestamp_date , current_date )
246+
247+ local result = util .format_time (now )
248+ assert .matches (' ^%d%d?:%d%d [AP]M$' , result )
249+ end )
250+ end )
251+ end )
0 commit comments