Ok, hope you dont mind but I've made some changes & here's the code
Fixed an issue where if DST Start and DST End, would use same year.
Simplified CalcDaylightSavingsDate (removed DaysInMth case statements, removed most DstModifier code)
Edited TimeZones table, added CHECKS, renamed objects, removed unused variables.
ETA 26Jul: Changed PROCEDURES back to FUNCTIONS.
ETA 29Jul: Added NULL error checking to functions, so handles neatly.
The code itself:
-- *************************
--
-- SQL 2005 Time Zone Conversion Functions - Setup Tables and Functions
--
-----------------------------------------------------------------------------
-- Created by: Robert Ford per http:
-- Created on: 17Nov2008
-- Modified on: 29Jul2010
-- Edited by 'Colin 2' per http:
-- Edited by 'shell_l_d' per http:
-----------------------------------------------------------------------------------
--
-- 17Nov2008
-- Creation Date
--
-- 30Nov2008
-- Made a modification based upon a note from an Austrailian user of Code project
-- to have an effective and end date in order to have multiple configurations for a single
-- timezone code. In Austrailia DST dates change often and would require to have the
-- timezone code updated regularly.
--
-- 11Jun2009
-- Made a modification to fix an issue with the code in situations where the DST end range date is
-- in a month that does not start with Sunday.
--
-- 21Jan2010
-- Fixed an issue with the GetGmtTime function in regards to time zones that are positive GMT adjusted.
--
-- 02Apr2010
-- Fixed an issue where the functions were not evaluating the hour prior to the conversion
-- time.
--
-- 15Apr2010
-- Edited by 'Colin 2' per http:
-- Fixed an issue where the calculate daylight savings date returns incorrect date if need 'Last' wk of mth, added 'L'.
--
-- 22Jul2010
-- Edited by 'shell_l_d' per http:
-- Fixed an issue where if DST Start > DST End, would use same year.
-- Simplified CalcDaylightSavingsDate (removed DaysInMth case statements, removed most DstModifier code)
-- Edited TimeZones table, added CHECKS, renamed objects, removed unused variables.
--
-- 26Jul2010
-- Edited by 'shell_l_d' per http:
-- Convert PROCEDURES (Colin 2) back to FUNCTIONS (RobertFord), so can use them in a select statement.
--
-- 29Jul2010
-- Edited by 'shell_l_d' per http:
-- Add error checking for NULL Date or TimeZone.
--
-- *************************
--
-- Table:
-- TimeZones
-- tz_Code - code for the timezone - used by functions to reference the other values (eg: 'NSW')
-- tz_Description - name to describe the time zone code (eg: 'New South Wales AUSTRALIA = GMT +10 = EST/EDT')
-- tz_StartDate - start date time for timezone (eg: '01 Jul 2008')
-- tz_EndDate - end date time for timezone (eg: '30 Jun 9999')
-- tz_OffsetHr - Standard GMT offset hours for adjusting the date time (eg: 10)
-- tz_OffsetMins - Standard GMT offset minutes for adjusting the date time (eg: 0)
-- tz_DstOffsetHr - Daylight Savings Time GMT offset hours for adjusting the date time (eg: 11)
-- tz_DstOffsetMins - Daylight Savings Time GMT offset minutes for adjusting the date time (eg: 0)
-- tz_DstStartMth - Daylight Savings Time start month (eg: 10 = Oct)
-- tz_DstStartWkOfMth - Daylight Savings Time start WkOfMth (eg: '1'=1st wk, '4'=4th wk, 'L'=Last wk)
-- tz_DstStartDayOfWk - Daylight Savings Time start DayOfWk (eg: 1=Sun, 2=Mon, 7=Sat)
-- tz_DstStartTime - Daylight Savings Time start Time (eg: '02:00' = 2am)
-- tz_DstEndMth - Daylight Savings Time end month (eg: 4 = Apr)
-- tz_DstEndWkOfMth - Daylight Savings Time end WkOfMth (eg: '1'=1st wk, '4'=4th wk, 'L'=Last wk)
-- tz_DstEndDayOfWk - Daylight Savings Time end DayOfWk (eg: 1=Sun, 2=Mon, 7=Sat)
-- tz_DstEndTime - Daylight Savings Time end Time (eg: '03:00' = 3am)
-- Notes: This table holds the time zone criterion used to convert date time values between GMT TimeZones
--
-- Functions: NOTE: these assume Sunday is first day of week, so assumes 'set datefirst 7' is set prior.
--
-- CalcDaylightSavingsDate
-- @TimeZone - Time zone to find the Daylight Savings Time (DST) for (value from TimeZones.tz_Code)
-- @DstDateType - 'Start' to find Start DST or 'End' to find End DST
-- @DateToConvert - UTC or GMT DATETIME to find DST Start or End Date for
-- RETURNS DATETIME- The Start or End of DST Time for the specified TimeZone, Type and Date.
--
-- GetGmtTime
-- @FromDate - UTC or GMT DATETIME to be converted
-- @FromTimeZone - Time zone that the date time being passed is in (value from TimeZones.tz_Code)
-- RETURNS DATETIME- The converted UTC or GMT DATETIME.
--
-- GetTzTime
-- @FromDate - UTC or GMT DATETIME to be converted
-- @ToTimeZone - Time zone to convert the date time to (value from TimeZones.tz_Code)
-- RETURNS DATETIME- The converted UTC or GMT DATETIME.
--
-- ConvertTimeZone
-- @FromDate - UTC or GMT DATETIME to be converted
-- @FromTimeZone - Time zone that the date time being passed is in (value from TimeZones.tz_Code)
-- @ToTimeZone - Time zone to convert the date time to (value from TimeZones.tz_Code)
-- RETURNS DATETIME- The converted UTC or GMT DATETIME.
--
-- =============================================
-- Create TABLE (TimeZones)
-- =============================================
-- Check to see if the table already exists and deletes it if it does
IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = object_id(N'[dbo].[TimeZones]') and OBJECTPROPERTY(id, N'IsUserTable') = 1)
DROP TABLE [dbo].[TimeZones]
GO
CREATE TABLE [dbo].[TimeZones] (
[tz_Code] [VARCHAR] (12) NOT NULL ,
[tz_Description] [VARCHAR] (60) NOT NULL ,
[tz_StartDate] DATETIME NOT NULL DEFAULT GETDATE(),
[tz_EndDate] DATETIME NOT NULL DEFAULT '31 Dec 9999',
[tz_OffsetHr] [int] NOT NULL DEFAULT -1,
[tz_OffsetMins] [int] NOT NULL DEFAULT 0,
[tz_DstOffsetHr] [int] DEFAULT NULL ,
[tz_DstOffsetMins] [int] DEFAULT NULL ,
[tz_DstStartMth] [int] DEFAULT NULL CHECK ( [tz_DstStartMth] IS NULL OR [tz_DstStartMth] BETWEEN 1 and 12 ) ,
[tz_DstStartWkOfMth] [VARCHAR] (1) DEFAULT NULL CHECK ( [tz_DstStartWkOfMth] in (NULL,'1','2','3','4','L') ) ,
[tz_DstStartDayOfWk] [int] DEFAULT NULL CHECK ( [tz_DstStartDayOfWk] IS NULL OR [tz_DstStartDayOfWk] BETWEEN 1 and 7 ) ,
[tz_DstStartTime] [DATETIME] DEFAULT NULL ,
[tz_DstEndMth] [int] DEFAULT NULL CHECK ( [tz_DstEndMth] IS NULL OR [tz_DstEndMth] BETWEEN 1 and 12 ) ,
[tz_DstEndWkOfMth] [VARCHAR] (1) DEFAULT NULL CHECK ( [tz_DstEndWkOfMth] in (NULL,'1','2','3','4','L') ) ,
[tz_DstEndDayOfWk] [int] DEFAULT NULL CHECK ( [tz_DstEndDayOfWk] IS NULL OR [tz_DstEndDayOfWk] BETWEEN 1 and 7 ) ,
[tz_DstEndTime] [DATETIME] DEFAULT NULL ,
CONSTRAINT [PK_TimeZones] PRIMARY KEY ([tz_Code],[tz_StartDate])
)
GO
-- =============================================
-- Insert Into TimeZones Table
-- =============================================
-- TimeZones from approx 1987
INSERT INTO TimeZones
SELECT 'AZ','Arizona USA = GMT -7 = MST','01 Jul 1980','30 Jun 9999',-7,0,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL
UNION ALL
SELECT 'BALIK','Balikpapan INDONESIA = GMT +8 = WITA','01 Jul 1980','30 Jun 9999',8,0,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL
UNION ALL
SELECT 'BEIJING','Beijing CHINA = GMT +8 = CST','01 Jul 1980','30 Jun 9999',8,0,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL
UNION ALL
SELECT 'JHB','Johannesburg SOUTH AFRICA = GMT +2 = SAST','01 Jul 1980','30 Jun 9999',2,0,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL
UNION ALL
SELECT 'LIMA','Lima PERU = GMT -5 = PET','01 Jul 1980','30 Jun 9999',-5,0,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL
UNION ALL
SELECT 'PUNE_MH','Pune Maharashtra INDIA = GMT +5.5 = IST','01 Jul 1980','30 Jun 9999',5,30,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL
GO
INSERT INTO TimeZones
SELECT 'NSW','New South Wales AUSTRALIA = GMT +10 = EST/EDT','01 Jul 1987','30 Jun 1995',10,0,11,0,10,'L',1,'02:00', 3,'3',1,'03:00'
UNION ALL
SELECT 'NSW','New South Wales AUSTRALIA = GMT +10 = EST/EDT','01 Jul 1989','30 Jun 1995',10,0,11,0,10,'L',1,'02:00', 3,'1',1,'03:00'
UNION ALL
SELECT 'NSW','New South Wales AUSTRALIA = GMT +10 = EST/EDT','01 Jul 1995','30 Jun 2000',10,0,11,0,10,'L',1,'02:00', 3,'L',1,'03:00'
UNION ALL
SELECT 'NSW','New South Wales AUSTRALIA = GMT +10 = EST/EDT','01 Jul 2000','01 Sep 2001',10,0,11,0, 8,'L',1,'02:00', 3,'L',1,'03:00'
UNION ALL
SELECT 'NSW','New South Wales AUSTRALIA = GMT +10 = EST/EDT','01 Jul 2001','30 Jun 2005',10,0,11,0,10,'L',1,'02:00', 3,'L',1,'03:00'
UNION ALL
SELECT 'NSW','New South Wales AUSTRALIA = GMT +10 = EST/EDT','01 Jul 2005','30 Jun 2006',10,0,11,0,10,'L',1,'02:00', 4,'1',1,'03:00'
UNION ALL
SELECT 'NSW','New South Wales AUSTRALIA = GMT +10 = EST/EDT','01 Jul 2006','30 Jun 2007',10,0,11,0,10,'L',1,'02:00', 3,'L',1,'03:00'
UNION ALL
SELECT 'NSW','New South Wales AUSTRALIA = GMT +10 = EST/EDT','01 Jul 2007','30 Jun 2008',10,0,11,0,10,'L',1,'02:00', 4,'1',1,'03:00'
UNION ALL
SELECT 'NSW','New South Wales AUSTRALIA = GMT +10 = EST/EDT','01 Jul 2008','30 Jun 9999',10,0,11,0,10,'1',1,'02:00', 4,'1',1,'03:00'
UNION ALL
SELECT 'PC_BC','Port Coquitlam BC CANADA = GMT -8 = PST/PDT','01 Jan 1987','31 Dec 2006',-8,0,-7,0, 4,'1',1,'02:00',10,'L',1,'02:00'
UNION ALL
SELECT 'PC_BC','Port Coquitlam BC CANADA = GMT -8 = PST/PDT','01 Jan 2007','31 Dec 9999',-8,0,-7,0, 3,'2',1,'02:00',11,'1',1,'02:00'
UNION ALL
SELECT 'AGO','Santiago CHILE = GMT -4 = CLST/CLT','01 Jul 1986','30 Jun 1987',-4,0,-3,0,10,'2',1,'00:00', 4,'2',1,'00:00'
UNION ALL
SELECT 'AGO','Santiago CHILE = GMT -4 = CLST/CLT','01 Jul 1987','30 Jun 1988',-4,0,-3,0,10,'2',1,'00:00', 3,'2',1,'00:00'
UNION ALL
SELECT 'AGO','Santiago CHILE = GMT -4 = CLST/CLT','01 Jul 1988','30 Jun 1989',-4,0,-3,0,10,'1',1,'00:00', 3,'2',1,'00:00'
UNION ALL
SELECT 'AGO','Santiago CHILE = GMT -4 = CLST/CLT','01 Jul 1989','30 Jun 1990',-4,0,-3,0,10,'3',1,'00:00', 3,'3',1,'00:00'
UNION ALL
SELECT 'AGO','Santiago CHILE = GMT -4 = CLST/CLT','01 Jul 1990','30 Jun 1991',-4,0,-3,0, 9,'3',1,'00:00', 3,'2',1,'00:00'
UNION ALL
SELECT 'AGO','Santiago CHILE = GMT -4 = CLST/CLT','01 Jul 1991','30 Jun 1992',-4,0,-3,0,10,'2',1,'00:00', 3,'3',1,'00:00'
UNION ALL
SELECT 'AGO','Santiago CHILE = GMT -4 = CLST/CLT','01 Jul 1992','30 Jun 1996',-4,0,-3,0,10,'3',1,'00:00', 3,'2',1,'00:00'
UNION ALL
SELECT 'AGO','Santiago CHILE = GMT -4 = CLST/CLT','01 Jul 1996','30 Jun 1997',-4,0,-3,0,10,'2',1,'00:00', 3,'L',1,'00:00'
UNION ALL
SELECT 'AGO','Santiago CHILE = GMT -4 = CLST/CLT','01 Jul 1997','30 Jun 1998',-4,0,-3,0,10,'2',1,'00:00', 3,'3',1,'00:00'
UNION ALL
SELECT 'AGO','Santiago CHILE = GMT -4 = CLST/CLT','01 Jul 1998','30 Jun 1999',-4,0,-3,0,09,'4',1,'00:00', 4,'1',1,'00:00'
UNION ALL
SELECT 'AGO','Santiago CHILE = GMT -4 = CLST/CLT','01 Jul 1999','30 Jun 2000',-4,0,-3,0,10,'2',1,'00:00', 3,'2',1,'00:00'
UNION ALL
SELECT 'AGO','Santiago CHILE = GMT -4 = CLST/CLT','01 Jul 2000','30 Jun 2001',-4,0,-3,0,10,'3',1,'00:00', 3,'2',1,'00:00'
UNION ALL
SELECT 'AGO','Santiago CHILE = GMT -4 = CLST/CLT','01 Jul 2001','30 Jun 2001',-4,0,-3,0,10,'2',1,'00:00', 3,'2',1,'00:00'
UNION ALL
SELECT 'AGO','Santiago CHILE = GMT -4 = CLST/CLT','01 Jul 2005','30 Jun 2006',-4,0,-3,0,10,'2',1,'00:00', 3,'2',1,'00:00'
UNION ALL
SELECT 'AGO','Santiago CHILE = GMT -4 = CLST/CLT','01 Jul 2006','30 Jun 2007',-4,0,-3,0,10,'3',1,'00:00', 3,'2',1,'00:00'
UNION ALL
SELECT 'AGO','Santiago CHILE = GMT -4 = CLST/CLT','01 Jul 2007','30 Jun 2008',-4,0,-3,0,10,'2',1,'00:00', 3,'L',1,'00:00'
UNION ALL
SELECT 'AGO','Santiago CHILE = GMT -4 = CLST/CLT','01 Jul 2008','30 Jun 2009',-4,0,-3,0,10,'2',1,'00:00', 3,'3',1,'00:00'
UNION ALL
SELECT 'AGO','Santiago CHILE = GMT -4 = CLST/CLT','01 Jul 2009','30 Jun 2010',-4,0,-3,0,10,'2',1,'00:00', 4,'1',1,'00:00'
UNION ALL
SELECT 'AGO','Santiago CHILE = GMT -4 = CLST/CLT','01 Jul 2010','30 Jun 2014',-4,0,-3,0,10,'2',1,'00:00', 3,'2',1,'00:00'
UNION ALL
SELECT 'AGO','Santiago CHILE = GMT -4 = CLST/CLT','01 Jul 2014','30 Jun 2015',-4,0,-3,0,10,'2',1,'00:00', 3,'3',1,'00:00'
UNION ALL
SELECT 'AGO','Santiago CHILE = GMT -4 = CLST/CLT','01 Jul 2015','30 Jun 2017',-4,0,-3,0,10,'2',1,'00:00', 3,'2',1,'00:00'
UNION ALL
SELECT 'AGO','Santiago CHILE = GMT -4 = CLST/CLT','01 Jul 2017','30 Jun 2018',-4,0,-3,0,10,'3',1,'00:00', 3,'2',1,'00:00'
UNION ALL
SELECT 'AGO','Santiago CHILE = GMT -4 = CLST/CLT','01 Jul 2018','30 Jun 9999',-4,0,-3,0,10,'2',1,'00:00', 3,'2',1,'00:00'
UNION ALL
SELECT 'MOSCOW','Moscow RUSSIA = GMT +3 = MSK/MSD','01 Jan 1985','31 Dec 1990',3,0,4,0, 3,'L',1,'02:00', 9,'3',1,'03:00'
UNION ALL
SELECT 'MOSCOW','Moscow RUSSIA = GMT +3 = MSK/MSD','01 Jan 1991','31 Aug 1991',3,0,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL
UNION ALL
SELECT 'MOSCOW','Moscow RUSSIA = GMT +3 = EEST/EET/MSK','01 Sep 1991','31 Jan 1992',3,0,2,0, 9,'L',1,'03:00', 1,'3',1,'02:00'
UNION ALL
SELECT 'MOSCOW','Moscow RUSSIA = GMT +3 = MSK/MSD','01 Feb 1992','31 Dec 1992',3,0,4,0, 3,'L',7,'00:00', 9,'L',7,'23:00'
UNION ALL
SELECT 'MOSCOW','Moscow RUSSIA = GMT +3 = MSK/MSD','01 Jan 1992','31 Dec 1992',3,0,4,0, 3,'L',7,'23:00', 9,'L',7,'23:00'
UNION ALL
SELECT 'MOSCOW','Moscow RUSSIA = GMT +3 = MSK/MSD','01 Jan 1993','31 Dec 9999',3,0,4,0, 3,'L',1,'02:00',10,'L',1,'03:00'
UNION ALL
SELECT 'MG','Minas Gerais BRAZIL = GMT -3 = BRST/BRT','01 Jul 1987','30 Jun 1988',-3,0,-2,0,10,'3',1,'00:00', 1,'L',1,'00:00'
UNION ALL
SELECT 'MG','Minas Gerais BRAZIL = GMT -3 = BRST/BRT','01 Jul 1988','30 Jun 1989',-3,0,-2,0,10,'3',1,'00:00', 2,'2',1,'00:00'
UNION ALL
SELECT 'MG','Minas Gerais BRAZIL = GMT -3 = BRST/BRT','01 Jul 1989','30 Jun 1990',-3,0,-2,0,10,'3',1,'00:00', 2,'3',1,'00:00'
UNION ALL
SELECT 'MG','Minas Gerais BRAZIL = GMT -3 = BRST/BRT','01 Jul 1991','30 Jun 1992',-3,0,-2,0,10,'3',1,'00:00', 2,'2',1,'00:00'
UNION ALL
SELECT 'MG','Minas Gerais BRAZIL = GMT -3 = BRST/BRT','01 Jul 1992','30 Jun 1993',-3,0,-2,0,10,'L',1,'00:00', 1,'L',1,'00:00'
UNION ALL
SELECT 'MG','Minas Gerais BRAZIL = GMT -3 = BRST/BRT','01 Jul 1993','30 Jun 1995',-3,0,-2,0,10,'3',1,'00:00', 2,'3',1,'00:00'
UNION ALL
SELECT 'MG','Minas Gerais BRAZIL = GMT -3 = BRST/BRT','01 Jul 1995','30 Jun 1996',-3,0,-2,0,10,'3',1,'00:00', 2,'2',1,'00:00'
UNION ALL
SELECT 'MG','Minas Gerais BRAZIL = GMT -3 = BRST/BRT','01 Jul 1996','30 Jun 1997',-3,0,-2,0,10,'1',1,'00:00', 2,'3',1,'00:00'
UNION ALL
SELECT 'MG','Minas Gerais BRAZIL = GMT -3 = BRST/BRT','01 Jul 1997','30 Jun 1998',-3,0,-2,0,10,'1',1,'00:00', 2,'4',1,'00:00'
UNION ALL
SELECT 'MG','Minas Gerais BRAZIL = GMT -3 = BRST/BRT','01 Jul 1998','30 Jun 1999',-3,0,-2,0,10,'2',1,'00:00', 2,'2',1,'00:00'
UNION ALL
SELECT 'MG','Minas Gerais BRAZIL = GMT -3 = BRST/BRT','01 Jul 1999','30 Jun 2000',-3,0,-2,0,10,'1',1,'00:00', 2,'4',1,'00:00'
UNION ALL
SELECT 'MG','Minas Gerais BRAZIL = GMT -3 = BRST/BRT','01 Jul 2000','30 Jun 2002',-3,0,-2,0,10,'2',1,'00:00', 2,'3',1,'00:00'
UNION ALL
SELECT 'MG','Minas Gerais BRAZIL = GMT -3 = BRST/BRT','01 Jul 2002','30 Jun 2003',-3,0,-2,0,11,'1',1,'00:00', 2,'3',1,'00:00'
UNION ALL
SELECT 'MG','Minas Gerais BRAZIL = GMT -3 = BRST/BRT','01 Jul 2003','30 Jun 2004',-3,0,-2,0,10,'3',1,'00:00', 2,'3',1,'00:00'
UNION ALL
SELECT 'MG','Minas Gerais BRAZIL = GMT -3 = BRST/BRT','01 Jul 2004','30 Jun 2005',-3,0,-2,0,11,'1',1,'00:00', 2,'3',1,'00:00'
UNION ALL
SELECT 'MG','Minas Gerais BRAZIL = GMT -3 = BRST/BRT','01 Jul 2005','30 Jun 2006',-3,0,-2,0,10,'3',1,'00:00', 2,'3',1,'00:00'
UNION ALL
SELECT 'MG','Minas Gerais BRAZIL = GMT -3 = BRST/BRT','01 Jul 2006','30 Jun 2007',-3,0,-2,0,11,'1',1,'00:00', 2,'4',1,'00:00'
UNION ALL
SELECT 'MG','Minas Gerais BRAZIL = GMT -3 = BRST/BRT','01 Jul 2007','30 Jun 2008',-3,0,-2,0,10,'2',1,'00:00', 2,'3',1,'00:00'
UNION ALL
SELECT 'MG','Minas Gerais BRAZIL = GMT -3 = BRST/BRT','01 Jul 2008','30 Jun 2009',-3,0,-2,0,10,'3',1,'00:00', 2,'2',1,'00:00'
UNION ALL
SELECT 'MG','Minas Gerais BRAZIL = GMT -3 = BRST/BRT','01 Jul 2009','30 Jun 2011',-3,0,-2,0,10,'3',1,'00:00', 2,'3',1,'00:00'
UNION ALL
SELECT 'MG','Minas Gerais BRAZIL = GMT -3 = BRST/BRT','01 Jul 2011','30 Jun 2012',-3,0,-2,0,10,'3',1,'00:00', 2,'4',1,'00:00'
UNION ALL
SELECT 'MG','Minas Gerais BRAZIL = GMT -3 = BRST/BRT','01 Jul 2012','30 Jun 9999',-3,0,-2,0,10,'3',1,'00:00', 2,'3',1,'00:00'
GO
SELECT *
FROM TimeZones
GO
-- =============================================
-- Create FUNCTION (CalcDaylightSavingsDate)
-- =============================================
IF EXISTS (SELECT * FROM sysobjects WHERE name = N'CalcDaylightSavingsDate')
DROP FUNCTION CalcDaylightSavingsDate
GO
CREATE FUNCTION [dbo].[CalcDaylightSavingsDate]
(@TimeZone AS VARCHAR(12),
@DstDateType AS VARCHAR(5),
@DateToConvert AS DATETIME)
RETURNS DATETIME
AS
BEGIN
-----------------------------------------------------------------------------
-- Created by: Robert Ford per http:
-- Created on: 17Nov2008
-- Modified on: 29Jul2010
-- Edited by 'Colin 2' per http:
-- Edited by 'shell_l_d' per http:
-----------------------------------------------------------------------------------
-- @TimeZone - Time zone to find the Daylight Savings Time (DST) for (value from TimeZones.tz_Code)
-- @DstDateType - 'Start' to find Start DST or 'End' to find End DST
-- @DateToConvert - UTC or GMT DATETIME to find DST Start or End Date for
-- RETURNS DATETIME - The Start or End of DST Time for the specified TimeZone, Type and Date.
--
-- NOTE: assumes Sunday is first day of week, so assumes 'set datefirst 7' is set prior.
-- Constructs a DATETIME from a TimeZone & DateTime because Daylight Savings Time (DST) starts/ends on a different
-- date every year (e.g. last Sunday in March is start of DST in Central European Timezone).
-- Uses DATETIME functions to use first day of year and first day of month DST changes & finally determines correct day.
-----------------------------------------------------------------------------
-- Declare Variables
DECLARE @DstOffsetHr AS INT
DECLARE @DstOffsetMins AS INT
DECLARE @DstMthIncrement AS INT
DECLARE @DstWkOfMth AS VARCHAR(1)
DECLARE @DstWkOfMthInt AS INT
DECLARE @DstDayOfWk AS INT
DECLARE @DstHr INT
DECLARE @DstMins INT
DECLARE @DstEndsNextYr INT
DECLARE @DstTempDate DATETIME --Holding variable for constructing the date.
DECLARE @DaysInMth AS INT
DECLARE @DstModifier INT
-- Abort if DateToConvert or TimeZone is NULL or DstDateType not 'Start' or 'End'
IF @DateToConvert IS NULL or @TimeZone IS NULL or @DstDateType NOT IN ('Start', 'End')
RETURN NULL
--Populate variables. This is depended on whether we are trying to find DST start or end
--in a given timezone relative to a given date.
SELECT
@DstOffsetHr = tz_DstOffsetHr,
@DstOffsetMins = tz_DstOffsetMins,
@DstMthIncrement = CASE @DstDateType WHEN 'Start' THEN tz_DstStartMth - 1
WHEN 'End' THEN tz_DstEndMth - 1
END,
@DstWkOfMth = CASE @DstDateType WHEN 'Start' THEN tz_DstStartWkOfMth
WHEN 'End' THEN tz_DstEndWkOfMth
END,
@DstDayOfWk = CASE @DstDateType WHEN 'Start' THEN tz_DstStartDayOfWk
WHEN 'End' THEN tz_DstEndDayOfWk
END,
@DstHr = CASE @DstDateType WHEN 'Start' THEN DATEPART( hh, tz_DstStartTime )
WHEN 'End' THEN DATEPART( hh, tz_DstEndTime )
END,
@DstMins = CASE @DstDateType WHEN 'Start' THEN DATEPART( mi, tz_DstStartTime )
WHEN 'End' THEN DATEPART( mi, tz_DstEndTime )
END,
@DstEndsNextYr = CASE WHEN tz_DstStartMth > tz_DstEndMth THEN 1 ELSE 0 END
FROM TimeZones
WHERE tz_Code = @TimeZone AND
@DateToConvert BETWEEN tz_StartDate AND tz_EndDate
-- It is possible for the DST code occurrence day to be passed as 'L', indicating the last day (usually Sunday) of a given month.
-- We need the integer variable for calcalations relating to 1 - 4.
IF @DstWkOfMth <> 'L'
SET @DstWkOfMthInt = CONVERT( INT, @DstWkOfMth )
ELSE
SET @DstWkOfMthInt = 4
-- Perform calculations to determine date
-- Set the holding date variable to the first day of the year for the year of the date being evaluated
SET @DstTempDate = CONVERT( DATETIME, '1/1/' + CONVERT( VARCHAR(4), DATEPART(year,@DateToConvert) ) )
-- Handle scenario where DST ends in the next year (eg: AUSTRALIA)
IF @DstDateType = 'End' AND @DstEndsNextYr = 1
SET @DstTempDate = DATEADD( year, 1, @DstTempDate )
-- Add month value for DST pattern to the holding date variable
SET @DstTempDate = DATEADD( month, @DstMthIncrement, @DstTempDate )
-- #days in month is: construct 1st of month (above), add 1 month and subtract one day.
-- This identifies the last day of the month which is also the number of days.
set @DaysInMth = convert( int, DATENAME( DAY, DATEADD(DAY,-1,DATEADD(Month,1,@DstTempDate) ) ) )
-- Determine the modifier value needed to adjust the date
SET @DstModifier = DATEPART(weekday,@DstTempDate) - 1
-- This is the main calculation to determine the DST date
SET @DstTempDate = DATEADD( day, (@DstWkOfMthInt*7)-@DstModifier, @DstTempDate )
SET @DstTempDate = DATEADD( hour, @DstHr, @DstTempDate )
SET @DstTempDate = DATEADD( minute, @DstMins, @DstTempDate )
-- Some of the days of the week occur five times in any given month. Adjust if needed.
IF @DstWkOfMth = 'L' AND ( @DaysInMth - DATEPART( day, @DstTempDate ) >= 7 )
SET @DstTempDate = DATEADD( day, 7, @DstTempDate )
RETURN @DstTempDate
END
GO
-- =============================================
-- Create STORED PROCEDURE (GetGmtTime)
-- =============================================
IF EXISTS (SELECT * FROM sysobjects WHERE name = N'GetGmtTime')
DROP FUNCTION GetGmtTime
GO
CREATE FUNCTION [dbo].[GetGmtTime]
(@FromDate AS DATETIME,
@FromTimeZone AS VARCHAR(12))
RETURNS DATETIME
AS
BEGIN
-----------------------------------------------------------------------------
-- Created by: Robert Ford per http:
-- Created on: 17Nov2008
-- Modified on: 29Jul2010
-- Edited by 'Colin 2' per http:
-- Edited by 'shell_l_d' per http:
-----------------------------------------------------------------------------------
-- @FromDate - UTC or GMT DATETIME to be converted
-- @FromTimeZone - Time zone that the date time being passed is in (value from TimeZones.tz_Code)
-- RETURNS DATETIME - The converted UTC or GMT DATETIME.
--
-- NOTE: assumes Sunday is first day of week, so assumes 'set datefirst 7' is set prior.
-----------------------------------------------------------------------------
-- Reqd for datepart in this and child procs, called functions assume that the first day of the week is a Sunday
-- Set Sunday as first Day of the wk
--SET DATEFIRST 7 -- CAN NOT DO THIS IN A FUNCTION
--Declare variables
DECLARE @OffsetHr AS INT
DECLARE @OffsetMins AS INT
DECLARE @DstOffsetHr AS INT
DECLARE @DstOffsetMins AS INT
DECLARE @DstStartDate AS DATETIME
DECLARE @DstEndDate AS DATETIME
DECLARE @NewDate DATETIME
-- Default to @FromDate
SET @NewDate = @FromDate
-- Abort conversion if FromDate or FromTimeZone is NULL
IF @FromDate IS NULL or @FromTimeZone IS NULL
RETURN @NewDate
-- This query gets the timezone information from the TimeZones table for the provided timezone
SELECT
@OffsetHr=tz_OffsetHr,
@OffsetMins=tz_OffsetMins,
@DstOffsetHr=tz_DstOffsetHr,
@DstOffsetMins=tz_DstOffsetMins
FROM TimeZones
WHERE tz_Code = @FromTimeZone AND
@FromDate BETWEEN tz_StartDate AND tz_EndDate
-- Increase the DATETIME by the hours and minutes assigned to the timezone
-- As the procedure converts TO GMT, the offset in the timezone table needs to be reversed
-- as they assume a conversion from GMT.
SET @NewDate = DATEADD( hh, @OffsetHr *-1, @FromDate )
SET @NewDate = DATEADD( mi, @OffsetMins*-1, @NewDate )
-- Get DST Start & End Dates
SELECT @DstStartDate = dbo.CalcDaylightSavingsDate( @FromTimeZone, 'Start', @FromDate )
SELECT @DstEndDate = dbo.CalcDaylightSavingsDate( @FromTimeZone, 'End', @FromDate )
-- Check to see if the date being evaluated falls between the
-- DST Start and End date/times
IF @FromDate BETWEEN @DstStartDate AND DATEADD( hour, -1, DATEADD(second,-1,@DstEndDate) )
BEGIN
SET @NewDate = DATEADD( hh, @DstOffsetHr *-1, @FromDate )
SET @NewDate = DATEADD( mi, @DstOffsetMins*-1, @NewDate )
END
RETURN @NewDate
END
GO
-- =============================================
-- Create STORED PROCEDURE (GetTzTime)
-- =============================================
IF EXISTS (SELECT * FROM sysobjects WHERE name = N'GetTzTime')
DROP FUNCTION GetTzTime
GO
CREATE FUNCTION [dbo].[GetTzTime]
(@FromDate AS DATETIME,
@ToTimeZone AS VARCHAR(12))
RETURNS DATETIME
AS
BEGIN
-----------------------------------------------------------------------------
-- Created by: Robert Ford per http:
-- Created on: 17Nov2008
-- Modified on: 29Jul2010
-- Edited by 'Colin 2' per http:
-- Edited by 'shell_l_d' per http:
-----------------------------------------------------------------------------------
-- @FromDate - UTC or GMT DATETIME to be converted
-- @ToTimeZone - Time zone to convert the date time to (value from TimeZones.tz_Code)
-- RETURNS DATETIME - The converted UTC or GMT DATETIME.
--
-- NOTE: assumes Sunday is first day of week, so assumes 'set datefirst 7' is set prior.
-----------------------------------------------------------------------------
-- Reqd for datepart in this and child procs, called functions assume that the first day of the week is a Sunday
-- Set Sunday as first Day of the wk
--SET DATEFIRST 7 -- CAN NOT DO THIS IN A FUNCTION
-- Declare Variables
DECLARE @OffsetHr AS INT
DECLARE @OffsetMins AS INT
DECLARE @DstOffsetHr AS INT
DECLARE @DstOffsetMins AS INT
DECLARE @DstStartDate AS DATETIME
DECLARE @DstEndDate AS DATETIME
DECLARE @NewDate AS DATETIME
-- Default to @FromDate
SET @NewDate = @FromDate
-- Abort conversion if FromDate or ToTimeZone is NULL
IF @FromDate IS NULL or @ToTimeZone IS NULL
RETURN @NewDate
-- This query gets the timezone information from the TimeZones table for the provided timezone
SELECT
@OffsetHr=tz_OffsetHr,
@OffsetMins=tz_OffsetMins,
@DstOffsetHr=tz_DstOffsetHr,
@DstOffsetMins=tz_DstOffsetMins
FROM TimeZones
WHERE tz_Code = @ToTimeZone AND
@FromDate BETWEEN tz_StartDate AND tz_EndDate
-- Increase the DATETIME by the hours and minutes assigned to the timezone
-- As the procedure converts FROM GMT, the offset in the timezone table can be used directly.
SET @NewDate = DATEADD( hh, @OffsetHr, @FromDate )
SET @NewDate = DATEADD( mi, @OffsetMins, @NewDate )
-- Get DST Start & End Dates
SELECT @DstStartDate = dbo.CalcDaylightSavingsDate( @ToTimeZone, 'Start', @FromDate )
SELECT @DstEndDate = dbo.CalcDaylightSavingsDate( @ToTimeZone, 'End', @FromDate )
-- Check to see if the date being evaluated falls between the
-- DST Start and End date/times
IF @NewDate BETWEEN @DstStartDate AND DATEADD(hour,-1,DATEADD(second,-1,@DstEndDate))
BEGIN
SET @NewDate = DATEADD( hh, @DstOffsetHr, @FromDate )
SET @NewDate = DATEADD( mi, @DstOffsetMins, @NewDate )
END
RETURN @NewDate
END
GO
-- =============================================
-- Create STORED PROCEDURE (ConvertTimeZone)
-- =============================================
IF EXISTS (SELECT * FROM sysobjects WHERE name = N'ConvertTimeZone')
DROP FUNCTION ConvertTimeZone
GO
CREATE FUNCTION [dbo].[ConvertTimeZone]
(@FromDate AS DATETIME,
@FromTimeZone AS VARCHAR(12),
@ToTimeZone AS VARCHAR(12))
RETURNS DATETIME
AS
BEGIN
-----------------------------------------------------------------------------
-- Created by: Robert Ford per http:
-- Created on: 17Nov2008
-- Modified on: 29Jul2010
-- Edited by 'Colin 2' per http:
-- Edited by 'shell_l_d' per http:
-----------------------------------------------------------------------------------
-- @FromDate - UTC or GMT DATETIME to be converted
-- @FromTimeZone - Time zone that the date time being passed is in (value from TimeZones.tz_Code)
-- @ToTimeZone - Time zone to convert the date time to (value from TimeZones.tz_Code)
-- RETURNS DATETIME - The converted UTC or GMT DATETIME.
--
-- NOTE: assumes Sunday is first day of week, so assumes 'set datefirst 7' is set prior.
-- This function uses GetGmtTime and GetTzTime functions to prepare the value that needs to be delivered.
-- Supports the use of both GMT and UTC codes to represent Universal Time Code or Greenwich Mean Time.
-----------------------------------------------------------------------------
-- Declare Variables
DECLARE @NewDate AS DATETIME
-- Default to @FromDate
SET @NewDate = @FromDate
-- Abort conversion if FromDate, FromTimeZone or ToTimeZone is NULL
IF @FromDate IS NULL or @FromTimeZone IS NULL or @ToTimeZone IS NULL
RETURN @NewDate
-- If FromTimeZone is not 'GMT' or 'UTC', then convert it to 'GMT'
IF NOT @FromTimeZone IN ('GMT','UTC')
select @NewDate = dbo.GetGmtTime( @FromDate, @FromTimeZone )
-- If ToTimeZone is NOT 'GMT' or 'UTC', then convert NewDate to the desired timezone
IF NOT @ToTimeZone IN ('GMT','UTC')
select @NewDate = dbo.GetTzTime( @NewDate, @ToTimeZone )
RETURN @NewDate
END
GO
AND some tests...
-- TimeZone Conversion Tests
-- Use www.timeanddate.com - TimeZone conversion utility & to see DST times since 1980 (ish)
--error checking for NULL's
SELECT dbo.ConvertTimeZone( NULL,NULL,NULL ), NULL
SELECT dbo.ConvertTimeZone( NULL,'NSW','AZ' ), NULL
SELECT dbo.ConvertTimeZone( '21 Nov 2010 23:45:00', NULL, 'AZ' ), '21 Nov 2010 23:45:00'
SELECT dbo.ConvertTimeZone( '21 Nov 2010 23:45:00', 'NSW', NULL ), '21 Nov 2010 23:45:00'
--21Nov2010 23:45 NSW > 21Nov2010 12:45 GMT > 21Nov2010 5:45 AZ
SELECT dbo.ConvertTimeZone( '21 Nov 2010 23:45:00','NSW','AZ' ), '21 Nov 2010 05:45'
--21Jul2010 23:45 NSW > 21Jul2010 13:45 GMT > 21Jul2010 6:45 AZ
SELECT dbo.ConvertTimeZone( '21 Jul 2010 23:45:00','NSW','AZ' ), '21 Nov 2010 06:45'
DECLARE @FromTimeZone AS VARCHAR(12)
DECLARE @FromDate AS DATETIME
SET @FromTimeZone = (SELECT 'NSW')
SET @FromDate='5 Jul 2001 14:00'
SELECT dbo.CalcDaylightSavingsDate(@FromTimeZone, 'Start', @FromDate), '28 Oct 2001'
SELECT dbo.CalcDaylightSavingsDate(@FromTimeZone, 'End', @FromDate), '31 Mar 2002'
SET @FromDate='5 Jul 2002 14:00'
SELECT dbo.CalcDaylightSavingsDate(@FromTimeZone, 'Start', @FromDate), '27 Oct 2002'
SELECT dbo.CalcDaylightSavingsDate(@FromTimeZone, 'End', @FromDate), '30 Mar 2003'
SET @FromDate='5 Jul 2003 14:00'
SELECT dbo.CalcDaylightSavingsDate(@FromTimeZone, 'Start', @FromDate), '26 Oct 2003'
SELECT dbo.CalcDaylightSavingsDate(@FromTimeZone, 'End', @FromDate), '28 Mar 2004'
SET @FromDate='5 Jul 2004 14:00'
SELECT dbo.CalcDaylightSavingsDate(@FromTimeZone, 'Start', @FromDate), '31 Oct 2004'
SELECT dbo.CalcDaylightSavingsDate(@FromTimeZone, 'End', @FromDate), '27 Mar 2005'
SET @FromDate='5 Jul 2005 14:00'
SELECT dbo.CalcDaylightSavingsDate(@FromTimeZone, 'Start', @FromDate), '30 Oct 2005'
SELECT dbo.CalcDaylightSavingsDate(@FromTimeZone, 'End', @FromDate), '2 Apr 2006'
SET @FromDate='5 Jul 2006 14:00'
SELECT dbo.CalcDaylightSavingsDate(@FromTimeZone, 'Start', @FromDate), '29 Oct 2006'
SELECT dbo.CalcDaylightSavingsDate(@FromTimeZone, 'End', @FromDate), '25 Mar 2007'
SET @FromDate='5 Jul 2007 14:00'
SELECT dbo.CalcDaylightSavingsDate(@FromTimeZone, 'Start', @FromDate), '28 Oct 2007'
SELECT dbo.CalcDaylightSavingsDate(@FromTimeZone, 'End', @FromDate), '6 Apr 2008'
SET @FromDate='5 Jul 2008 14:00'
SELECT dbo.CalcDaylightSavingsDate(@FromTimeZone, 'Start', @FromDate), '5 Oct 2008'
SELECT dbo.CalcDaylightSavingsDate(@FromTimeZone, 'End', @FromDate), '5 Apr 2009'
SET @FromDate='5 Jul 2009 14:00'
SELECT dbo.CalcDaylightSavingsDate(@FromTimeZone, 'Start', @FromDate), '4 Oct 2009'
SELECT dbo.CalcDaylightSavingsDate(@FromTimeZone, 'End', @FromDate), '4 Apr 2010'
SET @FromDate='5 Jul 2010 14:00'
SELECT dbo.CalcDaylightSavingsDate(@FromTimeZone, 'Start', @FromDate), '3 Oct 2010'
SELECT dbo.CalcDaylightSavingsDate(@FromTimeZone, 'End', @FromDate), '3 Apr 2011'
modified on Wednesday, August 4, 2010 10:56 PM
|