Get yesterday start and end datestamp and convert to Unix epoch - sql-server

I have a Stored Procedure that will run within a time period (between 2-3 am), but not always at the exact same time during that period.
As part of this procedure I need to do 3 steps:
1: Get the start of yesterday's date
So if today is 13/08/2020 13:51:02 I need the query to return 12/08/2020 00:00:00 and do this dynamically
2: Get the end of yesterday's date
In the above this would return 12/08/2020 23:59:59
3: Convert both values into Unix EPOCH timestamps
I have used in the past on a similar issue (that was less time-sensitive) the below bit of Code:
declare #yesterday date
set #yesterday = (SELECT convert(datetime, DATEADD(day, -1 ,getdate()), 23 ))
The Problem here is that this gives a value that is exactly 24 hours in the past, so if the SP is run at 2:15 am - it's time stamp will be different when it's run at 2:23 am or 2:53 am.
Once I've got a method of getting the start and end date to always be correct - I'll then use something like this solution to convert the Datetime into Epoch timestamps, unless someone who answers this question has a snazzy method of doing it all in one (for which I would be eternally grateful)

To get midnight yesterday, do this:
DECLARE #yesterday DATETIME
= DATEADD(DAY, -1, CAST(GETDATE() AS DATE));
To get midnight today do
DECLARE #today DATETIME = CAST(GETDATE() AS DATE);
To filter for times that happened yesterday, do
WHERE ts >= #yesterday AND ts < #today ...
The CAST operation truncates the date/time value returned by GETDATE() to midnight.
The form of WHERE with >= and < copes with the edge case correctly. It also happens to exploit an index on the column I called ts.

Related

Time difference calculation based on timezone and offset

We transfer data from Oracle SQL to SQL Server in the near real-time data warehouse. (Oracle is a transactional database) The time changes twice a year for the data we use. We have been having issues when calculating DateTime differences and I have to work on a short-term and long-term solutions where I need advice.
New data coming in :
I can create tables from scratch and use datetimeoffset data field for all the time variables. Would this
display dates in SQL Server queries in the local time zone and save them in their actual UTC time? And when I
calculate the differences, would it give the correct difference?
Existing data without offset information:
I already have data converted into the current timezone. We have 2 timezones AEST and AEDT.
When we calculate time differences for KPIs it gives incorrect times. Any ideas on how it can be resolved?
Can creating a function that converts the dates and times into UTC and subtracts the difference and returns the value would work?
An example below :
DECLARE #startdate DateTime = '2022-10-01 23:13:00.000'; --UTC 2022-10-01 13:13:00.00
DECLARE #enddate DateTime = '2022-10-02 12:08:00.000' --UTC 2022-10-02 01:08:00.00
select CAST((#enddate - #startdate) as time(0)) 'Difference'
Time difference: 12:55:00
Actual Time Difference: 11:55:00
For your example, if you include the time zone info with the data, the date calculations work out
DECLARE #startdate datetimeoffset(0) = '2022-10-01 23:13:00 +10:00'; --UTC 2022-10-01 13:13:00.00
DECLARE #enddate datetimeoffset(0) = '2022-10-02 12:08:00 +11:00' --UTC 2022-10-02 01:08:00.00
declare #minutes int = datediff(minute, #startdate, #enddate);
select #startdate at time zone 'utc',
#enddate at time zone 'utc';
select [hours] = #minutes / 60,
[minutes] = #minutes - 60 * (#minutes / 60)
To address the existing data, you can do a one-time conversion to datetimeoffset by running it through at time zone with the appropriate time zone name. In my example above, I used UTC but you can find all of the supported time zones in sys.time_zone_info.
As to your first question about the internal storage format and display, I'm not sure if it uses UTC internally or not. As to display, it will, by default, output them with the time zone that you stored them with. That is, if you stored a given value with +10:00 as the offset, that's how it will display. You can convert via methods already described above or with switchoffset(). Lastly, if it matters, you can get just the offset with datepart noting that it returns the offset in number of minutes (to conform with datepart needing to return an int).

Set report date for date other than today in SQL reporting

I have a report that I have been asked to produce. The first column of data is total time entered from the first of the year to the end of the previous month. The next column of data is total time from beginning of "current" month to the end of "current" month.
For example. If this report was being run for March then the first column would be total time for Jan and Feb and the second column would be total time for March. If I were to run it in April then the first column would be total time for Jan/Feb/Mar and the second column total time for April etc.
I am using various expressions to get first date of the year, last date of previous month, first date of this month, last date of this month. All working fine and it runs like a dream if you run the report in the current month (i.e. March) but if you want to run the report in April for March's data it won't do it as it's reading the date on the computer and using that to calculate the prev month.
In Crystal reports you can set a report date. Is there something similar in SQL reporting? I'm assuming you DECLARE the report date in your initial query but I haven't yet found the right combination of functions.
This report is for an external bit of software that we run.
The only parameters I can use are #FromDate and #ToDate and these are set as text rather than date
I was planning on using the #ToDate to set the report date but would obviously have to convert it from text first
Any guidance very much appreciated
Try this. You could actually do it without all the declare statements, I just did those to break it down to be easy to read.
declare #date_entered datetime = '2/7/2017'
declare #start_of_year datetime = DATEADD(yy, DATEDIFF(yy, 0, #date_entered), 0)
declare #start_of_month datetime = DATEADD(month, DATEDIFF(month, 0, #date_entered), 0)
declare #end_of_month datetime = DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,#date_entered)+1,0))
declare #end_of_last_month datetime = DATEADD(day,-1,#start_of_month)
-- Now remove weekends from days found
declare #amount_of_days_previous tinyint = datediff(day,#start_of_year,#end_of_last_month) - (datediff(wk, #start_of_year, #end_of_last_month) * 2)
declare #amount_of_days_this tinyint = DATEDIFF(day,#start_of_month,#end_of_month) - (datediff(wk, #start_of_month, #end_of_month) * 2)
select #amount_of_days_previous * 8 as WorkHoursinDaysPrevious,
#amount_of_days_this * 8 as WorkHoursinDaysThis
I had a think over the weekend and realised that I was approaching the problem all wrong. I was trying to set the report date so that my query could read that date and then go back and find the relevant date (first date in the year, last date of previous month etc).
What I needed to do was use my date criteria (FromDate and ToDate) in my DATEADD selections.
CONVERT(datetime,#FromDate,103) AS FIRSTDAYOFYEAR,
DATEADD(D, - 1, DATEADD(MONTH, DATEDIFF(MONTH, '19000101', CONVERT(datetime,#ToDate,103)), '19000101')) AS LASTDAYPREVMONTH,
DATEADD(MONTH,DATEDIFF(MONTH, '19000101',CONVERT(datetime,#ToDate,103)), '19000101') AS FIRSTDAYMONTH,
CONVERT(datetime,#ToDate,103) AS LASTDAYMONTH,
This is now much better because my "first date of year" can be anything the user wants, not just the first day of the year.

t-SQL Selecting a records where an event happened today accounting for midnight and timezones

I have a table with a date column and time column and it gets populated whenever the user performs a scheduled task. They generally have a 2hr window to perform the task and I can't seem to get my mind around how to see if that task was done today or not.
For example, if it is 12:30 AM, I need to check whether it was done at 11:30 PM on the prior day, or at 12:01 AM today. I store all the time and dates as central time and have an offset number to adjust for other timezones.
Here is where I'm stuck, this works for something done last night, but not if it was done today after midnight (#DatePartOfDateTime is the date I am checking (it has already been corrected for timezone) and #EndTime is the time I am checking to see if it happened):
select 1 from tblMedsDispensed
where DatePassed =
case when (#EndTime = '23:59' or #Offset < 0 and #EndTime < '21:59')
and TimePassed < '01:00'
-- For midnight, if the time passed is < 01:00 then roll it back a day
then dateadd(DAY,1,#DatePartOfDateTime )
else #DatePartOfDateTime end
and tblMedsDispensed.patdrugs_fk = tblPatDrugs.PatDrugs_id
Any ideas?
Here's a guess. I don't think you need to mess around with all those variables and time components. What you want to do is sort of rotate the clock so that your logical start of day lines up with a calendar day so the date comparisons are easy.
select *
from tblMedsDispensed
where
cast(dateadd(hh, #Offset - 1, DatePassed) as date) = cast('<literal date>' as date)
EDIT: I looked over your question again and I see you've got two columns. The idea here is the adjust the date value backward one day when the hour component is less than 1. I'm still not clear whether your tolerance is one hour or two hours.
dateadd(
hh,
case when hour(dateadd(hh, #Offset, <TimeColumn>)) < 1 then -1 else 0 end,
<DateColumn>
)

Sql Server Convert between UTC-0 string dates and the server local TimeZone

When I execute the following, when the server's TimeZone is +01:00:
Convert(datetime, '2015-02-10T23:00:00Z', 127)
The result is:
10.02.2015 23:00:00
That is the Date at UTC-0. My expected value would be 11.02.2015 00:00:00, that is the date converted to the server's TimeZone.
Convert function doesn't convert time to UTC. It just simply changes the format of the input string. Here is what you need to do.
Find difference in hours between server local time and UTC time:
DECLARE #hour INT
SELECT #hour = DATEDIFF(HOUR, GETUTCDATE(), GETDATE())
Add the difference to the date you're trying to convert:
SELECT CONVERT(DATETIME, DATEADD(hour, #hour, '2015-02-10T23:00:00Z'), 127)
If you know your timezone difference in hours and you know that it's unlikely to be changed, then use a shorter version:
SELECT CONVERT(DATETIME, DATEADD(hour, -1, '2015-02-10T23:00:00Z'), 127)
You shouldn't really depend on the time zone setting of a server. However, if you have a specific time zone in mind, you could use my SQL Server Time Zone Support project.
After installation:
SELECT Tzdb.UtcToLocal('2015-02-10T23:00:00Z', 'Europe/Paris')
Choose a time zone from the list here.

Getting today's midnight time as UTC

I have the following query which calculates today's midnight value (UTC) as a datetime:
SELECT CONVERT(DATE,GETDATE())+(GETDATE()-GETUTCDATE())
Result: 2011-11-03 19:00:00.000 (for GMT-5 on Nov. 4, 2011)
Not only that, but on occasion, it returns values like these:
2011-11-03 19:00:00.003
2011-11-03 19:00:00.007
2011-11-03 19:00:00.010
..., which are wrong!
There must be a better way to do this.
I already answered this with a solution using DATEADD and DATEDIFF with GETDATE() and GETUTCDATE(), similar to the example given in the original question, but since then I've discovered the datetimeoffset data type added in SQL Server 2008. This stores a datetime along with a timezone offset.
How you use this type will depend on whether you want to change the data type of your existing data. If you don't want to change anything, the following statement will return a datetime type with the local time of midnight:
SELECT CONVERT(datetime, SWITCHOFFSET(CONVERT(datetimeoffset,
CONVERT(date, GETDATE())),
DATENAME(TzOffset, SYSDATETIMEOFFSET())))
You could also convert any UTC time into local time using:
SELECT CONVERT(datetime, SWITCHOFFSET(CONVERT(datetimeoffset,
#myutctime,
DATENAME(TzOffset, SYSDATETIMEOFFSET())))
The datetimeoffset type is only available using SQL2008 and above. If you need to do this with 2005 and below, you can use a solution similar to the one in the original question, but altered to account for the fact that GETDATE() - GETUTCDATE() is not an atomic operation and will likely involve milliseconds of difference between when the two are executed.
SELECT DATEADD(minute,
DATEDIFF(minute, GETUTCDATE(), GETDATE()),
CONVERT(datetime, CONVERT(date, GETDATE())))
This will take the number minutes between GETDATE() and GETUTCDATE() and add them onto the local midnight time. Unfortunately, you have to convert back from date to datetime as DATEADD won't work with minutes if you give it a date. I'd suggest wrapping this into a user-defined function to make it look less verbose, e.g.
CREATE FUNCTION dbo.MidnightASUTC(#dt as datetime)
RETURNS datetime
AS
BEGIN
RETURN DATEADD(minute,
DATEDIFF(minute, GETUTCDATE(), GETDATE()),
CONVERT(datetime, CONVERT(date, #dt)))
END
SELECT dbo.MidnightAsUTC(GETDATE())
For a specific scenario like the one you've described ("today's midnight value (UTC) as a datetime"), a programmatic approach makes sense, but if you ever need to extend it to a different question (what was midnight UTC for this summer?), you may want to use a calendar table (to account for things like daylight savings time, etc).

Resources