PhoenixmlDb

Dates and Times

PhoeniXML DB uses UTC; the date-time forms it accepts, API key expiry, value indexes on dates, and embedded hosts

#Dates and Times

PhoeniXML DB uses UTC. A date-time without a timezone is UTC wherever the engine stores, indexes or compares it. A value written with a timezone (Z, +02:00) keeps its instant; write an offset to mean another zone. There is no time-zone setting.

gRPC server, EPS REST server

Embedded engine

Typed dateTime metadata, value-index keys, JSON index keys

UTC

UTC

API key Expires

UTC

not applicable

Comparison in a query or stylesheet (implicit-timezone(), current-dateTime(), comparing a value that has a timezone with one that has not)

UTC

UTC

In both servers and in the embedded engine implicit-timezone() is PT0S, and xs:dateTime('1996-12-12T13:13:00') < xs:dateTime('1996-12-12T13:13:00-02:00') is true, whatever the time zone of the machine or of the host process. The engine gives UTC to every query engine and XSLT transformer it makes (DocumentDatabase.ImplicitTimezone). A stylesheet's static expressions (use-when, static parameters and variables) are in UTC too.

A server process runs in UTC: it sets the TZ environment variable of its own process to UTC at startup. Console log timestamps, when turned on without UseUtcTimestamp, are UTC. A configuration key PhoenixmlDb:Query:ImplicitTimezone is ignored; the server starts and lists it in the effective-configuration event (3001).

#What is read as a date-time

Typed dateTime metadata, dateTime value-index keys and API key Expires accept these forms and nothing else:

Form

Example

Read as

yyyy-MM-dd

2024-01-15

that day at 00:00:00 UTC

yyyy-MM-ddTHH:mm:ss, optional fraction, optional Z or +hh:mm / -hh:mm

2024-01-15T12:00:00, 2024-01-15T07:00:00.5-05:00

UTC when no timezone is written

the same with a space in place of T

2024-01-15 12:00:00

the same

RFC 1123

Mon, 15 Jan 2024 12:00:00 GMT

that instant

The year has four digits (0001 to 9999). Seconds are required. A fraction has any number of digits and is kept to 100 nanoseconds. Leading and trailing white space is ignored.

Refused, among others: a time with no date (12:00:00), a date with no year (1/15, January 15), a year and month (2024-01), a two-digit year (1/15/24), 01/15/2024, 2024/01/15, 2024-1-5, 2024-01-15T12:00 (no seconds), 2024-01-15T12:00:00+0100, 2024-01-15T12:00:00+01, a lower-case t or z, 2024-01-15Z, and seconds since 1970.

A refused text:

  • as typed dateTime metadata: the request is refused with the metadata validation error;

  • in a document, at a path with a dateTime value index: the document is stored and that value has no index key;

  • as an API key Expires: startup stops, naming the entry.

An xs:date in typed metadata or at a path with a date value index is yyyy-MM-dd (white space around it ignored); any other text is refused as metadata and has no index key. An xs:date or xs:time value that carries a timezone is refused as typed metadata and has no index key.

#API key expiry

PhoenixmlDb:Auth:ApiKeys:<n>:Expires (gRPC server) and Auth:ApiKey:Clients:<n>:Expires (EPS REST server):

  • Text in one of the forms above. Without a timezone it is UTC: 2027-01-31 is 2027-01-31T00:00:00Z.

  • Absent: the key does not expire.

  • Empty, blank or not in one of the forms: startup stops with a message naming the entry. In the EPS REST server such an entry is also never valid when a request presents its key.

  • In the options classes ApiKeyOptions.Expires (gRPC server) and ApiKeyClientConfig.Expires (EPS) the property is string?.

#Startup log

Event

Level

Text

3010 ImplicitTimezone

Information

The implicit timezone of queries and stylesheets is UTC.

3011 ImplicitTimezoneNotUtc

Warning

Windows only, when the machine's time zone is not UTC (below): The machine's time zone is <zone>, not UTC, and on this platform a process cannot be given its own time zone. Queries and stylesheets use UTC; times taken from the local clock, such as console log timestamps without UseUtcTimestamp, are in the machine's zone. Set the machine's time zone to UTC.

For each API key with an expiry still ahead, one Information line gives the instant in UTC, the entry and its Id, for example PhoenixmlDb:Auth:ApiKeys:0 (Id 'svc') expires at 2026-12-31T00:00:00.0000000+00:00. (gRPC server) or Auth:ApiKey:Clients:0 (Id 'svc') expires at 2026-12-31T00:00:00.0000000+00:00. (EPS). A key already expired has a warning with expired at and the instant in UTC. The effective-configuration event (3001) shows Expires as configured.

#Value indexes on dates and times

A value index of type xs:dateTime, xs:date or xs:time answers one comparison: equality between the attribute and a string literal that is a value of the index's type, for example /order[@at = '2024-01-15T12:00:00']. That comparison is of text: it returns the rows whose attribute has exactly that text.

Every other comparison on those index types is answered by a scan, with the same result as on a container that has no index:

  • a comparison with a typed value: @at = xs:dateTime('…'), <, >=;

  • a comparison with a variable, of any type: let $x := … return /order[@at = $x], an external $v;

  • a range with a string: @at >= '2024-01-15T12:00:00Z';

  • a string literal that is not a value of the index's type: @day = '2024-01-15Z'.

For every index type, a string literal that is not a value of the index's type is answered by a scan. An external variable that holds a CLR DateTimeOffset is compared as text by the XQuery library.

The index writer reads numbers and dates with the invariant culture. A value index must be rebuilt when it was built on a machine whose time zone was not UTC (date-time values without a timezone), or on a machine whose regional settings were not English/invariant (numbers and dates). Nothing detects such an index. The rebuild operations:

Host

Operation

EPS REST server

POST /api/containers/{containerId}/indexes/rebuild

gRPC server

IndexService.RebuildIndex

Embedded

DocumentDatabase.RebuildIndexesAsync(containerName)

Typed dateTime metadata is stored as an instant. A value stored earlier keeps its instant; a rebuild does not change it.

#Embedded engine in a host that is not in UTC

The embedded engine does not change the time zone of its host process. Its queries are in UTC all the same. With rows a 2024-01-15T12:00:00 and b 2024-01-15T12:00:00Z, in a host in America/New_York or Pacific/Kiritimati:

Answer

/e[@t = xs:dateTime('2024-01-15T12:00:00')]

a, b

/e[@t = xs:dateTime('2024-01-15T12:00:00Z')]

a, b

metadata query for 2024-01-15T12:00:00

a, b

implicit-timezone(), the timezone of current-dateTime(), current-date(), current-time()

PT0S

order by and max over date-times with and without a timezone

by instant, no timezone read as UTC

A query gives the same answer in such a host as in a UTC process, whether it is evaluated once per document or once over the container (Documents & Storage).

An application that runs its own XsltTransformer with the database's stylesheet policy sets ImplicitTimezone itself, to DocumentDatabase.ImplicitTimezone. Without it the library takes the zone of the host.

#Windows

On Windows a process cannot set its own time zone. A server on a Windows machine whose time zone is UTC logs event 3010. On a machine in another zone it logs warning 3011 and starts. Queries and stylesheets are in UTC there as elsewhere: the engine gives each query engine and transformer UTC as its implicit timezone, whatever the zone of the process. What is stored, indexed and read from configuration is UTC on Windows as elsewhere. Times the process takes from the local clock, such as console log timestamps without UseUtcTimestamp, are in the machine's zone. Set the machine's time zone to UTC.

See also: Metadata · Indexing · Server Mode · Logging and Telemetry