Skip to content

Commit 99f26ff

Browse files
authored
feat(output): add configurable time_format with locale encoding fix (#385)
1 parent 20e559a commit 99f26ff

4 files changed

Lines changed: 31 additions & 7 deletions

File tree

lua/opencode/config.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ M.defaults = {
160160
},
161161
output = {
162162
filetype = 'opencode_output',
163+
time_format = nil,
163164
compact_assistant_headers = false,
164165
rendering = {
165166
markdown_debounce_ms = 250,

lua/opencode/types.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,7 @@
253253
---@field event_collapsing boolean
254254

255255
---@class OpencodeUIOutputConfig
256+
---@field time_format string|nil # Custom os.date format for timestamps, e.g. '%m/%d %H:%M'. Uses fixed default when nil.
256257
---@field tools { show_output: boolean, show_reasoning_output: boolean, use_folds: boolean, folding_threshold: number }
257258
---@field rendering OpencodeUIOutputRenderingConfig
258259
---@field max_messages integer|nil

lua/opencode/util.lua

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -224,11 +224,19 @@ function M.format_time(timestamp)
224224
return ''
225225
end
226226

227+
local config = require('opencode.config')
228+
local time_format = config.ui.output.time_format
229+
230+
if time_format then
231+
return os.date(time_format, timestamp)
232+
end
233+
234+
local saved = os.setlocale('C', 'time')
227235
local same_day = os.date('%Y-%m-%d') == os.date('%Y-%m-%d', timestamp)
228236
local same_year = os.date('%Y') == os.date('%Y', timestamp)
229237
local locale_time = vim.trim(os.date('%X', timestamp) or '')
238+
os.setlocale(saved, 'time')
230239

231-
-- Keep output close to previous formatting by dropping seconds when present.
232240
locale_time = locale_time:gsub('^(%d?%d:%d%d):%d%d(.*)$', '%1%2')
233241
if locale_time == '' then
234242
locale_time = vim.trim(os.date('%H:%M', timestamp) or '')
@@ -238,11 +246,16 @@ function M.format_time(timestamp)
238246
return locale_time
239247
end
240248

249+
saved = os.setlocale('C', 'time')
250+
local date_part
241251
if same_year then
242-
return string.format('%s %s', os.date('%d %b', timestamp), locale_time)
252+
date_part = os.date('%d %b', timestamp)
253+
else
254+
date_part = os.date('%d %b %Y', timestamp)
243255
end
256+
os.setlocale(saved, 'time')
244257

245-
return string.format('%s %s', os.date('%d %b %Y', timestamp), locale_time)
258+
return string.format('%s %s', date_part, locale_time)
246259
end
247260

248261
---@param timestamp number

tests/unit/util_spec.lua

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,17 @@ describe('util.format_time', function()
103103
return os.time({ year = year, month = month, day = day, hour = hour or 0, min = min or 0, sec = sec or 0 })
104104
end
105105

106+
local function c_locale_date(fmt, timestamp)
107+
local saved = os.setlocale('C', 'time')
108+
local result = os.date(fmt, timestamp)
109+
os.setlocale(saved, 'time')
110+
return result
111+
end
112+
106113
local function compact_locale_time(timestamp)
114+
local saved = os.setlocale('C', 'time')
107115
local locale_time = vim.trim(os.date('%X', timestamp) or '')
116+
os.setlocale(saved, 'time')
108117
locale_time = locale_time:gsub('^(%d?%d:%d%d):%d%d(.*)$', '%1%2')
109118
if locale_time == '' then
110119
locale_time = vim.trim(os.date('%H:%M', timestamp) or '')
@@ -152,21 +161,21 @@ describe('util.format_time', function()
152161
describe('other day timestamps', function()
153162
it('formats yesterday with date prefix and locale time', function()
154163
local result = util.format_time(yesterday)
155-
local expected_prefix = os.date('%d %b', yesterday) .. ' '
164+
local expected_prefix = c_locale_date('%d %b', yesterday) .. ' '
156165
assert.is_true(vim.startswith(result, expected_prefix))
157166
assert.equals(compact_locale_time(yesterday), result:sub(#expected_prefix + 1))
158167
end)
159168

160169
it('formats last week with date prefix and locale time', function()
161170
local result = util.format_time(last_week)
162-
local expected_prefix = os.date('%d %b', last_week) .. ' '
171+
local expected_prefix = c_locale_date('%d %b', last_week) .. ' '
163172
assert.is_true(vim.startswith(result, expected_prefix))
164173
assert.equals(compact_locale_time(last_week), result:sub(#expected_prefix + 1))
165174
end)
166175

167176
it('formats future date with full date and locale time', function()
168177
local result = util.format_time(next_year)
169-
local expected_prefix = os.date('%d %b %Y', next_year) .. ' '
178+
local expected_prefix = c_locale_date('%d %b %Y', next_year) .. ' '
170179
assert.is_true(vim.startswith(result, expected_prefix))
171180
assert.equals(compact_locale_time(next_year), result:sub(#expected_prefix + 1))
172181
assert.matches('%d%d%d%d', result)
@@ -230,7 +239,7 @@ describe('util.format_time', function()
230239
if os.date('%Y-%m-%d', early_tomorrow) == os.date('%Y-%m-%d') then
231240
assert.equals(compact_locale_time(early_tomorrow), early_result)
232241
else
233-
local expected_prefix = os.date('%d %b', early_tomorrow) .. ' '
242+
local expected_prefix = c_locale_date('%d %b', early_tomorrow) .. ' '
234243
assert.is_true(vim.startswith(early_result, expected_prefix))
235244
assert.equals(compact_locale_time(early_tomorrow), early_result:sub(#expected_prefix + 1))
236245
end

0 commit comments

Comments
 (0)