npgsql / efcore.pg

Entity Framework Core provider for PostgreSQL

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Date/time column type mapping to DateTimeOffset / DateOnly / TimeOnly

zehavibarak opened this issue · comments

Microsoft recommends using the DateTimeOffset rather than the DateTime.

In addition to DateTimeOffset, the DateOnly and TimeOnly structs were introduced in more recent versions of .Net Core.

It would be great to be able to use the Npgsql.NpgsqlCommand.ExecuteReaderAsync() method to get a more accurate CLR/Type when using timestamp with time zone Postgres column type.

Current behavior returns the traditional DateTime, which evolved since introduced back in earlier .Net versions.

Microsoft recommends using the DateTimeOffset rather than the DateTime.

Can you point to that recommendation? I certainly think it should be true for all cases.

It would be great to be able to use the Npgsql.NpgsqlCommand.ExecuteReaderAsync() method to get a more accurate CLR/Type when using timestamp with time zone Postgres column type.

I'm having trouble understanding what exactly you're asking for... A new type to represent PG timestamp with time zone?

Microsoft recommends using the DateTimeOffset](https://learn.microsoft.com/en-us/dotnet/api/system.datetimeoffset?view=net-8.0) rather than the DateTime.

Can you point to that recommendation? I certainly think it should be true for all cases.

See here "consider DateTimeOffset as the default date and time type..."

It would be great to be able to use the Npgsql.NpgsqlCommand.ExecuteReaderAsync() method to get a more accurate CLR/Type when using timestamp with time zone Postgres column type.

I'm having trouble understanding what exactly you're asking for... A new type to represent PG timestamp with time zone?

If we were to call ExecuteReaderAsync() on a table with a timestamp with time zone column, the type of the column returned would yield DateTime - rather than DateTimeOffset as one would expect.

using var conn = new NpgsqlConnection("...");
using cmd = conn.CreateCommand();
cmd.cmd.CommandText = "SELECT datetime1 FROM table1";
conn.Open();
var rdr = await cmd.ExecuteReaderAsync();
while (rdr.Read()) {
  Debug.Write(rdr.GetValue(0).GetType()); // <- this returns DateTime, NOT DateTimeOffset
}
conn.Close();

I haven't tried with DateOnly but assume it too would use CLR DateTime.

See here "consider DateTimeOffset as the default date and time type..."

For representing UTC timestamps, I'm not sure I'd necessarily choose DateTimeOffset with offset 0 over DateTime with Kind=Utc.

But more to the point, when using ADO.NET, it's best practice to use the generic GetFieldValue<T>() instead of GetValue() wherever possible; this is is the case in 99% per of scenarios, where the expected type is known. The exception is when doing dynamic programming, where unknown SQL is being executed against an unknonw schema, and the actual types are unknown. This is one reason why the "default" isn't extremely important for most applications.

In any case, changing the default would be a significant breaking changes, and I definitely don't feel that returning DateTimeOffset is that much more "correct" than DateTime, so as to justify something like this.

  1. My case is generic program which doesn't know the expected type in advance.
  2. Either way, would GetFIeldValue<DateTimeOffset> work or just try to cast an already converted DateTime?
  3. What about Date -> DateOnly, Time -> TimeOnly mapping? would those work?

It's preferable in my view to align with Microsoft recommendations.

Either way, would GetFIeldValue work or just try to cast an already converted DateTime?

Yes, it would work. I'm not sure what difference it would make if it "cast an already converted DateTime".

What about Date -> DateOnly, Time -> TimeOnly mapping? would those work?

Yes - it's really easy to just try this and see.

I'm going to go ahead and close this as we won't be changing the default, and the other questions have been answered. But if you have any other questions, feel free to post back here.