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 เซิร์ฟเวอร์อะไร


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(ควรเป็น 10,000) เพื่อให้แน่ใจ
Tom Chantler

ตัวเลือกจะเก็บไว้เป็นสตริงจากนั้นคุณสามารถโหลดโดยใช้ TimeSpan.Parse (ข้อความ) ไม่เหมาะจากมุมมองที่ขนาดหรือ SQL querys แต่สามารถแยกวิเคราะห์ใน TSQL ถ้าจำเป็น
วอลเตอร์ Vehoeven

65

ขอบคุณสำหรับคำแนะนำ. เนื่องจากไม่มีเทียบเท่าในเซิร์ฟเวอร์ SQL ฉันเพิ่งสร้างเขตข้อมูลที่ 2 ซึ่งแปลง TimeSpan เพื่อทำเครื่องหมายและเก็บไว้ในฐานข้อมูล ฉันป้องกันการจัดเก็บ TimeSpan

public Int64 ValidityPeriodTicks { get; set; }

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

6
นอกจากนี้สำหรับทุกคนที่ใช้ EF Core - ใน 2.1 คุณสามารถใช้การแปลงค่าและ TimeSpanToTicksConverter เพื่อทำแผนที่ช่วงเวลาเพื่อทำเครื่องหมายในฐานข้อมูลอย่างโปร่งใส
GraemeMiller

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
ตามที่ OP กล่าวว่า "เวลา" DataType ใน SQL Server รองรับเฉพาะสูงสุด 24 ชั่วโมงเขาต้องการจัดเก็บ> 24 ชั่วโมง
MichelZ

11
นอกจากนี้ TimeSpan (.NET) สามารถเป็นลบได้ในขณะที่ Time (SQL Server) ไม่สามารถทำได้
Edward

11
มีความแตกต่างที่สำคัญระหว่างเวลาและระยะเวลา เวลาแสดงเวลาในแต่ละวันในขณะที่ระยะเวลาคือความแตกต่างระหว่างสองช่วงเวลา เปรียบเทียบกับสถานที่ (เวลา) และระยะทาง (ระยะเวลา)
Ramon de Klein

3
^ แน่นอน - TimeชนิดSQL ไม่ได้หมายถึงการแสดงช่วงเวลา แต่ส่วนเวลาของค่า DateTime TimeSpanมันเป็นทางเลือกที่น่ากลัวสำหรับ
BrainSlugs83

19

ไม่เทียบเท่าโดยตรง เพียงเก็บเป็นตัวเลขเช่นจำนวนวินาทีหรือบางสิ่งบางอย่างที่เหมาะสมกับความแม่นยำที่คุณต้องการ


กล่าวคือ เก็บไว้เป็นแบบลอยและใช้ 'TimeSpan.FromSeconds' ตามmsdn.microsoft.com/en-us/library/ ......
CAD bloke

7

ฉันรู้ว่านี่เป็นคำถามเก่า แต่ฉันต้องการตรวจสอบให้แน่ใจว่ามีตัวเลือกอื่นสองสามข้อ

เนื่องจากคุณไม่สามารถจัดเก็บ TimeSpan มากกว่า 24 ชั่วโมงในเขตเวลาประเภทข้อมูล sql อาจมีตัวเลือกอื่นสองสามอย่าง

  1. ใช้ varchar (xx) เพื่อจัดเก็บ ToString ของ TimeSpan ประโยชน์ของสิ่งนี้คือความแม่นยำไม่จำเป็นต้องอบลงในประเภทข้อมูลหรือการคำนวณ (วินาทีเทียบกับมิลลิวินาทีเทียบกับวันเทียบกับป้อมปราการ) สิ่งที่คุณต้องใช้คือ TimeSpan.Parse / TryParse นี่คือสิ่งที่ฉันจะทำ

  2. ใช้วันที่สองวันที่และเวลาหรือชุดข้อมูลวันที่ที่เก็บผลลัพธ์ของวันแรก + ช่วงเวลา การอ่านจาก db เป็นเรื่องของ TimeSpan x = SecondDate - FirstDate การใช้ตัวเลือกนี้จะปกป้องคุณสำหรับไลบรารีการเข้าถึงข้อมูลอื่น ๆ ที่ไม่ใช่. NET เข้าถึงข้อมูลเดียวกัน แต่ไม่เข้าใจ TimeSpans ในกรณีที่คุณมีสภาพแวดล้อมดังกล่าว


1
ตัวเลือกที่ 2 ดูเหมือนจะเป็นประโยชน์ทุกครั้งแล้ว ขอบคุณ
rahicks

3

เพื่อให้สอดคล้องกับสิ่งที่อาจเป็นแหล่งที่มาของการสร้างช่วงเวลาที่มากที่สุด (การคำนวณความแตกต่างของ 2 ครั้งหรือวันที่ - เวลา) คุณอาจต้องการเก็บ. NET TimeSpanเป็น SQL Server DateTimeType

นี่เป็นเพราะใน SQL Server ความแตกต่างของ 2 DateTime( CastถึงFloat's' และจากนั้น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 คุณต้องแปลงเป็นDateTimeประเภท. NET เป็นอันดับแรกโดยเพิ่มลงDateTimeในวันที่ 1 มกราคม 1900 แน่นอนว่าเมื่อคุณอ่านมันลงใน. NET จาก SQL Server คุณต้องก่อน อ่านมันเป็น .NET DateTimeแล้วลบ 1 มกราคม 1900 จากมันจะแปลงเป็น TimeSpan.NET

สำหรับกรณีการใช้งานที่มีการสร้างช่วงเวลาจาก SQL Server DateTimeและภายใน SQL Server (เช่นผ่าน T-SQL) และ SQL Server ก่อนปี 2559 ขึ้นอยู่กับช่วงและความต้องการความแม่นยำของคุณอาจไม่สามารถจัดเก็บได้ เป็นมิลลิวินาที (ไม่ต้องพูดถึงTicks) เพราะIntประเภทที่ส่งคืนโดยDateDiff(เทียบกับล้นBigIntจาก SS 2016 + จากDateDiff_Big) หลังจาก ~ 24 วันมูลค่ามิลลิวินาทีประมาณ ~ 67 ปี วินาที ในขณะที่วิธีนี้จะจัดการกับช่วงเวลาที่มีความแม่นยำลดลงถึง 0.1 วินาทีและจาก -147 ถึง +8,099 ปี

คำเตือน:

  1. สิ่งนี้จะใช้งานได้หากความแตกต่างที่สัมพันธ์กับ 1 มกราคม 1900 จะส่งผลให้ค่าอยู่ในช่วงของDateTimeประเภทเซิร์ฟเวอร์ SQL (1 มกราคม 1753 ถึง 31 ธันวาคม 9999 หรือ 9999 ถึง +8,099 ปี) เราไม่ต้องกังวลมากไปกว่าTimeSpanฝั่งNET เนื่องจากมันสามารถเก็บได้ ~ 29 k ถึง +29 k ปี ผมไม่ได้พูดถึงเซิร์ฟเวอร์ SQL DateTime2ประเภท (ซึ่งเป็นช่วงในด้านลบที่มากขึ้นกว่า SQL Server DateTime's) เพราะก) ก็ไม่สามารถแปลงเป็นตัวเลขทางที่ง่ายCastและ b) DateTime' s ช่วงควรจะพอเพียง สำหรับกรณีการใช้งานส่วนใหญ่

  2. SQL Server DateTimeแตกต่างคำนวณผ่านCast- เพื่อ - Float- และ - วิธีกลับไม่ปรากฏว่ามีความถูกต้องเกิน 0.1 วินาที


ฉันลืมไปเลยว่าฉันอ่านคำถามนี้น้อยกว่าที่ฉันเขียน A และค้นหา A อีกครั้ง ฉันเริ่มอ่าน A และคิดกับตัวเอง: (ว้าวนี่เป็นคำตอบที่ดีที่สุดจนถึงตอนนี้!) : D
Tom

3

มีหลายวิธีในการนำเสนอช่วงเวลาในฐานข้อมูล

เวลา

ประเภทข้อมูลนี้ได้รับการสนับสนุนตั้งแต่ SQL Server 2008 และเป็นวิธีที่ต้องการTimeSpanในการจัดเก็บ ไม่จำเป็นต้องทำแผนที่ นอกจากนี้ยังทำงานได้ดีกับรหัส SQL

public TimeSpan ValidityPeriod { get; set; }

อย่างไรก็ตามตามที่ระบุไว้ในคำถามเดิมประเภทข้อมูลนี้ถูก จำกัด ไว้ที่ 24 ชั่วโมง

datetimeoffset

ประเภทข้อมูลแผนที่โดยตรงกับdatetimeoffset System.DateTimeOffsetมันใช้เพื่อแสดงการชดเชยระหว่างdatetime/ datetime2ถึง UTC แต่คุณสามารถใช้มัน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"); }
}

โบนัส: ระยะเวลาและระยะเวลา

ใช้ string คุณยังสามารถจัดเก็บ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 หลัก 2.1 คุณมีตัวเลือกที่จะใช้ราคาการแปลง

อย่างไรก็ตามเมื่อใช้สิ่งนี้EF จะไม่สามารถแปลงเคียวรีจำนวนมากเป็น SQL ได้ทำให้คิวรีทำงานในหน่วยความจำ อาจโอนข้อมูลจำนวนมากไปยังแอปพลิเคชันของคุณ

ดังนั้นอย่างน้อยตอนนี้มันอาจจะดีกว่าที่จะไม่ใช้มันและเพียงแค่ map ผลการสืบค้นโดยAutomapper


1

โดยทั่วไปแล้วฉันจะเก็บ TimeSpan เป็นรายการขนาดใหญ่ที่มีเครื่องหมายถูกจากคุณสมบัติ TimeSpan.Ticks ตามที่แนะนำไว้ก่อนหน้านี้ คุณยังสามารถเก็บ TimeSpan ในรูปแบบ varchar (26) ที่มีเอาต์พุตของ TimeSpan.ToString () ฟังก์ชั่นสเกลาร์ทั้งสี่ (ConvertFromTimeSpanString, ConvertToTimeSpanString, DateAddTicks, DateDiffTicks) ที่ฉันเขียนมีประโยชน์สำหรับการจัดการ TimeSpan ทางฝั่ง SQL และหลีกเลี่ยงแฮ็คที่จะสร้างช่วงที่มีขอบเขตเทียม หากคุณสามารถจัดเก็บช่วงเวลาใน. NET TimeSpan ได้ทั้งหมดก็ควรใช้งานได้กับฟังก์ชั่นเหล่านี้ด้วย นอกจากนี้ฟังก์ชั่นช่วยให้คุณทำงานกับ TimeSpans และ 100-nanosecond ทำเครื่องหมายแม้ในขณะที่ใช้เทคโนโลยีที่ไม่รวม. 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 ---
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.