c# - 使用 DateTimeOffset 作为参数问题进行过滤

标签 c# sql-server timezone timezone-offset nodatime

我正在使用 C# 程序处理日期。

我想过滤任何具有 DateTime、DateTime2、DateTimeOffset 列的表。

我将 LastRefreshDate 存储为 UTC 中的 DateTimeOffSet,并使用它来过滤这些表中的数据。我根据用于在这些表中存储日期的时区调整了 LastRefreshDate 的偏移量(使用 NodaTime)。通常,它由用户提供。

所以我创建了一个测试样本来解释这个问题。通常,SQL 查询是动态的,参数也是。这是示例代码:

[TestMethod]
public void Test()
{
    using (SqlConnection connection = new SqlConnection("Server=myserver;Database=mydb;User ID=admin;Password=admin"))
    {
        connection.Open();
        using (SqlCommand command = new SqlCommand("SELECT [TimeStamp] FROM  [dbo].[DATA] WHERE [TimeStamp] >= @p0", connection))
        {
            string datestring = "2019-06-18 13:35:20.1133868 -04:00";

            // Does not work
            // DateTimeOffset p0 = DateTimeOffset.Parse(datestring, CultureInfo.InvariantCulture);
            // Does work
            DateTime p0 = DateTime.Parse(datestring, CultureInfo.InvariantCulture);
            command.Parameters.AddWithValue("@p0", p0);
            using (SqlDataReader reader = command.ExecuteReader())
            {
                var dataTable = new DataTable();
                dataTable.Load(reader);
                var result = dataTable.Rows.Count == 0;
            }
        }
    }
}

我创建了 2 个 SQL fiddles 来演示这个问题。顺便说一下,我运行了 SQL Server Profiler,生成的查询与 fiddle 中的查询类似。

日期时间 fiddle :http://sqlfiddle.com/#!18/a06be/1

declare @p0 datetime = '2019-06-18 13:35:20'
SELECT 
    [TimeStamp]
FROM 
    [dbo].[DATA]
WHERE 
    ([TimeStamp] >= @p0)

DateTimeOffSet fiddle :http://sqlfiddle.com/#!18/a06be/2

declare @p0 datetimeoffset(7) ='2019-06-18 13:35:20.1133868 -04:00'
SELECT [TimeStamp]
FROM 
    [dbo].[DATA] 
WHERE 
    ([TimeStamp] >= @p0 )

我做了更多的测试。通过直接应用转换,SQL 查询就可以工作了。似乎 SQL Server implicit conversion与显式强制转换的行为方式不同。这是测试用例:

declare @p0 datetime
set @p0 = '2019-06-18 17:48:00.00'
declare @p1 datetimeoffset(7)
set @p1 = '2019-06-18 17:47:00.5385563 -04:00'

select 1 
where @p0 > cast(@p1 as datetime) -- working
--where @p0 > @p1                       -- not working

最佳答案

一些事情:

  • 在 SQL Server 中,如果您使用 CASTCONVERT 而不指定样式,则默认样式为 0,这将 datetimeoffset 转换为 datetimedatetime2 时,只需从 datetimeoffset 中获取日期和时间值,而不考虑偏移量。如果您想考虑偏移量,请使用 CONVERT 并为样式传递 1:

    DECLARE @p0 datetimeoffset = '2019-06-18 13:35:20.1133868 -04:00'
    SELECT convert(datetime, @p0, 0) as 'A', convert(datetime, @p0, 1) as 'B'
    -- A = 2019-06-18T13:35:20.113Z
    -- B = 2019-06-18T17:35:20.113Z
    
  • 当使用 datetimeoffset 参数查询 datetimedatetime2 字段时,隐式转换确实考虑了偏移量(就像上面的B)。

  • 在 C# 方面,请注意 DateTime.Parse。默认情况下,它会在提供偏移量时发出基于本地时间 的值。如果检查,您会看到 p0.Kind == DateTimeKind.Local。您可以传递 DateTimeStyles.AdjustToUniversal,但更好的办法是解析为 DateTimeOffset,就像您在“不起作用”代码中显示的那样。但是,不是传递完整的 DateTimeOffset,而是传递 UtcDateTime 属性:

    DateTime p0 = DateTimeOffset.Parse(datestring, CultureInfo.InvariantCulture).UtcDateTime;
    
  • 出于性能和稳定性的原因,您可以考虑使用 ParseExactTryParseExact 而不是 Parse。或者,由于您说过您已经在使用 Noda Time,您可以将其文本解析功能与 OffsetDateTimePattern 一起使用。您可以从那里调用 .ToDateTimeOffset().UtcDateTime.ToInstant().ToDateTimeUtc()

  • 或者,您可以将 SQL 数据库列定义为 datetimeoffset,然后您可以传递任何 DateTimeOffset 参数,查询时它会被规范化为 UTC。

关于c# - 使用 DateTimeOffset 作为参数问题进行过滤,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56690331/

相关文章:

javascript - 获取可解析时区

c# - 有什么方法可以在监 window 口中显示/比较对象引用?

c# - 用于双向映射的简单约定自动映射器(实体到/来自 ViewModel)

c# - VS C# 调试器中看似奇怪的行为

c# - 正确使用带时间戳数据的主键

java - 如何更新 OpenJDK 的时区信息?

c# - 在数据集中查找外键

sql - 为什么 'HASH JOIN' 或 'LOOP JOIN' 改进了这个存储过程?

sql - SQL语法错误,语法不正确?

javascript - 我可以让 node.js 日期始终使用 UTC/GMT 吗?