使用 DateTimeOffset 作为参数问题进行过滤
Filtering using a DateTimeOffset as a parameter issue
我正在使用 C# 程序处理日期。
我想筛选任何具有 DateTime、DateTime2、DateTimeOffset 列的 table。
我在 UTC 中将 LastRefreshDate
存储为 DateTimeOffSet
,我用它来过滤那些 table 上的数据。我根据用于在 table 中存储日期的时区调整 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 fiddle 来演示这个问题。顺便说一句,我 运行 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 服务器 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服务器中,如果使用CAST
或CONVERT
不指定样式,则默认样式为0
,当将 datetimeoffset
转换为 datetime
或 datetime2
只是从 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
参数查询 datetime
或 datetime2
字段时,隐式转换确实考虑了偏移量(就像 B
以上)。
在 C# 方面,请注意 DateTime.Parse
。默认情况下,它会在提供偏移量时发出基于 本地时间 的值。如果您检查,您会看到 p0.Kind == DateTimeKind.Local
。您可以传递 DateTimeStyles.AdjustToUniversal
,但更好的办法是像您在 "doesn't work" 代码中显示的那样解析为 DateTimeOffset
。但是然后不是通过完整的 DateTimeOffset
,而是通过 UtcDateTime
属性:
DateTime p0 = DateTimeOffset.Parse(datestring, CultureInfo.InvariantCulture).UtcDateTime;
出于性能和稳定性的原因,您可以考虑使用 ParseExact
或 TryParseExact
而不是 Parse
。或者,既然您说您已经在使用 Noda Time,那么您可以将其文本解析功能与 OffsetDateTimePattern
一起使用。从那里你可以调用 .ToDateTimeOffset().UtcDateTime
,或者 .ToInstant().ToDateTimeUtc()
.
或者,您可以将 SQL 数据库列定义为 datetimeoffset
,然后您可以传递任何 DateTimeOffset
参数,当正在查询。
我正在使用 C# 程序处理日期。
我想筛选任何具有 DateTime、DateTime2、DateTimeOffset 列的 table。
我在 UTC 中将 LastRefreshDate
存储为 DateTimeOffSet
,我用它来过滤那些 table 上的数据。我根据用于在 table 中存储日期的时区调整 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 fiddle 来演示这个问题。顺便说一句,我 运行 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 服务器 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服务器中,如果使用
CAST
或CONVERT
不指定样式,则默认样式为0
,当将datetimeoffset
转换为datetime
或datetime2
只是从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
参数查询datetime
或datetime2
字段时,隐式转换确实考虑了偏移量(就像B
以上)。在 C# 方面,请注意
DateTime.Parse
。默认情况下,它会在提供偏移量时发出基于 本地时间 的值。如果您检查,您会看到p0.Kind == DateTimeKind.Local
。您可以传递DateTimeStyles.AdjustToUniversal
,但更好的办法是像您在 "doesn't work" 代码中显示的那样解析为DateTimeOffset
。但是然后不是通过完整的DateTimeOffset
,而是通过UtcDateTime
属性:DateTime p0 = DateTimeOffset.Parse(datestring, CultureInfo.InvariantCulture).UtcDateTime;
出于性能和稳定性的原因,您可以考虑使用
ParseExact
或TryParseExact
而不是Parse
。或者,既然您说您已经在使用 Noda Time,那么您可以将其文本解析功能与OffsetDateTimePattern
一起使用。从那里你可以调用.ToDateTimeOffset().UtcDateTime
,或者.ToInstant().ToDateTimeUtc()
.或者,您可以将 SQL 数据库列定义为
datetimeoffset
,然后您可以传递任何DateTimeOffset
参数,当正在查询。