Мне нужно убедить NHibernate сохранить часовой пояс значений DateTime
, которые я сохраняю. Столбцы timestamp with time zone
.
У нас есть соглашение о том, что мы храним только дату и время UTC в базе данных, поэтому мы использовали timestamp without time zone
, но мне нужно было, чтобы база данных знала часовой пояс, чтобы отчет, который напрямую обращается к базе данных, мог работать правильно.
Я пытался сказать NHibernate, что у меня есть пользовательский тип для DateTime
следующим образом:
[Serializable]
public class UtcDateTimeUserType<T> : IUserType
{
private static readonly SqlType[] MySqlTypes = {SqlTypeFactory.DateTime };
public SqlType[] SqlTypes
{
get { return MySqlTypes; }
}
public System.Type ReturnedType
{
get { return typeof(T); }
}
public bool IsMutable
{
get { return false; }
}
public object DeepCopy(object value)
{
return value; // DateTime is immutable
}
public new bool Equals(object x, object y)
{
return object.Equals(x, y);
}
public object NullSafeGet(IDataReader resultSet,
string[] names,
object owner)
{
int index0 = resultSet.GetOrdinal(names[0]);
if (resultSet.IsDBNull(index0))
{
return null;
}
var value = resultSet.GetDateTime(index0);
return DateTime.SpecifyKind(value, DateTimeKind.Utc);
}
public void NullSafeSet(IDbCommand statement,
object value,
int index)
{
if (value == null)
{
((IDbDataParameter)statement.Parameters[index]).Value = DBNull.Value;
}
else
{
var dateTime = (DateTime)value;
if (dateTime == DateTime.MinValue) dateTime = DateTime.SpecifyKind(dateTime, DateTimeKind.Utc);
if (dateTime.Kind == DateTimeKind.Unspecified)
throw new InvalidOperationException(
"Attempting to save a datetime without a Kind set.. what time zone are you in?");
((IDbDataParameter) statement.Parameters[index]).Value = dateTime.ToUniversalTime();
}
}
public object Disassemble(object value)
{
return value;
}
public object Assemble(object cached, Object owner)
{
return cached;
}
public object Replace(object original, object target, object owner)
{
return original;
}
public int GetHashCode(Object x)
{
return x.GetHashCode();
}
}
public class NullableUtcDateTimeUserTypeConvention
: UserTypeConvention<UtcDateTimeUserType<DateTime?>>
{
}
public class UtcDateTimeUserTypeConvention
: UserTypeConvention<UtcDateTimeUserType<DateTime>>
{
}
Свободный NHibernateConfiguration
autoPersistenceModel = autoPersistenceModel.Conventions.Setup(s =>
{
s.Add<TableNameConvention>();
s.Add<PrimaryKeyConvention>();
s.Add<CustomForeignKeyConvention>();
s.Add<CascadeConvention>();
s.Add<CalculatedFieldPropertyConvention>();
s.Add<UtcDateTimeUserTypeConvention>();
s.Add<NullableUtcDateTimeUserTypeConvention>();
s.AddAssembly(assembly);
});
Это ловит код, пытающийся сохранить дату и время без установленного Часового пояса. Он также гарантирует, что все DateTime
UTC перед входом в базу данных, и правильно устанавливает их в UTC на обратном пути.
Но следующее утверждение не работает:
((IDbDataParameter) statement.Parameters[index]).Value = dateTime.ToUniversalTime();
Это все еще Unspecified
после назначения:
((DateTime)((IDbDataParameter) statement.Parameters[index]).Value).Kind
Из кода NHibernate я замечаю, что PostgresDialect имеет следующее:
RegisterColumnType(DbType.DateTime, "timestamp");
но ничего не упоминает timestamp with time zone
или timestamptz
.
Как сделать так, чтобы NHibernate сохранял DateTimes с их часовым поясом в Postgres9?