好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

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映射到 xsd:dateTime 类型.它的确切呈现方式取决于种类.

>对于DateTimeKind.Unspecified,它不包含偏移量.

>对于DateTimeKind.Utc,它将附加一个Z.

>对于DateTimeKind.Local,它将附加本地偏移量

当你在数据集中查看它时,你问为什么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, 1, 0, 0, 0, DateTimeKind.Unspecified));
dt.Rows.Add(new DateTime(2015, 1, 1, 0, 0, 0, DateTimeKind.Local));
dt.Rows.Add(new DateTime(2015, 1, 1, 0, 0, 0, DateTimeKind.Utc));

DataSet ds = new DataSet();
ds.Tables.Add(dt);
string xml = ds.GetXml();

Debug.Write(xml);

这是我运行时的输出(在美国太平洋时区):

<NewDataSet>
  <Table1>
    <Foo>2015-01-01T0-08:00</Foo>
  </Table1>
  <Table1>
    <Foo>2015-01-01T0-08:00</Foo>
  </Table1>
  <Table1>
    <Foo>2015-01-01T0-08:00</Foo>
  </Table1>
</NewDataSet>

但是,好消息是DateTimeOffset值更好一些:

DataTable dt = new DataTable();
dt.Columns.Add("Foo", typeof(DateTimeOffset));

dt.Rows.Add(new DateTimeOffset(2015, 1, 1, 0, 0, 0, TimeSpan.FromHours(11)));
dt.Rows.Add(new DateTimeOffset(2015, 1, 1, 0, 0, 0, TimeSpan.Zero));
dt.Rows.Add(new DateTimeOffset(2015, 1, 1, 0, 0, 0, TimeSpan.FromHours(-3)));

DataSet ds = new DataSet();
ds.Tables.Add(dt);
string xml = ds.GetXml();

Debug.Write(xml);

输出:

<NewDataSet>
  <Table1>
    <Foo>2015-01-01T0+11:00</Foo>
  </Table1>
  <Table1>
    <Foo>2015-01-01T0Z</Foo>
  </Table1>
  <Table1>
    <Foo>2015-01-01T0-03:00</Foo>
  </Table1>
</NewDataSet>

在大多数情况下,这是正确的,虽然从技术上来说它应该使用00:00而不是Z序列化第二个,但这在实践中并不重要.

我想说的最后一件事是,一般来说,DataSet是过去的遗物.在现代开发中,应该很少需要在日常代码中使用它.如果可能的话,我会认真考虑探索其他选择.

查看更多关于c# – 带有时区的SQL Server的DateTime的详细内容...

  阅读:56次