Comparing two dates which are in different time zone - sql-server

Is there a way to compare two dates (datetime datatype) which are in different time zones in SQL Server 2008 R2?
Do i need to worry about DST when converting PST to EST? Looks like there will always be 3 hour difference.
In the following script, the transaction date is in PST timezone and #Date parameter is in EST time zone;
DECLARE #Date DATETIME = '2019/03/09 00:01:57.000'
SELECT
f.[CustomerNum] ,
f.[Amount] ,
f.[TransactionDate] ,
cus.Linkdate
FROM
[Transaction] f
INNER JOIN
dbo.Customer cus ON cus.CusNum = f.CustomerNum AND cus.OID = f.OID
WHERE
f.TransactionDate > #Date
Would this work?
DATEADD(MINUTE, DATEPART(TZoffset, SYSDATETIMEOFFSET()), fpn.TransactionDate)> DATEADD(MINUTE, DATEPART(TZoffset, SYSDATETIMEOFFSET()), #Date)

The offset between PST and EST is 3 hours.
WHERE f.TransactionDate > DATEADD(hour, 3, #Date)

If your parameter is going to be time zone sensitive, it's better that you use a data type that explicitly depicts its time zone, like datetimeoffset. Then you can use switchoffset to change to the time zone you need.
DECLARE #Date DATETIMEOFFSET = '2019/03/09 00:01:57.000 -05:00' -- EST
SELECT
f.[CustomerNum] ,
f.[Amount] ,
f.[TransactionDate] ,
cus.Linkdate
FROM
[Transaction] f
INNER JOIN
dbo.Customer cus ON cus.CusNum = f.CustomerNum AND cus.OID = f.OID
WHERE
f.TransactionDate > cast(switchoffset(#Date,'-08:00') as datetime) -- PST

Related

build month Start and End dates intervals SQL

I have my getdate() = '2022-03-21 09:24:34.313'
I'd like to build Start Month and End Month dates intervals with SQL language (SQL server) , with the following screen :
You can use EOMONTH function and DATEADD function to get the data you want.
But, the best approach would be to use a calendar table and map it against the current date and get the data you want.
DECLARE #DATE DATE = getdate()
SELECT DATEADD(DAY,1,EOMONTH(#DATE,-1)) AS MonthM_Start, EOMONTH(#DATE) AS MonthM_End,
DATEADD(DAY,1,EOMONTH(#DATE,-2)) AS MonthOneBack_Start, EOMONTH(#DATE,-1) AS MonthOneBack_End,
DATEADD(DAY,1,EOMONTH(#DATE,-3)) AS MonthTwoBack_Start, EOMONTH(#DATE,-2) AS MonthTwoBack_End,
DATEADD(DAY,1,EOMONTH(#DATE,-4)) AS MonthThreeBack_Start, EOMONTH(#DATE,-3) AS MonthThreeBack_End
MonthM_Start
MonthM_End
MonthOneBack_Start
MonthOneBack_End
MonthTwoBack_Start
MonthTwoBack_End
MonthThreeBack_Start
MonthThreeBack_End
2022-03-01
2022-03-31
2022-02-01
2022-02-28
2022-01-01
2022-01-31
2021-12-01
2021-12-31
You can use a recursive CTE to avoid having to hard-code an expression for each month boundary you need, making it very easy to handle fewer or more months by just changing a parameter.
Do you really need the end date for processing? Seems more appropriate for a label, since date/time types can vary - meaning the last day of the month at midnight isn't very useful if you're trying to pull any data from after midnight on the last day of the month.
This also shows how to display the data for each month even if there isn't any data in the table for that month.
DECLARE #number_of_months int = 4,
#today date = DATEFROMPARTS(YEAR(GETDATE()), MONTH(GETDATE()), 1);
;WITH m(s) AS
(
SELECT #today UNION ALL SELECT DATEADD(MONTH, -1, s) FROM m
WHERE s > DATEADD(MONTH, 1-#number_of_months, #today)
)
SELECT MonthStart = m.s, MonthEnd = EOMONTH(m.s)--, other cols/aggs
FROM m
--LEFT OUTER JOIN dbo.SourceTable AS t
--ON t.datetime_column >= m
--AND t.datetime_column < DATEADD(MONTH, 1, m);
Output (without the join):
MonthStart
MonthEnd
2022-03-01
2022-03-31
2022-02-01
2022-02-28
2022-01-01
2022-01-31
2021-12-01
2021-12-31
Example db<>fiddle
But, as mentioned in a comment, you could easily store this information in a calendar table, too, and just outer join to that:
SELECT c.TheFirstOfMonth, c.TheLastOfMonth --, other cols/aggs
FROM dbo.CalendarTable AS c
LEFT OUTER JOIN dbo.SourceTable AS t
ON t.datetime_column >= c.TheFirstOfMonth
AND t.datetime_column < c.TheFirstOfNextMonth
WHERE c.FirstOfMonth >= DATEADD(MONTH, -4, GETDATE())
AND c.FirstOfMonth < GETDATE();

SQL - Number of minutes in a month given month number

I have a MS SQL table which contains a column containing month numbers (stored as an int).
I would like to get the number of minutes in the month using the month number.
After looking through Stack Overflow I came across the following code which I thought I could adapt to tell me the number of minutes by replacing the word DAY with MINUTE but to no avail.
DECLARE #ADate DATETIME
SET #ADate = GETDATE()
SELECT DAY(EOMONTH(#ADate)) AS DaysInMonth
The month is a 1 or 2 digit int depending on the month.
UPDATE:
I do have the year as well stored in another column.
Use DATEDIFF with MINUTE.
IF OBJECT_ID('tempdb..#Month') IS NOT NULL
DROP TABLE #Month
CREATE TABLE #Month (
MonthNumber INT,
Year INT)
INSERT INTO #Month (
MonthNumber,
Year)
VALUES
(1, 2018),
(2, 2018),
(2, 2016), -- Leap
(12, 2018)
SELECT
M.MonthNumber,
M.Year,
FirstDay = DATEFROMPARTS(M.Year, M.MonthNumber, 1),
FirstDayNextMonth = DATEADD(
MONTH,
1,
DATEFROMPARTS(M.Year, M.MonthNumber, 1)),
Minutes = DATEDIFF(
MINUTE,
DATEFROMPARTS(M.Year, M.MonthNumber, 1), -- FirstDay
DATEADD(MONTH, 1, DATEFROMPARTS(M.Year, M.MonthNumber, 1))) -- FirstDayNextMonth
FROM
#Month AS M
Results:
MonthNumber Year FirstDay FirstDayNextMonth Minutes
1 2018 2018-01-01 2018-02-01 44640
2 2018 2018-02-01 2018-03-01 40320
2 2016 2016-02-01 2016-03-01 41760
12 2018 2018-12-01 2019-01-01 44640
That's a deceptively tricky question. It's not only that the number of days in February changes in leap years. The number of hours in a date changes during the transition to Summer/Winter time. To calculate the correct difference in minutes between two dates you need to know the correct time offset as well.
Instead of looking up the offset for each date though, you can use the timezone. The timezone information contains all the rules needed to calculate the time offset for past and future dates, provided the OS's timezone information is kept up to date.
SQL Server 2016 and later support timezones with the AT TIME ZONE expression :
For example these queries:
select datetimefromparts(2018,3,25,0,0,0,0) at TIME ZONE 'E. Europe Standard Time'
select datetimefromparts(2018,3,26,0,0,0,0) at TIME ZONE 'E. Europe Standard Time'
Return
2018-03-25 00:00:00.000 +02:00
2018-03-26 00:00:00.000 +03:00
March 25 had 23 hours instead of 24. The difference of the two days in minutes is 1380 instead of the usual 1440:
select datediff(mi,
datetimefromparts(2018,3,25,0,0,0,0) at TIME ZONE 'E. Europe Standard Time',
datetimefromparts(2018,3,26,0,0,0,0) at TIME ZONE 'E. Europe Standard Time')
-----
1380
You can pass the timezone name as a parameter :
declare #mytimezone nvarchar(40)='E. Europe Standard Time'
select datetimefromparts(2018,3,25,0,0,0,0) at TIME ZONE #mytimezone
select datetimefromparts(2018,3,26,0,0,0,0) at TIME ZONE #mytimezone
To calculate the correct number of minutes in a month, you could use the difference in minutes between the current and the next month's 1st day on a specific timezone with AT TIME ZONE. This would ensure the correct offsets are used.
A query that calculates the difference in minutes correctly could look like this :
declare #mytimezone nvarchar(40)='E. Europe Standard Time'
select year,month,
datediff(mi,
datetimefromparts(M.Year,M.Month,1,0,0,0,0) at TIME ZONE #mytimezone,
dateadd(MONTH,1,datetimefromparts(M.Year,M.Month,1,0,0,0,0)) at TIME ZONE #mytimezone
)
from (values
(2016,2),
(2018,1),
(2018,2),
(2018,3),
(2018,4),
(2018,5),
(2018,6),
(2018,7),
(2018,8),
(2018,9),
(2018,10),
(2018,11),
(2018,12) ) M(Year,Month)
You can create a function to calculate the minutes. Either a scalar function that can be used in the SELECT clause :
CREATE FUNCTION MinutesInMonth
(
#Year INT,
#Month INT,
#timezone nvarchar(40)
)
RETURNS INT
AS
BEGIN
Return
datediff(mi,
datetimefromparts(#year,#month,1,0,0,0,0) at TIME ZONE #timezone,
dateadd(MONTH,1,datetimefromparts(#year,#month,1,0,0,0,0)) at TIME ZONE #timezone
)
END
...
select year,month,dbo.MinutesInMonth(year,month,#mytimezone)
from (values
(2018,10),
(2018,11),
(2018,12)
) M(Year,Month)
Or an inline table function that can be used in the FROM clause and gets inlined in the query itself :
CREATE FUNCTION MinutesInMonth
(
#Year INT,
#Month INT,
#timezone nvarchar(40)
)
RETURNS TABLE
RETURN (select datediff(mi,
datetimefromparts(#year,#month,1,0,0,0,0) at TIME ZONE #timezone,
dateadd(MONTH,1,datetimefromparts(#year,#month,1,0,0,0,0))
At TIME ZONE #timezone) as Minutes
)
select year,month,Minutes
from (values
(2016,2),
(2018,1),
(2018,2),
(2018,3),
(2018,4),
(2018,5),
(2018,6),
(2018,7),
(2018,8),
(2018,9),
(2018,10),
(2018,11),
(2018,12)
) M(Year,Month)
cross apply dbo.MinutesInMonth(year,month,#mytimezone)
Try this out :-
DECLARE #Year INT = 2016, #Month INT = 2;
DECLARE #Date DATE = DATETIMEFROMPARTS(#Year,#Month, 1, 0, 0, 0, 0) AT TIME ZONE 'Sri Lanka Standard Time';
SELECT DAY(EOMONTH(#Date)) * 24 * 60;
Alternatively you can create a scalar function as follows:-
CREATE FUNCTION GetMinutesOfMonth
(
#Year INT,
#Month INT,
#TimeZone VARCHAR(50) = NULL
)
RETURNS INT
AS
BEGIN
DECLARE #MinutesOfMonth INT, #CurrentTimeZone VARCHAR(50);
EXEC MASTER.dbo.xp_regread 'HKEY_LOCAL_MACHINE',
'SYSTEM\CurrentControlSet\Control\TimeZoneInformation',
'TimeZoneKeyName',#CurrentTimeZone OUT;
SET #TimeZone = ISNULL((SELECT [name] FROM SYS.time_zone_info WHERE [name] = #TimeZone), #CurrentTimeZone)
DECLARE #Date DATE = DATETIMEFROMPARTS(#Year, #Month, 1, 0, 0, 0, 0) AT TIME ZONE #TimeZone;
SELECT #MinutesOfMonth = DAY(EOMONTH(#Date)) * 24 * 60;
RETURN #MinutesOfMonth;
END
GO
and use it as:-
DECLARE #Year INT = 2016, #Month INT = 2;
SELECT dbo.GetMinutesOfMonth(#Year, #Month, 'Sri Lanka Standard Time');
If the timezone of the current machine is same as the dates being considered, you may use the function as follows:-
DECLARE #Year INT = 2016, #Month INT = 2;
SELECT dbo.GetMinutesOfMonth(#Year, #Month, DEFAULT);

Get results for one day back in sql server

I am trying to get results from 1 day back, for example if i have a job that runs today at 1:00:00 am the 22/05/2018 i want it to get back the results for the 21/05/2018 00:00:00 am to 21/05/2018 23:59:59 pm.
i tried the follwing
select *
from table
where CreatedDateTime BETWEEN DATEADD(day, -1, GETDATE()) AND DATEADD(day, -0, GETDATE()) // it brings back everything from yesterday and today
example of how my created date time is stored in the db 2018-05-21 16:39:09.4830000
The bewteen operator filters the dates based on >= and <=
You need :
select *
from table
where CreatedDateTime >= DATEADD(day, -1, GETDATE()) AND
CreatedDateTime < GETDATE();
I suspect you would need cast(... as date) if so, the you can directly express this as
select *
from table
where cast(CreatedDateTime as date) = cast(DATEADD(day, -1, GETDATE()) as date);
Here is a good BLOG on filtering date range in query.
SELECT *
FROM table
WHERE CreatedDateTime BETWEEN GETDATE() -1 AND GETDATE()

Select records created in a 24 hour time-frame

I'm trying to query our database to find all records that were created between 6am yesterday and 6am today. This will be run in a report at any point during the day so set times/dates are useless.
I have this so far:-
SELECT * FROM DaySummaryDetail DSD
WHERE DSD.FromDateTime BETWEEN DATEADD(DAY, -1, GetDate())
AND DATEADD(Day, 1, GetDate())
But obviously this only works for 24 hours ago from right now until right now. I can't figure out how to apply a time as well as date.
Every example I find online seems slightly different and uses set dates/times ie, >= 20/02/2015 06:00:00.
I normally use Oracle SQL which would simply work using this:-
ptt.mod_date_time >= TRUNC (SYSDATE - 1) - 2 / 24
AND ptt.mod_date_time <= TRUNC (SYSDATE - 1) + 22 / 24
This would return results from 10pm to 10pm but the format appears totally different in SQL Server.
You can get the datetime values you are after by doing the following:
SELECT DATEADD(HOUR,6,CONVERT(DATETIME, CONVERT(DATE ,GETDATE()))) Today6AM,
DATEADD(HOUR,-18,CONVERT(DATETIME, CONVERT(DATE ,GETDATE()))) Yesterday6AM
By doing this: CONVERT(DATE ,GETDATE()) you are stripping off the time portion of today's date. Converting it back to datetime gives you midnight for today.
The query adds 6 hours to midnight of the current day for 6am today and subtracts 18 hours from midnight of the current day to give you 6am on the previous day.
Output:
Today6AM Yesterday6AM
================================================
2015-02-20 06:00:00.000 2015-02-19 06:00:00.000
So adding that to your query:
SELECT *
FROM DaySummaryDetail DSD
WHERE DSD.FromDateTime
BETWEEN DATEADD(HOUR,-18,CONVERT(DATETIME, CONVERT(DATE ,GETDATE())))
AND DATEADD(HOUR,6,CONVERT(DATETIME, CONVERT(DATE ,GETDATE())))
DECLARE #StartTimestamp datetime
DECLARE #EndTimestamp datetime
DECLARE #HourPartOfSearchRange nvarchar(6)
SET #HourPartOfSearchRange = ' 06:30'
SET #StartTimestamp =
CAST((CONVERT(varchar(11), DATEADD(DAY,-1,#CurrentUTCDateTime), 106) + #HourPartOfSearchRange) AS datetime)
SET #EndTimestamp =
CAST((CONVERT(varchar(11), #CurrentUTCDateTime, 106) + #HourPartOfSearchRange) AS datetime)
SELECT * FROM dbo.Test Where Timestamp Between #StartTimestamp AND #EndTimestamp
today 6am is
dateadd(hour,6,cast(cast(getdate() as date) as datetime))
cast(getdate() as date) truncates the timepart, cast it back as datetime because dateadd won't add hours otherwise and add 6hours
One solution would be like so:
select *
from DaySummaryDetail DSD
where DSD.FromDateTime between cast(cast(cast(getdate()-1 as date) as varchar(30)) + ' 06:00:00.000' as datetime)
and cast(cast(cast(getdate() as date) as varchar(30)) + ' 06:00:00.000' as datetime)
This should help ...
SELECT DATEADD( hour, 6, CAST(CAST(GETDATE(), AS Date) AS DateTime) ) AS 'Today#6am'
SELECT DATEADD( hour, 6, CAST(CAST(GETDATE()-1, AS Date) AS DateTime) ) AS 'Yesterday#6am'
In SQL Server 2012 you can use SMALLDATETIMEFROMPARTS to construct a datetime value that is today at 6am like this:
SMALLDATETIMEFROMPARTS(YEAR(GETDATE()), MONTH(GETDATE()), DAY(GETDATE()), 6, 0)
Output: 2015-02-20 06:00:00
then you can use the above expression in place of GETDATE() in the WHERE clause:
DECLARE #TodayAt6AM DATETIME = SMALLDATETIMEFROMPARTS(YEAR(GETDATE()),
MONTH(GETDATE()),
DAY(GETDATE()),
6,
0)
SELECT *
FROM DaySummaryDetail DSD
WHERE DSD.FromDateTime BETWEEN DATEADD(DAY, -1, #TodayAt6AM) AND
DATEADD(Day, 1, #TodayAt6AM)

Getting plot data from date-range efficiently

I'm trying to create utilization graph for a telephone system. I have sets of data which is in the table format
ID *
StartDate
EndDate
From
To
What I'm trying to do is get SQL to to output me a list of plot points every 5 minutes, so basically
The count of active calls (between StartDate and EndDate) for every 5 minutes in a day.
The result beign something like
Date Time Count
2000-01-01 00:00:00 10
2000-01-01 00:05:00 2
2000-01-01 00:10:00 7
Can anyone suggest a way to generate said data? I'm at a loss here! The stuff I've been thinking abut all involves a creating a big loop and running a query for every 5 seconds which seems super inefficient.
The method I was originally thinking was :-
storedProc GetSamples(SampleStartDate, SampleEndDate)
Create memory table for result data
for every 5mins as sample between SampleStartDate and SampleEndDate
SELECT #SampleCount = COUNT(1) FROM Samples where 5mins BETWEEN StartDate AND EndDate
UPDATE memoryTable SET count=#SampleCount WHERE time = 5mins
end
end
If you have a recent enough version of SQL Server (2008+), you ought to be able to do this with a CTE joined to your phone call log, like this (CTE base found here):
DECLARE #StartDate DATETIME
DECLARE #EndDate DATETIME
SET #StartDate = '2000-01-01 00:00:00.000'
SET #EndDate = '2000-01-02 00:00:00.000'
;WITH DateSequence( [PlotPointDate] ) AS
(
SELECT #StartDate AS [PlotPointDate]
UNION ALL
SELECT DATEADD(MINUTE, 5, [PlotPointDate] )
FROM DateSequence
WHERE [PlotPointDate] < #EndDate
)
--select result
SELECT
PlotPointDate, COUNT(YourTable.ID) AS TotalActiveCalls
FROM
DateSequence
LEFT JOIN
YourTable ON
YourTable.StartDate <= DateSequence.PlotPointDate AND
(YourTable.EndDate >= DateSequence.PlotPointDate OR YourTable.EndDate IS NULL)
GROUP BY PlotPointDate
OPTION (MaxRecursion 10000)

Resources