os

The reduced os library, limited to clock, date, difftime and time.

The operating system library reduced to its time functions: os.clock, os.date, os.difftime and os.time.

At a glance

Function Description
os.clock() seconds elapsed since the client was loaded
os.time(t) current timestamp, or timestamp of a date table
os.date(format, time) format a timestamp, or get a date table
os.difftime(t2, t1) seconds between two timestamps

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
Name Type Description
t table a date in local time, fields below. Without it, the current time
Field Description
year, month, day required
hour default 12
min, sec default 0
isdst optional boolean

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
Name Type Description
format string "*t" for a table, otherwise a strftime format. A leading ! uses UTC instead of local time
time number seconds since 1970-01-01 UTC

Returns:

  • for "*t" or "!*t": a table with year, month, day, hour, min, sec, wday (1 is Sunday), yday (1 to 366) and isdst
  • otherwise the formatted string ("" for an empty format)
  • nil when time cannot 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"))
Directive Meaning
%a, %A weekday name, abbreviated or full
%b, %h, %B month name, abbreviated or full
%c date and time, locale format
%C century (20)
%d, %e day of the month, 01-31 or space padded
%D %m/%d/%y
%F %Y-%m-%d
%g, %G ISO 8601 week-based year, 2 or 4 digits
%H, %I hour, 24-hour (00-23) or 12-hour (01-12) clock
%j day of the year, 001-366
%m month, 01-12
%M minute, 00-59
%n, %t newline, tab
%p AM or PM
%r 12-hour time, %I:%M:%S %p
%R, %T %H:%M, %H:%M:%S
%S second, 00-59
%u, %w weekday number, 1-7 from Monday or 0-6 from Sunday
%U, %W, %V week of the year, Sunday first, Monday first, or ISO 8601
%x, %X date or time, locale format
%y, %Y year, 2 or 4 digits
%z, %Z UTC offset (+0100) or time zone name
%% a literal %

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
Name Type Description
t2 number later timestamp
t1 number earlier timestamp

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))