Search code examples
.netnhibernatepostgresqlfluent-nhibernatenpgsql

NHibernate Postgresql DateTime to Time Conversion


I'm using Fluent NHibernate to configure my mappings everything works when reading data but when trying to insert or update records that have a Postgresql type of Time I get the error message

"ERROR: 42804: column \"run_time\" is of type time with time zone but expression is of type timestamp without time zone"

It looks like NHibernate might be confused on which DbType to convert the DateTime to as you can see from PostgreSQL and C# Datatypes that there are several DbTypes that DateTime map to.

I also tried specifying a custom type for this column from IUserType but on the NullSafeSet override I get a NullReferenceError

public override void NullSafeSet(IDbCommand cmd, object value, int index)
{
    var obj = (DateTime)value;

    NHibernate.NHibernateUtil.Time.NullSafeSet(cmd, obj, index);
}

Is there some type of hint I can provide to either tell NHibernate to convert the DateTime to a Postgresql "time" type? Or can I achieve this by the IUserType and I'm just doing something wrong?


Solution

  • Figured it out!

    Apparently the conversion sheet I linked to is either wrong or out of date. It turns out that a System.TimeSpan object is what's needed for Npgsql to do a proper conversion to a Postgresql "time" object. It seems odd to me that they would try to convert something that represents a difference between two time's into what we think of as HH:mm:ss but that's the way it is.

    Rather than change the type of my RunTime property from System.DateTime to System.TimeSpan I've instead created a custom IUserType and have overriden NullSafeSet to look like

    public override void NullSafeSet(IDbCommand cmd, object value, int index)
    {
        var obj = (DateTime)value;
    
        ((IDbDataParameter) cmd.Parameters[index]).Value = new TimeSpan(0, obj.Hour, obj.Minute, obj.Second);
    }