fix(excmd): use realtime for v:starttime, :uptime #39425

Problem:
`v:starttime`, `:uptime` use a monotonic high-resolution timer. This
only works as long as the timer keeps running (if the computer is
suspended the timer is paused). This is somewhat unintuitive, and
doesn't match the behavior of the `uptime` shell command.

Solution:
Implement `os_realtime` to get the real time since the
epoch in nanoseconds.
This commit is contained in:
Till Bungert
2026-04-28 01:01:47 +02:00
committed by GitHub
parent 46c83ce321
commit a0820481f2
7 changed files with 30 additions and 9 deletions

View File

@@ -217,7 +217,7 @@ UI
VIMSCRIPT
• |v:exitreason| is set before |QuitPre|.
• |v:starttime| is the process start time (monotonic nanoseconds).
• |v:starttime| is the process start time (nanoseconds from UNIX epoch).
==============================================================================
CHANGED FEATURES *news-changed*

View File

@@ -635,11 +635,11 @@ v:stacktrace
*v:starttime* *starttime-variable*
v:starttime
Timestamp (monotonic nanoseconds) when the Nvim process
Timestamp (nanoseconds from UNIX epoch) when the Nvim process
started.
To see the current "uptime": >lua
vim.print(('uptime: %d seconds'):format((vim.uv.hrtime() - vim.v.starttime) / 1e9))
vim.print(('uptime: %d seconds'):format(os.time() - (vim.v.starttime / 1e9)))
<
Read-only.

View File

@@ -259,7 +259,9 @@ function M.ex_terminal(eap, shell_argv)
end
function M.ex_uptime()
local uptime = math.floor((uv.hrtime() - vim.v.starttime) / 1e9)
-- os.time() might lead to uptime == -1 when this is called too quickly after startup
local now = assert(uv.clock_gettime('realtime'))
local uptime = math.floor((now.sec * 1e9 + now.nsec - vim.v.starttime) / 1e9)
local uptime_display = time.fmt_rtime(uptime)
api.nvim_echo({ { N_('Up %s'):format(uptime_display) } }, true, {})
end

View File

@@ -667,13 +667,13 @@ vim.v.shell_error = ...
--- @type table[]
vim.v.stacktrace = ...
--- Timestamp (monotonic nanoseconds) when the Nvim process
--- Timestamp (nanoseconds from UNIX epoch) when the Nvim process
--- started.
---
--- To see the current "uptime":
---
--- ```lua
--- vim.print(('uptime: %d seconds'):format((vim.uv.hrtime() - vim.v.starttime) / 1e9))
--- vim.print(('uptime: %d seconds'):format(os.time() - (vim.v.starttime / 1e9)))
--- ```
---
--- Read-only.

View File

@@ -197,7 +197,7 @@ void early_init(mparm_T *paramp)
estack_init();
cmdline_init();
eval_init(); // init global variables
set_vim_var_nr(VV_STARTTIME, (varnumber_T)os_hrtime());
set_vim_var_nr(VV_STARTTIME, (varnumber_T)os_realtime());
init_path(argv0 ? argv0 : "nvim");
init_normal_cmds(); // Init the table of Normal mode commands.
runtime_init();

View File

@@ -32,6 +32,25 @@ uint64_t os_hrtime(void)
return uv_hrtime();
}
/// Obtain the current system time from a high-resolution
/// real-time clock source.
///
/// The real-time clock counts from the UNIX epoch (1970-01-01)
/// and is subject to time adjustments; it can jump back in time.
///
/// @return Nanoseconds since epoch or 0
int64_t os_realtime(void)
FUNC_ATTR_WARN_UNUSED_RESULT
{
uv_timespec64_t ts = { 0 };
int error_number;
if ((error_number = uv_clock_gettime(UV_CLOCK_REALTIME, &ts)) != 0) {
ELOG("uv_clock_gettime failed: %d %s", error_number, uv_err_name(error_number));
return 0;
}
return ts.tv_sec * 1000000000L + ts.tv_nsec;
}
/// Gets a millisecond-resolution, monotonically-increasing time relative to an
/// arbitrary time in the past.
///

View File

@@ -756,11 +756,11 @@ M.vars = {
starttime = {
type = 'integer',
desc = [=[
Timestamp (monotonic nanoseconds) when the Nvim process
Timestamp (nanoseconds from UNIX epoch) when the Nvim process
started.
To see the current "uptime": >lua
vim.print(('uptime: %d seconds'):format((vim.uv.hrtime() - vim.v.starttime) / 1e9))
vim.print(('uptime: %d seconds'):format(os.time() - (vim.v.starttime / 1e9)))
<
Read-only.
]=],