在软件开发中,经常需要将对象序列化成XML格式,然后存储到数据库中。对于层次结构的数据对象,如订单和订单行,这种方法尤其有用。本文不讨论这种方法的优缺点,而是提供一个解决方案,用于处理在序列化过程中传递System.DateTime值的问题。
首先,让来探讨一下DateTime值的问题。假设有一个类,如下所示:
public class MyClass {
public DateTime LocalDateTime {
get;
set;
}
}
这个类只暴露了一个属性,用于保存本地日期和时间。如果将其设置为DateTime.Now并序列化整个对象,LocalDateTime的值可能看起来像这样:
2010-01-17T12:29:23.5561314+02:00
现在,如果想要将这个字符串转换为SQL Server中的DATETIME类型(这就是在存储过程中解析序列化对象的XML时所做的),会得到一个错误消息:
Conversion failed when converting datetime from character string.
经过一些尝试,发现尝试转换的字符串不能包含时区信息,并且只支持毫秒。如果尝试执行这个语句:
SQL select cast('2010-01-17T12:29:23.556' as DateTime)
...它会很好地转换。因此,需要去掉时区信息,并将数字减少到只有毫秒。第一个很容易。在DateTime setter中,使用以下代码:
localDateTime = DateTime.SpecifyKind(value, DateTimeKind.Utc);
这将使DateTime值成为UTC时间,这意味着时区是+00:00,并且在序列化后,值现在将变为:
2010-01-17T12:29:23.5561314Z
这为提供了没有时区信息的本地时间。不用担心结尾的‘Z’。它对SQL Server来说是完全没问题的。如果想先将时间转换为UTC,可以使用:
utcDateTime = DateTime.SpecifyKind(value.ToUniversalTime(), DateTimeKind.Utc);
DateTime.ToUniversalTime()将DateTime值转换为通用时间,其余的都是一样的。
现在只剩下毫秒问题了。出现在毫秒后面的数字是ticks。一个tick是10000毫秒,这是时间的最小单位。所以需要以某种方式去掉ticks信息。
由于一个毫秒是10000个ticks,要去掉DateTime的ticks部分(使其等于0),可以将ticks除以10000,然后将结果乘以10000。System.DateTime结构还提供了一个方便的构造函数,它接受ticks的数量和DateTimeKind值来构建新的DateTime。所以可以使用它像这样:
localDateTime = new DateTime(value.Ticks / 10000 * 10000, DateTimeKind.Utc);
...当序列化时,值看起来像这样:
2010-01-17T12:29:23.556Z
这现在非常适合在SQL Server中使用。
不确定这是处理序列化输出的ticks部分的最快和最好的方法。然而,如果只需要日期,可以使用DateTime.Date结合DateTimeKind.Utc,输出将完全符合SQL Server的需要,就不需要处理ticks了。
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Xml.Serialization;
namespace DateTimeSerialization {
public class MyClass {
private DateTime localDateTime;
private DateTime utcDateTime;
public DateTime LocalDateTime {
get { return localDateTime; }
set {
localDateTime = new DateTime(value.Ticks / 10000 * 10000, DateTimeKind.Utc);
}
}
public DateTime UtcDateTime {
get { return utcDateTime; }
set {
long ticks = value.ToUniversalTime().Ticks / 10000 * 10000;
utcDateTime = new DateTime(ticks, DateTimeKind.Utc);
}
}
}
class Program {
static void Main(string[] args) {
MyClass myClass = new MyClass();
DateTime dateTime = DateTime.Now;
myClass.LocalDateTime = dateTime;
myClass.UtcDateTime = dateTime;
StringBuilder builder = new StringBuilder();
StringWriter writer = new StringWriter(builder);
XmlSerializer serializer = new XmlSerializer(typeof(MyClass));
serializer.Serialize(writer, myClass);
Console.WriteLine(builder.ToString());
Console.Read();
}
}
}