sql.types
import sql.types
sqllifts part of this module out to its own top level; each name below is shown with the path that reaches it. Anything still spelledsql.types.*needsimport sql.types.
How Zuri values and database values correspond, for the parts that are the same whichever engine is underneath.
Databases agree on very little. They all store numbers and text; past that, one has a native JSON type and another stores JSON in a text column, one has a date type with a time zone and another has no date type at all. The adapters differ where the engines differ, and share the conversions that do not have to.
The correspondence
| Zuri | Database |
|---|---|
nil | NULL |
bool | a boolean where the engine has one, otherwise 0 and 1 |
number | an integer where the value is whole, otherwise a float |
bigint | a 64 bit integer, or an error if it will not fit in one |
string | text |
bytes | a blob |
date.Date | a timestamp, or ISO 8601 text on an engine with no date type |
Time | a time interval on an engine that has one, otherwise text |
list, dict | JSON, native where the engine has it and text where it does not |
A value of any other type is refused rather than guessed at. An instance of a class in particular has no obvious storage form, and inventing one would make the round trip lossy in a way that only showed up on the way back out.
Time
Timestamps written as text use ISO 8601 with microseconds and an
explicit offset, which every engine here parses and which sorts
correctly as text. A date.Date carries an offset of its own, so
nothing is assumed about the local zone at either end.
Constants
ISO_FORMAT
sql.types.ISO_FORMAT = 'Y-m-d\TH:i:s.uP'
The format this module writes timestamps in: ISO 8601 with microsecond precision and an explicit UTC offset.
ISO_DATE_FORMAT
sql.types.ISO_DATE_FORMAT = 'Y-m-d'
The same, without the time, for a column that holds only a date.
Functions
is_temporal()
sql.types.is_temporal(value) -> bool
Whether value is a date.Date.
Parameters
value(any)
Returns bool
is_interval()
sql.types.is_interval(value) -> bool
Whether value is a Time.
Parameters
value(any)
Returns bool
is_structured()
sql.types.is_structured(value) -> bool
Whether value is something this module stores as JSON.
Parameters
value(any)
Returns bool
to_iso()
sql.types.to_iso(value) -> string
Formats a date.Date as ISO 8601 text.
Parameters
value(date.Date)
Returns string
from_iso()
sql.types.from_iso(text) -> date.Date|nil
Reads ISO 8601 text back into a date.Date.
Accepts what the engines actually write, which is more than one shape:
with or without a T, with or without fractional seconds, and with or
without an offset. Text that is not a timestamp at all comes back as
nil rather than raising, so a column whose declared type promises a
date but whose contents do not can still be read.
Parameters
text(string)
Returns date.Date|nil
to_json()
sql.types.to_json(value) -> string
Encodes value as JSON text.
Parameters
value(list|dict)
Returns string
from_json()
sql.types.from_json(text) -> any
Decodes JSON text, returning nil for anything that will not parse.
Parameters
text(string)
Returns any
flatten()
sql.types.flatten(value) -> nil|bool|number|bigint|string|bytes
Reduces value to something an engine with no richer type can store: a
timestamp becomes ISO text, a list or dictionary becomes JSON text, and
everything else is already storable and passes through.
Adapters for engines that do have those types encode them natively instead and never call this.
Parameters
value(any)
Returns nil|bool|number|bigint|string|bytes
Raises QueryError for a value with no storable form.
time()
sql.time(hours, minutes, seconds, microseconds, negative) -> Time
Builds a Time.
Parameters
hours(number|nil)minutes(number|nil)seconds(number|nil)microseconds(number|nil)negative(bool|nil)
Returns Time
time_from_seconds()
sql.time_from_seconds(total: number) -> Time
Builds a Time of total seconds, splitting it into parts.
Parameters
total(number) — Negative for a span that runs backwards.
Returns Time
parse_time()
sql.parse_time(text) -> Time|nil
Reads a SQL TIME literal.
Accepts HH:MM:SS, HH:MM, and either with fractional seconds, in each
case optionally signed. Text that is not a time comes back as nil
rather than raising, matching from_iso().
Parameters
text(string)
Returns Time|nil
integer_from_text()
sql.types.integer_from_text(text) -> number|bigint|nil
Reads an integer, however long, as a number where one holds it exactly and a bigint where it does not.
Parameters
text(string) — An optionally signed run of digits.
Returns number|bigint|nil — nil where text is not an integer.
Classes
Time
class sql.Time
A signed span of time, as a SQL TIME column holds one.
A TIME is not a point in the day. MySQL’s range runs from
-838:59:59.999999 to 838:59:59.999999, so a value can be negative
and can exceed twenty four hours, and reading one into a date.Date
would quietly lose both of those. It is a duration: the length of a
shift, the lateness of a delivery, the gap between two events.
The parts are kept as they were written rather than normalised, so a
value built as 90 minutes stays 90 minutes and does not silently become
an hour and a half. total_seconds() is there for arithmetic and
comparison, and compare() uses it, so two values that mean the same
length compare equal whichever way each was built.
Fields
| Field | Type | Description |
|---|---|---|
negative | bool | Whether the span runs backwards. |
hours | number | Whole hours. |
minutes | number | |
seconds | number | |
microseconds | number | Fractional seconds, in millionths. |
Constructor
sql.Time(hours, minutes, seconds, microseconds, negative)
Parameters
hours(number|nil)minutes(number|nil)seconds(number|nil)microseconds(number|nil)negative(bool|nil)
Time.total_seconds()
sql.Time.total_seconds() -> number
The whole span in seconds, negative where the span is.
Returns number — Fractional where there are microseconds.
Time.is_negative()
sql.Time.is_negative() -> bool
Whether the span runs backwards.
A zero span is never negative, whichever way it was built.
Returns bool
Time.is_zero()
sql.Time.is_zero() -> bool
Whether the span is of no length.
Returns bool
Time.compare()
sql.Time.compare(other) -> number
Orders this span against another by length.
Parameters
other(Time)
Returns number — Negative, zero or positive.
Time.equals()
sql.Time.equals(other) -> bool
Whether two spans are of the same length, however each was built.
Parameters
other(any)
Returns bool
Time.to_string()
sql.Time.to_string() -> string
The span as a SQL TIME literal, such as '-12:30:00'.
Microseconds appear only where there are some, which is the same choice the engines make when they print one.
Returns string
2026, Richard Ore and Zuri contributors