Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

sql.types

import sql.types

sql lifts part of this module out to its own top level; each name below is shown with the path that reaches it. Anything still spelled sql.types.* needs import 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

ZuriDatabase
nilNULL
boola boolean where the engine has one, otherwise 0 and 1
numberan integer where the value is whole, otherwise a float
biginta 64 bit integer, or an error if it will not fit in one
stringtext
bytesa blob
date.Datea timestamp, or ISO 8601 text on an engine with no date type
Timea time interval on an engine that has one, otherwise text
list, dictJSON, 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

FieldTypeDescription
negativeboolWhether the span runs backwards.
hoursnumberWhole hours.
minutesnumber
secondsnumber
microsecondsnumberFractional 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