Repository navigation
feat(sqlserver): datetimeoffset is a timestamptz - #3138
mtoy-googly-moogly merged 3 commits into
Conversation
A datetimeoffset is an instant, so it is a Malloy timestamptz rather than a native type. Its clock is read at UTC (SWITCHOFFSET), or in the query's time zone (AT TIME ZONE), never at the offset the value was written in, which is what CAST, DATEPART and the DATEDIFF anchor would otherwise read. Truncation and offsets work on the UTC clock and return a datetimeoffset; a timestamptz literal is written with AT TIME ZONE. Comparison, DATEDIFF and MAX already compare instants. The shared suite's timestamptz tests now run on SQL Server and pass on 2017 and 2022. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T2RKuL1PuzicjnDfKHz7uh Signed-off-by: James Estes <james.estes@credibledata.com>
AT TIME ZONE 'UTC' labels a datetime2 and moves a datetimeoffset, so the civil-time conversion is one expression for both types and the UTC case is the only one that reads the type. The dialect spec keeps the facts the shared suite cannot state. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T2RKuL1PuzicjnDfKHz7uh Signed-off-by: James Estes <james.estes@credibledata.com>
|
Reading this revealed some weakness in how we provide time computation support in Dialect, so Claude and I have now re-done that. PR #3145 changed how every dialect does time, and this PR should rebase onto it. It has landed, so please merge main. Expect conflicts in
|
…imeoffset Signed-off-by: James Estes <james.estes@credibledata.com> # Conflicts: # packages/malloy/src/dialect/sqlserver/sqlserver.ts
|
Merged main. The datetimeoffset handling is now just the two conversions: Against SQL Server 2017 and 2022 locally, every timestamptz and query-zone test in The 20 other |
A
datetimeoffsetcolumn is a Malloytimestamptz.Dialect-only: the SQL Server dialect file, its README, the dialect spec, and one expectation in the connection spec. No other dialect's SQL changes.
The dialect supplies the two civil-time conversions for a
datetimeoffsetand nothing else; truncation, offsets, extraction and casts reach it through the shared code indialect.ts, so they only ever operate on adatetime2.sqlConvertToCivilTimereads adatetimeoffset's clock withAT TIME ZONE, at UTC or in the query zone, never at the offset the value was written in. A bareCASTwould read that offset's clock.sqlConvertFromCivilTime, when the destination is atimestamptz, casts a UTC clock toDATETIMEOFFSET(at +00:00), or labels a query-zone clock withAT TIME ZONE.What the server does
Measured on 2017 and 2022. Three operations read a
datetimeoffset's clock at its own offset: a cast to a date gives the local date,DATEPARTgives the local hour, and a cast todatetime2drops the offset and keeps the local clock. Comparison,DATEDIFFbetween two values,MAXandDATEADDalready work on the instant.AT TIME ZONE 'UTC'labels adatetime2and moves adatetimeoffset, so one expression reads the UTC clock of either type. The same operator on adatetime2with a named zone labels it, which is how atimestamptzliteral is written.The T-SQL
hour(t),t.day,t + 1 month, no query zoneCAST((t AT TIME ZONE 'UTC') AS DATETIME2); a truncation or offset is cast back toDATETIMEOFFSETat +00:00timezone: 'America/Mexico_City'CAST((t AT TIME ZONE 'UTC') AT TIME ZONE N'Central Standard Time (Mexico)' AS DATETIME2); a truncation or offset comes back withAT TIME ZONEin that zonet::timestampCAST((t AT TIME ZONE 'UTC') AS DATETIME2), with or without a query zone: the cast keeps the instantt::dateDATEd::timestamptzdatetime2, cast toDATETIMEOFFSET, or labelled in the query zone@2024-03-20 12:34:56[America/Chicago]::timestamptz(CAST('2024-03-20 12:34:56' AS DATETIME2) AT TIME ZONE N'Central Standard Time')hours(a to b)DATEDIFF_BIG, as beforetimestamptzDATETIMEOFFSETTesting
Against SQL Server 2022 in Docker (
test/mssql/mssql_start.sh):test/src/databases/sqlserver/sqlserver.spec.tsandpackages/malloy-db-sqlserverpass, including fivedatetimeoffsettests: the instant, the clock at UTC without a query zone, comparison, a calendar offset, and measuring between two offsets.test/src/databases/all/time.spec.ts, which SQL Server CI does not run: every timestamptz and query-zone test passes, 17 of them newly enabled byhasTimestamptz. Twenty other tests in the file fail, the same twenty as onmain: the range-edge tests put a comparison in aCASE WHEN, which needs a boolean value SQL Server does not have, and one runsSELECT 1with an unnamed column.