How to calculate the local datetime from a utc datetime in tsql (sql 2005)?

前端 未结 10 1339
Happy的楠姐
Happy的楠姐 2021-02-02 14:13

i want to loop over a period of time in tsql, and print the utc datetimes and our local variant. We live in UTC +1, so i could easily add 1 hour, but in the summertime we live i

相关标签:
10条回答
  • 2021-02-02 15:06

    Here is a function (again US ONLY) but it is a bit more flexible. It will convert a UTC date to the server local time. It starts by adjusting the appointment date based on the current offset and then adjusts based on the difference of the current offset and the offset of the date of the appointment.

    CREATE FUNCTION [dbo].[fnGetServerTimeFromUTC]
    (
        @AppointmentDate AS DATETIME, 
        @DateTimeOffset DATETIMEOFFSET
    )
    RETURNS DATETIME
    AS
    BEGIN
        --DECLARE @AppointmentDate DATETIME;
        --SET @AppointmentDate = '2016-12-01 12:00:00'; SELECT @AppointmentDate;
    
        --Get DateTimeOffset from Server
        --DECLARE @DateTimeOffset; SET @DateTimeOffset = SYSDATETIMEOFFSET();
        DECLARE @DateTimeOffsetStr NVARCHAR(34) = @DateTimeOffset;
    
        --Set a standard DatePart value for Sunday (server configuration agnostic)
        DECLARE @dp_Sunday INT = 7 - @@DATEFIRST + 1;
    
        --2006 DST Start First Sunday in April (earliest is 04-01) Ends Last Sunday in October (earliest is 10-25)
        --2007 DST Start Second Sunday March (earliest is 03-08) Ends First Sunday Nov (earliest is 11-01)
        DECLARE @Start2006 NVARCHAR(6) = '04-01-';
        DECLARE @End2006 NVARCHAR(6) = '10-25-';
        DECLARE @Start2007 NVARCHAR(6) = '03-08-';
        DECLARE @End2007 NVARCHAR(6) = '11-01-';
    
        DECLARE @ServerDST SMALLINT = 0;
        DECLARE @ApptDST SMALLINT = 0;
        DECLARE @Start DATETIME;
        DECLARE @End DATETIME;
    
        DECLARE @CurrentMinuteOffset INT; 
    
        DECLARE @str_Year NVARCHAR(4) = LEFT(@DateTimeOffsetStr,4);
        DECLARE @Year INT = CONVERT(INT, @str_Year);
    
        SET @CurrentMinuteOffset = CONVERT(INT, SUBSTRING(@DateTimeOffsetStr,29,3)) * 60 + CONVERT(INT, SUBSTRING(@DateTimeOffsetStr,33,2)); --Hours + Minutes
    
        --Determine DST Range for Server Offset
        SET @Start = CASE 
            WHEN @Year <= 2006 THEN CONVERT(DATETIME, @Start2006 + @str_Year + ' 02:00:00')
            ELSE CONVERT(DATETIME, @Start2007 + @str_Year + ' 02:00:00')
            END;
        WHILE @dp_Sunday <> DATEPART(WEEKDAY, @Start) BEGIN
            SET @Start = DATEADD(DAY, 1, @Start)
        END;
    
        SET @End = CASE 
            WHEN @Year <= 2006 THEN CONVERT(DATETIME, @End2006 + @str_Year + ' 02:00:00')
            ELSE CONVERT(DATETIME, @End2007 + @str_Year + ' 02:00:00')
            END;
        WHILE @dp_Sunday <> DATEPART(WEEKDAY, @End) BEGIN
            SET @End = DATEADD(DAY, 1, @End)
        END;
    
        --Determine Current Offset based on Year
        IF @DateTimeOffset >= @Start AND @DateTimeOffset < @End SET @ServerDST = 1;
    
        --Determine DST status of Appointment Date
        SET @Year = YEAR(@AppointmentDate);
    
        SET @Start = CASE 
            WHEN @Year <= 2006 THEN CONVERT(DATETIME, @Start2006 + @str_Year + ' 02:00:00')
            ELSE CONVERT(DATETIME, @Start2007 + @str_Year + ' 02:00:00')
            END;
        WHILE @dp_Sunday <> DATEPART(WEEKDAY, @Start) BEGIN
            SET @Start = DATEADD(DAY, 1, @Start)
        END;
    
        SET @End = CASE 
            WHEN @Year <= 2006 THEN CONVERT(DATETIME, @End2006 + @str_Year + ' 02:00:00')
            ELSE CONVERT(DATETIME, @End2007 + @str_Year + ' 02:00:00')
            END;
        WHILE @dp_Sunday <> DATEPART(WEEKDAY, @End) BEGIN
            SET @End = DATEADD(DAY, 1, @End)
        END;
    
        --Determine Appointment Offset based on Year
        IF @AppointmentDate >= @Start AND @AppointmentDate < @End SET @ApptDST = 1;
    
        SET @AppointmentDate = DATEADD(MINUTE, @CurrentMinuteOffset + 60 * (@ApptDST - @ServerDST), @AppointmentDate)
    
        RETURN @AppointmentDate
    END
    GO
    
    0 讨论(0)
  • 2021-02-02 15:07

    Assuming you are using SQL 2005 upwards, you can develop a SQL CLR function to take a UTC date and convert to the local date.

    This link is an MSDN How-To explaining how you can create a scalar UDF in C#.

    Create a SQL function along the lines of

    [SqlFunction()]
    public static SqlDateTime ConvertUtcToLocal(SqlDateTime utcDate) 
    {
        // over to you to convert SqlDateTime to DateTime, specify Kind
        // as UTC, convert to local time, and convert back to SqlDateTime
    }
    

    Your sample above would then become

    set @localdate = dbo.ConvertUtcToLocal(@utcdate)
    

    SQL CLR has its overheads in terms of deployment, but I feel cases like this are where it fits in best.

    0 讨论(0)
  • 2021-02-02 15:08

    I recently had to do the same thing. The trick is figuring out the offset from UTC, but it's not a hard trick. You simply use DateDiff to get the difference in hours between local and UTC. I wrote a function to take care of this.

    Create Function ConvertUtcDateTimeToLocal(@utcDateTime DateTime)
    Returns DateTime
    Begin
        Declare @utcNow DateTime
        Declare @localNow DateTime
        Declare @timeOffSet Int
    
        -- Figure out the time difference between UTC and Local time
        Set @utcNow = GetUtcDate()
        Set @localNow = GetDate()
        Set @timeOffSet = DateDiff(hh, @utcNow, @localNow) 
    
        DECLARE @localTime datetime 
    
        Set @localTime = DateAdd(hh, @timeOffset, @utcDateTime) 
    
        -- Check Results
        return @localTime 
    
    End
    GO
    

    This does have on crucial short coming: If a time zone uses a fractional offset, such as Nepal which is GMT+5:45, this will fail because this only deals with whole hours. However, it should fit your needs just fine.

    0 讨论(0)
  • 2021-02-02 15:13

    While the question's title mentions SQL Server 2005, the question is tagged with SQL Server in general. For SQL Server 2016 and later, you can use:

    SELECT yourUtcDateTime AT TIME ZONE 'Mountain Standard Time'
    

    A list of time zones is available with SELECT * FROM sys.time_zone_info

    0 讨论(0)
提交回复
热议问题