 |
|
 |
Recently I discovered a performance issue with using this funciton as an inline function in a query that retrieved 10s of thousands of records. With out the function the query ran in only 10 seconds. When I added the function into the where clause the query performance degrated to over 10 minutes. For a real-time reporting application this is not acceptable.
Resolution: Instead of having the function in the where clause of the query I added some variables to the process. I typically write all my queries as stored procedures instead of views since it provides many additional options. In my procedure I declared a datetime variable and used the function to convert the date range variables to GMT and then used that variable in my where clause of the query as oppose to using the function to convert the GMT values to central time. This got the performance of the query back down to between 10 and 20 seconds.
For those that are wondering why I have pulling 10 thousand or more records into a real-time application, I really am not. This is an aggregation job that creates aggregates on a real-time basis for the reporting and getting the time down is very important to make sure that data is presented accuratly.
Robert Ford
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
It seems that I am not able to modify the article any more so I started a thread for code changes. I will reply to this thread each time there is a change to the code and provide the reasons for the change.
Robert Ford
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Thanks to Tom Medley for pointing out an issue with the code in situtations where the DST end range date is in a month that does not start on a Sunday. Here is the updated version of that procedure (I put version 3 because I had made a change to it previously due to a previous issue pointed out Amj Yamsani)
-- ============================================= -- Create scalar function (GET_TZTIME) -- ============================================= IF EXISTS (SELECT * FROM sysobjects WHERE name = N'GET_TZTIME') DROP FUNCTION GET_TZTIME GO
CREATE FUNCTION [dbo].[GET_TZTIME] (@DT AS DATETIME, @TZ AS VARCHAR(12)) RETURNS DATETIME AS BEGIN -- DECLARE VARIABLES DECLARE @NEWDT AS DATETIME DECLARE @OFFSETHR AS INT DECLARE @OFFSETMI AS INT DECLARE @DSTOFFSETHR AS INT DECLARE @DSTOFFSETMI AS INT --DECLARE @DSTDT AS VARCHAR(10) DECLARE @DSTEFFDT AS VARCHAR(10) DECLARE @DSTENDDT AS VARCHAR(10) DECLARE @DSTSTARTDT AS DATETIME DECLARE @DSTSTOPDT AS DATETIME -- This query gets the timezone information from the TIME_ZONES table for the provided timezone SELECT @OFFSETHR=offset_hr, @OFFSETMI=offset_mi, @DSTOFFSETHR=dst_offset_hr, @DSTOFFSETMI=dst_offset_mi, @DSTEFFDT=dst_eff_dt, @DSTENDDT=dst_END_dt FROM time_zones WHERE timezone_cd = @TZ AND @DT BETWEEN eff_dt AND end_dt
-- Increase the datetime by the hours and minutes assigned to the timezone SET @NEWDT = DATEADD(hh,@OFFSETHR,@DT) SET @NEWDT = DATEADD(mi,@OFFSETMI,@NEWDT)
-- Declare variables needed to convert MMWDHHmm pattern to for DST start date DECLARE @DSTSTARTMONTH INT DECLARE @DSTSTARTINSTANCE INT DECLARE @DSTSTARTDAY INT DECLARE @DSTSTARTHOUR INT DECLARE @DSTSTARTMINUTE INT DECLARE @DSTSTARTMODIFIER INT
-- Perform calculations to determine date SET @DSTSTARTMONTH = CONVERT(INT,SUBSTRING(@DSTEFFDT,1,2))-1 SET @DSTSTARTINSTANCE = CONVERT(INT,SUBSTRING(@DSTEFFDT,3,1)) SET @DSTSTARTDAY = CONVERT(INT,SUBSTRING(@DSTEFFDT,4,1)) SET @DSTSTARTHOUR = CONVERT(INT,SUBSTRING(@DSTEFFDT,5,2)) SET @DSTSTARTMINUTE = CONVERT(INT,SUBSTRING(@DSTEFFDT,7,2)) -- Set the start date variable to the first day of the year for the year of the date being evaluated SET @DSTSTARTDT = CONVERT(DATETIME,'1/1/' + CONVERT(VARCHAR(4),DATEPART(year,@NEWDT))) -- Add month value for start pattern to the start date variable SET @DSTSTARTDT = DATEADD(month,@DSTSTARTMONTH,@DSTSTARTDT) -- Determine the modifier value needed to adjust the date when the date desired is in a -- month that does not start on a Sunday SET @DSTSTARTMODIFIER = DATEPART(weekday,@DSTSTARTDT)
-- Check to see if the modifier date is greater than the date being evaluated -- This is important because it will change the final modifier -- (adding days in some instances and subtracting days in others IF @DSTSTARTMODIFIER > @DSTSTARTDAY BEGIN SET @DSTSTARTMODIFIER = 8 - @DSTSTARTMODIFIER END ELSE BEGIN SET @DSTSTARTMODIFIER = (@DSTSTARTMODIFIER-1)*-1 END
-- This is the main calculation to determine the date of the MMWDHHmm pattern SET @DSTSTARTDT = DATEADD(day,(@DSTSTARTINSTANCE*7)-(8-@DSTSTARTDAY) + @DSTSTARTMODIFIER,@DSTSTARTDT) SET @DSTSTARTDT = DATEADD(hour,@DSTSTARTHOUR,@DSTSTARTDT) SET @DSTSTARTDT = DATEADD(minute,@DSTSTARTMINUTE,@DSTSTARTDT)
-- Declare variables needed to convert MMWDHHmm pattern to for DST stop date DECLARE @DSTSTOPMONTH INT DECLARE @DSTSTOPINSTANCE INT DECLARE @DSTSTOPDAY INT DECLARE @DSTSTOPHOUR INT DECLARE @DSTSTOPMINUTE INT DECLARE @DSTSTOPMODIFIER INT
-- Perform calculations to determine date SET @DSTSTOPMONTH = CONVERT(INT,SUBSTRING(@DSTENDDT,1,2))-1 SET @DSTSTOPINSTANCE = CONVERT(INT,SUBSTRING(@DSTENDDT,3,1)) SET @DSTSTOPDAY = CONVERT(INT,SUBSTRING(@DSTENDDT,4,1)) SET @DSTSTOPHOUR = CONVERT(INT,SUBSTRING(@DSTENDDT,5,2)) SET @DSTSTOPMINUTE = CONVERT(INT,SUBSTRING(@DSTENDDT,7,2)) -- Set the start date variable to the first day of the year for the year of the date being evaluated SET @DSTSTOPDT = CONVERT(DATETIME,'1/1/' + CONVERT(VARCHAR(4),DATEPART(year,@NEWDT))) -- Determine the modifier value needed to adjust the date when the date desired is in a SET @DSTSTOPDT = DATEADD(month,@DSTSTOPMONTH,@DSTSTOPDT) -- Determine the modifier value needed to adjust the date when the date desired is in a -- month that does not start on a Sunday SET @DSTSTOPMODIFIER = DATEPART(weekday,@DSTSTOPDT)
-- Check to see if the modifier date is greater than the date being evaluated -- This is important because it will change the final modifier -- (adding days in some instances and subtracting days in others IF @DSTSTOPMODIFIER > @DSTSTOPDAY BEGIN SET @DSTSTOPMODIFIER = 8 - @DSTSTOPMODIFIER END ELSE BEGIN SET @DSTSTOPMODIFIER = (@DSTSTOPMODIFIER-1)*-1 END -- This is the main calculation to determine the date of the MMWDHHmm pattern SET @DSTSTOPDT = DATEADD(day,(@DSTSTOPINSTANCE*7)-(8-@DSTSTOPDAY) + @DSTSTOPMODIFIER,@DSTSTOPDT) SET @DSTSTOPDT = DATEADD(hour,@DSTSTOPHOUR,@DSTSTOPDT) SET @DSTSTOPDT = DATEADD(minute,@DSTSTOPMINUTE,@DSTSTOPDT)
-- Check to see if the date being evaluated falls between the -- DST start and stop date/times IF @NEWDT BETWEEN @DSTSTARTDT AND @DSTSTOPDT BEGIN -- Increase the datetime by the hours and minutes assigned to the timezone SET @NEWDT = DATEADD(hh,@DSTOFFSETHR,@DT) SET @NEWDT = DATEADD(mi,@DSTOFFSETMI,@NEWDT) END
-- Return the new date that has been converted from UTC time RETURN @NEWDT END GO
Robert Ford
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Seems like this implies there are only positive offsets when converting from a TZ time to a UTC time but GET_TZTIME respects the sign of the offset. Never mind - I see it is fixed in the downloadable code.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Some time zones (i.e. Mexico) are defined as ending on the LAST Sunday of a month which can be the 4th or 5th depending on month and year - there does not seem to be a way to use this method here. I have written a function that will translate the MMWDHHmm enpoints to actual datetimes given the dt they are going to be used with and that function will handle a 5 for week number meaning the last one in that month which could be 4 or 5. It also lets you change the function that does the comparisons to use dates which solves the previous problem I posted if it is a problem.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Tom,
Again, thanks for pointing out something that is missing. Due to my unfamiliarity of timezones outside of the US this did not occur to me. Would you mind providing more details of the changes that you made due to these missing options so that others may be able to benefit from it as well?
Robert Ford
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
I am concerned that I am missing something or that there is a problem with your posting (and possibly with the code you are using at work) that you may want to correct. I may be missing something but the logic seems to be:
Record the start and end dates of DST for a TimeZone as MMWDHHmm. Convert the DateTime in question to the same format and see if it is between the DST start and end dates. If - yes apply DST offset else apply regular offset.
I have copied the code and tried it and there are many datetimes that it fails to convert correctly. For example: 11/06/2010 is the first Saturday in November and it is before the first Sunday in November (11/07/2010) therefore it should be in DST. The code, however, says that 1117HHmm (first Saturday in November) is not between 0321HHmm and 1111HHmm. It seems that every first Monday - Saturday in November will be treated as after the first Sunday regardless of what year it is. Am I missing something or have I implemented the code incorrectly?
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Tom,
Thanks for your response. You are correct that there is a bug in the code. For those who read this and are not sure what this is about I will explain. The way the code for the function GET_TZTIME the evaluation on whether a date is within the DST range is not correct when the DST stop date is in a month that does not start on a Sunday. I did not initially notice this because this year November starts on a Sunday so I did not get any errors and everything was being evaluated correct.
Tonight I took some time to rework that function to resolve this issue. I have posted the changes to the code in a new thread to keep track of the code changes.
Thanks for taking the time to point out the issue so that I could get it resolved.
Robert Ford
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
 |
Trushitshah, The second two variables are the timezone_cd values that you want to change the date/time from and to. The timezone_cd values can be retrieved from the timezones table in the database. If you ran the SQL script in the article then and example would be if you are converting from central US time to arizona US time then the first value would be CT and the second would be AZ (e.g. SELECT dbo.new_time(GETDATE(),'CT','AZ')). If you have any other issues please let me now.
Robert Ford
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Hi Robert, Very good article. How would you manage a change in daylight savings for a particular year. In AUS we are regularly changing DST start and end dates for particular years. Could you add it to your code field so that there may be multiple rows in the table for a particular timezone?
|
| Sign In·View Thread·PermaLink | 5.00/5 (1 vote) |
|
|
|
 |
|
 |
Stribbed,
Thanks for the feedback. I had not realized that there were areas where the daylight time changes on a regular basis. I modified the table and functions so that the admin can have multiple entries for the timezone code with different effective and end dates so that there is not any interuption on their funcitonality. The effective and end dates is compared to the date provided to see if it falls withing the applicable range for that criteria. Now I did not add any triggers or anything to make that there are not any over lapping ranges, that is the only danger that I can see in my initial testing, though that can be done (though typically these types of control logic are handled in an application and not the database code). Please let me know if you have any other request, questions or problems.
Thanks,
Robert Ford robertford@ford-fam.com
Robert Ford
|
| Sign In·View Thread·PermaLink | 2.00/5 (1 vote) |
|
|
|
 |
|
 |
Only logical issue with this code is, it is switching the daylight to standard time on basis of GMT. But it should be based on local time.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Anj, Thanks for catching that mistake you are right. I made some adjustments to the code to resolve this issue though it seems I am no longer able to edit the article to add the changes so I will add them here.
-- ============================================= -- Create scalar function (GET_TZTIME) -- ============================================= IF EXISTS (SELECT * FROM sysobjects WHERE name = N'GET_TZTIME') DROP FUNCTION GET_TZTIME GO
CREATE FUNCTION [dbo].[GET_TZTIME] (@DT AS DATETIME, @TZ AS VARCHAR(12)) RETURNS DATETIME AS BEGIN -- DECLARE VARIABLES DECLARE @NEWDT AS DATETIME DECLARE @OFFSETHR AS INT DECLARE @OFFSETMI AS INT DECLARE @DSTOFFSETHR AS INT DECLARE @DSTOFFSETMI AS INT DECLARE @DSTDT AS VARCHAR(10) DECLARE @DSTEFFDT AS VARCHAR(10) DECLARE @DSTENDDT AS VARCHAR(10) -- This query gets the timezone information from the TIME_ZONES table for the provided timezone SELECT @OFFSETHR=offset_hr, @OFFSETMI=offset_mi, @DSTOFFSETHR=dst_offset_hr, @DSTOFFSETMI=dst_offset_mi, @DSTEFFDT=dst_eff_dt, @DSTENDDT=dst_END_dt FROM time_zones WHERE timezone_cd = @TZ AND @DT BETWEEN eff_dt AND end_dt
-- Increase the datetime by the hours and minutes assigned to the timezone SET @NEWDT = DATEADD(hh,@OFFSETHR,@DT) SET @NEWDT = DATEADD(mi,@OFFSETMI,@NEWDT)
-- GET THE DST parameter from the provided datetime -- This gets the month of the datetime provided (2 char value) SELECT @DSTDT = CASE LEN(DATEPART(month, @NEWDT)) WHEN 1 then '0' + CONVERT(VARCHAR(2),DATEPART(month, @NEWDT)) ELSE CONVERT(VARCHAR(2),DATEPART(month, @NEWDT)) END -- This gets the occurance of the day of the week within the month (i.e. first sunday, or second sunday...) (1 char value) SELECT @DSTDT = @DSTDT + CONVERT(VARCHAR(1),(DATEPART(day,@NEWDT) + 6) / 7) -- This gets the day of the week for the provided datetime (1 char value) SELECT @DSTDT = @DSTDT + CONVERT(VARCHAR(1),DATEPART(dw, @NEWDT)) -- This gets the hour for the provided datetime (2 char value) SELECT @DSTDT = @DSTDT + CASE LEN(DATEPART(hh, @NEWDT)) WHEN 1 then '0' + CONVERT(VARCHAR(2),DATEPART(hh, @NEWDT)) ELSE CONVERT(VARCHAR(2),DATEPART(hh, @NEWDT)) END -- This gets the minutes for the provided datetime (2 char value) SELECT @DSTDT = @DSTDT + CASE LEN(DATEPART(mi, @NEWDT)) WHEN 1 then '0' + CONVERT(VARCHAR(2),DATEPART(mi, @NEWDT)) ELSE CONVERT(VARCHAR(2),DATEPART(mi, @NEWDT)) END -- Checks to see if the DST parameter for the datetime provided is within the DST parameter for the timezone IF @DSTDT BETWEEN @DSTEFFDT AND @DSTENDDT BEGIN -- Increase the datetime by the hours and minutes assigned to the timezone SET @NEWDT = DATEADD(hh,@DSTOFFSETHR,@DT) SET @NEWDT = DATEADD(mi,@DSTOFFSETMI,@NEWDT) END
-- Return the new date that has been converted from UTC time RETURN @NEWDT END GO
Robert Ford
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Hello Robert,
Thanks for this great article.
I am confused getting the bits together since there have been some code changes.
Can you put the whole SQL script in single reply. I cant see the table for the DS effective Date abd End Date which should be linking to the time zone table as well.
Thanks Again, Imran
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Imran,
I am sorry for the not so timely reply. Here is the full script.
<pre>-- ************************* -- -- SQL 2005 Time Zone Conversion setup tables and functions -- -- Created by: Robert Ford -- Created on: 11/17/2008 -- Modified on: 11/30/2008 -- -- 11/17/2008 -- Creation Date -- -- 11/30/2008 -- 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. -- -- 06/11/2009 -- 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. -- -- ************************* -- -- Tables: -- TIME_ZONES -- TIMEZONE_CD VARCHAR(6) NOT NULL - this value is used by the functions ot reference the other values -- TIMEZONE_NAME VARCHAR(60) NOT NULL - name to describe the time zone code -- OFFSET_HR INT NOT NULL - the offset hours for adjusting the date time -- OFFSET_MI INT NOT NULL - the offset minutes for adjusting the date time -- DST_OFFSET_HR INT NOT NULL - the day light offset hours for adjusting the date time -- DST_OFFSET_MI INT NOT NULL - the day light offset minutes for adjusting the date time -- DST_EFF_DT VARCHAR(10) NOT NULL - the day light savings effective date time formate: MMWDHHmm -- DST_END_DT VARCHAR(10) NOT NULL - the day light savings end date time: MMWDHHmm -- Notes: This table holds the time zone creterion used to convert date time values -- DST EFF and END date format: MMWDHHmm -- MM = two digit month (e.g. March = 3) -- W = the week of the month (e.g. the second week of the month = 2) -- D = the day of the week, sunday is the start of the week which starts at 1 (e.g. Monday = 2) -- HH - two digit hour, 24 hour time (e.g. 2:00 am = 02, and 2:00 pm = 14) -- mm - two digit minute (e.g. 35 minutes after the hour is 35) -- -- example: On Sunday on the second week of the month for the month of March at 2:00 am -- would be 03210200 -- -- Functions: -- GET_UTCTIME -- @DT DATETIME - LOCAL DATETIME -- @TZ VARCHAR(12) - Time Zone code (must match a value in the database table) -- Notes: Pass the time zone code and date and the function will -- return a the date in UTC or GMT datetime -- GET_TZTIME -- @DT DATETIME - UTC or GMT DATETIME -- @TZ VARCHAR(12) - Time Zone code (must match a value in the database table) -- Notes: Pass the GMT or UTC date time and the time zone code -- and the function will provide the date time adjusted for the specified -- time zone. -- -- NEW_TIME -- @DT DATETIME - DATETIME to be converted -- @TZ1 VARCHAR(12) - Time zone that the date time being passed is in -- @TZ2 VARCHAR(12) - Time zone to convert the date time to -- Note: Pass the date time, the time zone code that the date time is in, -- and the time zone code that the date time should be converted to. -- This function uses the GET_UTCTIME and GET_TZTIME 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. -- -- -- 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].[TIME_ZONES]') and OBJECTPROPERTY(id, N'IsUserTable') = 1) DROP TABLE [dbo].[TIME_ZONES] GO
-- Create the TIME_ZONES table
CREATE TABLE [dbo].[TIME_ZONES] ( [TIMEZONE_CD] [varchar] (6) NOT NULL , [TIMEZONE_NAME] [varchar] (60) NOT NULL , [OFFSET_HR] [int] NOT NULL , [OFFSET_MI] [int] NOT NULL , [DST_OFFSET_HR] [int] NOT NULL , [DST_OFFSET_MI] [int] NOT NULL , [DST_EFF_DT] [varchar] (10) NOT NULL , [DST_END_DT] [varchar] (10) NOT NULL , [EFF_DT] DATETIME NOT NULL, [END_DT] DATETIME NOT NULL ) ON [PRIMARY] GO
-- Alter the table to add the primary key ALTER TABLE [dbo].[TIME_ZONES] WITH NOCHECK ADD CONSTRAINT [PK_TIME_ZONES] PRIMARY KEY CLUSTERED ( [TIMEZONE_CD], [EFF_DT] ) ON [PRIMARY] GO
-- Add the field default contraints ALTER TABLE [dbo].[TIME_ZONES] ADD CONSTRAINT [DF_TIME_ZONES_OFFSET] DEFAULT ((-1)) FOR [OFFSET_HR], CONSTRAINT [DF_TIME_ZONES_OFFSET_MI] DEFAULT (0) FOR [OFFSET_MI], CONSTRAINT [DF_TIME_ZONES_DST_OFFSET] DEFAULT ((-1)) FOR [DST_OFFSET_HR], CONSTRAINT [DF_TIME_ZONES_DST_OFFSET_MI] DEFAULT (0) FOR [DST_OFFSET_MI], CONSTRAINT [DF_TIME_ZONES_DST_EFF_DT] DEFAULT ('03210200') FOR [DST_EFF_DT], CONSTRAINT [DF_TIME_ZONES_DST_END_DT] DEFAULT ('11110200') FOR [DST_END_DT], CONSTRAINT DF_TIME_ZONES_EFF_DT DEFAULT GETDATE() FOR [EFF_DT], CONSTRAINT DF_TIME_ZONES_END_DT DEFAULT '12/31/9999' FOR [END_DT] GO
-- Insert time zone information into the TIME_ZONES table INSERT INTO time_zones(timezone_cd, timezone_name, offset_hr, offset_mi, dst_offset_hr, dst_offset_mi, dst_eff_dt, dst_end_dt) VALUES('AKT','Alaskan Time',-9,0,-8,0,'03210200','11110200') GO
INSERT INTO time_zones(timezone_cd, timezone_name, offset_hr, offset_mi, dst_offset_hr, dst_offset_mi, dst_eff_dt, dst_end_dt) VALUES('AT','Atlantic Time',-4,0,-3,0,'03210200','11110200') GO
INSERT INTO time_zones(timezone_cd, timezone_name, offset_hr, offset_mi, dst_offset_hr, dst_offset_mi, dst_eff_dt, dst_end_dt) VALUES('AZ','Arizona Time',-7,0,-7,0,'03210200','11110200') GO
INSERT INTO time_zones(timezone_cd, timezone_name, offset_hr, offset_mi, dst_offset_hr, dst_offset_mi, dst_eff_dt, dst_end_dt) VALUES('CT','Central Time',-6,0,-5,0,'03210200','11110200') GO
INSERT INTO time_zones(timezone_cd, timezone_name, offset_hr, offset_mi, dst_offset_hr, dst_offset_mi, dst_eff_dt, dst_end_dt) VALUES('ET','Eastern Time',-5,0,-4,0,'03210200','11110200') GO
INSERT INTO time_zones(timezone_cd, timezone_name, offset_hr, offset_mi, dst_offset_hr, dst_offset_mi, dst_eff_dt, dst_end_dt) VALUES('HT','Hawaii Time',-10,0,-10,0,'03210200','11110200') GO
INSERT INTO time_zones(timezone_cd, timezone_name, offset_hr, offset_mi, dst_offset_hr, dst_offset_mi, dst_eff_dt, dst_end_dt) VALUES('MT','Mountain Time',-7,0,-6,0,'03210200','11110200') GO
INSERT INTO time_zones(timezone_cd, timezone_name, offset_hr, offset_mi, dst_offset_hr, dst_offset_mi, dst_eff_dt, dst_end_dt) VALUES('NT','Newfoundland Time',-4,30,-2,30,'03210200','11110200') GO
INSERT INTO time_zones(timezone_cd, timezone_name, offset_hr, offset_mi, dst_offset_hr, dst_offset_mi, dst_eff_dt, dst_end_dt) VALUES('PT','Pacific Time',-8,0,-7,0,'03210200','11110200') GO
INSERT INTO time_zones(timezone_cd, timezone_name, offset_hr, offset_mi, dst_offset_hr, dst_offset_mi, dst_eff_dt, dst_end_dt) VALUES('YT','Yukon Time',-8,0,-8,0,'03210200','11110200') GO
-- ============================================= -- Create scalar function (GET_UTCTIME) -- ============================================= IF EXISTS (SELECT * FROM sysobjects WHERE name = N'GET_UTCTIME') DROP FUNCTION GET_UTCTIME GO
CREATE FUNCTION GET_UTCTIME (@DT AS DATETIME, @TZ AS VARCHAR(12)) RETURNS DATETIME AS BEGIN -- DECLARE VARIABLES DECLARE @NEWDT AS DATETIME DECLARE @OFFSETHR AS INT DECLARE @OFFSETMI AS INT DECLARE @DSTOFFSETHR AS INT DECLARE @DSTOFFSETMI AS INT DECLARE @DSTDT AS VARCHAR(10) DECLARE @DSTEFFDT AS VARCHAR(10) DECLARE @DSTENDDT AS VARCHAR(10) -- GET THE DST parameter from the provided datetime -- This gets the month of the datetime provided (2 char value) SELECT @DSTDT = CASE LEN(DATEPART(month, @DT)) WHEN 1 then '0' + CONVERT(VARCHAR(2),DATEPART(month, @DT)) ELSE CONVERT(VARCHAR(2),DATEPART(month, @DT)) END -- This gets the occurance of the day of the week within the month (i.e. first sunday, or second sunday...) (1 char value) SELECT @DSTDT = @DSTDT + CONVERT(VARCHAR(1),(DATEPART(day,@DT) + 6) / 7) -- This gets the day of the week for the provided datetime (1 char value) SELECT @DSTDT = @DSTDT + CONVERT(VARCHAR(1),DATEPART(dw, @DT)) -- This gets the hour for the provided datetime (2 char value) SELECT @DSTDT = @DSTDT + CASE LEN(DATEPART(hh, @DT)) WHEN 1 then '0' + CONVERT(VARCHAR(2),DATEPART(hh, @DT)) ELSE CONVERT(VARCHAR(2),DATEPART(hh, @DT)) END -- This gets the minutes for the provided datetime (2 char value) SELECT @DSTDT = @DSTDT + CASE LEN(DATEPART(mi, @DT)) WHEN 1 then '0' + CONVERT(VARCHAR(2),DATEPART(mi, @DT)) ELSE CONVERT(VARCHAR(2),DATEPART(mi, @DT)) END -- This query gets the timezone information from the TIME_ZONES table for the provided timezone SELECT @OFFSETHR=offset_hr, @OFFSETMI=offset_mi, @DSTOFFSETHR=dst_offset_hr, @DSTOFFSETMI=dst_offset_mi, @DSTEFFDT=dst_eff_dt, @DSTENDDT=dst_END_dt FROM time_zones WHERE timezone_cd = @TZ AND @DT BETWEEN eff_dt AND end_dt -- Checks to see if the DST parameter for the datetime provided is within the DST parameter for the timezone IF @DSTDT BETWEEN @DSTEFFDT AND @DSTENDDT BEGIN -- Increase the datetime by the hours and minutes assigned to the timezone SET @NEWDT = DATEADD(hh,ABS(@DSTOFFSETHR),@DT) SET @NEWDT = DATEADD(mi,ABS(@DSTOFFSETMI),@NEWDT) END -- If the DST parameter for the provided datetime is not within the defined -- DST eff and end dates for the timezone then use the standard time offset ELSE BEGIN -- Increase the datetime by the hours and minutes assigned to the timezone SET @NEWDT = DATEADD(hh,ABS(@OFFSETHR),@DT) SET @NEWDT = DATEADD(mi,ABS(@OFFSETMI),@NEWDT) END
-- Return the new date that has been converted to UTC time RETURN @NEWDT END GO
-- ============================================= -- Create scalar function (GET_TZTIME) -- ============================================= IF EXISTS (SELECT * FROM sysobjects WHERE name = N'GET_TZTIME') DROP FUNCTION GET_TZTIME GO
CREATE FUNCTION [dbo].[GET_TZTIME] (@DT AS DATETIME, @TZ AS VARCHAR(12)) RETURNS DATETIME AS BEGIN -- DECLARE VARIABLES DECLARE @NEWDT AS DATETIME DECLARE @OFFSETHR AS INT DECLARE @OFFSETMI AS INT DECLARE @DSTOFFSETHR AS INT DECLARE @DSTOFFSETMI AS INT --DECLARE @DSTDT AS VARCHAR(10) DECLARE @DSTEFFDT AS VARCHAR(10) DECLARE @DSTENDDT AS VARCHAR(10) DECLARE @DSTSTARTDT AS DATETIME DECLARE @DSTSTOPDT AS DATETIME -- This query gets the timezone information from the TIME_ZONES table for the provided timezone SELECT @OFFSETHR=offset_hr, @OFFSETMI=offset_mi, @DSTOFFSETHR=dst_offset_hr, @DSTOFFSETMI=dst_offset_mi, @DSTEFFDT=dst_eff_dt, @DSTENDDT=dst_END_dt FROM time_zones WHERE timezone_cd = @TZ AND @DT BETWEEN eff_dt AND end_dt
-- Increase the datetime by the hours and minutes assigned to the timezone SET @NEWDT = DATEADD(hh,@OFFSETHR,@DT) SET @NEWDT = DATEADD(mi,@OFFSETMI,@NEWDT)
-- Declare variables needed to convert MMWDHHmm pattern to for DST start date DECLARE @DSTSTARTMONTH INT DECLARE @DSTSTARTINSTANCE INT DECLARE @DSTSTARTDAY INT DECLARE @DSTSTARTHOUR INT DECLARE @DSTSTARTMINUTE INT DECLARE @DSTSTARTMODIFIER INT
-- Perform calculations to determine date SET @DSTSTARTMONTH = CONVERT(INT,SUBSTRING(@DSTEFFDT,1,2))-1 SET @DSTSTARTINSTANCE = CONVERT(INT,SUBSTRING(@DSTEFFDT,3,1)) SET @DSTSTARTDAY = CONVERT(INT,SUBSTRING(@DSTEFFDT,4,1)) SET @DSTSTARTHOUR = CONVERT(INT,SUBSTRING(@DSTEFFDT,5,2)) SET @DSTSTARTMINUTE = CONVERT(INT,SUBSTRING(@DSTEFFDT,7,2)) -- Set the start date variable to the first day of the year for the year of the date being evaluated SET @DSTSTARTDT = CONVERT(DATETIME,'1/1/' + CONVERT(VARCHAR(4),DATEPART(year,@NEWDT))) -- Add month value for start pattern to the start date variable SET @DSTSTARTDT = DATEADD(month,@DSTSTARTMONTH,@DSTSTARTDT) -- Determine the modifier value needed to adjust the date when the date desired is in a -- month that does not start on a Sunday SET @DSTSTARTMODIFIER = DATEPART(weekday,@DSTSTARTDT)
-- Check to see if the modifier date is greater than the date being evaluated -- This is important because it will change the final modifier -- (adding days in some instances and subtracting days in others IF @DSTSTARTMODIFIER > @DSTSTARTDAY BEGIN SET @DSTSTARTMODIFIER = 8 - @DSTSTARTMODIFIER END ELSE BEGIN SET @DSTSTARTMODIFIER = (@DSTSTARTMODIFIER-1)*-1 END
-- This is the main calculation to determine the date of the MMWDHHmm pattern SET @DSTSTARTDT = DATEADD(day,(@DSTSTARTINSTANCE*7)-(8-@DSTSTARTDAY) + @DSTSTARTMODIFIER,@DSTSTARTDT) SET @DSTSTARTDT = DATEADD(hour,@DSTSTARTHOUR,@DSTSTARTDT) SET @DSTSTARTDT = DATEADD(minute,@DSTSTARTMINUTE,@DSTSTARTDT)
-- Declare variables needed to convert MMWDHHmm pattern to for DST stop date DECLARE @DSTSTOPMONTH INT DECLARE @DSTSTOPINSTANCE INT DECLARE @DSTSTOPDAY INT DECLARE @DSTSTOPHOUR INT DECLARE @DSTSTOPMINUTE INT DECLARE @DSTSTOPMODIFIER INT
-- Perform calculations to determine date SET @DSTSTOPMONTH = CONVERT(INT,SUBSTRING(@DSTENDDT,1,2))-1 SET @DSTSTOPINSTANCE = CONVERT(INT,SUBSTRING(@DSTENDDT,3,1)) SET @DSTSTOPDAY = CONVERT(INT,SUBSTRING(@DSTENDDT,4,1)) SET @DSTSTOPHOUR = CONVERT(INT,SUBSTRING(@DSTENDDT,5,2)) SET @DSTSTOPMINUTE = CONVERT(INT,SUBSTRING(@DSTENDDT,7,2)) -- Set the start date variable to the first day of the year for the year of the date being evaluated SET @DSTSTOPDT = CONVERT(DATETIME,'1/1/' + CONVERT(VARCHAR(4),DATEPART(year,@NEWDT))) -- Determine the modifier value needed to adjust the date when the date desired is in a SET @DSTSTOPDT = DATEADD(month,@DSTSTOPMONTH,@DSTSTOPDT) -- Determine the modifier value needed to adjust the date when the date desired is in a -- month that does not start on a Sunday SET @DSTSTOPMODIFIER = DATEPART(weekday,@DSTSTOPDT)
-- Check to see if the modifier date is greater than the date being evaluated -- This is important because it will change the final modifier -- (adding days in some instances and subtracting days in others IF @DSTSTOPMODIFIER > @DSTSTOPDAY BEGIN SET @DSTSTOPMODIFIER = 8 - @DSTSTOPMODIFIER END ELSE BEGIN SET @DSTSTOPMODIFIER = (@DSTSTOPMODIFIER-1)*-1 END -- This is the main calculation to determine the date of the MMWDHHmm pattern SET @DSTSTOPDT = DATEADD(day,(@DSTSTOPINSTANCE*7)-(8-@DSTSTOPDAY) + @DSTSTOPMODIFIER,@DSTSTOPDT) SET @DSTSTOPDT = DATEADD(hour,@DSTSTOPHOUR,@DSTSTOPDT) SET @DSTSTOPDT = DATEADD(minute,@DSTSTOPMINUTE,@DSTSTOPDT)
-- Check to see if the date being evaluated falls between the -- DST start and stop date/times IF @NEWDT BETWEEN @DSTSTARTDT AND @DSTSTOPDT BEGIN -- Increase the datetime by the hours and minutes assigned to the timezone SET @NEWDT = DATEADD(hh,@DSTOFFSETHR,@DT) SET @NEWDT = DATEADD(mi,@DSTOFFSETMI,@NEWDT) END
-- Return the new date that has been converted from UTC time RETURN @NEWDT END GO
-- ============================================= -- Create scalar function (NEW_TIME) -- ============================================= IF EXISTS (SELECT * FROM sysobjects WHERE name = N'NEW_TIME') DROP FUNCTION NEW_TIME GO
CREATE FUNCTION NEW_TIME (@DT AS DATETIME, @TZ1 AS VARCHAR(12), @TZ2 AS VARCHAR(12)) RETURNS DATETIME AS BEGIN -- Declare variables DECLARE @NEWDT AS DATETIME -- Check to see if the provided timezone for the source datetime is in GMT or UTC time -- If it is not then convert the provided datetime to UTC time IF NOT @TZ1 IN ('GMT','UTC') BEGIN SELECT @NEWDT = dbo.GET_UTCTIME(@DT,@TZ1) END ELSE -- If the provided datetime is in UTC or GMT time then set the NEWTIME variable to this value BEGIN SET @NEWDT = @DT END
-- Check to see if the provided conversion timezone is GMT or UTC -- If it is then no conversion is needed. -- If it is not then convert the provided datetime to the desired timezone IF NOT @TZ2 IN ('GMT','UTC') BEGIN SELECT @NEWDT = dbo.GET_TZTIME(@NEWDT,@TZ2) END
-- Return the new converted datetime RETURN @NEWDT END GO</pre>
Robert Ford
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Thanks Robert,
Great Stuff ...
Have a question unrelated to the logic. Do you have an idea where can we get the list of the Daylight saving data information for different timezones. Is it something which I will need to compile or are there any lists available. i did some survey but looks like we can only buy them for about $3000.
Any feedback would be appreciated.
Thanks again ... brilliant piece of work.
Imran
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Imran,
The answer to this depends upon how you want it. If you just want the dst adjustment for the basic time zone then it is available on quite a few websites though you might have to do a little work getting it off the web page and onto something else. A little trick I use is with Excel. If the web page has a table on it and you have Excel 2003 or greater then there is a neat feature where you can right click on the table and select the option "Export to Excel" and it will download the table to Excel. This has saved me a lot of time for many different reasons. Some websites that you may be interested in are as follows; http://en.wikipedia.org/wiki/List_of_time_zone_abbreviations, http://en.wikipedia.org/wiki/List_of_time_zones, http://en.wikipedia.org/wiki/List_of_zoneinfo_time_zones. Now if you are in need of regional info or timezone by a region code, postal code, or zip code that gets a little tricky. The info is out there though I have not been able to find it downloadable for free. You would have to do some work moving it from a website to you own media.
I hope that this helps. Let me know if you have any other questions.
Robert Ford
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|