gpt4 book ai didi

c# - 使用 NHibernate 保存和加载 Utc DateTime

转载 作者:行者123 更新时间:2023-12-02 06:40:10 27 4
gpt4 key购买 nike

我在将 DateTime 保存到 SQL Lite 数据库时遇到问题。 (也许也适用于 MS SQL)

我想用 NHibernate 将 UTC 时间的 DateTime 保存到数据库并从数据库中加载它。我们使用UTC时间在hole应用程序中工作,当我们在ui上显示时间时,我们将其更改为本地时间。

我阅读了很多关于 DateTime 和 NHibernate 的内容:

  • http://jameskovacs.com/2011/01/26/datetime-support-in-nhibernate/
  • http://www.milkcarton.com/blog/2007/01/19/NHibernate+DateTime+And+UTC.aspx
  • Rehydrating fluent nhibernate configured DateTime as Kind Utc rather than Unspecified
  • https://nhibernate.jira.com/browse/NH-2520

  • 但没有任何作用。
    一些例子:

    更新前 : 在保存实体之前。

    已保存 : 是用 nhibernate (repo.save(o);) 保存后保存的对象。

    已加载 :当我通过 id 从存储库加载实体时。
    // The next 3 examples are with:
    o.Created = DateTime.UtcNow;

    映射类型: CustomType<TimestampType>()
    UtcTime: 16:44... LocalTime: 18:44
  • 更新前:2015-03-30T16:44:35.7636679Z 勾选:635633306757636679 种类:UTC
  • 已保存:2015-03-30T16:44:35.7636679Z 勾选:635633306757636679 种类:UTC
  • 已加载:2015-03-30T18:44:35.7636679 勾选:635633378757636679 种类:未指定

  • 这里的问题是,当我通过 id 重新加载对象时,新对象的时间是 18...(+2h)而不是 16.... 并且 DateTime 类型是未指定的。

    映射类型: CustomType<DateTimeType>()
    UtcTime: 16:49... LocalTime: 18:49
  • 更新前:2015-03-30T16:49:00.2754289Z 勾选:635633309402754289 种类:UTC
  • 保存时间:2015-03-30T16:49:00.2754289Z 勾选:635633309402754289 种类:UTC
  • 已加载:2015-03-30T16:49:00.0000000 勾选:635633309400000000 种类:未指定

  • 使用此解决方案,我取消了毫秒,并且 DateTime 类型也未指定。

    映射类型: CustomType<UtcDateTimeType>()
    UtcTime: 17:01... LocalTime: 19:01
  • 更新前:2015-03-30T17:01:32.9663859Z 勾选:635633316929663859 种类:UTC
  • 已保存:2015-03-30T17:01:32.9663859Z 勾选:635633316929663859 种类:UTC
  • 已加载:2015-03-30T19:01:32.0000000Z 勾选:635633388920000000 种类:UTC

  • 使用这个解决方案,我失去了毫秒,DateTime 类型是 utc 但它是错误的时间,应该是 17:01....

    所以另一个想法是在应用程序中只使用 DateTime.Now 并将 utc 时间保存在数据库中。一些例子:
    // The next 3 examples are with:
    o.Created = DateTime.Now;

    映射类型: CustomType<TimestampType>()
    UtcTime: 17:21... LocalTime: 19:21
  • 更新前:2015-03-30T19:21:44.7938077+02:00 勾选:635633401047938077 种类:本地
  • 已保存:2015-03-30T19:21:44.7938077+02:00 勾选:635633401047938077 种类:本地
  • 已加载:2015-03-30T19:21:44.7938077 勾选:635633401047938077 种类:未指定

  • 使用此解决方案,我有毫秒,DateTime 类型未指定,加载时间不是 utc。

    映射类型: CustomType<DateTimeType>()
    UtcTime: 17:19... LocalTime: 19:19
  • 更新前:2015-03-30T19:19:27.3114047+02:00 勾选:635633399673114047 种类:本地
  • 已保存:2015-03-30T19:19:27.3114047+02:00 勾选:635633399673114047 种类:本地
  • 已加载:2015-03-30T19:19:27.0000000 勾选:635633399670000000 种类:未指定

  • 使用此解决方案,我减少了毫秒数,DateTime 类型也未指定,加载时间不是 utc。

    映射类型: CustomType<UtcDateTimeType>()
    UtcTime: 17:14... LocalTime: 19:14
  • 更新前:2015-03-30T19:14:31.3030033+02:00 勾选:635633396713030033 种类:本地
  • 保存时间:2015-03-30T19:14:31.3030033+02:00 勾选:635633396713030033 种类:本地
  • 已加载:2015-03-30T21:14:31.0000000Z 勾选:635633468710000000 种类:UTC

  • 使用这个解决方案,我失去了毫秒,DateTime 类型是 utc 但它是错误的时间,应该是 17:14....

    所以我有一些问题:
  • 为什么 NHibernate 加载本地时间但类型为 utc(UtcDateTimeType 和 o.Created=DateTime.UtcNow)
  • 在hole应用程序和UI localtime中使用utc还是在任何地方使用localtime并在数据库中节省时间utc更好。

  • 我还创建了一个自己的映射:
    namespace Persistence.Common.NHibernate
    {
    using System;
    using System.Data;

    using global::NHibernate.Engine;
    using global::NHibernate.Type;

    /// <summary>
    /// This is almost the exact same type as the DateTime except it can be used
    /// in the version column, stores it to the accuracy the database supports,
    /// and will default to the value of DateTime.Now if the value is null.
    /// </summary>
    /// <remarks>
    /// <p>
    /// The value stored in the database depends on what your data provider is capable
    /// of storing. So there is a possibility that the DateTime you save will not be
    /// the same DateTime you get back when you check DateTime.Equals(DateTime) because
    /// they will have their milliseconds off.
    /// </p>
    /// <p>
    /// For example - SQL Server 2000 is only accurate to 3.33 milliseconds. So if
    /// NHibernate writes a value of <c>01/01/98 23:59:59.995</c> to the Prepared Command, MsSql
    /// will store it as <c>1998-01-01 23:59:59.997</c>.
    /// </p>
    /// <p>
    /// Please review the documentation of your Database server.
    /// </p>
    /// </remarks>
    [Serializable]
    public class CustomUtcTimestampType : TimestampType
    {
    public CustomUtcTimestampType()
    {
    }

    public override object Get(IDataReader rs, int index)
    {
    return Convert.ToDateTime(rs[index]).ToLocalTime();
    }

    /// <summary>
    /// Sets the value of this Type in the IDbCommand.
    /// </summary>
    /// <param name="st">The IDbCommand to add the Type's value to.</param>
    /// <param name="value">The value of the Type.</param>
    /// <param name="index">The index of the IDataParameter in the IDbCommand.</param>
    /// <remarks>
    /// No null values will be written to the IDbCommand for this Type.
    /// </remarks>
    public override void Set(IDbCommand st, object value, int index)
    {
    DateTime dateTime = (DateTime)((value is DateTime) ? value : DateTime.UtcNow);
    dateTime = DateTime.SpecifyKind(dateTime.ToUniversalTime(), DateTimeKind.Unspecified);
    ((IDataParameter)st.Parameters[index]).Value = dateTime;
    }

    public override string Name
    {
    get { return "CustomUtcTimestamp"; }
    }

    public override object FromStringValue(string xml)
    {
    return DateTime.Parse(xml);
    }

    #region IVersionType Members

    public override object Seed(ISessionImplementor session)
    {
    if (session == null)
    {
    return DateTime.UtcNow;
    }
    return Round(DateTime.UtcNow, session.Factory.Dialect.TimestampResolutionInTicks);
    }

    #endregion

    public object StringToObject(string xml)
    {
    return DateTime.Parse(xml);
    }

    public override string ObjectToSQLString(object value, global::NHibernate.Dialect.Dialect dialect)
    {
    return '\'' + value.ToString() + '\'';
    }
    }
    }

    最佳答案

  • 将日期时间加载为 UTC 的行为,我相信是使用类型 UtcDateTimeType 时的预期行为是假设时间设置在 UTC 中,并且在获取时也将其视为 UTC,

  • 如果您进行以下快速测试,
        public class UtcTime
    {
    public virtual long Id { get; set; }
    public virtual DateTime DateSaved { get; set; }
    }

    public class UtcTimeClassMap : ClassMap<UtcTime>
    {
    public UtcTimeClassMap()
    {
    Id(t => t.Id).GeneratedBy.Native();
    Map(t => t.DateSaved ).CustomType<UtcDateTimeType>();
    }
    }

    [Test]
    public void SimpleTest()
    {
    long id = 0;
    ISession session = _sessionFactory.OpenSession();
    using (ITransaction tran = session.BeginTransaction())
    {
    UtcTime utc = new UtcTime();
    utc.DateSaved = DateTime.Now;
    session.Save(utc);
    tran.Commit();

    Console.WriteLine(utc.DateSaved.Ticks + "_" + utc.DateSaved.Kind + "_" + utc.Date.ToString());

    id = utc.Id;
    }
    session.Flush();
    session.Clear();

    session = _sessionFactory.OpenSession();
    var retrieved = session.Get<UtcTime>(id);
    Console.WriteLine(retrieved.DateSaved.Ticks + "_" + retrieved.Date.Kind + "_" + retrieved.DateSaved.ToString());
    }

    输出
    INSERT INTO [UtcTime] (DateSaved) VALUES (?); select SCOPE_IDENTITY()
    635634005813892469_Local_31/03/2015 12:09:41 PM
    SELECT utctime0_.Id as Id3_0_, utctime0_.DateSaved as Date3_0_ FROM [UtcTime] utctime0_ WHERE utctime0_.Id=?
    635634005810000000_Utc_31/03/2015 12:09:41 PM

    即使我将 12:09:41 坚持为本地,当我取回它时它是 UTC,并且与它假设一切都发生在 UTC 的时间相同。此测试是使用 SQLServer 数据库完成的。

    如果我用 SQLite 重复相同的测试,输出是,
    INSERT INTO "UtcTime" (DateSaved) VALUES (?); select last_insert_rowid()
    635634005197863939_Local_31/03/2015 12:08:39 PM
    SELECT utctime0_.Id as Id3_0_, utctime0_.DateSaved as Date3_0_ FROM "UtcTime" utctime0_ WHERE utctime0_.Id=?
    635634401190000000_Utc_31/03/2015 11:08:39 PM

    我可以在这里看到一小时无法解释的差异(因为进行测试的本地时间和 UTC 之间的差异是 11 小时,这并不能解释。
    我能想到的唯一解释是 SQLLite 方言中可能存在某种错误。
  • 我认为保存 UTC 将是更好的选择,尤其是当用户在时区范围内时。但是,如果用户处于同一时区,则转换没有意义,因为它很难诊断错误,尤其是查询数据库,因为您必须始终转换值。

  • 作为精确解决此问题的方法,您可以添加这样的自定义类型,
    public class UTCTimeStampType : TimestampType
    {
    public override object Get(IDataReader rs, int index)
    {
    return ConvertToUtc(base.Get(rs, index));
    }

    public override object Get(IDataReader rs, string name)
    {
    return ConvertToUtc(base.Get(rs, name));
    }

    public override object FromStringValue(string xml)
    {
    return ConvertToUtc(base.FromStringValue(xml));
    }

    private DateTime ConvertToUtc(object value)
    {
    var dateTime = (DateTime) value;
    return new DateTime(dateTime.Ticks).ToUniversalTime();
    }
    }

    关于c# - 使用 NHibernate 保存和加载 Utc DateTime,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29352719/

    27 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com