Я создал две функции SQL для преобразования дат Dynamics AX UTC в соответствующую дату / время часового пояса из таблицы TIMEZONESRULESDATA. Похоже, они работают в сценариях, которые я тестировал, но я был бы рад получить обратную связь.
Первая функция получает UTC DateTime и TZID из любой таблицы AX и генерирует дату / время в часовом поясе с любой настройкой перехода на летнее время:
-- *** IMPORTANT NOTE: @@DATEFIRST must be 7 for this to work ***
CREATE FUNCTION [dbo].[ConvertUTCDateTime] (@DateTime DATETIME, @TZID INT)
RETURNS DATETIME
AS
BEGIN
DECLARE @AdjustedDateTime DATETIME
SET @AdjustedDateTime=@DateTime
-- Fields to be extracted from TIMEZONESRULESDATA record for TZID
DECLARE @Bias INT, @DBias INT
DECLARE @DMonth INT, @DDayOfWeek INT, @DDay INT, @DHour INT, @DMinute INT, @DSecond INT -- Start of Daylight Saving
DECLARE @SMonth INT, @SDayOfWeek INT, @SDay INT, @SHour INT, @SMinute INT, @SSecond INT -- End of Daylight Saving
-- Daylight Saving Date/Time ranges
DECLARE @DSTFromDateTime1 DATETIME, @DSTToDateTime1 DATETIME, @DSTFromDateTime2 DATETIME, @DSTToDateTime2 DATETIME
SELECT
@Bias=tzr.BIAS,@DBias=tzr.DBIAS,
@DMonth=tzr.DMONTH, @DDayOfWeek=tzr.DDAYOFWEEK, @DDay=tzr.DDAY, @DHour=tzr.DHOUR, @DMinute=tzr.DMINUTE, @DSecond=tzr.DSECOND,
@SMonth=tzr.SMONTH, @SDayOfWeek=tzr.SDAYOFWEEK, @SDay=tzr.SDAY, @SHour=tzr.SHOUR, @SMinute=tzr.SMINUTE, @SSecond=tzr.SSECOND
FROM MyAXDatabase..TIMEZONESRULESDATA tzr
WHERE tzr.RULEID=@TZID
IF @Bias IS NOT NULL
BEGIN
SET @AdjustedDateTime=DATEADD(MINUTE, (-1)*@Bias, @DateTime ) -- Standard Time Zone Adjustment from UTC
IF @DMonth>0 -- If there is Daylight Saving
BEGIN
SET @DSTFromDateTime1=dbo.GetDSTDateTime(@AdjustedDateTime, @DMonth, @DDayOfWeek, @DDay, @DHour, @DMinute, @DSecond ) -- Get DS Start date in year
SET @DSTToDateTime2=dbo.GetDSTDateTime(@AdjustedDateTime, @SMonth, @SDayOfWeek, @SDay, @SHour, @SMinute, @SSecond ) -- Get DS End date in year
IF @DSTFromDateTime1>@DSTToDateTime2
BEGIN
SET @DSTToDateTime1= DATEADD(SECOND, -1, CAST(DATEFROMPARTS( YEAR(@AdjustedDateTime)+1, 1, 1) as DATETIME)) -- End of Current Year
SET @DSTFromDateTime2= DATEFROMPARTS( YEAR(@AdjustedDateTime), 1, 1) -- Start of Current Year
END
ELSE
BEGIN
SET @DSTToDateTime1=@DSTToDateTime2
SET @DSTFromDateTime2=@DSTFromDateTime1
END
IF @AdjustedDateTime BETWEEN @DSTFromDateTime1 AND @DSTToDateTime1
OR @AdjustedDateTime BETWEEN @DSTFromDateTime2 AND @DSTToDateTime2
SET @AdjustedDateTime=DATEADD(MINUTE, (-1)*@DBias, @AdjustedDateTime ) -- Make Daylight Saving adjustment if in DST date range
END
END
RETURN @AdjustedDateTime
END
Вторая функция определяет дату начала или окончания перехода на летнее время на основе параметров, заданных в полях TIMEZONESRULESDATA:
-- *** IMPORTANT NOTE: @@DATEFIRST must be 7 for this to work ***
CREATE FUNCTION [dbo].[GetDSTDateTime](
@DateTime DATETIME, -- Base Date
@Month INT, -- Month for Start/End of DST
@DayOfWeek INT, -- Day of Week 0=Sun..6=Sat (based on coding in TIMEZONESRULESDATA table)
@Day INT, -- Week of the Month (confusing?!) 1-5 ; 5 means last week
@Hour INT,
@Minute INT,
@Second INT
) RETURNS DATETIME
AS
BEGIN
DECLARE @MyDateTime DATETIME
SET @MyDateTime=DATEFROMPARTS( YEAR(@DateTime), @Month, 1) -- First day of DST Start/End Month from @BaseDate year
SET @DayOfWeek=@DayOfWeek + 1 -- Adjust to tie in with SQL DoW 1-7
-- Establish first selected DayOfWeek in the month
IF @DayOfWeek >= DATEPART(WEEKDAY, @MyDateTime )
SET @MyDateTime=DATEADD(DAY, @DayOfWeek - DATEPART(WEEKDAY, @MyDateTime), @MyDateTime)
ELSE
SET @MyDateTime=DATEADD(DAY, 7-(DATEPART(WEEKDAY, @MyDateTime) - @DayOfWeek), @MyDateTime)
-- Add the appropriate number of weeks
SET @MyDateTime=DATEADD(DAY, 7*(@Day-1), @MyDateTime)
-- For last week of month ensure that date is in correct month
WHILE MONTH(@MyDateTime)<>@Month
BEGIN
SET @MyDateTime=DATEADD(DAY, -7, @MyDateTime)
END;
-- Add on Hours, Minutes and Seconds
SET @MyDateTime=DATEADD(SECOND, @Second, DATEADD(MINUTE, @Minute, DATEADD(HOUR, @Hour, @MyDateTime)))
RETURN @MyDateTime
END