← Back to list

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:

Donie Sweeton · 2025-12-17 03:02 · 4 claps · 2.4 min read
#dotnet #postgresql #npgsql #backend-development #software-engineering
Open on Medium ↗
Wiki topics: 🌐 · Web Development

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” → rejected
  • DateTime.Now → “I am local time, insert as-is” → accepted
  • DateTime.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.Now works 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