Microsoft.Data.Sqlite in .NET 10: DateTimeOffset Now Assumes UTC

You upgrade to .NET 10 — required before .NET 8 and 9 both hit end of support on November 10, 2026 — change no application code, and timestamps read back from SQLite are suddenly off by your server's UTC offset. No exception, no warning, no compiler error. The value is just different now.

reference guide .NET 10 SQLite

// the exact behavior change, before and after

SQLite itself doesn't enforce timezone semantics — it stores timestamps as plain text or numbers and leaves interpretation to the client. Microsoft.Data.Sqlite makes an assumption when a stored value carries no explicit offset, and that assumption changed in .NET 10:

three separate changes, same release
GetDateTimeOffset(), no offset in the datawas assumed local timezone → now assumed UTC
writing a DateTimeOffset to a REAL columnwas written as-is → now converted to UTC first
GetDateTime(), offset present in the datareturned DateTimeKind.Local → now returns DateTimeKind.Utc
what this actually looks like
the code — unchanged across both versions
var offset = reader.GetDateTimeOffset(0);  // column stores "2026-01-15 10:30:00", no offset
on .NET 9, server in UTC+2
2026-01-15T10:30:00+02:00   // assumed the server's local timezone
on .NET 10, same code, same data, same server
2026-01-15T10:30:00+00:00   // now assumed UTC -- a genuinely different instant in time
why
.NET 10 changed the assumption to align with SQLite's own convention: a timestamp with no offset is treated as UTC. It's arguably the more correct default -- SQLite itself has never assumed local time -- but "more correct" and "silent" is exactly what makes this dangerous for existing data written under the old assumption.
the fix, if you need the old behavior while you migrate
AppContext.SetSwitch("Microsoft.Data.Sqlite.Pre10TimeZoneHandling", true);
// set this once at startup, before any SQLite connection is opened
This switch is documented as a temporary compatibility path, not a permanent fix. Every day it stays on, new data keeps being written under the old (arguably wrong) local-time assumption — decide deliberately whether to migrate existing stored values to the new UTC convention, rather than leaving the switch flipped indefinitely.

// why this is easy to miss

Nothing here throws. Tests that don't assert on the exact offset or DateTimeKind pass unchanged. The bug surfaces downstream — a scheduled report running at the wrong hour, an audit log timestamp that's off by a few hours, a comparison against DateTimeOffset.UtcNow that's now subtly wrong in the other direction than before. If timestamps out of SQLite look "shifted" by a consistent amount right after a .NET 10 upgrade, that consistent amount is very likely your server's UTC offset — and this is why.

// this lands in the same migration wave as the EF Core work

If you're moving to .NET 10 because of the November 2026 end-of-support deadline and also run EF Core migrations against the same database, the migrations-history mismatch covered here is worth checking in the same pass — both are the kind of change that doesn't show up until something's already been running in production for a while. For the general rules around UTC and epoch time this bug intersects with, see the Unix Timestamp & Epoch Time guide.

// frequently asked questions

What changed with DateTimeOffset in .NET 10's Microsoft.Data.Sqlite?

When a stored value carries no explicit offset, GetDateTimeOffset() now assumes UTC instead of the local timezone. Writing a DateTimeOffset to a REAL column is now converted to UTC first instead of written as-is. GetDateTime() with an offset present now returns DateTimeKind.Utc instead of DateTimeKind.Local. All three changed in the same release, silently — same code, same data, no exception.

Why don't I get an error when this SQLite timezone assumption changes?

Nothing throws. Tests that don't assert on the exact offset or DateTimeKind pass unchanged. The bug surfaces downstream instead — a scheduled report running at the wrong hour, an audit log timestamp off by a few hours. If timestamps out of SQLite look shifted by a consistent amount right after a .NET 10 upgrade, that consistent amount is very likely your server's UTC offset.

How do I revert to the old SQLite DateTimeOffset behavior in .NET 10?

AppContext.SetSwitch("Microsoft.Data.Sqlite.Pre10TimeZoneHandling", true), set once at startup before any SQLite connection is opened. This is documented as a temporary compatibility path, not a permanent fix — every day it stays on, new data keeps being written under the old local-time assumption.

// try it yourself

Convert between epoch, UTC, and local time directly with the Unix Timestamp / Epoch Converter to confirm exactly what offset a given stored value should resolve to before and after this change.