¿Cuál es el tipo de SQL correcto para almacenar un intervalo de tiempo .Net con valores> 24:00:00?

196

Estoy tratando de almacenar un .Net TimeSpanen SQL Server 2008 R2.

EF Code First parece estar sugiriendo que debería almacenarse como a Time(7)en SQL.

Sin embargo, TimeSpanen .Net puede manejar períodos más largos de 24 horas.

¿Cuál es la mejor manera de manejar el almacenamiento de .Net TimeSpanen el servidor SQL?

GraemeMiller
fuente
15
Lo estoy usando para almacenar la duración de los eventos recurrentes. Por lo tanto, quería capturar la duración del evento independientemente de la fecha
GraemeMiller,
1
Relacionados no duplicados. Les escribí a los dos. Uno es sobre Code First y cómo cambiar el mapa para TimeSpan. El otro es sobre el mapeo de tiempo real de tipo .Net a SQL.
GraemeMiller

Respuestas:

222

Lo almacenaría en la base de datos como BIGINTay almacenaría la cantidad de ticks (por ejemplo, TimeSpan.Ticks propiedad ).

De esa manera, si quisiera obtener un objeto TimeSpan cuando lo recupere, podría hacer TimeSpan.FromTicks (valor), lo que sería fácil.

Tom Chantler
fuente
3
¿Cómo manejaría los cálculos en sql, digamos que necesita calcular cuántas horas contiene?
Peter
10
Probablemente me convierto las garrapatas en un objeto de momento como este: SELECT CAST(DATEADD(MILLISECOND, @Ticks/CAST(10000 AS BIGINT), '1900-01-01') AS TIME). La '1900-01-01'fecha no importa, por supuesto, es solo la tercera variable requerida por la DATEADD(...)función. Recuerde que hay 100 nanosegundos en una marca, pero si lo usa DATEADD(NANOSECOND..., es probable que obtenga un desbordamiento, por lo tanto, use milisegundos. También recuerde que debe verificar este hecho usando C # TimeSpan.TicksPerMillisecond(debe ser 10000) para estar seguro.
Tom Chantler
Una opción es almacenarlo como una cadena, luego puede cargarlo usando TimeSpan.Parse (texto). no es ideal desde una perspectiva de tamaño o consultas SQL, pero se puede analizar en TSQL si es necesario
Walter Vehoeven
65

Gracias por el consejo. Como no hay equivalente en el servidor SQL. Simplemente creé un segundo campo que convirtió el TimeSpan en ticks y lo almacené en la base de datos. Entonces evité almacenar el TimeSpan

public Int64 ValidityPeriodTicks { get; set; }

[NotMapped]
public TimeSpan ValidityPeriod
{
    get { return TimeSpan.FromTicks(ValidityPeriodTicks); }
    set { ValidityPeriodTicks = value.Ticks; }
}
GraemeMiller
fuente
66
También para cualquiera que use EF Core: en 2.1 puede usar conversiones de valores y TimeSpanToTicksConverter para asignar intervalos de tiempo a ticks en la base de datos de manera transparente
GraemeMiller
30

Si no tiene que almacenar más de 24 horas, solo puede almacenar tiempo , ya que SQL Server 2008 y versiones posteriores son

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

No se necesitan conversiones si solo necesita almacenar 24 horas o menos.

Fuente: http://msdn.microsoft.com/en-us/library/cc716729(v=vs.110).aspx

Pero , si desea almacenar más de 24 h, tendrá que almacenarlo en ticks, recuperar los datos y luego convertirlos a TimeSpan. Por ejemplo

int timeData = yourContext.yourTable.FirstOrDefault();
TimeSpan ts = TimeSpan.FromMilliseconds(timeData);
Alejandro del Río
fuente
23
Como dice el OP, el tipo de datos "time" en SQL Server solo admite hasta 24 h, quiere almacenar> 24 h
MichelZ
11
Además, TimeSpan (.NET) puede ser negativo, mientras que Time (SQL Server) no.
Edward
11
Hay una gran diferencia entre un tiempo y una duración. La hora representa la hora de un día determinado, mientras que la duración es la diferencia entre dos momentos. Compárelo con una ubicación (tiempo) y distancia (duración).
Ramon de Klein
3
^ Exactamente. - El Timetipo SQL no pretende representar una duración, sino la parte Time de un valor DateTime; Es una elección terrible para TimeSpan.
BrainSlugs83
19

No hay un equivalente directo. Simplemente guárdelo numéricamente, por ejemplo, número de segundos o algo apropiado para su precisión requerida.

Miedo de planetas
fuente
es decir. guárdelo como flotante y use `TimeSpan.FromSeconds` según msdn.microsoft.com/en-us/library/…
CAD bloke
7

Sé que esta es una vieja pregunta, pero quería asegurarme de que se tengan en cuenta algunas otras opciones.

Como no puede almacenar un TimeSpan mayor de 24 horas en un campo de tipo de datos sql de tiempo; Un par de otras opciones podrían ser.

  1. Use un varchar (xx) para almacenar el ToString del TimeSpan. El beneficio de esto es que la precisión no tiene que integrarse en el tipo de datos o el cálculo (segundos frente a milisegundos frente a días frente a quincenas) Todo lo que necesita hacer es usar TimeSpan.Parse / TryParse. Esto es lo que haría.

  2. Use una segunda fecha, datetime o datetimeoffset, que almacena el resultado de la primera fecha + intervalo de tiempo. Leer desde la base de datos es una cuestión de TimeSpan x = SecondDate - FirstDate. El uso de esta opción lo protegerá para que otras bibliotecas que no tengan acceso a datos .NET accedan a los mismos datos pero no entiendan TimeSpans; en caso de que tengas tal ambiente.

almiar
fuente
1
La opción 2 suena como si pudiera ser útil de vez en cuando. thx
rahicks
3

Para ser coherente con la que probablemente sea la fuente más probable de generar un intervalo de tiempo (calculando la diferencia de 2 veces o fecha-hora), es posible que desee almacenar un .NET TimeSpancomo un DateTimetipo de servidor SQL .

Esto se debe a que en SQL Server, la diferencia de 2 DateTime'( Casta Float' s y luego de Castnuevo a a DateTime) es simplemente DateTimerelativa al 1 de enero de 1900. Ej. Una diferencia de +0.1 segundo sería el 1 de enero de 1900 00: 00: 00.100 y -0.1 segundo sería el 31 de diciembre de 1899 23: 59: 59.900.

Para convertir un .NET TimeSpanen un DateTimetipo de servidor SQL , primero debe convertirlo en un DateTimetipo .NET agregándolo al 1 DateTimede enero de 1900. Por supuesto, cuando lo lea en .NET desde SQL Server, primero léalo en un .NET DateTimey luego reste el 1 de enero de 1900 para convertirlo en .NET TimeSpan.

Para los casos de uso en los que el período de tiempo se genera desde SQL Server DateTimey dentro de SQL Server (es decir, a través de T-SQL) y SQL Server es anterior a 2016, dependiendo de sus necesidades de rango y precisión, puede no ser práctico almacenarlos como milisegundos (sin mencionar Ticks) porque el IntTipo devuelto por DateDiff(frente a los BigIntde SS 2016 + DateDiff_Big) se desborda después de ~ 24 días de milisegundos y ~ 67 años. de segundos Mientras que, esta solución manejará períodos de tiempo con precisión de hasta 0.1 segundos y de -147 a +8,099 años.

ADVERTENCIAS

  1. Esto solo funcionaría si la diferencia relativa al 1 de enero de 1900 daría como resultado un valor dentro del rango de un DateTimetipo de servidor SQL (1 de enero de 1753 al 31 de diciembre de 9999, también conocido como -147 a +8,099 años). No tenemos que preocuparnos tanto por el TimeSpanlado de .NET , ya que puede contener ~ 29 k a +29 k años. No mencioné el servidor SQLDateTime2 (cuyo rango, en el lado negativo, es mucho mayor que el de SQL Server DateTime), porque: a) no se puede convertir a un valor numérico a través de un simple Castyb) DateTimeel rango debería ser suficiente para la gran mayoría de los casos de uso.

  2. SQL Server DateTimediferencias calculadas a través de la Cast- a - Float- y - método de respaldo no parece ser precisa más allá de 0,1 segundos.

Tom
fuente
Olvidé incluso haber leído esta Q mucho menos que escribí esta A, y estaba buscando una A nuevamente. Comencé a leer esta A y pensé para mí mismo: (¡Guau, esta es la mejor respuesta hasta ahora!). : D
Tom
3

Hay varias formas de presentar un intervalo de tiempo en la base de datos.

hora

Este tipo de datos es compatible desde SQL Server 2008 y es la forma preferida de almacenar un TimeSpan. No se necesita mapeo. También funciona bien con código SQL.

public TimeSpan ValidityPeriod { get; set; }

Sin embargo, como se indicó en la pregunta original, este tipo de datos está limitado a 24 horas.

datetimeoffset

El datetimeoffsettipo de datos se asigna directamente a System.DateTimeOffset. Se usa para expresar el desplazamiento entre a datetime/ datetime2a UTC, pero también puede usarlo para TimeSpan.

Sin embargo, dado que el tipo de datos sugiere una semántica muy específica, también debe considerar otras opciones.

datetime / datetime2

Un enfoque podría ser usar los tipos datetimeo datetime2. Esto es mejor en escenarios donde necesita procesar los valores en la base de datos directamente, es decir. para vistas, procedimientos almacenados o informes. El inconveniente es que debe restar el valor DateTime(1900,01,01,00,00,00)de la fecha para recuperar el intervalo de tiempo en su lógica empresarial.

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; }
}

Empezando

Otro enfoque podría ser convertir el TimeSpan en ticks y usar el biginttipo de datos. Sin embargo, este enfoque tiene el inconveniente de que es engorroso usarlo en consultas SQL.

public long ValidityPeriod { get; set; }

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

varchar (N)

Esto es mejor para los casos en que el valor debería ser legible por los humanos. También puede usar este formato en consultas SQL utilizando la CONVERT(datetime, ValidityPeriod)función. Dependiendo de la precisión requerida, necesitará entre 8 y 25 caracteres.

public string ValidityPeriod { get; set; }

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

Bonificación: período y duración

Usando una cadena, también puede almacenar tipos de datos NodaTime , especialmente Durationy Period. El primero es básicamente lo mismo que un TimeSpan, mientras que el segundo respeta que algunos días y meses son más largos o más cortos que otros (es decir, enero tiene 31 días y febrero tiene 28 o 29; algunos días son más largos o más cortos debido al horario de verano ) En tales casos, usar un TimeSpan es la elección incorrecta.

Puede usar este código para convertir períodos:

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)));
    }
}

Y luego úsalo como

public string ValidityPeriod { get; set; }

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

Me gusta mucho NodaTimey, a menudo, me salva de errores difíciles y mucho dolor de cabeza. El inconveniente aquí es que realmente no puede usarlo en consultas SQL y necesita hacer cálculos en la memoria.

Tipo definido por el usuario CLR

También tiene la opción de usar un tipo de datos personalizado y admitir una TimeSpanclase personalizada directamente. Consulte Tipos definidos por el usuario de CLR para más detalles.

El inconveniente aquí es que el tipo de datos podría no comportarse bien con los informes SQL. Además, algunas versiones de SQL Server (Azure, Linux, Data Warehouse) no son compatibles.

Conversiones de valor

A partir de EntityFramework Core 2.1, tiene la opción de usar Conversiones de valor .

Sin embargo, al usar esto, EF no podrá convertir muchas consultas en SQL, lo que hará que las consultas se ejecuten en la memoria; potencialmente transfiriendo montones y montones de datos a su aplicación.

Entonces, al menos por ahora, podría ser mejor no usarlo, y simplemente mapear el resultado de la consulta con Automapper .

MovGP0
fuente
1

Por lo general, almaceno un TimeSpan como un bigint poblado con ticks de la propiedad TimeSpan.Ticks como se sugirió anteriormente. También puede almacenar un TimeSpan como varchar (26) poblado con la salida de TimeSpan.ToString (). Las cuatro funciones escalares (ConvertFromTimeSpanString, ConvertToTimeSpanString, DateAddTicks, DateDiffTicks) que escribí son útiles para manejar TimeSpan en el lado de SQL y evitar los ataques que producirían rangos limitados artificialmente. Si puede almacenar el intervalo en un .NET TimeSpan, también debería funcionar con estas funciones. Además, las funciones le permiten trabajar con TimeSpans y ticks de 100 nanosegundos, incluso cuando se utilizan tecnologías que no incluyen .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 ---
JamieVer
fuente