在.Net的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,结果日期将是未指定的类型,以及任何到UTC的转换(实际上应该是无操作,因为在往返期间应该保留日期的UTC-ness ),至少会改变一天中的时间,有可能在昨天做出约会,这取决于你是在格林威治的东边还是西边。
日期不应写为:
<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的时间表,从...开始 每天的开始时刻(in 每个时区),即'00:00:00',最多 但不包括'24:00:00'(即 与下一个的'00:00:00'相同 天)。对于非时间化的值, 顶部开放的间隔不相称地覆盖 非时间时间线,每个时间线一个 天。对于timezoned值, 间隔从每分钟开始 因此重叠。
根本问题在于,如果我执行以下操作:
或以下内容:
这些程序中的任何一个都应该让我与我投入的日期相同。
解决方法
到目前为止我能看到的唯一方法是获得往返行为,我希望实现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(); }
}
答案 0 :(得分:9)
我打开了一个Connect问题并从微软那里得到了回复,证实了我的恐惧:
我们有不同的行为 处理日期,时间和日期时间 值。对于DateTime值,如果 XmlDateTimeSerializationMode不是 本地有关该种类的信息 (UTC,Local或Unspecified)是 保存。这也是如此 反序列化。但是,对于日期和 时间,它们总是被序列化 格式相同:( yyyy-MM-dd for 日期和HH:mm:ss.fffffff.zzzzzz for 时间)。关于善良的信息 在序列化和丢失时丢失了 反序列化。我们正在开放 我们方面的文档错误 改进文档 此
答案 1 :(得分:0)
我没有看到您描述的问题。
您的示例代码未反序列化。我添加了一些反序列化的代码,并且正如我所期望的那样往返。我没有看到日期向后移动一天,或向前移动一天。
我注意到无论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>