API reference

legend
lus stable Lus addition
unstable unstable channel only
lua inherited from Lua
deprecated deprecated in Lus
removed removed in Lus
fastcall vm has special optimization
pledge requires this permission at runtime

Environment Functions

lua_G
since 0.1.0 · type: table

A global variable (not a function) that holds the global environment.

since 0.1.0 · type: string

A global variable containing the running Lus version string.

luafastcallassert(v, [message]) → any
since 0.1.0

Checks whether v is truthy. If so, returns all its arguments. Otherwise, raises an error with message (defaults to "assertion failed!").

luacollectgarbage([opt: string], [arg]) → any
since 0.1.0

Controls the garbage collector. The opt argument selects the operation:

  • "collect" (the default) performs a full collection.
  • "stop" and "restart" control the collector.
  • "count" returns the memory in use, in kilobytes.
  • "step" performs a collection step.
  • "isrunning" returns whether the collector is running.
  • "incremental" and "generational" set the GC mode.
  • "param" queries or sets a GC parameter.
luadofile([filename: string]) → any
since 0.1.0
requiresloadfs:read

Opens and executes the contents of filename as a Lus chunk. When called without arguments, executes from standard input. Returns all values returned by the chunk.

luaerror(message, [level: integer])
since 0.1.0

Raises an error with message as the error object. The level argument specifies where the error position points: 1 (default) is the caller, 2 is the caller’s caller, and so on. Level 0 omits position information.

lusfromcsv(s: string, [headers: boolean], [delimiter: string]) → table
since 1.6.0

Parses a CSV string s and returns a table of rows. Each row is a sequential table of string values. When headers is true, the first row is used as column headers and subsequent rows are returned as tables keyed by those headers. The optional delimiter defaults to ",".

lusfromjson(s: string) → any
since 0.1.0

Parses a JSON string s and returns the corresponding Lus value. Tables become Lus tables, JSON null becomes nil.

luafastcallgetmetatable(object) → table|nil
since 0.1.0

Returns the metatable of object. If the metatable has a __metatable field, returns that value instead. Returns nil if the object has no metatable.

luaipairs(t: table) → function
since 0.1.0

Returns an iterator function that traverses the integer keys of t in order, from t[1] up to the first absent index. Intended for use in for loops.

luaload(chunk: string|function, [chunkname: string], [mode: string], [env: table]) → function|nil
since 0.1.0
requiresload

Loads a chunk. If chunk is a string, the chunk is that string. If it is a function, it is called repeatedly to get the chunk pieces. Returns the compiled chunk as a function, or nil plus an error message on failure.

The optional arguments refine the load:

  • chunkname names the chunk for error messages.
  • mode controls whether text ("t"), binary ("b"), or both ("bt", the default) are allowed.
  • env sets the first upvalue as the chunk’s environment.
lualoadfile([filename: string], [mode: string], [env: table]) → function|nil
since 0.1.0
requiresloadfs:read

Loads a chunk from filename (or standard input if absent) without executing it. Returns the compiled chunk as a function, or nil plus an error message on failure. See load for the meaning of mode and env.

luapairs(t: table) → function
since 0.1.0

Returns an iterator function, the table t, and nil, so that the construction for k, v in pairs(t) iterates over all key—value pairs of t. If t has a __pairs metamethod, calls it with t as argument and returns its results.

removedpcall

Removed. Use the catch expression instead: local ok, result = catch f(...).

luspledge(name...: string) → boolean
since 0.1.0

Grants or checks a permission. Returns true if the permission was granted, false if it was denied or the state is sealed.

The name arguments specify the permissions to grant or check.

Special permissions: "all" grants all permissions (CLI only), "seal" prevents future permission changes. The ~ prefix rejects a permission permanently.

pledge("fs")           -- grant filesystem access
pledge("fs:read=/tmp") -- grant read access to /tmp only
pledge("~network")     -- reject network permission
pledge("seal")         -- lock permissions
luaprint(...)
since 0.1.0

Prints its arguments to standard output, converting each to a string with tostring. Arguments are separated by tabs, followed by a newline.

unstablequery(q: query) → function
since 1.7.0

Evaluates the query q and returns a fresh iterator over its result records, for use in a generic for. Each call to query(q) runs the query again, so a stored query value observes source changes on its next loop. Every result is a record: a projected field table, or the source row itself for a whole-row projection.

The logical pipeline is fixed. Every query runs these stages in order:

  1. Fetch each source.
  2. Join the sources.
  3. Filter with where.
  4. Group with group by, then filter groups with having.
  5. Project with select, applying distinct.
  6. Sort with order by.
  7. Cut with limit and offset.

Sources come in three shapes. A plain table serves as an array of records through the built-in in-memory provider. A value whose metatable carries __query supplies its own row iterator. Native sources such as io.sqlite table references push accepted conjuncts down as parameterized SQL. All providers share the same semantics, so the same query text returns the same rows wherever it runs.

The following example filters an in-memory table and projects one field.

global query, print

local people = {
    { name = "Alice", age = 30 },
    { name = "Bob",   age = 17 },
}

for row in query(@people as p where p.age >= 18 select p.name) do
    print(row.name)
end
luafastcallrawequal(v1, v2) → boolean
since 0.1.0

Checks equality of v1 and v2 without invoking the __eq metamethod. Returns true if they are primitively equal.

luafastcallrawget(t: table, k) → any
since 0.1.0

Gets the value of t[k] without invoking the __index metamethod.

luafastcallrawlen(v: table|string) → integer
since 0.1.0

Returns the length of v without invoking the __len metamethod. v must be a table or string.

luafastcallrawset(t: T, k, v) → T
since 0.1.0

Sets t[k] = v without invoking the __newindex metamethod. Returns t.

luarequire(modname: string) → any
since 0.1.0

Loads and runs the module modname. Searches package.searchers in order to find a loader. Caches results in package.loaded so subsequent calls return the same value.

luaselect(index: integer|string, ...) → any
since 0.1.0

If index is a number, returns all arguments after argument number index. If index is the string "#", returns the total number of extra arguments.

luafastcallsetmetatable(t: T, metatable: table|nil) → T
since 0.1.0

Sets the metatable of table t to metatable. If metatable is nil, removes the metatable. Raises an error if the existing metatable has a __metatable field. Returns t.

lustocsv(t: table, [delimiter: string]) → string
since 1.6.0

Converts a table of rows t to a CSV string. Each row must be a sequential table of values. Non-string values are converted via tostring; nil values produce empty fields. Fields containing the delimiter, newlines, or double quotes are automatically quoted. The optional delimiter defaults to ",".

lustojson(value, [filter: function]) → string
since 0.1.0

Converts a Lus value to a JSON string. The optional filter argument controls serialization. Objects with a __json metamethod use it for custom serialization.

luafastcalltonumber(e, [base: integer]) → number|nil
since 0.1.0

Converts e to a number. If e is already a number, returns it. If e is a string, tries to parse it as a numeral. If e is an enum value, returns its 1-based index. The optional base (2—36) specifies the base for string conversion. Returns nil on failure.

luafastcalltostring(v) → string
since 0.1.0

Converts v to a human-readable string. If v has a __tostring metamethod, calls it with v as argument and returns the result.

luafastcalltype(v) → string
since 0.1.0

Returns the type of v as a string: "nil", "number", "string", "boolean", "table", "function", "thread", "userdata", "vector", "enum", or "query".

luawarn(msg1: string, ...)
since 0.1.0

Emits a warning by concatenating all arguments (which must be strings) and sending the result to the warning system. Warnings beginning with "@" are control messages: "@on" enables and "@off" disables warnings.

removedxpcall

Removed. Use the catch expression instead, pairing it with debug.traceback when a traceback is needed.

Table Library

lustable.clone(t: T, [deep: boolean]) → T
since 0.1.0

Creates a copy of the table t. If deep is true, nested tables are recursively cloned. Deep copies preserve circular references.

local x = table.clone(t)        -- shallow copy
local y = table.clone(t, true)  -- deep copy
lustable.compact(t: T) → T
since 1.6.0

Shrinks the internal storage of t to fit its current contents and returns t. Removing keys from a table never shrinks its array or hash storage on its own, so a table that was once large keeps its capacity; table.compact releases that memory. Raises an error if t is readonly.

local cache = build_huge_table()
prune(cache)            -- removes most entries
table.compact(cache)    -- releases the now-unused capacity
luatable.concat(list: table, [sep: string], [i: integer], [j: integer]) → string
since 0.1.0

Concatenate the string or number elements of table list, separated by sep (empty string by default), from index i to j.

luatable.create(narray: integer, [nhash: integer]) → table
since 0.1.0

Create a new table with pre-allocated space for narray array elements and nhash hash elements. Useful for performance when the final table size is known.

lustable.filter(array: table, predicate: function) → table
since 1.6.0

Returns a new table containing only elements of array for which predicate(element) returns a truthy value. The original table is not modified. The result is a dense sequence.

lustable.groupby(array: table, keyfunc: function) → table
since 1.6.0

Groups elements of array by the result of keyfunc(element). Returns a new table where keys are the grouping results and values are tables of grouped elements.

luatable.insert(list: table, [pos: integer], [value])
since 0.1.0

Insert value at position pos in list, shifting up subsequent elements. If pos is omitted, appends to the end.

lustable.map(array: table, func: function) → table
since 1.6.0

Applies func to each element of array, returning a new table of results. The function receives (element, index) as arguments. The original table is not modified.

lusfastcalltable.mean(array: table) → number
since 1.6.0

Computes the arithmetic mean of numeric values in array. Strings naming a number count as numeric values and are coerced. Other non-numeric values are skipped. Returns NaN if no numeric values exist.

lusfastcalltable.median(array: table) → number
since 1.6.0

Computes the median of numeric values in array. Returns the middle value for odd-length arrays, or the average of the two middle values for even-length arrays. Strings naming a number count as numeric values and are coerced. Other non-numeric values are skipped. Does not modify the original table. Returns NaN if no numeric values exist.

luatable.move(a1: table, f: integer, e: integer, t: integer, [a2: table]) → table
since 0.1.0

Copy elements from table a1 positions f through e into table a2 (defaults to a1) starting at position t. Returns a2.

luatable.pack(...) → table
since 0.1.0

Return a new table with the given arguments as array elements, plus a field "n" set to the total number of arguments.

lustable.reduce(array: table, func: function, [initial]) → any
since 1.6.0

Reduces array to a single value by iteratively applying func(accumulator, element, index). If initial is not provided, the first element is used and iteration starts from the second. Raises an error if initial is not provided and the array is empty.

luatable.remove(list: table, [pos: integer]) → any
since 0.1.0

Remove the element at position pos from list, shifting down subsequent elements. Default pos is #list, so it removes the last element. Returns the removed value.

lusfastcalltable.reshape(array: table, rows: integer, cols: integer) → table
since 1.6.0

Reshapes a 1D array into a 2D matrix with the specified dimensions, filled in row-major order. The array length must equal rows * cols.

luatable.sort(list: table, [comp: function])
since 0.1.0

Sort list elements in place. If comp is given, it must be a function that takes two elements and returns true when the first is less than the second. The sort is not stable.

lustable.sortby(array: table, keyfunc: function, [asc: boolean])
since 1.6.0

Sorts array in-place by the key returned by keyfunc(element). When asc is false, sorts in descending order. Default is ascending. Returns nothing.

lusfastcalltable.stdev(array: table, [sample: boolean]) → number
since 1.6.0

Computes the standard deviation of numeric values in array. When sample is true, divides by n-1 (sample standard deviation). Otherwise divides by n (population standard deviation). Strings naming a number count as numeric values and are coerced. Other non-numeric values are skipped. Returns NaN if no numeric values exist.

lusfastcalltable.sum(array: table) → number
since 1.6.0

Computes the sum of numeric values in array. Strings naming a number count as numeric values and are coerced. Other non-numeric values are skipped. Returns 0 for an empty table.

lusfastcalltable.transpose(matrix: table) → table
since 1.6.0

Transposes a 2D table (matrix) so that result[i][j] == matrix[j][i]. All rows must have the same length. Raises an error for non-rectangular matrices.

luatable.unpack(list: table, [i: integer], [j: integer]) → any
since 0.1.0

Return the elements from table list from index i to j. Defaults to i = 1 and j = #list.

lustable.unzip(array_of_tuples: table) → ...: table
since 1.6.0

Splits a table of tuples into separate tables (inverse of table.zip). Returns multiple tables, one per position in the tuples. All tuples must have the same length.

lustable.zip(...) → table
since 1.6.0

Combines multiple tables element-wise into a table of tuples. The result length equals the length of the shortest input table. Each tuple is a table {table1[i], table2[i], ...}.

String Library

luafastcallstring.byte(s: string, [i: integer], [j: integer]) → integer
since 0.1.0

Return the byte values of characters s[i] through s[j]. Defaults to i = 1 and j = i.

luafastcallstring.char(...) → string
since 0.1.0

Return a string of characters from the given byte values.

luastring.dump(function: function, [strip: boolean]) → string
since 0.1.0

Return a binary string containing a serialized representation of function, so that a later load on this string returns a copy of the function. If strip is true, debug information is omitted.

luastring.find(s: string, pattern: string, [init: integer], [plain: boolean]) → integer|nil
since 0.1.0

Look for the first match of pattern in string s. Returns the start and end indices of the match, plus any captures. Returns nil if no match is found. If init is given, the search starts at that position. If plain is true, performs a plain substring search with no pattern matching.

luastring.format(formatstring: string, ...) → string
since 0.1.0

Return a formatted string following the description in formatstring, which follows printf-style directives. Accepts %d, %i, %u, %f, %e, %g, %x, %o, %s, %q, %c, and %%.

luastring.gmatch(s: string, pattern: string, [init: integer]) → function
since 0.1.0

Return an iterator function that, each time it is called, returns the next captures from pattern in string s. If pattern has no captures, the whole match is returned. If init is given, the search starts at that position.

luastring.gsub(s: string, pattern: string, repl: string|table|function, [n: integer]) → string
since 0.1.0

Return a copy of s where all (or the first n) occurrences of pattern are replaced by repl. The replacement repl can be a string, table, or function. Also returns the total number of substitutions as a second value.

lusfastcallstring.join(t: table, delimiter: string) → string
since 1.6.0

Joins elements of table t into a single string with delimiter between each element. Non-string values are converted via tostring. Empty table returns an empty string.

luafastcallstring.len(s: string) → integer
since 0.1.0

Return the length of string s in bytes.

luafastcallstring.lower(s: string) → string
since 0.1.0

Return a copy of s with all uppercase letters changed to lowercase, according to the current locale.

lusfastcallstring.ltrim(s: string, [chars: string]) → string
since 1.6.0

Removes leading characters from string s. If chars is not provided, removes whitespace (space, tab, newline, carriage return). Otherwise removes all contiguous characters present in chars from the left.

luastring.match(s: string, pattern: string, [init: integer]) → string|nil
since 0.1.0

Look for the first match of pattern in string s. Returns the captures from the match, or the whole match if there are no captures. Returns nil if no match is found.

luastring.pack(fmt: string, ...) → string
since 0.1.0

Return a binary string containing the values packed according to format string fmt. See string.unpack for format codes.

luastring.packsize(fmt: string) → integer
since 0.1.0

Return the size of the string that would result from string.pack with format fmt. The format must not contain variable-length options.

luastring.rep(s: string, n: integer, [sep: string]) → string
since 0.1.0

Return a string consisting of n copies of s, separated by sep (empty string by default).

luafastcallstring.reverse(s: string) → string
since 0.1.0

Return a string with the bytes of s in reverse order.

lusfastcallstring.rtrim(s: string, [chars: string]) → string
since 1.6.0

Removes trailing characters from string s. If chars is not provided, removes whitespace (space, tab, newline, carriage return). Otherwise removes all contiguous characters present in chars from the right.

lusfastcallstring.split(s: string, delimiter: string) → table
since 1.6.0

Splits string s on the literal delimiter, returning a table of substrings. An empty delimiter splits into individual characters. Consecutive delimiters produce empty string entries.

luafastcallstring.sub(s: string, i: integer, [j: integer]) → string
since 0.1.0

Return the substring of s from position i to j (inclusive). Negative indices count from the end of the string. Default for j is -1 (end of string).

lusstring.transcode(s: string, from: string, to: string) → string
since 0.1.0

Transcodes string s from encoding from to encoding to. Supported encodings depend on the platform.

lusfastcallstring.trim(s: string, [chars: string]) → string
since 1.6.0

Removes leading and trailing characters from string s. If chars is not provided, removes whitespace (space, tab, newline, carriage return). Otherwise removes all contiguous characters present in chars from both ends.

luastring.unpack(fmt: string, s: string, [pos: integer]) → any
since 0.1.0

Return the values packed in string s according to format fmt, starting at position pos (default 1). Also returns the position after the last read byte.

luafastcallstring.upper(s: string) → string
since 0.1.0

Return a copy of s with all lowercase letters changed to uppercase, according to the current locale.

Math Library

luafastcallmath.abs(x: number) → number
since 0.1.0

Returns the absolute value of x.

luafastcallmath.acos(x: number) → number
since 0.1.0

Returns the arc cosine of x in radians.

luafastcallmath.asin(x: number) → number
since 0.1.0

Returns the arc sine of x in radians.

luafastcallmath.atan(y: number, [x: number]) → number
since 0.1.0

Returns the arc tangent of y/x in radians, using the signs of both arguments to determine the quadrant. x defaults to 1.

luafastcallmath.ceil(x: number) → integer
since 0.1.0

Returns the smallest integer not less than x.

luafastcallmath.cos(x: number) → number
since 0.1.0

Returns the cosine of angle x (in radians).

luafastcallmath.deg(x: number) → number
since 0.1.0

Converts angle x from radians to degrees.

luafastcallmath.exp(x: number) → number
since 0.1.0

Returns e raised to the power x.

luafastcallmath.floor(x: number) → integer
since 0.1.0

Returns the largest integer not greater than x.

luafastcallmath.fmod(x: number, y: number) → number
since 0.1.0

Returns the remainder of the division of x by y, rounding the quotient towards zero.

luamath.frexp(x: number) → number
since 0.1.0

Returns m and e such that x = m * 2^e, where m is in [0.5, 1) and e is an integer.

since 0.1.0

The float value HUGE_VAL, greater than any other numeric value.

luafastcallmath.ldexp(m: number, e: integer) → number
since 0.1.0

Returns m * 2^e.

luafastcallmath.log(x: number, [base: number]) → number
since 0.1.0

Returns the logarithm of x in the given base. The default base is e (natural logarithm).

luafastcallmath.max(x: number, ...) → number
since 0.1.0

Returns the maximum value among its arguments.

since 0.1.0

The maximum value for an integer.

luafastcallmath.min(x: number, ...) → number
since 0.1.0

Returns the minimum value among its arguments.

since 0.1.0

The minimum value for an integer.

luamath.modf(x: number) → number
since 0.1.0

Returns the integral part and the fractional part of x.

since 0.1.0

The value of pi.

luafastcallmath.rad(x: number) → number
since 0.1.0

Converts angle x from degrees to radians.

luamath.random([m: integer], [n: integer]) → number
since 0.1.0

When called without arguments, returns a uniform pseudo-random float in [0,1). With one integer m, returns an integer in [1, m]. With two integers m and n, returns an integer in [m, n].

luamath.randomseed([x: integer], [y: integer])
since 0.1.0

Sets x as the seed for the pseudo-random generator. When called without arguments, seeds with a system-dependent value. Equal seeds produce equal sequences.

luafastcallmath.sin(x: number) → number
since 0.1.0

Returns the sine of angle x (in radians).

luafastcallmath.sqrt(x: number) → number
since 0.1.0

Returns the square root of x.

luafastcallmath.tan(x: number) → number
since 0.1.0

Returns the tangent of angle x (in radians).

luafastcallmath.tointeger(x: number) → integer|nil
since 0.1.0

Converts x to an integer if its value is convertible to one, meaning a number with an integer value or a string naming such a number. Returns nil when x cannot be converted.

luafastcallmath.type(x) → string
since 0.1.0

Returns "integer" if x is an integer, "float" if it is a float, or nil if x is not a number.

luafastcallmath.ult(m: integer, n: integer) → boolean
since 0.1.0

Returns true if integer m is below integer n when compared as unsigned integers.

IO Library

luaio.close([file: file]) → boolean|nil
since 0.1.0

Closes file, or the default output file if no argument is given. Returns the status from the file close operation. For files opened with io.popen, returns the exit status.

luaio.flush() → boolean|nil
since 0.1.0

Flushes the write buffer of the default output file.

luaio.input([file: file|string]) → userdata
since 0.1.0

When called with a filename, opens that file and sets it as the default input. When called with a file handle, sets it as the default input. When called with no arguments, returns the current default input file.

luaio.lines([filename: string], ...) → function
since 0.1.0
requiresfs:read

Returns an iterator function that reads from filename (or the default input if absent) according to the given formats. The file is automatically closed when the iterator finishes. Default format reads lines.

luaio.open(filename: string, [mode: string]) → userdata|nil
since 0.1.0

Opens filename in the given mode and returns a file handle, or nil plus an error message on failure.

Modes follow C fopen conventions: "r" (read, the default), "w" (write), "a" (append), plus an optional "b" suffix for binary. A read mode requires only fs:read. Any write or append mode requires fs:write.

luaio.output([file: file|string]) → userdata
since 0.1.0

When called with a filename, opens that file and sets it as the default output. When called with a file handle, sets it as the default output. When called with no arguments, returns the current default output file.

luaio.popen(prog: string, [mode: string]) → userdata|nil
since 0.1.0
requiresexec

Starts the program prog in a separated process and returns a file handle for reading or writing, depending on mode ("r" for reading, default; "w" for writing).

luaio.read(...) → string|nil
since 0.1.0

Reads from the default input file according to the given formats. Formats: "n" reads a number, "a" reads the whole file, "l" reads a line (default, strips newline), "L" reads a line keeping the newline, or an integer n reads n bytes.

unstableio.sqlite(path: string, [mode: string]) → database
since 1.7.0

Opens the SQLite database at path and returns a database handle. SQLite is embedded in the runtime, so no external library or system package is involved. Passing ":memory:" opens a private in-memory database that needs no filesystem pledge at all. The optional mode is "rw" (the default, which reads, writes, and creates) or "r" (read-only).

A file-backed open pledge-checks the database path together with its -journal, -wal, and -shm sidecar files: fs:read always, and fs:write too under "rw". Writable file-backed databases run in WAL mode, which supports concurrent readers alongside a single writer.

The handle runs statements and queries through six methods:

  • db:exec(sql) runs one or more statements for their side effects.
  • db:run(sql, ...) runs a single statement with bound parameters and returns the affected row count and the last inserted row id.
  • db:query(sql, ...) runs a single statement and returns an iterator over the result rows, where each row is a table keyed by column name.
  • db:prepare(sql) compiles a reusable statement. The statement’s stmt:run(...) and stmt:query(...) execute it with fresh bindings, and stmt:close() releases it early.
  • db:transaction(fn) runs fn inside a transaction. It commits when fn returns, passing through all of fn’s return values, and rolls back when fn raises.
  • db:close() closes the handle.

Placeholders carry every value into a statement, so interpolated or concatenated text reaches the database as data. Positional ? placeholders bind from the extra arguments in order. Named placeholders (:name, @name, or $name) bind from a single table argument keyed by the bare name. Numbered ?NNN placeholders are not supported, and one statement never mixes named and positional styles.

Values map between Lus and SQLite types in both directions:

  • nil binds as NULL, and NULL reads back as an absent row field.
  • boolean binds as 1 or 0.
  • integer binds as INTEGER, and INTEGER reads back as an integer.
  • float binds as REAL, and REAL reads back as a float. Binding NaN stores NULL, following SQLite.
  • string binds as TEXT, and TEXT reads back as a string.
  • vector binds as BLOB, and BLOB reads back as a vector.

The handle manages its native resources the same way io.open file objects do. It carries a __close metamethod, so local db <close> = io.sqlite(path) closes the database when its block exits, including through an error, and a __gc finalizer releases any handle a program leaves open. Closing a database finalizes its live statements first. Prepared statements and query iterations follow the same lifetime rules with their own __close and __gc.

Indexing the handle with a key that is not a method yields a table reference for use as a query source, so @db.users as u where u.age > 18 select u.name pushes down as a parameterized SELECT. The reference anchors its database, and the push-down makes the same injection-safety guarantee as a hand-written statement.

SQLite never touches a path outside the pledged set: temp structures live in memory, ATTACH and VACUUM INTO are denied, and URI filenames are rejected at open. A database handle lives in the interpreter state that opened it and cannot be sent to a worker, so a worker uses a database by opening the path itself.

The following example pledges the database files, creates a table, and reads matching rows back with a named placeholder.

global io, pledge

pledge("fs:read=./app.db*", "fs:write=./app.db*", "seal")

local db <close> = io.sqlite("app.db")
db:exec("CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT)")
db:run("INSERT INTO users (name) VALUES (?)", "Alice")
for row in db:query("SELECT id, name FROM users WHERE name = :name",
                    { name = "Alice" }) do
    print(row.id, row.name)
end
since 0.1.0 · type: file

Standard error file handle.

since 0.1.0 · type: file

Standard input file handle.

since 0.1.0 · type: file

Standard output file handle.

luaio.tmpfile() → userdata|nil
since 0.1.0
requiresfs:write

Returns a handle for a temporary file, opened in update mode. The file is automatically removed when the program ends.

luaio.type(obj) → string|nil
since 0.1.0

Returns "file" if obj is an open file handle, "closed file" if it is a closed file handle, or nil if it is not a file handle.

luaio.write(...) → userdata|nil
since 0.1.0

Writes each argument to the default output file. Arguments must be strings or numbers. Returns the file handle on success, or nil plus an error message on failure.

OS Library

luaos.clock() → number
since 0.1.0

Returns the CPU time used by the program, in seconds.

luaos.date([format: string], [time: integer]) → string|table
since 0.1.0

Returns a string or table with the date and time, formatted according to format. If format starts with "!", the time is formatted in UTC. If format is "*t", the result is a table with fields year, month, day, hour, min, sec, wday, yday, and isdst.

The optional time argument specifies the time to format and defaults to the current time.

luaos.difftime(t2: integer, t1: integer) → number
since 0.1.0

Returns the difference t2 - t1 in seconds, where t1 and t2 are values returned by os.time.

luaos.execute([command: string]) → boolean|nil
since 0.1.0
requiresexec

Passes command to the operating system shell for execution. Returns true (or nil on failure), an exit type string ("exit" or "signal"), and the exit or signal number.

luaos.exit([code: boolean|integer], [close: boolean])
since 0.1.0

Terminates the program. The optional code argument is the exit code (true means success, false means failure, default is true). If close is true, closes the Lua state before exiting.

luaos.getenv(varname: string) → string|nil
since 0.1.0
requiresenv

Returns the value of the environment variable varname, or nil if the variable is not defined.

The env pledge can be scoped to individual variables, e.g. pledge("env=HOME").

lusos.platform() → string
since 0.1.0

Returns a string identifying the current platform (e.g., "linux", "darwin", "windows"). The names are the lowercase uname -s style identifiers, so Apple platforms report "darwin".

removedos.remove

Removed. Use fs.remove instead.

removedos.rename

Removed. Use fs.move instead.

luaos.setlocale([locale: string], [category: string]) → string|nil
since 0.1.0

Sets the program’s current locale. The category selects which category to set (e.g., "all", "collate", "ctype", "monetary", "numeric", "time"). If locale is nil, returns the current locale for the given category. Returns the locale name, or nil on failure.

luaos.time([t: table]) → integer
since 0.1.0

Returns the current time as a number (seconds since epoch) when called without arguments. When called with a table argument t, returns the time represented by that table (with fields year, month, day, hour, min, sec).

luaos.tmpname() → string
since 0.1.0
requiresfs:write

Returns a temporary file name suitable for use as a temporary file.

Debug Library

since 0.1.0

Enters interactive debugging mode. Prompts for commands from standard input. Not available in all environments.

lusdebug.format(source: string, [chunkname: string], [indent_width: integer]) → string
since 0.1.0

Formats Lus source code. Returns the formatted source string.

The optional chunkname specifies the name used in error messages. The optional indent_width sets the indentation width (defaults to 2).

luadebug.gethook([thread: thread]) → function
since 0.1.0

Returns the current hook function, hook mask (a string), and hook count for the given thread (or the current thread).

luadebug.getinfo(f: function|integer, [what: string]) → table
since 0.1.0

Returns a table with information about a function or stack level. The what string selects which fields to fill: "n" (name), "S" (source), "l" (line), "t" (tail call), "u" (upvalues), "f" (function), "L" (valid lines). Default is all.

luadebug.getlocal(f: function|integer, [local_: integer]) → string|nil
since 0.1.0

Returns the name and value of local variable local at stack level f. Returns nil if there is no local variable with that index. Negative indices access vararg arguments.

luadebug.getmetatable(value) → table|nil
since 0.1.0

Returns the metatable of value without checking __metatable.

luadebug.getregistry() → table
since 0.1.0

Returns the registry table.

luadebug.getupvalue(f: function, up: integer) → string|nil
since 0.1.0

Returns the name and value of upvalue up of function f. Returns nil for invalid indices.

luadebug.getuservalue(u: userdata, [n: integer]) → any
since 0.1.0

Returns the n-th user value associated with userdata u, plus a boolean indicating whether the userdata has that value.

lusdebug.parse(code: string, [chunkname: string], [options: table]) → table|nil
since 0.1.0

Parses a Lus source string and returns its AST (Abstract Syntax Tree) as a nested table structure. Returns nil if parsing fails.

The optional chunkname argument specifies the name used in error messages (defaults to "=(parse)").

local ast = debug.parse("local x = 1 + 2")
-- Returns: {type = "chunk", line = 1, children = {...}}

Each AST node is a table with at minimum type (node type string) and line (source line number).

unstabledebug.queryplan(q: query) → string
since 1.7.0

Renders the execution plan of the query q as a human-readable string without running it. The string gives tests and programs a direct observable for push-down decisions.

The plan lists each source with its provider (in-memory, __query, or native) and the number of conjuncts pushed down to it. It then reports the disposition of every where conjunct: pushed down, kept in memory as a host closure, or kept in memory as a translatable expression.

luadebug.sethook(hook: function, mask: string, [count: integer])
since 0.1.0

Sets the given function as a debug hook. The mask string may contain "c" (call), "r" (return), "l" (line). The count argument sets a count hook. Call with no arguments to remove the hook.

luadebug.setlocal(level: integer, local_: integer, value) → string|nil
since 0.1.0

Sets the value of local variable local at stack level level to value. Returns the variable name, or nil if the index is invalid.

luadebug.setmetatable(value, t: table|nil) → any
since 0.1.0

Sets the metatable of value to t (which can be nil). Returns value.

luadebug.setupvalue(f: function, up: integer, value) → string|nil
since 0.1.0

Sets the value of upvalue up of function f to value. Returns the upvalue name, or nil for invalid indices.

luadebug.setuservalue(u: userdata, value, [n: integer]) → userdata
since 0.1.0

Sets the n-th user value associated with userdata u to value. Returns u.

luadebug.traceback([message], [level: integer]) → string
since 0.1.0

Returns a traceback string of the call stack. The optional message is prepended. The level argument specifies where to start the traceback (default 1).

luadebug.upvalueid(f: function, n: integer) → userdata
since 0.1.0

Returns a unique identifier (light userdata) for upvalue n of function f. This can be used to check whether different closures share upvalues.

luadebug.upvaluejoin(f1: function, n1: integer, f2: function, n2: integer)
since 0.1.0

Makes upvalue n1 of closure f1 refer to the same upvalue as n2 of closure f2.

Coroutine Library

luacoroutine.close(co: thread) → boolean
since 0.1.0

Closes coroutine co, putting it in a dead state. If the coroutine was suspended with pending to-be-closed variables, those variables are closed. Returns true on success, or false plus an error message if closing a variable raises an error.

luacoroutine.create(f: function) → thread
since 0.1.0

Creates a new coroutine from function f and returns it. Does not start execution; use coroutine.resume to begin.

luacoroutine.isyieldable() → boolean
since 0.1.0

Returns true if the running coroutine can yield. A coroutine is not yieldable when it is the main thread or inside a non-yieldable C function.

luacoroutine.resume(co: thread, ...) → boolean
since 0.1.0

Starts or resumes execution of coroutine co. On the first resume, the extra arguments are passed to the coroutine body function. On subsequent resumes, the extra arguments become the results of the yield that suspended the coroutine.

Returns true plus any values passed to yield or returned by the body, or false plus an error message on failure.

luacoroutine.running() → thread
since 0.1.0

Returns the running coroutine and a boolean. The boolean is true if the running coroutine is the main thread.

luacoroutine.status(co: thread) → string
since 0.1.0

Returns the status of coroutine co as a string: "running", "suspended", "normal", or "dead".

luacoroutine.wrap(f: function) → function
since 0.1.0

Creates a coroutine from function f and returns a function that resumes it each time it is called. Arguments to the wrapper are passed as extra arguments to resume. Returns the values passed to yield, excluding the first boolean. Raises an error on failure (unlike resume).

luacoroutine.yield(...) → any
since 0.1.0

Suspends the running coroutine. Arguments to yield become the extra return values of the resume that restarted this coroutine.

Package Library

since 0.1.0 · type: string

A string describing some compile-time configurations for packages.

since 0.1.0 · type: string

The path used by require to search for a C loader.

since 0.1.0 · type: table

A table used by require to control which modules are already loaded.

luapackage.loadlib(libname: string, funcname: string) → function|nil
since 0.1.0

Dynamically loads C library libname. If funcname is "*", links the library globally. Otherwise, returns the C function funcname from the library. Returns nil plus an error message on failure.

since 0.1.0 · type: string

The path used by require to search for a Lus loader.

since 0.1.0 · type: table

A table to store loaders for specific modules.

since 0.1.0 · type: table

A table holding the sequence of searcher functions used by require.

luapackage.searchpath(name: string, path: string, [sep: string], [rep: string]) → string|nil
since 0.1.0

Searches for name in the given path string, replacing each "?" with name (with "." replaced by sep). Returns the first file that exists, or nil plus a string listing all files tried.

UTF-8 Library

luafastcallutf8.char(...) → string
since 0.1.0

Return a string containing the UTF-8 encoding of the given codepoints.

since 0.1.0

The pattern which matches exactly one UTF-8 byte sequence.

luafastcallutf8.codepoint(s: string, [i: integer], [j: integer]) → integer
since 0.1.0

Return the codepoints of all characters in s from position i to j (default i = 1, j = i).

luautf8.codes(s: string) → function
since 0.1.0

Return an iterator function that traverses s, returning the byte position and codepoint of each UTF-8 character.

luafastcallutf8.len(s: string, [i: integer], [j: integer]) → integer|nil
since 0.1.0

Return the number of UTF-8 characters in s from position i to j. Returns nil plus the position of the first invalid byte on failure.

luafastcallutf8.offset(s: string, n: integer, [i: integer]) → integer|nil
since 0.1.0

Return the byte position of the n-th UTF-8 character, counting from byte position i (default 1 for positive n, #s + 1 for non-positive).

Filesystem Library

lusfs.copy(source: string, dest: string) → boolean
since 0.1.0

Copies the file at source to dest. Returns true on success, or nil plus an error message on failure.

lusfs.createdirectory(path: string) → boolean
since 0.1.0
requiresfs:write

Creates a directory at path. Returns true on success, or nil plus an error message on failure.

lusfs.createlink(target: string, link: string) → boolean
since 0.1.0
requiresfs:write

Creates a symbolic link at link pointing to target. Returns true on success, or nil plus an error message on failure.

lusfs.follow(path: string) → string
since 0.1.0
requiresfs:read

Resolves a symbolic link at path and returns the target path. Returns nil plus an error message on failure.

lusfs.list(path: string) → table
since 0.1.0
requiresfs:read

Returns a table of filenames in the directory at path. Returns nil plus an error message on failure.

lusfs.move(source: string, dest: string) → boolean
since 0.1.0
requiresfs:write

Moves (renames) the file or directory at source to dest. Returns true on success, or nil plus an error message on failure.

since 0.1.0

The fs.path sub-module.

since 0.1.0

The platform path list delimiter (: on POSIX, ; on Windows).

lusfs.path.join(...) → string
since 0.1.0

Joins path components into a single path using the platform path separator.

lusfs.path.name(path: string) → string
since 0.1.0

Returns the final component (filename) of path.

lusfs.path.parent(path: string) → string
since 0.1.0

Returns the parent directory of path, or nil if path has no parent.

since 0.1.0

The platform path separator (/ on POSIX, \ on Windows).

lusfs.path.split(path: string) → table
since 0.1.0

Splits path into a table of its components.

lusfs.remove(path: string) → boolean
since 0.1.0
requiresfs:write

Removes the file or directory at path. Returns true on success, or nil plus an error message on failure.

lusfs.type(path: string) → string|nil
since 0.1.0
requiresfs:read

Returns the type of the filesystem entry at path as a string: "file", "directory", "link", or nil if it does not exist.

Network Library

lusnetwork.fetch(url: string, [options: table]) → table
since 0.1.0
requiresnetwork:http

Performs an HTTP(S) request to url. The optional options table may specify method, headers, and body. Returns the response body as a string, plus the HTTP status code and a response headers table.

since 0.1.0

The network.tcp sub-module.

lusnetwork.tcp.bind(host: string, port: integer) → userdata
since 0.1.0
requiresnetwork:tcp

Binds a TCP server to host and port. Returns a server object that can accept incoming connections.

lusnetwork.tcp.connect(host: string, port: integer) → userdata
since 0.1.0
requiresnetwork:tcp

Connects to a TCP server at host and port. Returns a connection object for reading and writing.

since 0.1.0

The network.udp sub-module.

lusnetwork.udp.open([port: integer], [address: string]) → userdata
since 0.1.0
requiresnetwork:udp

Opens a UDP socket, optionally bound to port and address. Returns a socket object for sending and receiving datagrams.

Vector Library

lusfastcallvector.clone(v: vector) → vector
since 0.1.0

Creates a copy of the vector v.

lusfastcallvector.create(capacity: integer, [fast: boolean]) → vector
since 0.1.0

Creates a new vector with the given capacity in bytes. If fast is true, the buffer is not zero-initialized (faster but contents are undefined).

local v = vector.create(1024)        -- zero-initialized
local v = vector.create(1024, true)  -- fast, uninitialized
lusvector.pack(v: vector, offset: integer, fmt: string, ...)
since 0.1.0

Packs values into the vector v starting at offset. Uses the same format string as string.pack.

local v = vector.create(16)
vector.pack(v, 0, "I4I4I4", 1, 2, 3)
lusfastcallvector.resize(v: vector, newsize: integer)
since 0.1.0

Resizes the vector to newsize bytes. New bytes are zero-initialized. Existing data within the new size is preserved.

lusfastcallvector.size(v: vector) → integer
since 0.1.0

Returns the size of the vector in bytes. Equivalent to #v.

lusvector.unpack(v: vector, offset: integer, fmt: string) → any
since 0.1.0

Unpacks values from the vector v starting at offset. Uses the same format string as string.unpack. Returns unpacked values followed by the next offset.

local a, b, c, nextpos = vector.unpack(v, 0, "I4I4I4")
lusvector.unpackmany(v: vector, offset: integer, fmt: string, [count: integer]) → function
since 0.1.0

Returns an iterator that repeatedly unpacks values from v using the format fmt. Optional count limits the number of iterations.

for a, b in vector.unpackmany(v, 0, "I4I4") do
  print(a, b)
end

Archive Library

lusvector.archive.brotli.compress(data: string|vector, [quality: integer], [lgwin: integer]) → string|vector
since 1.6.0

Compresses data using Brotli format (RFC 7932). The optional quality (0-11, default 11) controls compression quality. The optional lgwin (10-24, default 22) sets the LZ77 window size. Returns the same type as the input.

lusvector.archive.brotli.decompress(data: string|vector) → string|vector
since 1.6.0

Decompresses Brotli-compressed data. Returns the same type as the input.

lusvector.archive.deflate.compress(data: string|vector, [level: integer]) → string|vector
since 1.6.0

Compresses data using raw deflate format (RFC 1951) without headers. The optional level (0-9, default 6) controls the compression ratio. Returns the same type as the input.

lusvector.archive.deflate.decompress(data: string|vector) → string|vector
since 1.6.0

Decompresses raw deflate-compressed data. No header or checksum validation is performed. Returns the same type as the input.

lusvector.archive.gzip.compress(data: string|vector, [level: integer]) → string|vector
since 1.6.0

Compresses data using gzip format. The optional level (0-9, default 6) controls the compression ratio. Returns the same type as the input.

lusvector.archive.gzip.decompress(data: string|vector) → string|vector
since 1.6.0

Decompresses gzip-compressed data. Validates the gzip header and CRC32 checksum. Returns the same type as the input.

lusvector.archive.lz4.compress(data: string|vector, [level: integer]) → string|vector
since 1.6.0

Compresses data using LZ4 frame format. The optional level (1-12, default 1) controls the speed-ratio trade-off. Returns the same type as the input.

lusvector.archive.lz4.decompress(data: string|vector) → string|vector
since 1.6.0

Decompresses LZ4 frame-compressed data. Validates the frame header and optional checksum. Returns the same type as the input.

lusvector.archive.lz4.decompress_hc(data: string|vector) → string|vector
since 1.6.0

Decompresses data written in the legacy LZ4 frame format, identified by the magic number 0x184C2102. Older LZ4 tools produced this format, so decompress_hc reads archives written by them. New code should compress with compress and decompress with decompress, which use the current LZ4 frame format. Returns the same type as the input.

lusvector.archive.zstd.compress(data: string|vector, [level: integer]) → string|vector
since 1.6.0

Compresses data using Zstandard format (RFC 8878). The optional level (-7 to 22, default 3) controls the speed-ratio trade-off. Returns the same type as the input.

lusvector.archive.zstd.decompress(data: string|vector) → string|vector
since 1.6.0

Decompresses Zstandard-compressed data. Validates the frame header and optional content checksum. Returns the same type as the input.

Worker Library

lusworker.create(path: string, ...) → worker
since 0.1.0
requiresloadfs:read

Spawns a new worker running the script at path. Optional varargs are serialized and can be received by the worker via worker.peek(). Returns a worker handle. Requires load and fs:read pledges.

local w = worker.create("worker.lus", "hello", 42)
-- worker can receive "hello" and 42 via worker.peek()
lusworker.message(value)
since 0.1.0

(Worker-side only) Sends value to the worker’s outbox for the parent to receive via worker.receive().

lusworker.peek() → any
since 0.1.0

(Worker-side only) Blocking receive from the worker’s inbox. Blocks until a message from the parent (via worker.send()) is available.

lusworker.receive(w1: worker, ...) → any
since 0.1.0

Blocking select-style receive from one or more workers. Blocks until at least one worker has a message. Returns one value per worker: the message if available, or nil if that worker has no message. Propagates worker errors.

local msg = worker.receive(w)
-- or multi-worker select:
local m1, m2 = worker.receive(w1, w2)
lusworker.send(w: worker, value)
since 0.1.0

Sends value to worker w’s inbox. The worker can receive it via worker.peek(). Values are deep-copied.

lusworker.status(w: worker) → string
since 0.1.0

Returns the status of worker w: "running" if the worker is still executing, or "dead" if it has finished or errored.

Time Library

unstabletime
since 1.7.0

The time module: date, time, and duration handling. It provides three immutable value types:

  • instant - an absolute point on the UTC timeline (second + nanosecond precision).
  • duration - a signed span of time.
  • zoned - a civil (wall-clock) date and time observed at a particular zone offset.

Arithmetic on an instant is exact SI-seconds (instant + duration), while calendar arithmetic on a zoned value is a separate, DST-aware operation (zoned:add), so the two can never be confused. Distinct from os.time/os.date, which work in raw integer epochs: bridge them with time.instant(os.time()) and instant:epoch().

unstabletime.days(n: integer) → duration
since 1.7.0

Returns a duration of n days. A concise alternative to time.duration{ days = n } for single-unit spans.

local timeout = time.days(5)
unstabletime.duration(fields: table) → duration
since 1.7.0

Constructs a duration from a table summing any of days, hours, minutes, seconds, millis, micros, nanos (each optional, may be negative).

A duration supports d:seconds(), d:millis(), d:parts() (a normalized {negative, days, hours, minutes, seconds, nanos} breakdown), the operators +, -, unary -, and duration * integer, and the comparisons ==, <, <=. See also the scalar helpers time.days, time.hours, time.minutes, time.seconds, time.millis.

unstabletime.hours(n: integer) → duration
since 1.7.0

Returns a duration of n hours. A concise alternative to time.duration{ hours = n } for single-unit spans.

local timeout = time.hours(5)
unstabletime.instant(epoch_seconds: integer, [nanos: integer]) → instant
since 1.7.0

Constructs an instant from epoch_seconds (Unix seconds since 1970-01-01 UTC) and an optional nanos fraction. Use this to lift an os.time() value into the time type system.

An instant supports: i:epoch(), i:nanos(), i:format(fmt), i:iso(), i:at(zone) (render as a zoned in a zone or a fixed offset in seconds), the operators i + duration, i - duration, i - instant (a duration), and the comparisons ==, <, <=.

unstabletime.localzone() → zone
since 1.7.0

Returns the system’s local zone, detected from the environment: $TZ, then /etc/localtime//etc/timezone on Unix, or the current Windows time-zone mapped to its IANA name. Falls back to UTC when detection fails. The resolved zone is looked up in the embedded tzdata, so it carries full DST history.

print(time.now():at(time.localzone()):iso())
unstabletime.millis(n: integer) → duration
since 1.7.0

Returns a duration of n millis. A concise alternative to time.duration{ millis = n } for single-unit spans.

local timeout = time.millis(5)
unstabletime.minutes(n: integer) → duration
since 1.7.0

Returns a duration of n minutes. A concise alternative to time.duration{ minutes = n } for single-unit spans.

local timeout = time.minutes(5)
unstabletime.monotonic() → instant
since 1.7.0

Returns a monotonic instant for timing durations. Monotonic instants never move backwards and are immune to wall-clock adjustments (NTP, DST), but they have no calendar meaning: they may only be subtracted from another monotonic reading to yield a duration. Formatting one or calling :epoch() raises an error.

local start = time.monotonic()
do_work()
print(`took {time.monotonic() - start}`)
unstabletime.month
since 1.7.0

A first-class enum of the months, january = 1 through december = 12. It may be used wherever a month integer is expected (e.g. the month field of time.zoned, zoned:with, and zoned:add), taking its 1-based index. The month field of a zoned returns a plain integer, for arithmetic and parity with os.date.

local z = time.zoned{ year = 2026, month = time.month.july, day = 2 }
print(z.month)                      -- 7
unstabletime.now() → instant
since 1.7.0

Returns an instant for the current point in time, read from the system wall clock (UTC).

local n = time.now()
print(n:iso())          -- "2026-07-02T15:04:05Z"
print(n:epoch())        -- integer Unix seconds
unstabletime.offset(seconds: integer) → zone
since 1.7.0

Returns a fixed-offset zone at seconds east of UTC (negative for west), for cases where a full IANA zone is unnecessary or unavailable. Its :name() is the formatted offset, e.g. "-04:00". Unlike a named zone, a fixed offset never varies with the instant.

local z = time.offset(-5 * 3600)   -- UTC-05:00
unstabletime.parse(s: string, [format: string]) → instant|zoned
since 1.7.0

Parses a date/time string s. Returns a zoned when the input carries an explicit numeric offset (e.g. -04:00 or -0400), or an instant when it is in UTC (Z) or offset-free (assumed UTC).

Without a format, s is read as ISO 8601: a date alone (2026-07-02, midnight UTC), a date and time separated by T or a space, optional seconds, and fractional seconds to nanosecond precision.

With a format, s is matched against an explicit pattern using the specifiers %Y %y %m %d %e %H %M %S %z and %%. A space in the pattern matches a run of whitespace; any other literal must match exactly. Parsing is portable and does not depend on the platform’s strptime.

local i = time.parse("2026-07-02T13:15:00Z")            -- ISO -> instant
local z = time.parse("02/07/2026 09:15 -0400",
                     "%d/%m/%Y %H:%M %z")               -- pattern -> zoned
unstabletime.seconds(n: integer) → duration
since 1.7.0

Returns a duration of n seconds. A concise alternative to time.duration{ seconds = n } for single-unit spans.

local timeout = time.seconds(5)
unstabletime.tzdata_version() → string
since 1.7.0

Returns the IANA tzdata release string embedded in this build, e.g. "2026b". Regenerate the embedded database with scripts/update-tzdata.sh.

unstabletime.weekday
since 1.7.0

A first-class enum of the days of the week, following ISO 8601 numbering: monday = 1 through sunday = 7. The weekday field of a zoned returns one of these values.

local z = time.zoned{ year = 2026, month = 7, day = 2 }
assert(z.weekday == time.weekday.thursday)
print(tonumber(z.weekday))          -- 4
unstabletime.zone(name: string) → zone
since 1.7.0

Returns a zone for an IANA name such as "America/New_York" or "Europe/Paris". Raises if the name is unknown.

Names resolve from an embedded, zstd-compressed copy of the IANA tzdata database, and time.tzdata_version() reports the release. No filesystem access or fs pledge is required: the database is compiled into the runtime, so named zones behave identically on every platform, including ones without a system tz database.

A zone supports z:name() and z:offset(instant), which returns the offset east of UTC, in seconds, that the zone observes at that instant, honouring historical DST transitions. Pass a zone to instant:at(zone) to render a civil time, or to time.zoned(fields, zone) to interpret wall-clock fields.

The following example renders the current time on the New York wall clock.

local ny = time.zone("America/New_York")
print(time.now():at(ny):iso())   -- current time as New York wall clock
unstabletime.zoned(fields: table, [zone: zone|integer]) → zoned
since 1.7.0

Constructs a zoned civil datetime from a table of fields (year, month, day, hour, minute, second, nanosecond). Missing fields default to the start of 1970-01-01. The second argument selects how the wall-clock fields are interpreted: pass a zone (from time.zone, time.offset, or time.localzone) for a DST-aware resolution, an integer for a fixed offset east of UTC, or omit it for UTC.

A zoned exposes read-only fields year, month, day, hour, minute, second, nanosecond, weekday, yearday, and offset, plus the methods z:instant(), z:format(fmt), and z:iso(). month is an integer (1-12). weekday is a time.weekday enum value (monday through sunday).

Two methods derive new values:

  • z:with{...} returns a copy with the named civil fields replaced.
  • z:add{...} returns a copy advanced by calendar amounts (years, months, weeks, days, hours, minutes, seconds, nanos, negative allowed).

Both operate on the wall clock and re-resolve the offset through the originating zone. z:add{days = 1} keeps the same wall-clock time the next day even across a DST transition, and adding months clamps the day to the target month’s length (Jan 31 plus one month is Feb 28 or 29).

The following example builds a fixed-offset civil time and reads it back.

local z = time.zoned({ year = 2026, month = 7, day = 2, hour = 9 }, -4 * 3600)
print(z:iso())          -- "2026-07-02T09:00:00-04:00"
print(z.weekday)        -- 4  (Thursday)