DateTime和xsd:date的往返XML序列化?

les*_*ode 12 .net datetime xml-serialization

好的,我在这里错过了什么?MSDN对DateTimeSerializationMode说以下内容:

在.Net Framework的2.0及更高版本中,将此属性设置为RoundtripDateTime,以检查它们是在本地,UTC还是未指定的时区,并以保持此信息的方式进行序列化.这是默认行为,建议用于所有不与旧版本框架通信的新应用程序.

然而:

namespace ConsoleApplication1 {
    public class DateSerTest {
        [XmlElement(DataType = "date")]
        public DateTime Date { get; set; }
    }

    class Program {
        static void Main(string[] args) {
            DateSerTest d = new DateSerTest { 
                Date = DateTime.SpecifyKind(new DateTime(2009,8,18), DateTimeKind.Utc),
            };
            XmlSerializer ser = new XmlSerializer(typeof(DateSerTest));
            using (FileStream fs = new FileStream("out.xml", FileMode.Create)) {
                ser.Serialize(fs, d);
            }

            // out.xml will contain:
            // <Date>2009-08-18</Date>

            using (FileStream fs = new FileStream("out.xml", FileMode.Open)) {
                DateSerTest d1 = (DateSerTest) ser.Deserialize(fs);
                Console.WriteLine(d1.Date); // yields: 8/18/2009 12:00:00 AM
                Console.WriteLine(d1.Date.Kind); // yields: Unspecified
            }

            // in.xml:
            // <DateSerTest>
            //     <Date>2009-08-18Z</Date>
            // </DateSerTest>

            using (FileStream fs = new FileStream("in.xml", FileMode.Open)) {
                DateSerTest d1 = (DateSerTest) ser.Deserialize(fs);
                Console.WriteLine(d1.Date); // yields: 8/17/2009 8:00:00 PM
                Console.WriteLine(d1.Date.Kind); // yields: Local
                using (FileStream fs1 = new FileStream("out2.xml", FileMode.Create)) {
                    ser.Serialize(fs1, d1);

                    // out2.xml will contain:
                    // <Date>2009-08-17</Date>
                }
            }
            Console.ReadKey();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

因此,对于定义为"date"而不是"dateTime"的XSD元素,日期不会序列化为UTC.这是一个问题,因为如果我反序列化这个XML,结果日期将是未指定的类型,以及任何到UTC的转换(实际上应该是无操作,因为在往返期间应该保留日期的UTC-ness ),至少会改变一天中的时间,有可能在昨天做出约会,这取决于你是在格林威治的东部还是西部.

不应该将日期写成:

  <Date>2009-08-18Z</Date>
Run Code Online (Sandbox Code Playgroud)

实际上,如果我反序列化包含上述内容的文档,我会得到一个已经转换为本地时间的DateTime(我在纽约,所以那是8月17日20:00),如果我立即将该对象序列化回XML,我明白了:

  <Date>2009-08-17</Date>
Run Code Online (Sandbox Code Playgroud)

因此,UTC在进入的过程中被转换为Local,并且该Local的时间部分在出路时掉线,这将使其在返回途中未指定.我们已经失去了对8月18日原始UTC日期规范的所有了解.

以下是W3C对xsd:date的说法:

[定义:]日期的值空间由dateTime时间轴上的正好一天的开顶间隔组成,从每天的开始时刻(每个时区)开始,即'00:00:00 ',但不包括'24:00:00'(与第二天的'00:00:00'相同).对于非时间化值,顶部开放区间不连续地覆盖非时间化时间线,每天一个.对于时间值,间隔从每分钟开始,因此重叠.

根本问题是,如果我执行以下操作:

  1. 构造(或以其他方式接收)UTC DateTime值.
  2. 使用将该字段定义为xsd:date的模式序列化为XML
  3. 将该XML反序列化为DateTime.
  4. 将DateTime转换为UTC(由于"往返"应保留此值,因此应该没有效果).

或者以下内容:

  1. 反序列化包含UTC xsd:date对象的XML文档(例如,2009-08-18Z).
  2. 将其序列化回新的XML文档而不触及它.

这些程序中的任何一个都应该让我与我投入的日期相同.

解决方法

到目前为止我能看到的唯一方法是获得我期望的往返行为,假设所有xsd:date元素都代表UTC,实现Date属性如下:

[XmlElement(DataType = "date")]
public DateTime Date {
    get { return _dt; }
    set { _dt = value.Kind == DateTimeKind.Unspecified ? 
                    DateTime.SpecifyKind(value, DateTimeKind.Utc) : 
                    value.ToUniversalTime(); }
}
Run Code Online (Sandbox Code Playgroud)

les*_*ode 9

我打开了一个Connect问题并从微软那里得到了回复,证实了我的担忧:

我们在处理Date,Time和DateTime值时有不同的行为.对于DateTime值,如果XmlDateTimeSerializationMode不是Local,则保留有关类型(UTC,Local或Unspecified)的信息.反序列化时也是如此.但是,对于日期和时间,它们总是以相同的格式序列化:(对于日期为yyyy-MM-dd,对于时间为HH:mm:ss.fffffff.zzzzzz).因此,序列化和反序列化会丢失有关种类的信息.我们正在打开文档错误,以便改进有关此文档的文档.

  • 取决于'已解决'的意思......微软的声明告诉我,API不能做正确的事情,但他们会通过记录异常来"解决"它. (2认同)