When PostgreSQL Rejects Your DateTime: The Real Reason DateTime.Now Works but DateTime.UtcNow Fails
If you’re using .NET with PostgreSQL through Entity Framework Core, you’ve probably hit this cryptic Npgsql exception at least once:
When PostgreSQL Rejects Your DateTime: The Real Reason DateTime.Now Works but DateTime.UtcNow Fails
If you’re using .NET with PostgreSQL through Entity Framework Core, you’ve probably hit this cryptic Npgsql exception at least once:
Cannot write DateTime with Kind=UTC to PostgreSQL type 'timestamp without time zone'
It looks random. It appears during an insert. And the strangest part?
**DateTime.UtcNow fails.
DateTime.Now works.**
At first glance, that feels backwards. UTC is supposed to be the safe standard, right?
But the real reason has nothing to do with “correctness” and everything to do with PostgreSQL column types — especially timestamp without time zone.
Let’s break down the hidden rule developers miss.
The Invisible Battle: .NET DateTime Kind vs PostgreSQL Timestamp Type
A DateTime in .NET isn’t just a date. It carries a hidden property:
DateTime.Kind = Utc | Local | Unspecified
PostgreSQL, on the other hand, has two main timestamp types:
timestamp without time zone(plain value, no timezone)timestamp with time zone(stored as UTC internally)
Now the key part:
Npgsql will not let you insert a UTC DateTime into a timestamp without time zone.
Why?
Because that would silently lose timezone information.
So Npgsql throws the exception instead of guessing or converting.
Then, Why Does DateTime.Now Work?
DateTime.Now is:
Kind = Local
And Npgsql treats Local like a normal value — because Local is already timezone-bound and doesn’t explicitly claim to be UTC.
So the flow looks like this:
DateTime.UtcNow→ “I am explicitly UTC” → rejectedDateTime.Now→ “I am local time, insert as-is” → acceptedDateTime.SpecifyKind(date, Unspecified)→ “I have no timezone meaning” → accepted
It isn’t that DateTime.Now is “correct”.
It’s simply not UTC, so Npgsql doesn’t stop it.
Why This Can Become a Silent Bug Later
There are several ways UTC timestamps can sneak back in:
- JSON serializers
- API clients
- Mobile apps
- JS Date objects (usually UTC)
- Background jobs
- External libraries
When even one UTC timestamp touches a column mapped as timestamp without time zone, the same exception returns — sometimes months later.
That’s why people think the error is random.
It’s not random. It’s a DateTimeKind mismatch waiting to explode.
Should You Really Use DateTime?Now?
It depends on your project:
When it’s fine
If everything in your system runs in the same timezone, and you don’t care about UTC consistency.
When it’s dangerous
If your project involves:
- distributed services
- different client time zones
- Logs that require accuracy
- audit trails
- scheduled jobs
Then mixing Local + UTC timestamps becomes a long-term headache you don’t want.
The Real Fix (Choose Based on Your Needs)
1. Use timestamp with time zone + DateTime.UtcNow
This is the most predictable and consistent setup.
2. Use DateTimeOffset instead of DateTime
This avoids nearly all timezone headaches.
3. Keep your DB as a timestamp without time zone, but normalize
Convert before saving:
DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Unspecified)
This makes your timestamps compatible, but you lose timezone semantics.
Quick Recap
- Npgsql rejects UTC DateTime when saving into a timestamp without time zone.
DateTime.Nowworks because it’s Kind = Local, not UTC.- The error isn’t random — it’s a mismatch between .NET DateTimeKind and PostgreSQL timestamp type.
- Long-term safety: prefer DateTimeOffset or timestamptz.
- If you rely on local time, stay consistent — or unexpected UTC values will break inserts again.
👉 Follow me if you’re a dev still connecting the dots. I write to make tech simpler — because I need it too. 💬 If there’s another confusing tech topic you’d like explained, drop it in the comments. It helps me — and everyone — learn better.
메타데이터
- post_id
- d3bb6cd9e4cc
- slug
- when-postgresql-rejects-your-datetime-the-real-reason-datetime-now-works-but-datetime-utcnow-fails-d3bb6cd9e4cc
- url
- https://medium.com/@sweetondonie/when-postgresql-rejects-your-datetime-the-real-reason-datetime-now-works-but-datetime-utcnow-fails-d3bb6cd9e4cc
- canonical_url
- https://medium.com/@sweetondonie/when-postgresql-rejects-your-datetime-the-real-reason-datetime-now-works-but-datetime-utcnow-fails-d3bb6cd9e4cc
- author_url
- https://medium.com/@sweetondonie
- status
- ok
- fetched_at
- 2026-07-14 03:47:51