OK, what am I missing here? MSDN says the following with regard to DateTimeSerializationMode:
In versions 2.0 and later of the .Net Framework, with this
I opened a Connect issue and got this back from Microsoft, confirming my fears:
We have different behaviors for handling Date, Time and DateTime values. For DateTime values, if XmlDateTimeSerializationMode is not Local the information about the kind (UTC, Local or Unspecified) is preserved. This is also true while deserializing. However, for Date and Time, they are always serialized out with the same format: (yyyy-MM-dd for Date and HH:mm:ss.fffffff.zzzzzz for Time). So the information about kind is lost on serializing and deserializing. We are opening a documentation bug on our side in order to improve the documentation about this.
I don't see the problem you described.
Your sample code doesn't deserialize. I added some code to deserialize, and it roundtrips as I would expect. I did not see the date move back a day, or forward a day.
I did notice that the time portion of the d.Date field is stripped for serialization, regardless of the DateTimeKind. This seems correct to me. It doesn't make sense to me, intuitively, to either serialize a timezone with a "Date", or to convert to UTC. It would be surprising to me that if I had a Date value of 8-18-2009, and when serialized, it showed up as 8-19-2009Z. So I think the way it works now seems correct.
[XmlElement(DateType="time")]
(xsd:time), the timezone would not be included. I didn't test this. So the problem as I see it is, this behavior, which makes sense to me, isn't documented clearly, especially with the changes introduced for roundtripping. The fact that DataType="date" and DataType="time" do not convert to UTC for serialization, should be clearly stated.
you wrote:
and any conversion to UTC will change at least the time of day,
But I didn't see this at all. When I convert a time that is DateTimeKind.Unspecified, to Utc, it doesn't change the time of day. It just changes the kind.
class Program
{
static System.IO.MemoryStream StringToMemoryStream(string s)
{
byte[] a = System.Text.Encoding.ASCII.GetBytes(s);
return new System.IO.MemoryStream(a);
}
static void Main(string[] args)
{
var settings = new System.Xml.XmlWriterSettings { OmitXmlDeclaration = true, Indent= true };
XmlSerializerNamespaces _ns = new XmlSerializerNamespaces();
_ns.Add( "", "" );
Console.WriteLine("\nDate Serialization testing...");
for (int m=0; m < 2; m++)
{
var builder = new System.Text.StringBuilder();
DateTime t = DateTime.Parse("2009-08-18T22:31:24.0019-04:00");
DateSerTest d = new DateSerTest
{
Date = t,
DateTime = t
};
Console.WriteLine("\nRound {0}", m+1);
if (m==1)
d.Date = d.Date.ToUniversalTime();
Console.WriteLine("d.Date {2,-11} = {0} Kind({1})", d.Date.ToString("u"), d.Date.Kind.ToString(),
(m==1) ? "(converted)" : "(original)" );
Console.WriteLine("d.DateTime = {0} Kind({1})", d.DateTime.ToString("u"), d.DateTime.Kind.ToString());
XmlSerializer ser = new XmlSerializer(typeof(DateSerTest));
Console.WriteLine("\nSerialize d");
using ( var writer = System.Xml.XmlWriter.Create(builder, settings))
{
ser.Serialize(writer, d, _ns);
}
string xml = builder.ToString();
Console.WriteLine("{0}", xml);
Console.WriteLine("\nDeserialize into d2");
System.IO.MemoryStream ms = StringToMemoryStream(xml);
DateSerTest d2= (DateSerTest) ser.Deserialize(ms);
Console.WriteLine("d2.Date = {0} Kind({1})", d2.Date.ToString("u"), d2.Date.Kind.ToString());
Console.WriteLine("d2.DateTime= {0} Kind({1})", d2.DateTime.ToString("u"), d2.DateTime.Kind.ToString());
Console.WriteLine("\nAfter SpecifyKind");
d2.Date = DateTime.SpecifyKind(d2.Date, DateTimeKind.Utc);
Console.WriteLine("d2.Date = {0} Kind({1})", d2.Date.ToString("u"), d2.Date.Kind.ToString());
Console.WriteLine("\nRe-Serialize d2");
builder = new System.Text.StringBuilder();
using ( var writer = System.Xml.XmlWriter.Create(builder, settings))
{
ser.Serialize(writer, d2, _ns);
}
xml = builder.ToString();
Console.WriteLine("{0}", xml);
}
}
}
The results:
Date Serialization testing... Round 1 d.Date (original) = 2009-08-18 22:31:24Z Kind(Local) d.DateTime = 2009-08-18 22:31:24Z Kind(Local) Serialize d <DateSerTest> <Date>2009-08-18</Date> <DateTime>2009-08-18T22:31:24.0019-04:00</DateTime> </DateSerTest> Deserialize into d2 d2.Date = 2009-08-18 00:00:00Z Kind(Unspecified) d2.DateTime= 2009-08-18 22:31:24Z Kind(Local) After SpecifyKind d2.Date = 2009-08-18 00:00:00Z Kind(Utc) Re-Serialize d2 <DateSerTest> <Date>2009-08-18</Date> <DateTime>2009-08-18T22:31:24.0019-04:00</DateTime> </DateSerTest> Round 2 d.Date (converted) = 2009-08-19 02:31:24Z Kind(Utc) d.DateTime = 2009-08-18 22:31:24Z Kind(Local) Serialize d <DateSerTest> <Date>2009-08-19</Date> <DateTime>2009-08-18T22:31:24.0019-04:00</DateTime> </DateSerTest> Deserialize into d2 d2.Date = 2009-08-19 00:00:00Z Kind(Unspecified) d2.DateTime= 2009-08-18 22:31:24Z Kind(Local) After SpecifyKind d2.Date = 2009-08-19 00:00:00Z Kind(Utc) Re-Serialize d2 <DateSerTest> <Date>2009-08-19</Date> <DateTime>2009-08-18T22:31:24.0019-04:00</DateTime> </DateSerTest>