DateTime和xsd:date的往返XML序列化?
好的,我在这里错过了什么? 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();
}
}
}
因此,对于定义为“date”而非“dateTime”的XSD元素,日期不会被序列化为UTC。 这是一个问题,因为如果我反序列化这个XML,那么结果日期将是Kind Unspecified,并且任何转换为UTC(实际上应该是一个no-op,因为日期的UTC-ness应该在往返期间保留),至少会改变一天的时间,根据你在格林威治的东边还是西边,有50%的机会在昨天做出日期。
不应该把日期写成:
<Date>2009-08-18Z</Date>
?
事实上,如果我反序列化包含上述内容的文档,我会得到一个已经转换为本地时间的DateTime(我在纽约,因此即8月17日20:00),如果我立即将该对象序列化回XML,我得到:
<Date>2009-08-17</Date>
因此,UTC在途中被转换为Local,并且该Local的时间部分在出路时下降,这将使其在返回的路上处于未指定状态。 我们已经失去了8月18日原始UTC日期规范的所有知识。
以下是W3C关于xsd:date的说法:
[定义:]日期的值空间由dateTime的时间轴上的开始时间间隔(在每个时区的开始时刻开始)(即每个时区中)组成,即'00:00:00 ',但不包括'24:00:00'(与第二天的'00:00:00'相同)。 对于非时间分辨值,顶部开放时间间隔不包括非时间分布时间线,每天一个。 对于分时值,间隔从每分钟开始并因此重叠。
根本的问题是,如果我做到以下几点:
或者以下:
这些程序中的任何一个都应该让我和我放入的日期相同。
解决方法
我期望能够看到的往返行为的唯一方法是按如下方式实现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(); }
}
我打开了一个连接问题,并从微软那里得到了回复,证实了我的担忧:
我们有不同的行为来处理日期,时间和日期时间值。 对于DateTime值,如果XmlDateTimeSerializationMode不是Local,则会保留关于种类(UTC,Local或Unspecified)的信息。 反序列化时也是如此。 但是,对于日期和时间,它们总是以相同的格式序列化:(日期和日期的yyyy-MM-dd:日期和时间的mm:ss.fffffff.zzzzzz)。 所以有关类的信息在序列化和反序列化中丢失了。 我们正在打开一个文档错误,以便改进有关这方面的文档。
我没有看到你描述的问题。
您的示例代码不会反序列化。 我添加了一些反序列化的代码,并且按照我的预期往返。 我没有看到日期向后移动,或者前进一天。
我注意到,无论DateTimeKind如何,d.Date字段的时间部分都被去除了序列化。 这对我来说似乎是正确的。 直观地说,对我来说,通过“Date”序列化时区或转换为UTC是毫无意义的。 如果我的日期值为8-18-2009,并且在序列化时显示为8-19-2009Z,那将是令人惊讶的。 所以我认为它现在的工作方式似乎是正确的。
[XmlElement(DateType="time")]
(xsd:time),时区不会被包含。 我没有测试这个。 因此,我所看到的问题是,这种对我来说很有意义的行为没有清楚记录,尤其是对于往返移动引入的更改。 应明确声明DataType =“date”和DataType =“time”不能转换为UTC序列化的事实。
你写了:
任何到UTC的转换至少会改变一天的时间,
但我根本没有看到这一点。 当我将DateTimeKind.Unspecified时间转换为Utc时,它不会改变一天的时间。 它只是改变了那种。
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);
}
}
}
结果:
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>链接地址: http://www.djcxy.com/p/8129.html
上一篇: Roundtrip XML Serialization of DateTime and xsd:date?
下一篇: How Do I Serialize DateTime Objects in .NET in a Standards Compliant Way