title: Roundtrip XML Serialization of DateTime and xsd:date?
body: <p>OK, what am I missing here? MSDN says the following with regard to DateTimeSerializationMode:</p>
  
  <blockquote>
    <p>In versions 2.0 and later of the .Net
    Framework, with this property set to
    RoundtripDateTime objects are examined
    to determine whether they are in the
    local, UTC or an unspecified time
    zone, and are serialized in such a way
    that this information is preserved.
    This is the default behavior and is
    recommended for all new applications
    that do not communicate with older
    versions of the framework.</p>
  </blockquote>
  
  <p>However:</p>
  
  <pre><code>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:
              // &lt;Date&gt;2009-08-18&lt;/Date&gt;
  
              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:
              // &lt;DateSerTest&gt;
              //     &lt;Date&gt;2009-08-18Z&lt;/Date&gt;
              // &lt;/DateSerTest&gt;
  
              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:
                      // &lt;Date&gt;2009-08-17&lt;/Date&gt;
                  }
              }
              Console.ReadKey();
          }
      }
  }
  </code></pre>
  
  <p>So for XSD elements defined as "date" rather than "dateTime", the date is not serialized as UTC. This is a problem, because if I deserialize this XML the resulting date will be of Kind Unspecified, and any conversion to UTC (which should in fact be a no-op because the UTC-ness of the date should have been preserved during the roundtrip), will change at least the time of day, with a 50% chance of making the date yesterday, depending on whether you're east or west of Greenwich.</p>
  
  <p>Shouldn't the date get written as:</p>
  
  <pre><code>  &lt;Date&gt;2009-08-18Z&lt;/Date&gt;
  </code></pre>
  
  <p>?</p>
  
  <p>Indeed, if I deserialize a document that contains the above, I get a DateTime that's already been converted to Local time (I'm in New York so that's Aug 17th 20:00), and if I immediately serialize that object back to XML, I get:</p>
  
  <pre><code>  &lt;Date&gt;2009-08-17&lt;/Date&gt;
  </code></pre>
  
  <p>So, UTC was converted to Local on the way in, and the time part of that Local dropped on the way out, which will make it Unspecified on the way back in again. We've lost all knowledge of the original UTC date specification of August 18th.</p>
  
  <p>Here's what the W3C says about xsd:date:</p>
  
  <blockquote>
    <p>[Definition:]   The ·value space· of
    date consists of top-open intervals of
    exactly one day in length on the
    timelines of dateTime, beginning on
    the beginning moment of each day (in
    each timezone), i.e. '00:00:00', up to
    but not including '24:00:00' (which is
    identical with '00:00:00' of the next
    day). For nontimezoned values, the
    top-open intervals disjointly cover
    the nontimezoned timeline, one per
    day. For timezoned values, the
    intervals begin at every minute and
    therefore overlap.</p>
  </blockquote>
  
  <p>The fundamental problem is that if I do the following:</p>
  
  <ol>
  <li>Construct (or otherwise receive) a UTC DateTime value.</li>
  <li>Serialize to XML with a schema defining that field as xsd:date</li>
  <li>Deserialize that XML back to a DateTime.</li>
  <li>Convert the DateTime to UTC (which should have no effect since the "roundtrip" should have preserved this).</li>
  </ol>
  
  <p>Or the following:</p>
  
  <ol>
  <li>Deserialize an XML document containing a UTC xsd:date object (eg. 2009-08-18Z).</li>
  <li>Serialize it back to a new XML document without touching it.</li>
  </ol>
  
  <p>Either of these procedures should get me the same date I put in.</p>
  
  <p><strong>Workaround</strong></p>
  
  <p>The only way I can see so far to get the <strong>roundtrip</strong> behaviour I expect is to implement the Date property as follows, on the assumption that all xsd:date elements represent UTC:</p>
  
  <pre><code>[XmlElement(DataType = "date")]
  public DateTime Date {
      get { return _dt; }
      set { _dt = value.Kind == DateTimeKind.Unspecified ? 
                      DateTime.SpecifyKind(value, DateTimeKind.Utc) : 
                      value.ToUniversalTime(); }
  }
  </code></pre>