diff --git a/lua/opencode/config.lua b/lua/opencode/config.lua index 315344a9..923c84b4 100644 --- a/lua/opencode/config.lua +++ b/lua/opencode/config.lua @@ -160,6 +160,7 @@ M.defaults = { }, output = { filetype = 'opencode_output', + time_format = nil, compact_assistant_headers = false, rendering = { markdown_debounce_ms = 250, diff --git a/lua/opencode/types.lua b/lua/opencode/types.lua index c418b199..4b83a9d4 100644 --- a/lua/opencode/types.lua +++ b/lua/opencode/types.lua @@ -253,6 +253,7 @@ ---@field event_collapsing boolean ---@class OpencodeUIOutputConfig +---@field time_format string|nil # Custom os.date format for timestamps, e.g. '%m/%d %H:%M'. Uses fixed default when nil. ---@field tools { show_output: boolean, show_reasoning_output: boolean, use_folds: boolean, folding_threshold: number } ---@field rendering OpencodeUIOutputRenderingConfig ---@field max_messages integer|nil diff --git a/lua/opencode/util.lua b/lua/opencode/util.lua index 1f51a76f..f414ea01 100644 --- a/lua/opencode/util.lua +++ b/lua/opencode/util.lua @@ -224,11 +224,19 @@ function M.format_time(timestamp) return '' end + local config = require('opencode.config') + local time_format = config.ui.output.time_format + + if time_format then + return os.date(time_format, timestamp) + end + + local saved = os.setlocale('C', 'time') local same_day = os.date('%Y-%m-%d') == os.date('%Y-%m-%d', timestamp) local same_year = os.date('%Y') == os.date('%Y', timestamp) local locale_time = vim.trim(os.date('%X', timestamp) or '') + os.setlocale(saved, 'time') - -- Keep output close to previous formatting by dropping seconds when present. locale_time = locale_time:gsub('^(%d?%d:%d%d):%d%d(.*)$', '%1%2') if locale_time == '' then locale_time = vim.trim(os.date('%H:%M', timestamp) or '') @@ -238,11 +246,16 @@ function M.format_time(timestamp) return locale_time end + saved = os.setlocale('C', 'time') + local date_part if same_year then - return string.format('%s %s', os.date('%d %b', timestamp), locale_time) + date_part = os.date('%d %b', timestamp) + else + date_part = os.date('%d %b %Y', timestamp) end + os.setlocale(saved, 'time') - return string.format('%s %s', os.date('%d %b %Y', timestamp), locale_time) + return string.format('%s %s', date_part, locale_time) end ---@param timestamp number diff --git a/tests/unit/util_spec.lua b/tests/unit/util_spec.lua index 778b5aa8..958ece76 100644 --- a/tests/unit/util_spec.lua +++ b/tests/unit/util_spec.lua @@ -103,8 +103,17 @@ describe('util.format_time', function() return os.time({ year = year, month = month, day = day, hour = hour or 0, min = min or 0, sec = sec or 0 }) end + local function c_locale_date(fmt, timestamp) + local saved = os.setlocale('C', 'time') + local result = os.date(fmt, timestamp) + os.setlocale(saved, 'time') + return result + end + local function compact_locale_time(timestamp) + local saved = os.setlocale('C', 'time') local locale_time = vim.trim(os.date('%X', timestamp) or '') + os.setlocale(saved, 'time') locale_time = locale_time:gsub('^(%d?%d:%d%d):%d%d(.*)$', '%1%2') if locale_time == '' then locale_time = vim.trim(os.date('%H:%M', timestamp) or '') @@ -152,21 +161,21 @@ describe('util.format_time', function() describe('other day timestamps', function() it('formats yesterday with date prefix and locale time', function() local result = util.format_time(yesterday) - local expected_prefix = os.date('%d %b', yesterday) .. ' ' + local expected_prefix = c_locale_date('%d %b', yesterday) .. ' ' assert.is_true(vim.startswith(result, expected_prefix)) assert.equals(compact_locale_time(yesterday), result:sub(#expected_prefix + 1)) end) it('formats last week with date prefix and locale time', function() local result = util.format_time(last_week) - local expected_prefix = os.date('%d %b', last_week) .. ' ' + local expected_prefix = c_locale_date('%d %b', last_week) .. ' ' assert.is_true(vim.startswith(result, expected_prefix)) assert.equals(compact_locale_time(last_week), result:sub(#expected_prefix + 1)) end) it('formats future date with full date and locale time', function() local result = util.format_time(next_year) - local expected_prefix = os.date('%d %b %Y', next_year) .. ' ' + local expected_prefix = c_locale_date('%d %b %Y', next_year) .. ' ' assert.is_true(vim.startswith(result, expected_prefix)) assert.equals(compact_locale_time(next_year), result:sub(#expected_prefix + 1)) assert.matches('%d%d%d%d', result) @@ -230,7 +239,7 @@ describe('util.format_time', function() if os.date('%Y-%m-%d', early_tomorrow) == os.date('%Y-%m-%d') then assert.equals(compact_locale_time(early_tomorrow), early_result) else - local expected_prefix = os.date('%d %b', early_tomorrow) .. ' ' + local expected_prefix = c_locale_date('%d %b', early_tomorrow) .. ' ' assert.is_true(vim.startswith(early_result, expected_prefix)) assert.equals(compact_locale_time(early_tomorrow), early_result:sub(#expected_prefix + 1)) end