NHibernate продолжает терять часовой пояс моего DateTime в PostgreSQL 9.0 - PullRequest
3 голосов
/ 19 сентября 2011

Мне нужно убедить 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?

1 Ответ

3 голосов
/ 21 сентября 2011

Похоже, что в драйвере NpgSQL есть ошибка:

Назначение ((IDbDataParameter) statement.Parameters[index]).Value = dateTime.ToUniversalTime(); вызывает set для свойства Value.Это преобразует DateTime в NpgsqlTimeStamp и сохраняет его, затем преобразует это обратно в System.DatetTime и сохраняет это значение.

Проблема в том, что он использует NpgsqlTimeStamp, а не NpgsqlTimeStampTZ, поэтому он теряет информацию о часовом поясе.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...