Skip to content

fix #1340: DateTime fields error in npgsql#1353

Merged
aharwood2 merged 7 commits into
devfrom
1340-timezone-pgsql-fix
Jun 11, 2026
Merged

fix #1340: DateTime fields error in npgsql#1353
aharwood2 merged 7 commits into
devfrom
1340-timezone-pgsql-fix

Conversation

@sjmf

@sjmf sjmf commented May 30, 2026

Copy link
Copy Markdown
Contributor

Fixes #1340. Resolves errors saving DateTime fields, and a further crash on startup introduced by the original fix when running with DB_PROVIDER=postgresql caused by Npgsql 6+'s strict DateTime / timestamp type enforcement.

PPMToolContext.cs

Added code to OnModelCreating that maps all DateTime and DateTime? properties to timestamp without time zone only when the PostgreSQL provider is active. This tells Npgsql to treat all date/time values as local/unspecified rather than requiring UTC.

Without the OnModelCreating block, EF Core defaults to mapping DateTime -> timestamptz on PostgreSQL. The DB columns are now timestamp without time zone after the migration, but EF Core would still send timestamptz parameters for every query, and PostgreSQL would reject every comparison. The error raised was:

Cannot apply binary operation on types 'timestamp with time zone' and 'timestamp without time zone', convert one of the operands first.

Migration 20260527120344_FixDateTimeTimezone

Generated the migration for Npgsql, which converts 20 existing timestamptz columns to timestamp without time zone.

This didn't work out of the box. I had to re-write the Up() to use raw SQL with a USING col AT TIME ZONE 'UTC' clause. The generated AlterColumn omitted this clause and PostgreSQL rejects the cast without it.

Up() was originally generated with:

migrationBuilder.AlterColumn<DateTime>(
    name: "StartDate",
    table: "Projects",
    type: "timestamp without time zone",
    ...);

... which EF Core was translating to ALTER TABLE "Projects" ALTER COLUMN "StartDate" TYPE timestamp without time zone;. But PostgreSQL requires a USING clause whenever the cast between the old and new type isn't implicit, which timestamptz -> timestamp is not. PostgreSQL didn't know which timezone to use to strip the offset. Without USING, it threw:

ERROR: column "StartDate" cannot be cast automatically to type timestamp without time zone
HINT: You might need to specify "USING StartDate::timestamp without time zone"

I added USING "StartDate" AT TIME ZONE 'UTC' to fix this, though this doesn't quite match the hint, which as-written would just cast without timezone conversion!

InnateCodeService.cs

Replaced DateTime.UtcNow with DateTime.Today. DateTime.UtcNow has Kind=Utc, which Npgsql maps to timestamptz, producing a type mismatch against the updated timestamp EndDate column. DateTime.Today is correct here since EndDate is just storing a regular date (where timezone precision is meaningless) in a timestamp field.

ApiKeyConfigComponent.razor

Wrapped two DateTime.UtcNow.AddDays(n) calls with DateTime.SpecifyKind(..., DateTimeKind.Unspecified). Without this, saving an API key crashed because Npgsql rejects storing a Kind=Utc value into a timestamp without time zone column. See the Npgsql datetime documentation:

"Starting with 6.0, Npgsql maps UTC DateTime to timestamp with time zone, and Local/Unspecified DateTime
to timestamp without time zone; trying to send a non-UTC DateTime as timestamptz will throw an exception, etc."
The inverse also applies: trying to send a UTC DateTime to a timestamp without time zone column throws.

Program.cs

Changed Migrate() to MigrateAsync(). On Npgsql 10, the synchronous overload throws an InvalidCastException internally which prints non-fatal fail: Program[0] logs, and the migration never runs.

See the Npgsql 10.0 release notes and npgsql/npgsql#5865 for the stated direction away from synchronous APIs. Confirmed safe on SQLite, which wraps sync operations in the async API as documented behaviour:

Testing done

  • Run with DB_PROVIDER=sqlite: migrations apply, app starts, seeding works, no regressions that I can see!
  • Run with DB_PROVIDER=postgresql: migrations apply, Projects and People pages now load without timezone errors, API key creation saves without error.

sjmf added 3 commits May 27, 2026 13:22
…ross database schema (towards #1340)

- Added migration to alter timestamp columns in various tables to 'timestamp without time zone' to ensure proper handling of date and time values.
- Updated model snapshot to reflect changes in column types for consistency.
…ecks and `SpecifyKind` for API key expiration (towards #1340)
@sjmf sjmf linked an issue May 30, 2026 that may be closed by this pull request
@github-actions

This comment has been minimized.

@sjmf sjmf requested review from PhilBradbury and aharwood2 May 30, 2026 01:42
sjmf added 2 commits May 30, 2026 02:44
@sjmf sjmf added the database 🔥 Relates to the DB itself or modification of the schema label May 30, 2026
@sjmf sjmf added this to the v1.14.1 milestone May 30, 2026
@sjmf sjmf changed the title 1340 timezone pgsql fix fix #1340: DateTime fields in npgsql Jun 2, 2026
@sjmf sjmf changed the title fix #1340: DateTime fields in npgsql fix #1340: DateTime fields error in npgsql Jun 2, 2026
@sjmf sjmf changed the title fix #1340: DateTime fields error in npgsql fix #1340: DateTime fields error in npgsql Jun 2, 2026
@sjmf sjmf requested review from gdonval and philiptharrison June 9, 2026 10:01
@aharwood2 aharwood2 merged commit 6e85917 into dev Jun 11, 2026
12 checks passed
@aharwood2 aharwood2 deleted the 1340-timezone-pgsql-fix branch June 11, 2026 14:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

database 🔥 Relates to the DB itself or modification of the schema

Projects

None yet

Development

Successfully merging this pull request may close these issues.

DateTime with Kind=Unspecified crashes SaveChanges on PostgreSQL

2 participants