Каков правильный тип SQL для хранения .Net Timespan со значениями> 24:00:00?

196

Я пытаюсь сохранить .Net TimeSpanв SQL Server 2008 R2.

EF Code First предполагает, что он должен храниться в виде Time(7)SQL.

Однако TimeSpanв .Net можно обрабатывать более длительные периоды, чем 24 часа.

Каков наилучший способ хранения .Net TimeSpanна сервере SQL?

GraemeMiller
источник
15
Я использую его для хранения длины повторяющихся событий. Поэтому я хотел зафиксировать продолжительность события независимо от даты
GraemeMiller
1
Связанные не дублируют. Я написал их обоих. Один из них о Code First и о том, как изменить карту для TimeSpan. Другое касается фактического .Net типа Timespan для сопоставления SQL.
GraemeMiller

Ответы:

223

Я бы сохранил его в базе данных как BIGINTи я бы сохранил количество тиков (например, свойство TimeSpan.Ticks ).

Таким образом, если бы я хотел получить объект TimeSpan при его получении, я мог бы просто сделать TimeSpan.FromTicks (значение), что было бы легко.

Том Чантлер
источник
3
Как вы будете обрабатывать вычисления в SQL? Допустим, вам нужно рассчитать, сколько часов он содержит?
Питер
10
Я бы , вероятно , преобразовать тики в объект времени , как это: SELECT CAST(DATEADD(MILLISECOND, @Ticks/CAST(10000 AS BIGINT), '1900-01-01') AS TIME). '1900-01-01'Дата не имеет значения, конечно, это всего лишь третья переменная требует DATEADD(...)функции. Помните, что в тике есть 100 наносекунд, но если вы используете, DATEADD(NANOSECOND...вы, вероятно, получите переполнение, следовательно, используя миллисекунды. Также помните, что вы должны проверить этот факт, используя C # TimeSpan.TicksPerMillisecond(должно быть 10000), чтобы быть уверенным.
Том Чантлер
Можно сохранить его как строку, затем вы можете загрузить его, используя TimeSpan.Parse (текст). не идеален с точки зрения размера или запросов SQL, но при необходимости может быть проанализирован в TSQL
Вальтер Веховен,
65

Спасибо за совет. Как нет эквивалента в SQL-сервере. Я просто создал 2-е поле, которое преобразовало TimeSpan в тики и сохранило его в БД. Я тогда предотвратил сохранение TimeSpan

public Int64 ValidityPeriodTicks { get; set; }

[NotMapped]
public TimeSpan ValidityPeriod
{
    get { return TimeSpan.FromTicks(ValidityPeriodTicks); }
    set { ValidityPeriodTicks = value.Ticks; }
}
GraemeMiller
источник
6
Также для тех, кто использует EF Core - в версии 2.1 вы можете использовать преобразования значений и TimeSpanToTicksConverter для прозрачного
отображения временных интервалов и отметок
30

Если вам не нужно хранить более 24 часов, вы можете просто сохранить время , поскольку в SQL Server 2008 и более поздних версиях сопоставление

time (SQL Server) <-> TimeSpan(.NET)

Конверсии не нужны, если вам нужно хранить только 24 часа или меньше.

Источник: http://msdn.microsoft.com/en-us/library/cc716729(v=vs.110).aspx

Но если вы хотите хранить более 24 часов, вам нужно будет хранить его в тиках, извлекать данные и затем преобразовывать их в TimeSpan. Например

int timeData = yourContext.yourTable.FirstOrDefault();
TimeSpan ts = TimeSpan.FromMilliseconds(timeData);
Алехандро дель Рио
источник
23
Как говорит ОП, «временный» DataType в SQL Server поддерживает только до 24 часов, он хочет хранить> 24
часа
11
Кроме того, TimeSpan (.NET) может быть отрицательным, а Time (SQL Server) - нет.
Эдвард
11
Существует большая разница между временем и продолжительностью. Время представляет собой время в определенный день, а продолжительность - это разница между двумя моментами. Сравните это с местоположением (временем) и расстоянием (продолжительностью).
Рамон де Кляйн
3
^ Точно. - Timeтип SQL предназначен не для представления длительности, а для части времени значения DateTime; это ужасный выбор для TimeSpan.
BrainSlugs83
19

Прямого эквивалента нет. Просто сохраните его в числовом виде, например, количество секунд или что-то соответствующее вашей требуемой точности.

fearofawhackplanet
источник
то есть. сохраните его как число с плавающей точкой и используйте `TimeSpan.FromSeconds` в соответствии с msdn.microsoft.com/en-us/library/…
CAD bloke
7

Я знаю, что это старый вопрос, но я хотел убедиться, что есть несколько других вариантов.

Поскольку вы не можете хранить TimeSpan больше 24 часов в поле типа данных sql time; пара других вариантов может быть.

  1. Используйте varchar (xx) для хранения ToString TimeSpan. Преимуществом этого является то, что точность не должна быть включена в тип данных или вычисление, (секунды против миллисекунд против дней против удач). Все, что вам нужно, это использовать TimeSpan.Parse / TryParse. Это то, что я бы сделал.

  2. Используйте вторую дату, datetime или datetimeoffset, которая хранит результат первой даты + временной интервал. Чтение из БД - это вопрос TimeSpan x = SecondDate - FirstDate. Использование этой опции защитит вас от доступа других библиотек доступа к данным .NET к тем же данным, но не понимающих TimeSpans; в случае, если у вас есть такая среда.

стог
источник
1
Вариант 2 звучит так, как будто он может пригодиться время от времени. thx
rahicks
3

Чтобы соответствовать тому, что, вероятно, является наиболее вероятным источником генерации промежутка времени (вычисляя разницу в 2 раза или дату-время), вы можете сохранить .NET TimeSpanкак DateTimeтип SQL Server .

Это связано с тем, что в SQL Server разница в 2 DateTime( Castto Float, а затем Castобратно в a DateTime) является просто DateTimeотносительной относительно 1 января 1900 года. Пример. Разница в +0,1 секунды будет равна 1 января 1900 г. 00: 00: 00,100, а -0,1 секунды - 31 декабря 1899 г. 23: 59: 59,900.

Чтобы преобразовать .NET TimeSpanв DateTimeтип SQL Server , вы должны сначала преобразовать его в DateTimeтип .NET , добавив его к DateTime1 января 1900 года. Конечно, когда вы читаете его в .NET из SQL Server, вы должны сначала прочитайте это в .NET DateTimeи затем вычтите 1 января 1900 из этого, чтобы преобразовать это в .NET TimeSpan.

Для случаев использования, когда промежутки времени генерируются из SQL Server DateTimeи в SQL Server (т.е. через T-SQL), а SQL Server - до 2016 года, в зависимости от вашего диапазона и требований к точности, их хранение может оказаться нецелесообразным в миллисекундах (не говоря уже Ticks), потому что Intтип, возвращаемый DateDiff(по сравнению BigIntс SS 2016 + DateDiff_Big) переполняется через ~ 24 дня миллисекунд и ~ 67 лет. секунд. Принимая во внимание, что это решение будет обрабатывать промежутки времени с точностью до 0,1 секунды и от -147 до +8,099 лет.

ПРЕДУПРЕЖДЕНИЯ:

  1. Это сработает только в том случае, если разница относительно 1 января 1900 года приведет к значению в диапазоне типа SQL Server DateTime(с 1 января 1753 по 31 декабря 9999 или от -147 до +8,099 лет). Нам не нужно сильно беспокоиться о TimeSpanстороне .NET , поскольку она может хранить от ~ 29 до +29 лет. Я не упомянул DateTime2тип SQL Server (чей диапазон, с отрицательной стороны, намного больше, чем у SQL Server DateTime), потому что: a) он не может быть преобразован в числовое с помощью простого Castи b) DateTimeдолжен быть достаточный диапазон для подавляющего большинства случаев использования.

  2. SQL Server DateTimeразличие , подсчитанное по Cast- к - Float- и - метод обратный не появляется , чтобы быть точными за 0,1 секунды.

Том
источник
Я забыл, что даже читаю этот вопрос гораздо меньше, чем написал этот «А», и снова искал «А». Я начал читать это и подумал про себя: (Ух ты, это лучший ответ на данный момент!). : D
Том
3

Существует несколько способов представления временного интервала в базе данных.

время

Этот тип данных поддерживается начиная с SQL Server 2008 и является предпочтительным способом хранения TimeSpan. Нет необходимости в отображении. Это также хорошо работает с кодом SQL.

public TimeSpan ValidityPeriod { get; set; }

Однако, как указано в исходном вопросе, этот тип данных ограничен 24 часами.

DateTimeOffset

Тип datetimeoffsetданных отображается непосредственно на System.DateTimeOffset. Он используется для выражения смещения между datetime/ datetime2UTC, но вы также можете использовать его для TimeSpan.

Однако, поскольку тип данных предлагает очень специфическую семантику, вам следует также рассмотреть другие варианты.

datetime / datetime2

Одним из подходов может быть использование datetimeили datetime2типов. Это лучше всего в сценариях, где вам нужно обрабатывать значения в базе данных напрямую, т.е. для просмотра, хранимых процедур или отчетов. Недостатком является то, что вам необходимо вычесть значение DateTime(1900,01,01,00,00,00)из даты, чтобы получить временной интервал в вашей бизнес-логике.

public DateTime ValidityPeriod { get; set; }

[NotMapped]
public TimeSpan ValidityPeriodTimeSpan
{
    get { return ValidityPeriod - DateTime(1900,01,01,00,00,00); }
    set { ValidityPeriod = DateTime(1900,01,01,00,00,00) + value; }
}

BIGINT

Другим подходом может быть преобразование TimeSpan в тики и использование bigintтипа данных. Однако этот подход имеет тот недостаток, что его громоздко использовать в запросах SQL.

public long ValidityPeriod { get; set; }

[NotMapped]
public TimeSpan ValidityPeriodTimeSpan
{
    get { return TimeSpan.FromTicks(ValidityPeriod); }
    set { ValidityPeriod = value.Ticks; }
}

VARCHAR (N)

Это лучше всего подходит для случаев, когда значение должно быть читаемым человеком. Вы также можете использовать этот формат в запросах SQL, используя CONVERT(datetime, ValidityPeriod)функцию. В зависимости от требуемой точности вам потребуется от 8 до 25 символов.

public string ValidityPeriod { get; set; }

[NotMapped]
public TimeSpan ValidityPeriodTimeSpan
{
    get { return TimeSpan.Parse(ValidityPeriod); }
    set { ValidityPeriod = value.ToString("HH:mm:ss"); }
}

Бонус: период и продолжительность

Используя строку, вы также можете хранить типы данных NodaTime , особенно Durationи Period. Первый в основном такой же, как TimeSpan, в то время как последний учитывает, что некоторые дни и месяцы длиннее или короче других (т.е. в январе 31 день, а в феврале 28 или 29; некоторые дни длиннее или короче из-за перехода на летнее время ). В таких случаях использование TimeSpan - неправильный выбор.

Вы можете использовать этот код для преобразования периодов:

using NodaTime;
using NodaTime.Serialization.JsonNet;

internal static class PeriodExtensions
{
    public static Period ToPeriod(this string input)
    {
        var js = JsonSerializer.Create(new JsonSerializerSettings());
        js.ConfigureForNodaTime(DateTimeZoneProviders.Tzdb);
        var quoted = string.Concat(@"""", input, @"""");
        return js.Deserialize<Period>(new JsonTextReader(new StringReader(quoted)));
    }
}

А потом использовать как

public string ValidityPeriod { get; set; }

[NotMapped]
public Period ValidityPeriodPeriod
{
    get => ValidityPeriod.ToPeriod();
    set => ValidityPeriod = value.ToString();
}

Мне очень нравится, NodaTimeи это часто спасает меня от хитрых ошибок и много головной боли. Недостатком здесь является то, что вы действительно не можете использовать его в запросах SQL, и вам нужно выполнять вычисления в памяти.

Пользовательский тип CLR

У вас также есть возможность использовать пользовательский тип данных и TimeSpanнапрямую поддерживать пользовательский класс. См. Пользовательские типы CLR для деталей.

Недостатком здесь является то, что тип данных может плохо работать с отчетами SQL. Кроме того, некоторые версии SQL Server (Azure, Linux, Data Warehouse) не поддерживаются.

Преобразование значений

Начиная с EntityFramework Core 2.1, у вас есть возможность использовать преобразование значений .

Однако при использовании этого EF не сможет конвертировать многие запросы в SQL, в результате чего запросы будут выполняться в памяти; потенциально переносит много и много данных в ваше приложение.

Поэтому, по крайней мере, на данный момент, может быть, лучше не использовать его, а просто отобразить результат запроса с помощью Automapper .

MovGP0
источник
1

Обычно я храню TimeSpan как bigint, заполненный тиками из свойства TimeSpan.Ticks, как было предложено ранее. Вы также можете сохранить TimeSpan как varchar (26), заполненный выводом TimeSpan.ToString (). Четыре скалярные функции (ConvertFromTimeSpanString, ConvertToTimeSpanString, DateAddTicks, DateDiffTicks), которые я написал, полезны для обработки TimeSpan на стороне SQL и позволяют избежать взломов, которые могут привести к искусственно ограниченным диапазонам. Если вы можете сохранить интервал в .NET TimeSpan, он также должен работать с этими функциями. Кроме того, функции позволяют работать с TimeSpans и тиками по 100 наносекунд даже при использовании технологий, не включающих .NET Framework.

DROP FUNCTION [dbo].[DateDiffTicks]
GO

DROP FUNCTION [dbo].[DateAddTicks]
GO

DROP FUNCTION [dbo].[ConvertToTimeSpanString]
GO

DROP FUNCTION [dbo].[ConvertFromTimeSpanString]
GO

SET ANSI_NULLS OFF
GO

SET QUOTED_IDENTIFIER OFF
GO

-- =============================================
-- Author:      James Coe
-- Create date: 2011-05-23
-- Description: Converts from a varchar(26) TimeSpan string to a bigint containing the number of 100 nanosecond ticks.
-- =============================================
/*
    [-][d.]hh:mm:ss[.fffffff] 

    "-" 
     A minus sign, which indicates a negative time interval. No sign is included for a positive time span.

    "d" 
     The number of days in the time interval. This element is omitted if the time interval is less than one day. 

    "hh" 
     The number of hours in the time interval, ranging from 0 to 23. 

    "mm" 
     The number of minutes in the time interval, ranging from 0 to 59. 

    "ss" 
     The number of seconds in the time interval, ranging from 0 to 59. 

    "fffffff" 
     Fractional seconds in the time interval. This element is omitted if the time interval does not include 
     fractional seconds. If present, fractional seconds are always expressed using seven decimal digits.
    */
CREATE FUNCTION [dbo].[ConvertFromTimeSpanString] (@timeSpan varchar(26))
RETURNS bigint
AS
BEGIN
    DECLARE @hourStart int
    DECLARE @minuteStart int
    DECLARE @secondStart int
    DECLARE @ticks bigint
    DECLARE @hours bigint
    DECLARE @minutes bigint
    DECLARE @seconds DECIMAL(9, 7)

    SET @hourStart = CHARINDEX('.', @timeSpan) + 1
    SET @minuteStart = CHARINDEX(':', @timeSpan) + 1
    SET @secondStart = CHARINDEX(':', @timespan, @minuteStart) + 1
    SET @ticks = 0

    IF (@hourStart > 1 AND @hourStart < @minuteStart)
    BEGIN
        SET @ticks = CONVERT(bigint, LEFT(@timespan, @hourstart - 2)) * 864000000000
    END
    ELSE
    BEGIN
        SET @hourStart = 1
    END

    SET @hours = CONVERT(bigint, SUBSTRING(@timespan, @hourStart, @minuteStart - @hourStart - 1))
    SET @minutes = CONVERT(bigint, SUBSTRING(@timespan, @minuteStart, @secondStart - @minuteStart - 1))
    SET @seconds = CONVERT(DECIMAL(9, 7), SUBSTRING(@timespan, @secondStart, LEN(@timeSpan) - @secondStart + 1))

    IF (@ticks < 0)
    BEGIN
        SET @ticks = @ticks - @hours * 36000000000
    END
    ELSE
    BEGIN
        SET @ticks = @ticks + @hours * 36000000000
    END

    IF (@ticks < 0)
    BEGIN
        SET @ticks = @ticks - @minutes * 600000000
    END
    ELSE
    BEGIN
        SET @ticks = @ticks + @minutes * 600000000
    END

    IF (@ticks < 0)
    BEGIN
        SET @ticks = @ticks - @seconds * 10000000.0
    END
    ELSE
    BEGIN
        SET @ticks = @ticks + @seconds * 10000000.0
    END

    RETURN @ticks
END
GO

-- =============================================
-- Author:      James Coe
-- Create date: 2011-05-23
-- Description: Converts from a bigint containing the number of 100 nanosecond ticks to a varchar(26) TimeSpan string.
-- =============================================
/*
[-][d.]hh:mm:ss[.fffffff] 

"-" 
 A minus sign, which indicates a negative time interval. No sign is included for a positive time span.

"d" 
 The number of days in the time interval. This element is omitted if the time interval is less than one day. 

"hh" 
 The number of hours in the time interval, ranging from 0 to 23. 

"mm" 
 The number of minutes in the time interval, ranging from 0 to 59. 

"ss" 
 The number of seconds in the time interval, ranging from 0 to 59. 

"fffffff" 
 Fractional seconds in the time interval. This element is omitted if the time interval does not include 
 fractional seconds. If present, fractional seconds are always expressed using seven decimal digits.
*/
CREATE FUNCTION [dbo].[ConvertToTimeSpanString] (@ticks bigint)
RETURNS varchar(26)
AS
BEGIN
    DECLARE @timeSpanString varchar(26)

    IF (@ticks < 0)
    BEGIN
        SET @timeSpanString = '-'
    END
    ELSE
    BEGIN
        SET @timeSpanString = ''
    END

    -- Days
    DECLARE @days bigint

    SET @days = FLOOR(ABS(@ticks / 864000000000.0))

    IF (@days > 0)
    BEGIN
        SET @timeSpanString = @timeSpanString + CONVERT(varchar(26), @days) + '.'
    END

    SET @ticks = ABS(@ticks % 864000000000)
    -- Hours
    SET @timeSpanString = @timeSpanString + RIGHT('0' + CONVERT(varchar(26), FLOOR(@ticks / 36000000000.0)), 2) + ':'
    SET @ticks = @ticks % 36000000000
    -- Minutes
    SET @timeSpanString = @timeSpanString + RIGHT('0' + CONVERT(varchar(26), FLOOR(@ticks / 600000000.0)), 2) + ':'
    SET @ticks = @ticks % 600000000
    -- Seconds
    SET @timeSpanString = @timeSpanString + RIGHT('0' + CONVERT(varchar(26), FLOOR(@ticks / 10000000.0)), 2)
    SET @ticks = @ticks % 10000000

    -- Fractional Seconds
    IF (@ticks > 0)
    BEGIN
        SET @timeSpanString = @timeSpanString + '.' + LEFT(CONVERT(varchar(26), @ticks) + '0000000', 7)
    END

    RETURN @timeSpanString
END
GO

-- =============================================
-- Author:      James Coe
-- Create date: 2011-05-23
-- Description: Adds the specified number of 100 nanosecond ticks to a date.
-- =============================================
CREATE FUNCTION [dbo].[DateAddTicks] (
    @ticks bigint
    , @starting_date datetimeoffset
    )
RETURNS datetimeoffset
AS
BEGIN
    DECLARE @dateTimeResult datetimeoffset

    IF (@ticks < 0)
    BEGIN
        -- Hours
        SET @dateTimeResult = DATEADD(HOUR, CEILING(@ticks / 36000000000.0), @starting_date)
        SET @ticks = @ticks % 36000000000
        -- Seconds
        SET @dateTimeResult = DATEADD(SECOND, CEILING(@ticks / 10000000.0), @dateTimeResult)
        SET @ticks = @ticks % 10000000
        -- Nanoseconds
        SET @dateTimeResult = DATEADD(NANOSECOND, @ticks * 100, @dateTimeResult)
    END
    ELSE
    BEGIN
        -- Hours
        SET @dateTimeResult = DATEADD(HOUR, FLOOR(@ticks / 36000000000.0), @starting_date)
        SET @ticks = @ticks % 36000000000
        -- Seconds
        SET @dateTimeResult = DATEADD(SECOND, FLOOR(@ticks / 10000000.0), @dateTimeResult)
        SET @ticks = @ticks % 10000000
        -- Nanoseconds
        SET @dateTimeResult = DATEADD(NANOSECOND, @ticks * 100, @dateTimeResult)
    END

    RETURN @dateTimeResult
END
GO

-- =============================================
-- Author:      James Coe
-- Create date: 2011-05-23
-- Description:  Gets the difference between two dates in 100 nanosecond ticks.
-- =============================================
CREATE FUNCTION [dbo].[DateDiffTicks] (
    @starting_date datetimeoffset
    , @ending_date datetimeoffset
    )
RETURNS bigint
AS
BEGIN
    DECLARE @ticks bigint
    DECLARE @days bigint
    DECLARE @hours bigint
    DECLARE @minutes bigint
    DECLARE @seconds bigint

    SET @hours = DATEDIFF(HOUR, @starting_date, @ending_date)
    SET @starting_date = DATEADD(HOUR, @hours, @starting_date)
    SET @ticks = @hours * 36000000000
    SET @seconds = DATEDIFF(SECOND, @starting_date, @ending_date)
    SET @starting_date = DATEADD(SECOND, @seconds, @starting_date)
    SET @ticks = @ticks + @seconds * 10000000
    SET @ticks = @ticks + CONVERT(bigint, DATEDIFF(NANOSECOND, @starting_date, @ending_date)) / 100

    RETURN @ticks
END
GO

--- BEGIN Test Harness ---
SET NOCOUNT ON

DECLARE @dateTimeOffsetMinValue datetimeoffset
DECLARE @dateTimeOffsetMaxValue datetimeoffset
DECLARE @timeSpanMinValueString varchar(26)
DECLARE @timeSpanZeroString varchar(26)
DECLARE @timeSpanMaxValueString varchar(26)
DECLARE @timeSpanMinValueTicks bigint
DECLARE @timeSpanZeroTicks bigint
DECLARE @timeSpanMaxValueTicks bigint
DECLARE @dateTimeOffsetMinMaxDiffTicks bigint
DECLARE @dateTimeOffsetMaxMinDiffTicks bigint

SET @dateTimeOffsetMinValue = '0001-01-01T00:00:00.0000000+00:00'
SET @dateTimeOffsetMaxValue = '9999-12-31T23:59:59.9999999+00:00'
SET @timeSpanMinValueString = '-10675199.02:48:05.4775808'
SET @timeSpanZeroString = '00:00:00'
SET @timeSpanMaxValueString = '10675199.02:48:05.4775807'
SET @timeSpanMinValueTicks = -9223372036854775808
SET @timeSpanZeroTicks = 0
SET @timeSpanMaxValueTicks = 9223372036854775807
SET @dateTimeOffsetMinMaxDiffTicks = 3155378975999999999
SET @dateTimeOffsetMaxMinDiffTicks = -3155378975999999999

-- TimeSpan Conversion Tests
PRINT 'Testing TimeSpan conversions...'

DECLARE @convertToTimeSpanStringMinTicksResult varchar(26)
DECLARE @convertFromTimeSpanStringMinTimeSpanResult bigint
DECLARE @convertToTimeSpanStringZeroTicksResult varchar(26)
DECLARE @convertFromTimeSpanStringZeroTimeSpanResult bigint
DECLARE @convertToTimeSpanStringMaxTicksResult varchar(26)
DECLARE @convertFromTimeSpanStringMaxTimeSpanResult bigint

SET @convertToTimeSpanStringMinTicksResult = dbo.ConvertToTimeSpanString(@timeSpanMinValueTicks)
SET @convertFromTimeSpanStringMinTimeSpanResult = dbo.ConvertFromTimeSpanString(@timeSpanMinValueString)
SET @convertToTimeSpanStringZeroTicksResult = dbo.ConvertToTimeSpanString(@timeSpanZeroTicks)
SET @convertFromTimeSpanStringZeroTimeSpanResult = dbo.ConvertFromTimeSpanString(@timeSpanZeroString)
SET @convertToTimeSpanStringMaxTicksResult = dbo.ConvertToTimeSpanString(@timeSpanMaxValueTicks)
SET @convertFromTimeSpanStringMaxTimeSpanResult = dbo.ConvertFromTimeSpanString(@timeSpanMaxValueString)

-- Test Results
SELECT 'Convert to TimeSpan String from Ticks (Minimum)' AS Test
    , CASE 
        WHEN @convertToTimeSpanStringMinTicksResult = @timeSpanMinValueString
            THEN 'Pass'
        ELSE 'Fail'
        END AS [Test Status]
    , @timeSpanMinValueTicks AS [Ticks]
    , CONVERT(varchar(26), NULL) AS [TimeSpan String]
    , CONVERT(varchar(26), @convertToTimeSpanStringMinTicksResult) AS [Actual Result]
    , CONVERT(varchar(26), @timeSpanMinValueString) AS [Expected Result]
UNION ALL
SELECT 'Convert from TimeSpan String to Ticks (Minimum)' AS Test
    , CASE 
        WHEN @convertFromTimeSpanStringMinTimeSpanResult = @timeSpanMinValueTicks
            THEN 'Pass'
        ELSE 'Fail'
        END AS [Test Status]
    , NULL AS [Ticks]
    , @timeSpanMinValueString AS [TimeSpan String]
    , CONVERT(varchar(26), @convertFromTimeSpanStringMinTimeSpanResult) AS [Actual Result]
    , CONVERT(varchar(26), @timeSpanMinValueTicks) AS [Expected Result]
UNION ALL
SELECT 'Convert to TimeSpan String from Ticks (Zero)' AS Test
    , CASE 
        WHEN @convertToTimeSpanStringZeroTicksResult = @timeSpanZeroString
            THEN 'Pass'
        ELSE 'Fail'
        END AS [Test Status]
    , @timeSpanZeroTicks AS [Ticks]
    , CONVERT(varchar(26), NULL) AS [TimeSpan String]
    , CONVERT(varchar(26), @convertToTimeSpanStringZeroTicksResult) AS [Actual Result]
    , CONVERT(varchar(26), @timeSpanZeroString) AS [Expected Result]
UNION ALL
SELECT 'Convert from TimeSpan String to Ticks (Zero)' AS Test
    , CASE 
        WHEN @convertFromTimeSpanStringZeroTimeSpanResult = @timeSpanZeroTicks
            THEN 'Pass'
        ELSE 'Fail'
        END AS [Test Status]
    , NULL AS [Ticks]
    , @timeSpanZeroString AS [TimeSpan String]
    , CONVERT(varchar(26), @convertFromTimeSpanStringZeroTimeSpanResult) AS [Actual Result]
    , CONVERT(varchar(26), @timeSpanZeroTicks) AS [Expected Result]
UNION ALL
SELECT 'Convert to TimeSpan String from Ticks (Maximum)' AS Test
    , CASE 
        WHEN @convertToTimeSpanStringMaxTicksResult = @timeSpanMaxValueString
            THEN 'Pass'
        ELSE 'Fail'
        END AS [Test Status]
    , @timeSpanMaxValueTicks AS [Ticks]
    , CONVERT(varchar(26), NULL) AS [TimeSpan String]
    , CONVERT(varchar(26), @convertToTimeSpanStringMaxTicksResult) AS [Actual Result]
    , CONVERT(varchar(26), @timeSpanMaxValueString) AS [Expected Result]
UNION ALL
SELECT 'Convert from TimeSpan String to Ticks (Maximum)' AS Test
    , CASE 
        WHEN @convertFromTimeSpanStringMaxTimeSpanResult = @timeSpanMaxValueTicks
            THEN 'Pass'
        ELSE 'Fail'
        END AS [Test Status]
    , NULL AS [Ticks]
    , @timeSpanMaxValueString AS [TimeSpan String]
    , CONVERT(varchar(26), @convertFromTimeSpanStringMaxTimeSpanResult) AS [Actual Result]
    , CONVERT(varchar(26), @timeSpanMaxValueTicks) AS [Expected Result]

-- Ticks Date Add Test
PRINT 'Testing DateAddTicks...'

DECLARE @DateAddTicksPositiveTicksResult datetimeoffset
DECLARE @DateAddTicksZeroTicksResult datetimeoffset
DECLARE @DateAddTicksNegativeTicksResult datetimeoffset

SET @DateAddTicksPositiveTicksResult = dbo.DateAddTicks(@dateTimeOffsetMinMaxDiffTicks, @dateTimeOffsetMinValue)
SET @DateAddTicksZeroTicksResult = dbo.DateAddTicks(@timeSpanZeroTicks, @dateTimeOffsetMinValue)
SET @DateAddTicksNegativeTicksResult = dbo.DateAddTicks(@dateTimeOffsetMaxMinDiffTicks, @dateTimeOffsetMaxValue)

-- Test Results
SELECT 'Date Add with Ticks Test (Positive)' AS Test
    , CASE 
        WHEN @DateAddTicksPositiveTicksResult = @dateTimeOffsetMaxValue
            THEN 'Pass'
        ELSE 'Fail'
        END AS [Test Status]
    , @dateTimeOffsetMinMaxDiffTicks AS [Ticks]
    , @dateTimeOffsetMinValue AS [Starting Date]
    , @DateAddTicksPositiveTicksResult AS [Actual Result]
    , @dateTimeOffsetMaxValue AS [Expected Result]
UNION ALL
SELECT 'Date Add with Ticks Test (Zero)' AS Test
    , CASE 
        WHEN @DateAddTicksZeroTicksResult = @dateTimeOffsetMinValue
            THEN 'Pass'
        ELSE 'Fail'
        END AS [Test Status]
    , @timeSpanZeroTicks AS [Ticks]
    , @dateTimeOffsetMinValue AS [Starting Date]
    , @DateAddTicksZeroTicksResult AS [Actual Result]
    , @dateTimeOffsetMinValue AS [Expected Result]
UNION ALL
SELECT 'Date Add with Ticks Test (Negative)' AS Test
    , CASE 
        WHEN @DateAddTicksNegativeTicksResult = @dateTimeOffsetMinValue
            THEN 'Pass'
        ELSE 'Fail'
        END AS [Test Status]
    , @dateTimeOffsetMaxMinDiffTicks AS [Ticks]
    , @dateTimeOffsetMaxValue AS [Starting Date]
    , @DateAddTicksNegativeTicksResult AS [Actual Result]
    , @dateTimeOffsetMinValue AS [Expected Result]

-- Ticks Date Diff Test
PRINT 'Testing Date Diff Ticks...'

DECLARE @dateDiffTicksMinMaxResult bigint
DECLARE @dateDiffTicksMaxMinResult bigint

SET @dateDiffTicksMinMaxResult = dbo.DateDiffTicks(@dateTimeOffsetMinValue, @dateTimeOffsetMaxValue)
SET @dateDiffTicksMaxMinResult = dbo.DateDiffTicks(@dateTimeOffsetMaxValue, @dateTimeOffsetMinValue)

-- Test Results
SELECT 'Date Difference in Ticks Test (Min, Max)' AS Test
    , CASE 
        WHEN @dateDiffTicksMinMaxResult = @dateTimeOffsetMinMaxDiffTicks
            THEN 'Pass'
        ELSE 'Fail'
        END AS [Test Status]
    , @dateTimeOffsetMinValue AS [Starting Date]
    , @dateTimeOffsetMaxValue AS [Ending Date]
    , @dateDiffTicksMinMaxResult AS [Actual Result]
    , @dateTimeOffsetMinMaxDiffTicks AS [Expected Result]
UNION ALL
SELECT 'Date Difference in Ticks Test (Max, Min)' AS Test
    , CASE 
        WHEN @dateDiffTicksMaxMinResult = @dateTimeOffsetMaxMinDiffTicks
            THEN 'Pass'
        ELSE 'Fail'
        END AS [Test Status]
    , @dateTimeOffsetMaxValue AS [Starting Date]
    , @dateTimeOffsetMinValue AS [Ending Date]
    , @dateDiffTicksMaxMinResult AS [Actual Result]
    , @dateTimeOffsetMaxMinDiffTicks AS [Expected Result]

PRINT 'Tests Complete.'
GO
--- END Test Harness ---
JamieSee
источник