SQl Server Timezone offset Calculation - sql-server

I am storing all my dates in SQL Server Datetime fields in UTC Date.
There is a requirement where by I have to calculate local Datetime in a procedure from the UTC Date field, and for that i have the Time zone offset of the local datetime.
For ex. my Timezone offset is:'05:30:00'
and UTC Date is: 2013-02-09 08:34:12.037
Desired output: 2013-02-09 14:04:12.037
Now is there a simple way where of doing this without DateAdd and splitting the offset in hours and minutes.

You should be able to use the SWITCHOFFSET function. Here is an example:
declare #dt datetime;
set #dt = '2013-02-09 08:34:12.037';
select SWITCHOFFSET(CONVERT(datetimeoffset, #dt), '+05:30') as 'DATETIMEOFFSET',
CAST(SWITCHOFFSET(CONVERT(datetimeoffset, #dt), '+05:30') as datetime) as 'DATETIME'
-- Outputs:
--
-- 2013-02-09 14:04:12.0370000 +05:30 2013-02-09 14:04:12.037

Use the convert with 112:
declare #d datetime
set #d = getdate()
declare #s nvarchar(20)
set #s = convert(varchar, #d, 112)
print #s
That string will have the year, month, seconds etc.. always on the same position
Extract the desired part with substring:
print substring(#s, 1, 4) -- the year
Now recalculate the entire thing to minutes, by multiplying the hours by 60 and adding the minutes. Now substract your minutes delta from that number. Build a new string with the adjusted date-time, and convert that back to datetime. But... if you need to know the date as well, and you want to do it correct.... there is some coding left.
My advice: do use dateadd it's simple and correct (substract minutes is my advice).

Related

T-SQL truncate time (not round) to the nearest minute

Looking for the most efficient and elegant way to do truncate the time to the minute
-- I need to truncate the time to the minute,
-- this code almost works but rounds up
SELECT
CAST('2021-09-02T15:15:30.9233333' AS datetime2(7)) AS EventDatetime2,
CAST(CAST('2021-09-02T15:15:30.9233333' AS datetime2(7)) AS TIME(0)) AS EventTime
As Larnu posted, if you want to round up or down depending on the seconds value, a simple convert to smalldatetime will do.
If you want to truncate, there are several ways, the simplest is probably just to add minutes to midnight (only posting because I prefer without the magic dates like 1900-01-01):
DECLARE #dt datetime2(7) = '2021-09-02T15:15:30.9233333';
DECLARE #d datetime2(7) = CONVERT(date, #dt);
SELECT DATEADD(MINUTE, DATEDIFF(MINUTE, #d, #dt), #d);
Another way is more intuitive but a little ugly:
DECLARE #dt datetime2(7) = '2021-09-02T15:15:30.9233333';
SELECT SMALLDATETIMEFROMPARTS
(
DATEPART(YEAR, #dt),
DATEPART(MONTH, #dt),
DATEPART(DAY, #dt),
DATEPART(HOUR, #dt),
DATEPART(MINUTE, #dt)
);
If you want to "round" to the nearest minute you could just CONVERT the value to a smalldatetime; they are only accurate to 1 minute:
SELECT CONVERT(smalldatetime,CONVERT(datetime2,'2021-09-02T15:15:30.9233333'));
If you want to, you can then CONVERT back to your original data type.
If you want to truncate (so strip the minutes) you could use the old DATEDIFF and DATEADD method:
DECLARE #DateTime2 datetime2(7) = '2021-09-02T15:15:30.9233333';
SELECT DATEADD(MINUTE,DATEDIFF(MINUTE,'19000101',#DateTime2),CONVERT(datetime2(7),'19000101'));
Just another option using left() and the implicit conversion.
Depending on the actual USE CASE, the outer convert() is optional
Example
DECLARE #dt datetime2(7) = '2021-09-02T15:15:30.9233333';
select convert(smalldatetime,left(#dt,16))
Results
2021-09-02 15:15:00
Combining the DATEADD and CAST(... AS SMALLDATETIME) approaches effectively gives you a minute "floor", like so:
SELECT CAST('2021-09-02T15:15:30.9233333' AS DATETIME2(7)) AS EventDatetime2,
CAST(CAST('2021-09-02T15:15:30.9233333' AS DATETIME2(7)) AS TIME(0)) AS EventTime,
CAST(CAST(DATEADD(
SECOND,
(DATEPART(SECOND, CAST('2021-09-02T15:15:30.9233333' AS DATETIME2(7))) * -1),
CAST('2021-09-02T15:15:30.9233333' AS DATETIME2(7))) AS SMALLDATETIME) AS TIME(0));
EventDatetime2
EventTime
(No column name)
2021-09-02 15:15:30.9233333
15:15:31
15:15:00
The DATEADD in this example subtracts the number of seconds from the datetime before converting it to a smalldatetime, so when that cast/convert does its rounding it will always go to the lower minute.
If, however, your input value is the sort of string literal that the wording of your question implies, you could also do this:
SELECT CAST(SUBSTRING('2021-09-02T15:15:30.9233333', CHARINDEX('T', '2021-09-02T15:15:30.9233333')+1, 6) + '00' AS TIME(0));
and get this result:
15:15:00

How to get date from yyyy-mm-dd to yyyy-mm-dd in SQL?

I want to get date from yyyy-mm-dd to yyyy-mm-dd in SQL.
Example: I have two parameter #startdate : 2015-12-28 and #enddate : 2016-01-02, and database in SQLServer, datatype is varchar(10)
DATE_ORDER
28-12-2015
30-12-1996
29-12-2016
30-12-1997
24-12-2015
27-12-1993
03-01-2016
01-01-1992
02-01-2016
etc...
Ok,now I want to get data from #startdate : 2015-12-28 and #enddate : 2016-01-02. I use SELECT * FROM TABLE_X WHERE DATE_ORDER >= #startdate AND DATE_ORDER <= #enddate . But the results are not what I expected. Here are the results I want
28-12-2015
30-12-1996
29-12-2016
30-12-1997
01-01-1992
02-01-2016
I think to solve this problem, I need to do two things :
First, get date range from #startdate to #enddate , in here 28/12/2015, 29/12/2015, 30/12/2015, 31/12/2015, 01/01/2016, 02/01/2016.
The second: get the date in database same in range 28/12, 29/12, 30/12, 31/12, 01/01, 02/01, ignoring the year.
Can you give me some ideas about this ?
Your actual format is "105-italian" find details here.
You can convert your existing VARCHAR(10)-values with this line to real datetime
SELECT CONVERT(DATETIME,YourColumn,105)
Next thing to know is, that you should not use BETWEEN but rather >=StartDate AND < NakedDateOfTheFollowingDay to check date ranges
So to solve your need Get date-range from 2015-12-28 to 2016-01-02 you might do something like this:
DECLARE #Start DATETIME={d'2015-12-28'};
DECLARE #End DATETIME={d'2016-01-02'};
SELECT *
FROM YourTable
WHERE CONVERT(DATETIME,YourDateColumn,105)>=#Start AND CONVERT(DATETIME,YourDateColumn,105)<#End+1
Attention Be aware, that the conversion lets your expression be not sargable. No index will be used.
Better was to store your date as correctly typed data to avoid conversions...
Try this query
SET DATEFIRST 1
DECLARE #wk int SET #wk = 2
DECLARE #yr int SET #yr = 2011
--define start and end limits
DECLARE #todate datetime, #fromdate datetime
SELECT #fromdate = dateadd (week, #wk, dateadd (YEAR, #yr-1900, 0)) - 4 -
datepart(dw, dateadd (week, #wk, dateadd (YEAR, #yr-1900, 0)) - 4) + 1
SELECT #todate = #fromdate + 6
;WITH DateSequence( Date ) AS
(
SELECT #fromdate AS Date
UNION ALL
SELECT dateadd(DAY, 1, Date)
FROM DateSequence
WHERE Date < #todate
)
--select result
SELECT * FROM DateSequence OPTION (MaxRecursion 1000)
So, after the 2nd or 3rd edit, it slowly becomes clear, what you want (i hope).
So you REALLY WANT to get the dates with the year beeing ignored.
As someone pointed out already, date-values are stored internally not as string, but as internal datatype date (whatever that is in memory, i don't know).
If you want to compare DATES, you cannot do that with ignorance of any part. If you want to, you have to build a NEW date value of day and month of given row and a hard coded year (2000 or 1 or whatever) for EVERY row.
SELECT * FROM TABLE_X WHERE convert(date,'2000' + substring(convert(char(8),convert(datetime, 'DATE_ORDER', 105),112),5,4),112) >= #startdate AND convert(date,'2000' + substring(convert(char(8),convert(datetime, 'DATE_ORDER', 105),112),5,4),112) <= #enddate
If your startdate and enddate go OVER sylvester, you have to do 2 queries, on from startdate to 1231, one from 0101 to enddate.

Convert date in millisecond to date

In my table, I have a column that contains date in millisecond like this:
table a
dateinmili
1440301846096 //first six month date
1443589721039 //second six month date
I use that for my Android device and it works fine. When I want to use this time in a PROCEDURE in SQL Server and convert this time to human time (understandable for human) and date I have a problem.
I'm in Iran which uses UTC time in first six Persian date month 4.30 and 3.30 in second six month.
For convert date in PROCEDURE I use this code:
CONVERT(nVARCHAR(10),DATEADD(mi, DATEDIFF(mi, GETUTCDATE(), GETDATE()), DATEADD(ss,dateinmili/1000,'1970-01-01')),8) as date
DATEADD(mi, DATEDIFF(mi, GETUTCDATE(), GETDATE()), DATEADD(ss,dateinmili/1000,'1970-01-01')) as time
and here is my problem:
When I convert date in second six month and date registered in first six month of year, I get 1 hour difference between real time and converted time. I know that is because
DATEDIFF(mi, GETUTCDATE(), GETDATE())
method which return different between UTC time and local time when ever its called (in my example return 3:30 not 4:30 ) but I don't know how can I fix that?
I can add column which contain current UTC time but I am looking for another way.
update
I see this question and it's not my problem convert long to date.
My problem is in my country UTC time is not constant in whole year and change between 3.30 and 4.30, for example I have date registered in first six month (Persian six month) like 1440271800000 and convert it now which we are in second six month (Persian six month) and use this code for convert.
declare #unixTS bigint
set #unixTS = 1440271800000
select dateadd(ms, #unixTS%(3600*24*1000),
dateadd(day, #unixTS/(3600*24*1000), '1970-01-01 03:30:00.0')
)
I get this
2015-08-22 23:00:00.000
but it's not right date; the right date is:
2015-08-23 00:00:00.000
because when time registered UTC was 4.30 and not 3.30 but know when I convert
it UTC is 3.30.
I wish if there was a method in SQL which return past UTC time different; I mean put a date to that and return that time different between local time and gmt time my problem solved.
I hope you understand my problem.
In the US we have Daylight Savings Time in the summer, in most areas that means that we are also not fixed offset from UTC. Older versions of MS Dynamics CRM used to save everything in UTC, so when we wanted to export data in local time, we had a similar exercise. I created a set of SQL functions that would take the standard GMT offset and the datetime to I wanted to convert and figure out whether to apply the standard or DST offset and return the local datetime. If your offset follows a set of rules, then you can modify this:
CREATE function [dbo].[DC_GMTtoLocal]
(#OrigGMT datetime,
#StandardOffset int)
RETURNS datetime
AS
BEGIN
DECLARE #RevDate datetime
set #RevDate = CASE dbo.DC_DaylightSavingTime_IsInEffect(#OrigGMT)
WHEN 1 THEN DATEADD(hour, - #StandardOffset + 1, #OrigGMT) -- in DST
ELSE DATEADD(hour, - #StandardOffset, #OrigGMT) -- Not In DST
END
return #RevDate
END
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE function [dbo].[DC_DaylightSavingTime_IsInEffect]
(#DtTime datetime)
RETURNS tinyint
AS
BEGIN
DECLARE #DLSStart datetime
, #DLSEnd datetime
, #DLSActive tinyint
SET #DLSActive = 0
If DATEADD(YEAR,3,GETDATE()) > #DtTime
BEGIN
SET #DLSStart =(SELECT dbo.DC_GetDaylightSavingsTimeStart(CONVERT(varchar,DATEPART(YEAR,#DtTime))))
SET #DLSEnd =(SELECT dbo.DC_GetDaylightSavingsTimeEnd(CONVERT(varchar,DATEPART(YEAR,#DtTime))))
IF #DtTime BETWEEN #DLSStart AND #DLSEnd
BEGIN
SET #DLSActive = 1
END
--SET #DLSActive = 0
END
RETURN #DLSActive
END
GO
CREATE function [dbo].[DC_GetDaylightSavingsTimeStart]
(#Year varchar(4))
RETURNS smalldatetime
as
--Start date: We evaluate the day of the week corresponding to the first day of the month and find the second Sunday of March using a Case statement
begin
declare #DTSStartWeek smalldatetime, #DTSEndWeek smalldatetime
set #DTSStartWeek = '03/01/' + convert(varchar,#Year)
return case datepart(dw,#DTSStartWeek)
when 1 then
dateadd(hour,170,#DTSStartWeek)
when 2 then
dateadd(hour,314,#DTSStartWeek)
when 3 then
dateadd(hour,290,#DTSStartWeek)
when 4 then
dateadd(hour,266,#DTSStartWeek)
when 5 then
dateadd(hour,242,#DTSStartWeek)
when 6 then
dateadd(hour,218,#DTSStartWeek)
when 7 then
dateadd(hour,194,#DTSStartWeek)
end
end
GO
CREATE function [dbo].[DC_GetDaylightSavingsTimeEnd]
(#Year varchar(4))
RETURNS smalldatetime
as
-- End date: We evaluate the day of the week corresponding to the first day of the month and find the first Sunday of March using a Case statement
begin
declare #DTSEndWeek smalldatetime
set #DTSEndWeek = '11/01/' + convert(varchar,#Year)
return case datepart(dw,dateadd(week,1,#DTSEndWeek))
when 1 then
dateadd(hour,2,#DTSEndWeek)
when 2 then
dateadd(hour,146,#DTSEndWeek)
when 3 then
dateadd(hour,122,#DTSEndWeek)
when 4 then
dateadd(hour,98,#DTSEndWeek)
when 5 then
dateadd(hour,74,#DTSEndWeek)
when 6 then
dateadd(hour,50,#DTSEndWeek)
when 7 then
dateadd(hour,26,#DTSEndWeek)
end
end
GO

Create "BETWEEN/AND"-capable DATETIMEs from given DATETIME

I want to create two DATETIME variables I can use to check with BETWEEN AND when given just one DATETIME in a stored procedure on SQL Server 2008.
So, when I get 2012/12/31 15:32:12 as input, I want to generate two new variables out of that, being #from = 2012/12/31 00:00:00 and #to = 2012/12/31 23:59:59.
These two variables are used to check if the records lie between them - that is, are on the same day as the input date.
I fooled around using CAST and CONVERT, but I don't really konw how to manipulate the dates in the way I want.
Should I do this another way? Or are there functions I'm not aware of?
Now it is version independedt
declare #from datetime, #to datetime
SET #from = convert(varchar, convert(datetime, '2012/12/31 15:32:12', 111), 112)
SET #to = DATEADD(day, 1, #from)
select * from yourtable where test date >= #from AND date < #to
You can;
declare #input datetime = '2012/12/31 15:32:12'
declare #from datetime = dateadd(day, 0, datediff(day, 0, #input))
declare #to datetime = dateadd(second, -1, dateadd(day, 1, #from))
>>>
2012-12-31 00:00:00.000 2012-12-31 23:59:59.000
Be careful of accuracy on your #to. 23:59:59.001 is a valid date but won't show up in your range if you subtract an entire second.
It is more common to set your #from and then use < #from + 1 instead of BETWEEN. (The plus adds whole days in SQL).
First convert your input date to a varchar using the appropriate date format(111 in this case), For the to date, append the midnight hour
Then cast your varchar back to datetime.
Example :
SELECT #from = CAST(CONVERT(VARCHAR(10), GETDATE(), 111) AS DATETIME)
,#to = CAST(CONVERT(VARCHAR(10), GETDATE(), 111)+' 23:59:59:997' AS DATETIME)
Here is a useful chart of datetime formats with brief explanations.
http://www.sql-server-helper.com/tips/date-formats.aspx

SQL Server adding one month to a date represented as an 8 digit decimal

A SQL Server application we use (accpac) represents dates as an 8 digit decimal in ISO format (example: today's date is 20100802)
I need to add one month to this. I've found a way to do it, but there must be a better way.
The steps of my solution are:
declare #accpacDate as decimal
set #accpacDate = 20100101
declare #date1 as date
declare #date2 as date
set #date1=cast(CAST(#accpacDate as varchar(8)) as datetime) /*get the starting value as a date */
set #date2=DATEADD(month,1,#date1)
select CONVERT(varchar(8),#date2,112) as aVarchar
select convert(decimal,CONVERT(varchar(8),#date2,112)) as aDecimal
SELECT CONVERT(VARCHAR(8),DATEADD(MONTH,1,CONVERT(VARCHAR(8),20100802,112)),112)
It seems about right what you are doing.
String and Date manipulation is pretty core in SQL, no fancy wrappers for auto-converting and manipulating date formats (accpac, memories, shiver).
You could write that into a user function, to add days to a accpac date, and return the result:
create function accpacadd
( #accpacdate decimal,
#days int)
RETURNS decimal
AS BEGIN
declare #date1 as datetime
set #date1=cast(CAST(#accpacDate as varchar(8)) as datetime) /*get the starting value as a date */
set #date1=DATEADD(day, #days, #date1)
return convert(decimal, CONVERT(varchar(8), #date1, 112))
END
So then you can just call it with min code:
select dbo.accpacadd(20100102, 5)
select dbo.accpacadd(20100102, -5)
Gives 20100107 and 20091228 respectively

Resources