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



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



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



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

что является лучшим способом для обработки хранения .Сети TimeSpan в SQL server?

806   7  

7 ответов:

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

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

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

public Int64 ValidityPeriodTicks { get; set; }

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

Если вам не нужно хранить более 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);

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

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

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

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

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

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

это потому, что в SQL Server, разница 2 DateTime ' s (Cast до Floatи затем Cast обратно в DateTime) это просто DateTime относительно января. 1, 1900. Бывший. Разница +0,1 секунды будет 1 января 1900 года 00:00:00.100 и -0,1 вторым будет декабрь. 31, 1899 23:59:59.900.

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

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

предупреждения:

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

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

обычно, я хранить значение типа TimeSpan как значение типа bigint заполняется клещей от времени.Свойство клещей, как предлагалось ранее. Вы также можете сохранить TimeSpan как varchar (26), заполненный выводом TimeSpan.ToString (). Четыре скалярные функции (ConvertFromTimeSpanString, ConvertToTimeSpanString, DateAddTicks, DateDiffTicks), которые я написал, полезны для обработки TimeSpan на стороне SQL и избегают хаков, которые создавали бы искусственно ограниченные диапазоны. Если вы можете сохранить интервал в .NET TimeSpan вообще он должен работать и с этими функциями. Кроме того, функции позволяют работать с временными интервалами и 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 ---

Comments

    Ничего не найдено.