Хорошо, что мне здесь не хватает? MSDN сообщает следующее относительно DateTimeSerializationMode:
В версиях 2.0 и более поздних версий .Net Framework, с этим свойством Объекты 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();
}
}
}
Итак, для элементов XSD, определенных как "дата", а не "dateTime", дата не сериализуется как UTC. Это проблема, потому что, если я десериализую этот XML, результирующая дата будет иметь вид Unspecified и любое преобразование в UTC (что на самом деле должно быть no-op, поскольку UTC-дата даты должна была быть сохранена во время кругового путешествия), изменится, по крайней мере, на время дня, с 50% шансом сделать вчерашнюю дату, в зависимости от того, находитесь вы на востоке или западе от Гринвича.
Не следует ли писать дату:
<Date>2009-08-18Z</Date>
?
В самом деле, если я десериализую документ, который содержит выше, я получаю DateTime, который уже был преобразован в локальное время (я в Нью-Йорке так, что 17 августа 20:00), и если я немедленно сериализую этот объект обратно к XML, я получаю:
<Date>2009-08-17</Date>
Итак, UTC был преобразован в Local по пути внутрь, а временная часть этого Local сброшена на выходе, что сделает его Unspecified на обратном пути снова. Мы потеряли все знания о первоначальной спецификации даты UTC от 18 августа.
Вот что говорит W3C о xsd: date:
[Определение:] · · значение · пространства дата состоит из верхних открытых интервалов ровно один день в длину на временные рамки dateTime, начиная с начальный момент каждого дня (в каждый часовой пояс), то есть '00: 00: 00 ', до но не включая "24: 00: 00" (который идентичный "00: 00: 00" следующего день). Для неточечных значений верхние открытые интервалы непересекаются несрочная временная шкала, по одному в день. Для измеренных значений времени интервалы начинаются каждую минуту и поэтому перекрываются.
Основная проблема заключается в том, что если я делаю следующее:
- Построить (или получить другое) значение UTC DateTime.
- Сериализовать XML с помощью схемы, определяющей это поле как xsd: date
- Отключить этот XML обратно к DateTime.
- Преобразуйте DateTime в UTC (который не должен иметь эффекта, так как "roundtrip" должен был сохранить это).
Или следующее:
- Дезертициализировать XML-документ, содержащий объект UTC xsd: date (например, 2009-08-18Z).
- Сериализовать его обратно в новый XML-документ, не касаясь его.
Любая из этих процедур должна получить мне ту же дату, которую я вставлял.
Обход
Единственный способ, который я вижу до сих пор, чтобы получить поведение roundtrip, которое я ожидаю, заключается в том, чтобы реализовать свойство Date следующим образом, исходя из предположения, что все элементы xsd: date представляют UTC:
[XmlElement(DataType = "date")]
public DateTime Date {
get { return _dt; }
set { _dt = value.Kind == DateTimeKind.Unspecified ?
DateTime.SpecifyKind(value, DateTimeKind.Utc) :
value.ToUniversalTime(); }
}