Get DateTime as UTC with Dapper
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
UTC handling with Dapper is less about Dapper itself and more about database type semantics plus application conventions. Many bugs come from reading DateTime values with unspecified kind and then converting incorrectly on the assumption that the clock value itself is wrong. The safest approach is to standardize storage and mapping rules across write and read paths so every timestamp has a clear meaning.
Understand Database Type Behavior
Different SQL types preserve different timezone information.
Common SQL Server choices:
datetimeanddatetime2store date and time but no timezone offset.datetimeoffsetstores date, time, and offset.
When you read datetime or datetime2 into C#, DateTime.Kind is often Unspecified. If your team assumes stored values are UTC, you must mark them as UTC after reading or prefer DateTimeOffset mapping.
Write UTC Values Consistently
Always generate timestamps using UTC at write time.
Do not mix local time writes with UTC reads. Mixed semantics are the main cause of drift.
Read as DateTime and Normalize Kind
If the column is UTC but stored in datetime2, explicitly set kind after retrieval.
This does not change clock value, only kind metadata, which is what you want for UTC stored values.
That distinction matters. SpecifyKind is correct only when the stored value already represents UTC and merely lacks metadata. If the database actually contains local time, SpecifyKind would silently label the wrong moment as UTC.
Prefer DateTimeOffset for Fewer Ambiguities
DateTimeOffset preserves offset context and often avoids accidental kind mistakes.
If you control schema design, datetimeoffset plus DateTimeOffset mapping is usually easier to reason about over time.
Add a Dapper Type Handler for Centralized Policy
For larger codebases, a type handler can enforce UTC normalization consistently.
Register at startup:
Centralizing the rule reduces per query mistakes.
Test Round Trip Behavior
Write tests that insert known UTC values, read them back, and compare using round trip format.
Also test serialization boundaries if values are sent through JSON APIs, because timezone handling can shift there too.
API Serialization Boundaries
Even with perfect Dapper mapping, UTC bugs can appear when serializing to JSON and deserializing on clients. Standardize output format using ISO round trip strings and verify clients treat them as UTC.
If APIs expose both local and UTC forms, name fields explicitly such as createdAtUtc and createdAtLocal to prevent consumer confusion.
Clear naming helps at the database level too. A column named CreatedAtUtc communicates intent better than CreatedAt, and that alone prevents many incorrect conversions during maintenance work.
Common Pitfalls
- Storing local times while naming columns as UTC.
- Assuming
DateTime.Kindis UTC when readingdatetime2values. - Mixing
DateTimeandDateTimeOffsetconventions without clear policy. - Converting unspecified values with wrong assumptions about source timezone.
- Handling UTC conversion ad hoc in many repositories instead of centralized logic.
Summary
- Decide and document a strict UTC policy for storage and retrieval.
- Use UTC at write time and normalize kind semantics at read time.
- Prefer
DateTimeOffsetwhen schema and API design allow it. - Consider Dapper type handlers to enforce rules centrally.
- Validate with round trip tests to catch timezone drift early.

