Фильтрация с использованием DateTimeOffset в качестве параметра проблемы - PullRequest
4 голосов
/ 20 июня 2019

Я играл с датами, используя программу на C #.

Я хочу выполнить фильтрацию для любой таблицы, в которой есть столбцы DateTime, DateTime2, DateTimeOffset.

Я храню LastRefreshDate как DateTimeOffSet в UTC и использую его для фильтрации данных в этих таблицах. Я корректирую смещение (используя NodaTime) LastRefreshDate на основе часового пояса, используемого для хранения дат в этих таблицах. Обычно это дается пользователем.

Итак, я создал тестовый образец, чтобы объяснить проблему. Как правило, запросы 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, которые демонстрируют проблему. Кстати, я запустил SQL Server Profiler, и сгенерированные запросы аналогичны запросам в скриптах.

Скрипка DateTime: 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 неявное преобразование не ведет себя так же, как явное приведение. Вот тестовый пример:

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

1 Ответ

1 голос
/ 20 июня 2019

Несколько вещей:

  • В SQL Server, если вы используете 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
    
  • При запросе к полю datetime или datetime2 с использованием параметра datetimeoffset смещение действительно учитывается при неявном преобразовании (оно похоже на B выше).

  • На стороне C # будьте осторожны с DateTime.Parse. По умолчанию он генерирует значение на основе по местному времени , если указано смещение. Если вы проверите, вы увидите p0.Kind == DateTimeKind.Local. Вы можете передать DateTimeStyles.AdjustToUniversal, но лучшая идея - проанализировать как DateTimeOffset, как вы показали в своем коде "не работает". Но затем вместо передачи полного DateTimeOffset передайте свойство UtcDateTime:

    DateTime p0 = DateTimeOffset.Parse(datestring, CultureInfo.InvariantCulture).UtcDateTime;
    
  • По соображениям производительности и стабильности вы можете рассмотреть возможность использования ParseExact или TryParseExact вместо Parse. Или, поскольку вы сказали, что уже используете Noda Time, вы можете использовать его функции разбора текста с OffsetDateTimePattern. Оттуда вы либо позвоните .ToDateTimeOffset().UtcDateTime, либо .ToInstant().ToDateTimeUtc().

  • Кроме того, вы можете просто определить столбцы базы данных SQL как datetimeoffset, затем вы можете передать любой параметр DateTimeOffset, и он будет нормализован к UTC при запросе.

...