datetime-and-time-handlinglisted
Install: claude install-skill Sarmkadan/dotnet-senior-skills
# DateTime and Time Handling
## DateTimeOffset by default
`DateTime` carries a `Kind` flag that nothing enforces: a `Kind.Unspecified` value round-tripped through JSON, a database, or `ToLocalTime()` silently reinterprets the same ticks as a different instant. `DateTimeOffset` carries the offset in the value - comparisons and serialization are unambiguous.
```csharp
// WRONG: is this UTC? Local? Depends on who wrote it and which driver read it back.
public DateTime CreatedAt { get; set; }
// RIGHT
public DateTimeOffset CreatedAt { get; set; }
```
Decision table:
- **Instants** (created-at, expires-at, audit, logs, tokens): `DateTimeOffset`, stored as UTC. This is 90% of fields.
- **Calendar dates** (birthday, invoice date, holiday): `DateOnly`. A birthday has no timezone; storing it as midnight `DateTime` shifts it a day for half the planet.
- **Wall-clock times** (store opening hours): `TimeOnly` plus a timezone id stored separately.
- **Future local events** (a meeting at "10:00 Sofia time" next March): store local time + IANA timezone id, convert at read time. Pre-converting to UTC bakes in today's offset rules; a DST law change makes the stored instant wrong.
Review flag: `DateTime.Now` anywhere in server code. Server-local time depends on the box's timezone; two instances in different regions disagree. `DateTime.UtcNow` is acceptable in legacy code; new code uses `DateTimeOffset.UtcNow` - via `TimeProvider` (below).
## TimeProvider: the clock is a dependency
Any l