Skip to content

API Reference

/

Values

Datetime

The SurrealDB datetime type holds an instant in UTC, to the nanosecond. The Python SDK sends a datetime.datetime or a Datetime as a datetime, and reads every datetime back as a PreciseDatetime, a datetime subclass that keeps the nanoseconds. This page documents how datetimes convert and the two classes involved.

Import
from surrealdb import Datetime, PreciseDatetime

Source: datetime.py

Send a datetime.datetime, with or without a time zone:

  • An aware datetime is converted to UTC.

  • A naive datetime is read as UTC, not as the local time of the machine, so the same code sends the same instant on every host.

  • Every datetime reads back as a PreciseDatetime in UTC.

import datetime as dt
from surrealdb import RecordID, Surreal

with Surreal("ws://localhost:8000") as db:
    db.signin({"username": "root", "password": "secret"})
    db.use("surrealdb", "docs")

    plus_two = dt.timezone(dt.timedelta(hours=2))
    db.create(RecordID("post", 1), {
        "published": dt.datetime(2025, 6, 1, 14, 0, tzinfo=plus_two),
        "edited": dt.datetime(2025, 6, 1, 14, 0),
    })

    post = db.select(RecordID("post", 1)).execute()
    print(repr(post["published"]))
    print(repr(post["edited"]))
Output
PreciseDatetime(2025, 6, 1, 12, 0, tzinfo=datetime.timezone.utc)
PreciseDatetime(2025, 6, 1, 14, 0, tzinfo=datetime.timezone.utc)

SurrealDB holds datetimes from the year -262143 to the year 262142, and Python's datetime holds the years 1 to 9999. Reading a datetime outside the years 1 to 9999 raises UnexpectedResponseError.

For a date and time of day with no time zone, SurrealDB 4.0 has the timestamp type, and for a calendar date the date type. Both are on the Dates, times and intervals page, together with how a datetime.date is sent.

PreciseDatetime is a datetime.datetime subclass that also holds the nanoseconds below one microsecond, which datetime cannot store. It is what every datetime from SurrealDB reads back as, so a value that is read and written back keeps all nine fractional digits. A PreciseDatetime is a datetime in every other way: isinstance(value, datetime) is true, it compares and sorts with plain datetime values, and pickle and copy keep its nanoseconds.

Syntax
PreciseDatetime(
    year, month, day,
    hour=0, minute=0, second=0, microsecond=0,
    tzinfo=None, *, nanosecond=0,
)

Parameter

Type

Description

year

,

month

,

day

,

hour

,

minute

,

second

,

microsecond

,

tzinfo

as

datetime

The same arguments as

datetime.datetime

.

nanosecondint

The nanoseconds after

microsecond

, from

0

to

999

. Keyword only. Defaults to

0

. A value outside that range raises

ValueError

.

PropertyTypeDescription
nanosecondintThe nanoseconds after microsecond, from 0 to 999.
MethodReturnsDescription
isoformat_with_nanoseconds()strISO 8601 text in UTC with nine fractional digits, such as 2024-02-29T12:00:00.123456789Z. A naive value is read as UTC.
PreciseDatetime.from_seconds_and_nanos(seconds, nanoseconds)PreciseDatetimeClass method. The UTC instant seconds after the Unix epoch, plus nanoseconds.

str(), isoformat() and repr() come from datetime, so they show microseconds only. Arithmetic and replace() also come from datetime, and their results have nanosecond set to 0. The nanoseconds survive storage and retrieval, but not calculation.

import datetime as dt
from surrealdb import Surreal

with Surreal("ws://localhost:8000") as db:
    db.signin({"username": "root", "password": "secret"})
    db.use("surrealdb", "docs")

    sql = "RETURN d'2024-02-29T12:00:00.123456789Z'"
    stamp = db.query(sql).first()

    print(stamp.microsecond, stamp.nanosecond)
    print(stamp.isoformat_with_nanoseconds())
    print((stamp + dt.timedelta(days=1)).nanosecond)
Output
123456 789
2024-02-29T12:00:00.123456789Z
0

To build one yourself, pass nanosecond as a keyword:

import datetime as dt
from surrealdb import PreciseDatetime

stamp = PreciseDatetime(
    2024, 2, 29, 12, 0, 0, 123456,
    tzinfo=dt.timezone.utc,
    nanosecond=789,
)
print(stamp.isoformat_with_nanoseconds())
Output
2024-02-29T12:00:00.123456789Z

A Datetime holds ISO 8601 text to send as a SurrealDB datetime. Use it when a datetime already exists as text, or to send more than six fractional digits without building a PreciseDatetime. Reading never gives a Datetime: every datetime reads back as a PreciseDatetime.

Syntax
Datetime(dt)

Parameter

Type

Description

dtstr

An ISO 8601 datetime, such as

"2025-06-01T09:00:00Z"

or

"2025-06-01T14:00:00.123456789+02:00"

. Up to nine fractional digits are kept, and text with no zone is read as UTC.

The constructor does not check the text. The SDK parses it when the value is sent, and text that is not an ISO 8601 datetime raises ValueError at that point.

Property or methodTypeDescription
dtstrThe text, as given.
str(value)strA SurrealQL datetime literal, such as d"2025-06-01T09:00:00Z".

Two Datetime values are equal when their text is equal, so the same instant written in two ways gives two values that are not equal. A Datetime is hashable.

from surrealdb import Datetime, Surreal

launch = Datetime("2025-06-01T09:00:00.123456789Z")
print(str(launch))

with Surreal("ws://localhost:8000") as db:
    db.signin({"username": "root", "password": "secret"})
    db.use("surrealdb", "docs")

    sql = "RETURN <string> $at"
    print(db.query(sql, {"at": launch}).first())
Output
d"2025-06-01T09:00:00.123456789Z"
2025-06-01T09:00:00.123456789Z

Was this page helpful?