os
The operating system library reduced to its time functions: os.clock, os.date, os.difftime and os.time.
At a glance
Functions
os.clock(): number
os.clock(): number
Returns: seconds, with millisecond resolution. On Windows the C clock() measures wall-clock time since the client runtime started, not CPU time: it keeps running while the game is idle or minimized.
For game time use the frame dt (on_frame(dt), the think signal) or xiao.timer, whose clock only moves while frames are rendered. os.clock is fine to measure how long a piece of code takes.
local t0 = os.clock()
for i = 1, 1e6 do local _ = i * i end
print(("loop: %.1f ms"):format((os.clock() - t0) * 1000))
os.time(t): number | nil
os.time(t?: table): number | nil
Out-of-range fields are normalized: month 13 is January of the next year.
Returns: seconds since 1970-01-01 UTC, or nil if the date cannot be represented.
Errors: field 'day' missing in date table (or 'month', 'year'), bad argument #1 to 'time' (table expected, got string).
print(os.time({ year = 2026, month = 1, day = 1, hour = 0 }))
os.date(format, time): string | table | nil
os.date(format?: string = "%c", time?: number = os.time()): string | table | nil
Returns:
- for
"*t"or"!*t": a table withyear,month,day,hour,min,sec,wday(1 is Sunday),yday(1 to 366) andisdst - otherwise the formatted string (
""for an empty format) nilwhentimecannot be converted (not finite, negative in local time, or past the year 3000 or so)
Errors: os.date: invalid conversion specifier '%Q' for a directive that is not in the table below (%k, %s, %P, a lone % at the end, %# or %E without a letter...), bad argument #2 to 'date' (number expected, got string).
Fixed
The format is checked before it reaches the Windows strftime: an unknown directive is a Lua error that pcall catches, not a crash of the game. The text of the error names the directive. The format is read up to its first \0, like strftime does.
print(pcall(os.date, "%Y %k"))
Every directive takes an optional Microsoft # flag, then an optional E or O modifier, in that order: %#d drops the leading zero, %#c and %#x give the long form, %Ey and %Od are the same as %y and %d, %#Ec is valid. Any other order (%E#c) or a glibc flag (%-d, %0d) is an invalid conversion specifier.
print(os.date("%Y-%m-%d %H:%M:%S"))
local t = os.date("*t")
print(t.year, t.month, t.day)
os.difftime(t2, t1): number
os.difftime(t2: number, t1?: number = 0): number
Returns: t2 - t1 in seconds. The Windows C runtime returns 0 when one of the timestamps is negative (before 1970).
local started = os.time()
print(os.difftime(os.time(), started))
Complete example
A timestamped log line, and the time left until midnight.
local function log(text)
print(os.date("[%H:%M:%S] ") .. text)
end
local now = os.time()
local today = os.date("*t", now)
local midnight = os.time({ year = today.year, month = today.month, day = today.day + 1, hour = 0 })
log(("%.0f minutes until midnight"):format(os.difftime(midnight, now) / 60))