Quantcast
Channel: BI Bits.co - Business Intelligence
Viewing all articles
Browse latest Browse all 15

Convert DATETIME From UTC to Local Time

0
0

Recently I had to convert date time data that was recorded as a UTC datetime and display it as local time. My solution is somewhat similar to Aaron Bertran's solution here, but I thought I would provide a slightly different solution anyway.

I start out by creating a calendar of dates between January 1 and the year of the @StartDate and December 31 and the year of the @EndDate plus 1.

From the calendar table I then create another table with the UTC begin and end datetimes for the DST period and the UTC begin and end datetimes for the standard time period. In each row I also include the local datetime for the begin and end periods. I end up with 2 rows for every year (except for the last year, which has only 1 row.)

Once I have the DST table created I use logic in my code where, for example, I take the UTCCreatedDateTime and convert that to Local_DateTime by finding the row where UTCCreatedDateTime >= dst.UTCPeriodStart and UTCCreatedDateTime < dst.UTCNextPeriodStart. For each row found, I use the following logic to determine the local time. SELECT DATEADD(hh, dst.OffsetUTCToLocalCentralTime, c.UTCCreatedDateTime) AS LocalDateTime.

I live in the Central time zone so the offset from UTC is 6 hours during Central Standard Time and 5 hours during Daylight Saving Time.

Here is how I created the DST table.

DECLARE @StartDate smalldatetime = '20100101'
        , @EndDate smalldatetime = CAST(YEAR(GETDATE() + 1) AS char(4)) + '1231'
        , @OffsetHoursUTC_CST int = 6        -- Difference between UTC time and Central Standard Time

; WITH cteYears
    AS (
        SELECT YYYY = YEAR(@StartDate)
        UNION ALL
        SELECT YYYY + 1
            FROM cteYears
            WHERE YYYY     )
    , cteMonths
    AS (
        SELECT MM = 1
        UNION ALL
        SELECT MM + 1
            FROM cteMonths
            WHERE MM     )
    , cteDays
    AS (
        SELECT DD = 1
        UNION ALL
        SELECT DD + 1
            FROM cteDays
            WHERE DD     )
    , cteCalendar
    AS (
        SELECT YYYY = YYYY
                , MM = MM
                , DD = DD
                , DateKey = (YYYY * 10000) + (MM * 100) + DD
                , FullDate = CAST(CAST((YYYY * 10000) + (MM * 100) + DD AS char(8)) AS DATE)
                , [MostRecentSunday] = DATEADD(d, -((DATEPART(WEEKDAY, CAST(CAST((YYYY * 10000) + (MM * 100) + DD AS char(8)) AS DATE)) + 6 + @@DATEFIRST) % 7), CAST(CAST((YYYY * 10000) + (MM * 100) + DD AS char(8)) AS DATE))
                , [WeekNumberInMonth] = ROW_NUMBER() OVER(PARTITION BY YYYY, MM, DATENAME(WEEKDAY, CAST(CAST((YYYY * 10000) + (MM * 100) + DD AS char(8)) AS DATE)) ORDER BY (YYYY * 10000) + (MM * 100) + DD)
            FROM cteYears
                CROSS JOIN cteMonths
                CROSS JOIN cteDays
            WHERE ISDATE((YYYY * 10000) + (MM * 100) + DD) = 1
    )

-- The following works for Years after 2006
-- For Years 1987 - 2006, Daylight Saving Time was in effect from the first Sunday in April through the last Sunday in October
-- There is a 6 hour difference between UTC time and Central Standard Time (it is 5 hours during DST.)
-- Recent DST Periods = (3/14/10 - 11/7/10, 3/13/11 - 11/6/11, 3/11/12 - 11/4/12, 3/10/13 - 11/3/13, 3/9/14 - 11/2/14)
    , cteDSTPeriods
    AS (
        SELECT marDates.DateKey
                , marDates.FullDate
                , marDates.[MostRecentSunday]
                , marDates.[WeekNumberInMonth]
                , CAST(CONVERT(char(8), marDates.FullDate, 112) + ' 02:00:00' AS DATETIME) AS LocalPeriodStart
                , CAST(CONVERT(char(8), novDates.FullDate, 112) + ' 02:00:00' AS DATETIME) AS LocalNextPeriodStart
                , DATEADD(hh, 6, CAST(CONVERT(char(8), marDates.FullDate, 112) + ' 02:00:00' AS DATETIME)) AS UTCPeriodStart
                , DATEADD(hh, 5, CAST(CONVERT(char(8), novDates.FullDate, 112) + ' 02:00:00' AS DATETIME)) AS UTCNextPeriodStart
                , -5 AS OffsetUTCToLocalCentralTime
            FROM cteCalendar marDates
                INNER JOIN cteCalendar novDates
                    ON marDates.YYYY = novDates.YYYY
                        AND DATENAME(WEEKDAY, CAST(novDates.FullDate AS DATETIME)) = 'Sunday'
                        AND novDates.MM = 11
                        AND novDates.WeekNumberInMonth = 1
            WHERE DATENAME(WEEKDAY, CAST(marDates.FullDate AS DATETIME)) = 'Sunday'
                AND marDates.MM = 3
                AND marDates.WeekNumberInMonth = 2
        UNION ALL
        SELECT novDates.DateKey
                , novDates.FullDate
                , novDates.[MostRecentSunday]
                , novDates.[WeekNumberInMonth]
                , CAST(CONVERT(char(8), novDates.FullDate, 112) + ' 02:00:00' AS DATETIME) AS LocalPeriodStart
                , CAST(CONVERT(char(8), marDates.FullDate, 112) + ' 02:00:00' AS DATETIME) AS LocalNextPeriodStart
                , DATEADD(hh, 5, CAST(CONVERT(char(8), novDates.FullDate, 112) + ' 02:00:00' AS DATETIME)) AS UTCPeriodStart
                , DATEADD(hh, 6, CAST(CONVERT(char(8), marDates.FullDate, 112) + ' 02:00:00' AS DATETIME)) AS UTCNextPeriodStart
                , -6 AS OffsetUTCToLocalCentralTime
            FROM cteCalendar novDates
                INNER JOIN cteCalendar marDates
                    ON novDates.YYYY + 1 = marDates.YYYY
                        AND DATENAME(WEEKDAY, CAST(marDates.FullDate AS DATETIME)) = 'Sunday'
                        AND marDates.MM = 3
                        AND marDates.WeekNumberInMonth = 2
            WHERE DATENAME(WEEKDAY, CAST(novDates.FullDate AS DATETIME)) = 'Sunday'
                AND novDates.MM = 11
                AND novDates.WeekNumberInMonth = 1
    )

SELECT *
    FROM cteDSTPeriods
    ORDER BY DateKey

Viewing all articles
Browse latest Browse all 15

Latest Images

Trending Articles





Latest Images