Split out of #398 / #402.
Repro
await using var conn = new NpgsqlConnection(cs);
await conn.OpenAsync();
await using var cmd = new NpgsqlCommand("select @n", conn);
cmd.AddNamedParameter("n", DateTime.UtcNow);
await cmd.ExecuteScalarAsync();
ArgumentException: Cannot write DateTime with Kind=UTC to PostgreSQL type
'timestamp without time zone', consider using 'timestamp with time zone'.
(Parameter 'value')
Verified against a live Postgres. DateTime.Now (Kind=Local) works; DateTime.UtcNow does not.
Cause
DatabaseProvider.AddNamedParameter types the parameter from the value's CLR type when the caller supplies no explicit type:
else if (value != null)
{
SetParameterType(parameter, ToParameterType(value.GetType()));
}
PostgresqlProvider maps typeof(DateTime) to NpgsqlDbType.Timestamp unconditionally. But Postgres' choice depends on the value, not the type: Npgsql resolves Kind=Utc to timestamptz and Kind=Local/Unspecified to timestamp. A per-type mapping cannot express that, so every UTC DateTime gets the wrong type.
| value |
Npgsql resolves |
Weasel stamps |
DateTime.UtcNow |
TimestampTz |
Timestamp ❌ |
DateTime.Now |
Timestamp |
Timestamp ✅ |
DateTime is the only divergence across the common CLR types — string, char, int, long, short, double, float, decimal, bool, Guid, DateTimeOffset, TimeSpan, DateOnly, TimeOnly, byte[], string[], int[], List<int>, IPAddress all agree.
The sibling AddParameter(cmd, DateTime.UtcNow) works, because it stamps nothing and lets Npgsql resolve per value. See the companion issue on that asymmetry.
Likely also affects DateTime[] / IEnumerable<DateTime> parameters via the array branch.
Split out of #398 / #402.
Repro
Verified against a live Postgres.
DateTime.Now(Kind=Local) works;DateTime.UtcNowdoes not.Cause
DatabaseProvider.AddNamedParametertypes the parameter from the value's CLR type when the caller supplies no explicit type:PostgresqlProvidermapstypeof(DateTime)toNpgsqlDbType.Timestampunconditionally. But Postgres' choice depends on the value, not the type: Npgsql resolvesKind=UtctotimestamptzandKind=Local/Unspecifiedtotimestamp. A per-type mapping cannot express that, so every UTCDateTimegets the wrong type.DateTime.UtcNowTimestampTzTimestamp❌DateTime.NowTimestampTimestamp✅DateTimeis the only divergence across the common CLR types —string,char,int,long,short,double,float,decimal,bool,Guid,DateTimeOffset,TimeSpan,DateOnly,TimeOnly,byte[],string[],int[],List<int>,IPAddressall agree.The sibling
AddParameter(cmd, DateTime.UtcNow)works, because it stamps nothing and lets Npgsql resolve per value. See the companion issue on that asymmetry.Likely also affects
DateTime[]/IEnumerable<DateTime>parameters via the array branch.