c# – 带有时区的SQL Server的DateTime
当我执行查询并使用DataReader访问该值并将其转换为字符串时,我没有得到TimeZone(2015-02-17T00:00:00).
但是在创建DataSet然后将其转换为XML时,我在DateTime字段中获取TimeZone(2015-02-17T00:00:00 11:00). 从datareader检索数据的代码是var dateTime = reader [“dte_tme”].ToString()产生17/02/2015 12:00:00 AM(没有TimeZone). string dateTime = reader["dte_tme"].ToString(); DateTime dt = Convert.ToDateTime(dateTime); 所以我知道字段’dte_tme’是一个DateTime字段,可能并不总是有值.我正在将其转换为字符串,然后将其转换回DateTime.然后将dt的值序列化为json.我得到的输出是2015-02-17T00:00:00而不是2015-02-17T00:00:00 11:00.我检查了dt的TimeZone,它是未指定的. 我从DataSet的XML创建的DateTime对象将TimeZone作为Local,序列化为2015-02-17T00:00:00 11:00. 为什么这种不一致? 另外,有没有办法使用DataReader获取TimeZone的DateTime? 我的最终目标是以ISO 8601格式序列化DateTime字段. 解决方法
这是一种非常常见的反模式:
string dateTime = reader["dte_tme"].ToString(); DateTime dt = Convert.ToDateTime(dateTime); 正确的咒语如下: DateTime dt = (DateTime) reader["dte_tme"]; 虽然reader [“dte_time”]的返回类型是一个对象,但该对象包含一个DateTime.如果设置断点,则会看到DateTime已经存在.您只需要cast它就可以将其分配给DateTime变量.这叫做unboxing. 如果SQL数据库中的datetime列可以为空,那么您应该像这样测试: DateTime? dt = reader["dte_tme"] == DBNull.Value ? null : (DateTime) reader["dte_tme"]; 或者有时您会看到这样,这同样可以接受: DateTime? dt = reader["dte_tme"] as DateTime?; 从数据库中检索它时,绝对不需要在任何时候将其视为字符串.如果它是数据库中的日期时间,则它是C#中的DateTime. 从datareader中提取数据时,应使用转换操作,即使使用其他数据类型(如整数,小数甚至字符串)也是如此.您可以在SQL Server数据类型和.NET数据类型in the chart here之间查看其他类型映射. 现在关于时区,这是一个不同的问题.首先,要了解DateTime没有保留时区.它只知道它被分配的DateTimeKind.默认情况下,类型是未指定的,这实际上意味着“我不知道;它可能是任何东西”. 也就是说,不同的协议有不同的要求. JSON没有日期的预定义格式,但最常见的约定(和最佳实践)是以ISO8601格式存储日期,即YYYY-MM-DDTHH:mm:ss.时区信息是可选的,当DateTime的.Kind为DateTimeKind.Unspecified时,通常不会包含时区信息.如果它是Utc,那么你会在最后看到Z,如果它是Local,那么你会看到本地时区的偏移量,例如11:00.也就是说,在该特定时刻,任何偏移都适合于该时区.偏移量与“time zone”不同,因为不同的偏移量可能在不同时间在同一时区内应用 – 通常为daylight saving time. XML有点不同. .NET中的大多数XML序列化都将使用W3C XML Schema规范,并将DateTime映射到 >对于DateTimeKind.Unspecified,它不包含偏移量. 当你在数据集中查看它时,你问为什么Kind是Local?那是因为DataSet有一种丑陋的行为,假设所有时间都是本地的.它基本上忽略.Kind属性并假定DateTimeKind.Local的行为.这是一个长期存在的错误. 理想情况下,您将在SQL Server中使用datetimeoffset类型,在.NET中使用DateTimeOffset类型.这避免了“类型”问题,并在JSON中很好地序列化(当您使用像JSON.NET这样的现代序列化器时).但是,在XML中,它应该映射到xsd:dateTime并像本地DateTime一样呈现,只是使用正确的偏移量.然而它最终看起来像这样: <Value xmlns:d2p1="http://schemas.datacontract.org/2004/07/System"> <d2p1:DateTime>2015-03-18T03:34:11.3097587Z</d2p1:DateTime> <d2p1:OffsetMinutes>-420</d2p1:OffsetMinutes> </Value> 那就是DataContractXmlSerializer.如果您使用XmlSerializer,则它根本无法呈现.您只需获得一个空节点,例如< Value />. 但是,即使说了所有这些,你说你使用的是DataSet,并且它带有它自己的一组行为.在坏的方面,它将假设所有DateTime值都具有DateTimeKind.Local – 即使它们没有,如上所述.考虑以下: DataTable dt = new DataTable(); dt.Columns.Add("Foo",typeof (DateTime)); dt.Rows.Add(new DateTime(2015,1,DateTimeKind.Unspecified)); dt.Rows.Add(new DateTime(2015,DateTimeKind.Local)); dt.Rows.Add(new DateTime(2015,DateTimeKind.Utc)); DataSet ds = new DataSet(); ds.Tables.Add(dt); string xml = ds.GetXml(); Debug.Write(xml); 这是我运行时的输出(在美国太平洋时区): <NewDataSet> <Table1> <Foo>2015-01-01T00:00:00-08:00</Foo> </Table1> <Table1> <Foo>2015-01-01T00:00:00-08:00</Foo> </Table1> <Table1> <Foo>2015-01-01T00:00:00-08:00</Foo> </Table1> </NewDataSet> 但是,好消息是DateTimeOffset值更好一些: DataTable dt = new DataTable(); dt.Columns.Add("Foo",typeof(DateTimeOffset)); dt.Rows.Add(new DateTimeOffset(2015,TimeSpan.FromHours(11))); dt.Rows.Add(new DateTimeOffset(2015,TimeSpan.Zero)); dt.Rows.Add(new DateTimeOffset(2015,TimeSpan.FromHours(-3))); DataSet ds = new DataSet(); ds.Tables.Add(dt); string xml = ds.GetXml(); Debug.Write(xml); 输出: <NewDataSet> <Table1> <Foo>2015-01-01T00:00:00+11:00</Foo> </Table1> <Table1> <Foo>2015-01-01T00:00:00Z</Foo> </Table1> <Table1> <Foo>2015-01-01T00:00:00-03:00</Foo> </Table1> </NewDataSet> 在大多数情况下,这是正确的,虽然从技术上来说它应该使用00:00而不是Z序列化第二个,但这在实践中并不重要. 我想说的最后一件事是,一般来说,DataSet是过去的遗物.在现代开发中,应该很少需要在日常代码中使用它.如果可能的话,我会认真考虑探索其他选择. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |