XML服务错误且不可预测地解析DateTime字段

时间:2011-04-08 15:26:22

标签: .net web-services xml-serialization

我有一个具有以下属性的实体:

[ScriptIgnore]
public DateTime Date
{
    get { return new DateTime(this.Ticks); }
    set { this.Ticks = value.Ticks; }
}

它设置字段Ticks,这是DateTime存储在数据库中的方式。

问题是,我看到XML带有一个格式如下的Date字段:

<Date>2011-04-08T12:29:00.000Z</Date>

这是DateTime的完美字符串表示。在测试中,此字符串将解析为您期望的DateTime。

但是系统中的日期设置为2011-04-10 12:29,或者恰好是未来两天。

这种情况不一致,不可预测。我可以提交数百份提交,而且所有提交都是正确的。但是当客户在凌晨时分提交它们时,其中一小部分会在未来两天结束日期。这引出了几个目前无法回答的问题:

  1. 将XML反序列化为DateTime时使用了什么构造函数或解析方法?

  2. 客户端在中部时间,我们的服务器在东部时间,(即使所有时间总是转换为GMT)这会产生什么影响吗?

  3. 大多数错误都发生在凌晨,但是无法在剩下的时间内复制,给我,或者这只是一个奇怪的巧合?

  4. 更新

    阅读以下博文后:http://blogs.msdn.com/b/bclteam/archive/2005/03/07/387677.aspx .net DateTime确实存在反序列化问题。它确实使用本地服务器时间,即使它不应该。这并没有解释我的所有问题,但它确实导致我决定停止在XML服务中使用DateTime类型,而是让客户端提交日期时间作为刻度。只要.Net在解析longs时没有失败,我们应该没问题。

    更新2:

    客户反对提交日期时间作为Ticks,因为每个操作系统对Ticks有不同的定义,(客户端使用Java,不确定操作系统)所以我们决定使用字符串。客户端根本没有更改他们提交的XML,但是我们可以手动将字符串解析为日期时间,而不是让解串器执行它。

    我更新了这个属性:

    [ScriptIgnore]
    public string Date
    {
        get { return new DateTime(this.Ticks).ToString(); }
        set
        {
            if (string.IsNullOrEmpty(value))
            {
                this.Ticks = 0;
            }
            else
            {
                    this.Ticks = DateTime.Parse(value).ToUniversalTime().Ticks;
            }
        }
    }
    

    问题仍然存在,今天凌晨1点,客户进口了大约20条记录,其中只有2条记录神秘地向前移动。在查看成功解析日期和非成功解析日期的原始XML之后,我发现格式没有任何区别。

    非常感谢任何帮助。

3 个答案:

答案 0 :(得分:2)

这绝对是一个奇怪的案例。有几件事可以改变解析的结果。

  1. CultureInfo:默认情况下,Parse使用CurrentCulture。如果您的全球化设置中有一些有趣的东西会触发您当前文化的变化,那么这可能会受到影响。
  2. 每个MSDN:'Parse方法尝试解析一个带有几个隐式解析模式的字符串',如果你在其中抛出文化模式,你最终可能会一团糟。
  3. 我的建议:

    1. 使用ParseExact方法而不是Parse并在日期时间字符串上强制执行格式。
    2. 在解析您的日期时间时使用InvariantCulture(如果您的应用程序日期时间格式是通用的,我认为这是因为您存储了刻度)
    3. 在你的二传手上试试这个:

      if (string.IsNullOrEmpty(value))  
      {  
         this.Ticks = 0;  
      }  
      else  
      {  
         CultureInfo provider = CultureInfo.InvariantCulture;  
         var dateTime = DateTime.ParseExact(value, "yyyy-MM-dd hh:mm:ss", provider);  
         this.Ticks = dateTime.ToUniversalTime().Ticks;  
      }
      
      祝你好运!

      编辑1
      我想到了其他一些东西:ThreadSafety。如果您从多个线程访问Ticks的反射对象(在您的setter中执行此操作.Ticks),您应该将其作为原子操作。

      //same setter code except last line...
      var oldTicks = this.Ticks;
      var newTicks = dateTime.ToUniversalTime().Ticks;  
      while(oldTicks != Interlocked.Exchange(ref this.Ticks, newTicks))  
      { //your break code here  }
      

      Sorta :)
      如果有任何一个有助于解开这个谜团,请告诉我。

答案 1 :(得分:2)

我没有看到这样的问题,但是当WebService方法返回DateTime而不是包含<的对象时,确实遇到了从.NET 2.0 SOAP Web服务丢失时区信息的问题/ em> DateTime

根据这一经验,我了解到您应该快速专注于确保您能够隔离并重现问题。我建议你建立一个测试工具。

使用您知道在整个系统中失败的示例DateTime值,模拟发送无限请求的客户端。在调试模式下使用断点运行服务测试工具,以便在收到与检查的值不同的值时暂停执行。使用Fiddler等工具来捕获客户端流量可以提供额外的洞察力。

如果未能解决问题,那么您应该重新评估您的证据,并在确定下一个候选人时再次重复该过程。不要害怕重试一些你认为自己已经测试过或者看过的东西,魔鬼几乎总是在这些细节中。


另一个想法 - 这可能是并发/多线程问题吗?如果您有确凿的证据表明已提交特定值并收到了不同的值,那么您是否希望确保收到的值是由其他客户端发送的 - 或者可以从系统内的其他位置设置?

答案 2 :(得分:1)

您可以通过声明所有传输的日期(在远程系统之间移动)实际上是UTC来解决这些问题,例如:

public DateTime StartDateUtc
{
...
}

public DateTime EndDateUtc
{
...
}

您只需将转换的责任保留在本地时间和从本地时间转移到用户界面代码(或应用程序的最高层)。

请注意,您无法通过约定/文档真正强制执行此技术,并确保属性和方法在语义上标记为“UTC”。坏程序员总是能够将本地日期传递给这些属性:

StartDateUtc = DateTime.UtcNow; // this is correct
StartDateUtc = DateTime.Now; // this is wrong