why ss round to nearest minute that will change day - sql

I am using sql server 2008 R2 and due to one problem i am able to know that smalldatetime round ss to nearest minute . here is content from MSDN .
ss is two digits, ranging from 00 to 59, that represent the second. Values that are 29.998 seconds or less are rounded down to the nearest minute, Values of 29.999 seconds or more are rounded up to the nearest minute.
Now i face one problem due to this, that i have parameter type in my storedproceduer is smalldatetime when i pass '2014-03-23 23:59:59' its converted to date 2014-03-24 00:00:00
I found the solution that i should convert parameters from smalldatetime to nvarchar(30)
and problem solved.
but my real question why such behavior of smalldatetime rounding of ss that change the day ?
you can try it with following queries
DECLARE #EndDate smallDatetime
SET #EndDate = '2014-03-23 23:59:59'
SELECT #EndDate
DECLARE #EndDate smallDatetime
SET #EndDate = '2014-03-23 23:59:30'
SELECT #EndDate
DECLARE #EndDate smallDatetime
SET #EndDate = '2014-03-23 23:59:29'
SELECT #EndDate

Alarm bells go off in my head any time I see someone using 23:59:59 in SQL.
This is almost always because they want to return a full days worth of data in the query by doing something like:
WHERE date_field BETWEEN '2014-03-19 00:00:00' AND '2014-03-19 23:59:59'
This is because BETWEEN includes the boundary values and you might not want values at 2014-03-20 00:00:00 included.
The above query is equivalent to:
WHERE date_field >= '2014-03-19 00:00:00'
AND date_field <= '2014-03-19 23:59:59'
The Correct Approach
WHERE date_field >= '2014-03-19 00:00:00'
AND date_field < '2014-03-20 00:00:00'
Notice the subtle difference in the operators.

Because that is the nearest minute.
Given it is defined as rounding up at 29.998 seconds or above, what other result would you expect? At 23:58:30, you expect it to round to 23:59 - why should the behaviour be any different a minute later?
If you want a more precise date/time measurement, use datetime, or datetime2 - not nvarchar

Related

Day light savings time is resulting in incorrect dates in database

BACKGROUND:
I have an issue with the day light savings time change. Any records entered into the database (side note: I have no access to the script/code which enters this data to fix it) between 2021-03-28 and 2021-10-31 get entered into the database with the incorrect date. For example:
Records entered in on 2021-03-26 end up in the database as 2021-03-26 00:00:00, which is correct.
Records entered in on 2021-03-29 end up in the database as 2021-03-28 23:00:00 which is incorrect.
So when I try to search for records entered in on2021-03-26, the query works fine, but if I try to search for records entered in on 2021-03-29, it returns records from the wrong date because of the hour change.
SAMPLE DATA:
ColDate, ColName
2021-03-26 00:00:00, SomeName -- CORRECT DATE
2021-03-26 00:00:00, SomeName -- CORRECT DATE
2021-03-26 00:00:00, SomeName -- CORRECT DATE
2021-03-28 23:00:00, SomeName -- INCORRECT DATE
2021-03-28 23:00:00, SomeName -- INCORRECT DATE
2021-03-28 23:00:00, SomeName -- INCORRECT DATE
WORKING EXAMPLE:
DECLARE #StartDate datetime
DECLARE #EndDate datetime
SET #StartDate = '2021-03-26 00:00:00'
SET #EndDate = '2021-03-26 23:59:59'
SELECT *
FROM tblName
WHERE ColDate BETWEEN #StartDate AND #EndDate
The above will return:
ColDate, ColName
2021-03-26 00:00:00, SomeName -- CORRECT DATE
2021-03-26 00:00:00, SomeName -- CORRECT DATE
2021-03-26 00:00:00, SomeName -- CORRECT DATE
NONE WORKING EXAMPLE:
DECLARE #StartDate datetime
DECLARE #EndDate datetime
SET #StartDate = '2021-03-29 00:00:00'
SET #EndDate = '2021-03-29 23:59:59'
SELECT *
FROM tblName
WHERE ColDate BETWEEN #StartDate AND #EndDate
The above will return nothing from the sample data.
QUESTION:
How do I get around this issue? As mentioned earlier, I have no control of the data entry and the developers have no interest in fixing the issue.
Do I need to use IF statements and check if the date is between 2021-03-28 and 2021-10-31 and adjust the date by 1 hour accordingly? Or is there a better way to resolve this?
UPDATE - POSSIBLE SOLUTION:
The following query seems to work (2021-03-26):
DECLARE #StartDate datetime
DECLARE #EndDate datetime
SET #StartDate = '2021-03-26 00:00:00'
SET #EndDate = '2021-03-26 23:59:59'
SELECT
ColDate AT TIME ZONE 'UTC' AT TIME ZONE 'GMT Standard Time',
ColName
FROM tblName
WHERE ColDate BETWEEN #StartDate AT TIME ZONE 'GMT Standard Time' AT TIME ZONE 'UTC' AND #EndDate AT TIME ZONE 'GMT Standard Time' AT TIME ZONE 'UTC'
The following query seems to work (2021-03-29):
DECLARE #StartDate datetime
DECLARE #EndDate datetime
SET #StartDate = '2021-03-29 00:00:00'
SET #EndDate = '2021-03-29 23:59:59'
SELECT
ColDate AT TIME ZONE 'UTC' AT TIME ZONE 'GMT Standard Time',
ColName
FROM tblName
WHERE ColDate BETWEEN #StartDate AT TIME ZONE 'GMT Standard Time' AT TIME ZONE 'UTC' AND #EndDate AT TIME ZONE 'GMT Standard Time' AT TIME ZONE 'UTC'
UPDATE - QUESTION:
The above update seems to work, but am I overlooking anything?
Your updated answer seems great. But if the correct entered date is always without time and only incorrect ones are entered with time part then you can also just subtract 1 hour from #startdate while using it in where clause.
DECLARE #StartDate datetime
DECLARE #EndDate datetime
SET #StartDate = '2021-03-29 00:00:00'
SET #EndDate = '2021-03-29 23:59:59'
SELECT *
FROM tblName
WHERE ColDate BETWEEN dateadd(day,-1,#StartDate) AND #EndDate
But it won't work if there are rows with time part other than day light saving issue.
if I am overlooking anything
You might.
You should try to understand how exactly the data entry app works. You can't change it, but you should be able to reverse engineer the code. Once you understand how the code transforms the timestamps, you may try to come up with the reversing transformation.
In your proposed solution you convert timestamp to a GMT Standard Time time zone. You should verify if your data entry app operates with this time zone. Different time zones switch between day light savings on different dates. It is not like the whole world moves their clock on the same day of the year. Some time zones don't have a day light saving at all.
Also, rules for time zones tend to change over time. Governments like to make changes. So, if your database spans across lengthy period you may discover that the rules for the time zones that are built into SQL Server now are not the same that were in place when the data was recorded. For example, Perth in Western Australia moved their clocks in 1991-1992, then again in 2006-2009, then stopped doing it. At least, DST is not observed now.
So, things to consider:
what time zone to use?
does all of your data fall into the same time zone? If the data comes from several different time zones, their rules may be different and you need to know which transformation to apply to which parts of the data.
does your data span across the period when the rules for your chosen time zone changed?
So, in general case it becomes pretty hard to fix the timestamps after the fact.
You can't change your app now, but a note for the future.
In my system I make sure to store both local and UTC time. When an entry is generated by a local computer, it usually knows its local time zone and UTC time accurately. When I have a central database with timestamps from various locations I do not attempt to convert local->UTC or UTC->local. I use appropriate field. Some reports use local times, some reports use UTC.
Daylight (DST) time is actually an offset comparing with Local time. The majority of SQL databases has UTC internal conversion when using datetime in column type.
A very nice practice to resolve your issue is to convert your datetime string to milliseconds!!!
var myDate1 = Date.parse("2021-03-26 00:00:00")
//1616709600000
var myDate2 = Date.parse("2021-03-28 00:00:00")
//1616882400000
TIP: calculate here the time zone offset programmatic and convert it to milliseconds, now you can add it to MyDate1, MyDate2 before run your Query
var time_zone_correction = today.getTimezoneOffset()*60*1000;
When you are ready call your query with the following syntax:
DECLARE #UTC1 BIGINT
DECLARE #UTC2 BIGINT
SET #UTC1 = '"+myDate1+"'
SET #UTC2 = '"+myDate2+"'
SELECT * FROM yourTableHere WHERE [DATE]
BETWEEN DATEADD(SECOND, #UTC1/1000, '19700101')AND DATEADD(SECOND, #UTC2/1000, '19700101') order by [DATE]"
Moreover you can check your SQL for registered timeZones by running this select * from sys.time_zone_info
Note: Milliseconds has precision over Date() object conversion from strings, also the execution time of a BIGINT compared to string datetime conversion is much faster.
In terms of code re-usability, if you add your offset (milliseconds calculation) for time zones before executing your query and not inside, you end up with a dynamic and more "template" approach of writing a query.
Give it a try and research the web, there are tons of tutorials for dealing with UTC, DST and timezone manipulation!
I hope my approach will help you!
I think you need 2 time funcs (see bellow)
dbo.fnToDbTime for Filter times
dbo.fnToYourTime for format Result time
DECLARE #StartDate datetime = '2021-03-28'
DECLARE #EndDate datetime = #StartDate + 1
SET #StartDate = dbo.fnToDbTime(#StartDate)
SET #EndDate = dbo.fnToDbTime(#EndDate)
SELECT [ColDate], [ColName], dbo.fnToYourTime([ColDate]) GoodTime
FROM tblName
WHERE #StartDate <= ColDate AND ColDate < #EndDate
GO
CREATE FUNCTION dbo.fnToDbTime (#YourTime datetime)
RETURNS datetime
AS
BEGIN
DECLARE #DbTime datetime
IF #YourTime < '2021-03-29 00:00:00'
SET #DbTime = #YourTime
ELSE IF #YourTime < '2021-10-31 00:00:00'
SET #DbTime = DATEADD(HH, -1, #YourTime)
RETURN #DbTime
END
GO
CREATE FUNCTION dbo.fnToYourTime (#DbTime datetime)
RETURNS datetime
AS
BEGIN
DECLARE #YourTime datetime
IF #DbTime < '2021-03-28 23:00:00'
SET #YourTime = #DbTime
ELSE IF #DbTime < '2021-10-30 23:00:00'
SET #YourTime = DATEADD(HH, 1, #DbTime)
RETURN #YourTime
END
GO

SQL Server - Get Datetime with Minute and Seconds Removed (Date and Hour only) [duplicate]

This question already has answers here:
Best approach to remove time part of datetime in SQL Server
(23 answers)
Closed 2 years ago.
How to get Datetime to retain only its Date and Hour?
The Minutes and Seconds should be converted to 0.
That is, to Convert a DateTime to Date and Hour only.
eg: I want, datetime GetDate() or '2020-01-01 12:45:50' to be, '2020-01-01 12:00:00'
The fastest and most efficient solution to get DateTime without Minutes and Seconds is:
DECLARE #testDate DATETIME = GETDATE();
SELECT DATEADD(hour, DATEDIFF(hour, 0, #testDate), 0);
Below are other alternatives, but slightly less efficient because of the extra CASTs.
Adding the below queries as some might find it more comprehensible.
DECLARE #testDate DATETIME = GETDATE();
SELECT CAST(CAST(#testDate AS DATE) AS DATETIME)+CAST(DATEPART(HOUR,#testDate) AS FLOAT)/24;
SELECT DATEADD(HOUR,DATEPART(HOUR,#testDate),CAST(CAST(#testDate AS DATE) AS DATETIME));

SQL Server - Check if given date+time is between two datetime variables by exact date+time

I have two datetime columns in a DB table: #Start and #End.
Both columns contain the date and time, for example:
#Start: 2018-10-01 19:00:00
#End: 2018-10-10 23:59:00
I want to know if the current date is exactly between both datetimes considering the dates and the times.
So, 2018-10-08 16:37 and 2018-10-10 23:59:00 would match this range
and 2018-10-11 00:00:00 would not.
(In this case this date is one minute later than the End date, so it is not between my datetime range).
SELECT Id FROM Table1 WHERE GETDATE() BETWEEN Start AND End
I don't use GETDATE() in real code, I use an argument. The problem is that current date argument may contain seconds and milliseconds like 23:59:59.123. My code treats such date as not conforming given range. But I don't care about s/ms.
Is there a workaround?
Update:
The precision I want to achieve is in minutes. So I do not even need to take in account the seconds nor the milliseconds. The date time format I would be working on would be 'yyyy-MM-dd hh-mm' but I do not know how to use the BETWEEN clause converting the Start and End to the shown format so I can compare the dates.
You would seem to want this logic:
WHERE GETDATE() >= Start
AND GETDATE() < DATEADD(minute, 1, End)
Assuming that the time part of End is 23:59:00 it covers all possible values between 23:59:00 and 23:59:59.999...999.
SELECT Id FROM Table1 WHERE GETDATE() BETWEEN '2018-10-01 19:00:00' AND '2018-10-10 23:59:00'
TRY
SELECT Id FROM Table1 WHERE
CONVERT(varchar(16),GETDATE(),121) BETWEEN
CONVERT(varchar(16),[Start], 121)
AND
CONVERT(varchar(16),[END],121);
Example of rounding without strings
DECLARE #GetDateMinutes as datetime2;
DECLARE #X as datetime2 = getdate();
--round to minutes, could be made into a function
SET #GetDateMinutes = dateadd(minute,datepart(minute,#x),dateadd(hour, datepart(hour,#x),cast(CAST(#x as date) as datetime2)))
select #x, #GetDateMinutes
Truncate the seconds using the technique described here to avoid all string conversions, then just do your comparison. Here's a fully contained example that uses cross apply and values to encapsulate the truncation logic for start and end:
-- truncate minutes from current date time
declare #currentDateTime datetime2(0) = DateAdd(minute, DateDiff(minute, 0, Convert(datetime2(0), N'2018-10-01 23:58:32.912')), 0);
select #currentDateTime as CurrentDateTime
, a.*
from (values -- create a table of dummy values
(Convert(datetime2(3), N'2018-10-01 19:48:14.735'), Convert(datetime2(3), N'2018-10-10 02:00:00.000'))
, (Convert(datetime2(3), N'2018-10-01 22:43:19.532'), Convert(datetime2(3), N'2018-11-01 12:17:26.663'))
) as a (StartDateTime, EndDateTime)
cross apply (values(
-- truncate minutes from start date time
DateAdd(minute, DateDiff(minute, 0, Convert(datetime2(0), a.StartDateTime)), 0)
-- truncate minutes from end date time
, DateAdd(minute, DateDiff(minute, 0, Convert(datetime2(0), a.EndDateTime)), 0)
)) as b (StartDateTimeWithoutSeconds, EndDateTimeWithoutSeconds)
where #currentDateTime between b.StartDateTimeWithoutSeconds and b.EndDateTimeWithoutSeconds;
Your data appears to already have the s/ms truncated from start and end but figured I'd apply the same logic to all values involved just to be consistent. Here's the formula for stripping s/ms without all the "noise" from the example:
DateAdd(minute, DateDiff(minute, 0, Convert(datetime2(0), <SomeDateTime>)), 0)

DateDiff - DST issue

Background
I have a scenario where I am calculating the difference between two dates. While the increment differences are spot on, the final calculation has introduced a 60 minute (1 hour) disparity.
After investigation and "hair pulling" episodes, I have identified that the DST transition in November is the cause for the 60 minute (1 hr) disparity.
Scenario
declare #sdate datetime = '2016-10-29 06:03:00.000PM'
declare #edate datetime = '2016-11-29 11:59:00.000PM'
select
DATEDIFF(HOUR, #sdate, #edate),
DATEDIFF(Minute, #sdate, #edate),
DATEDIFF(Second, #sdate, #edate)
Question
Ultimately, I need to simply return the number of seconds, or minutes, between the #sdate and #edate variables. I know there will be two times during the year, where the difference value will be off by 60 minutes (1 hour), plus or minus, and want to account for that known disparity within my sql statement.
How can i account for the DST adjustment within a set-based operation, if possible?
Currently, I am getting 44996 as the difference, but that is the un-adjusted time-change difference. I am looking for 45056, which is the adjusted time-change difference.
The datetime type in SQL Server has no awareness of time zone or offset from UTC. In order to take DST into account, you need to use the datetimeoffset type.
declare #sdate datetimeoffset = '2016-10-29 06:03:00.000PM -05:00'
declare #edate datetimeoffset = '2016-11-29 11:59:00.000PM -06:00'
select
DATEDIFF(Hour, #sdate, #edate),
DATEDIFF(Minute, #sdate, #edate),
DATEDIFF(Second, #sdate, #edate)
This will give the results you asked for, taking into account that the offsets for the start and end differed by an hour.
The tricky part is how to determine the offset to begin with. If you are running SQL 2016, or Azure SQL DB, then you could use the AT TIME ZONE function to determine it.
declare #dt datetime = '2016-10-29 06:03:00.000PM'
declare #dto = #dt AT TIME ZONE 'Central Standard Time'
But since you said you are running SQL 2012, you'll have to either write your own functions that understand when DST starts and ends in your time zone, or you can use my SQL Server Time Zone Support package to do that:
declare #dt datetime = '2016-10-29 06:03:00.000PM'
declare #tz varchar = 'America/Chicago'
declare #dto = Tzdb.SwitchZone(Tzdb.LocalToUtc(#dt, #tz, 1, 1), #tz)
Note there is an open item to simplify this to a single function, but the above should work for now.

SQL Server DateTimeOffset matching same "day"

My application needs to collect "Tuesday's" purchases for all locations world wide, where "Tueday" is the location's Tuesday (regardless of time zone). And if the user needs to re-run the report next week, I need to still get "Last Tuesday's" data. All of our data is stored using DateTimeOffset.
So
9/4/12 00:00:00 -7 through 9/4/12 23:59:59 -7
must MATCH
9/4/12 00:00:00 +11 through 9/4/12 23:59:59 +11
when I am executing my WHERE clause.
I can't convert to UTC in the WHERE clause because that will pick up the data for "Tuesday" in London (depending on DST), not the location's Tuesday.
I tried converting from DateTimeOffset to DateTime, but that seems to convert to UTC. (In my tests, passing 9/1/12 through 9/30/12 picked up 8/31/12 data.)
Is there a trick to doing something like this with TSQL?
Thanks!
IMHO
DateTimeOffset = DateTime+Offset(from UTC)
So your data is already representing Client's Local date and time. Just cast it to DateTime and you will get the client's local Date and time.
But in-case if you want to add the Offset to the datetime and want the resultant Datetime then
DECLARE #PurchaseDate DATETIMEOFFSET(7) = CAST('2007-05-08 12:30:29.1234567 +5:00' AS datetimeoffset(7))
SELECT CAST(SWITCHOFFSET (#PurchaseDate , '+00:00') AS DATETIME)
have a look at this blog for further info.
http://blogs.msdn.com/b/bartd/archive/2009/03/31/the-death-of-datetime.aspx
When casting a DATETIMEOFFSET as DATETIME it takes the date and time as offset in the value and simply drops the time zone. The same is true when casting as DATE or TIME. So, I think you can simply cast the column as DATE and compare that to the date-only value you wish to match:
DECLARE #targetDate DATETIME2 = '2012-09-04' --Or we could use DATE here
SELECT [PurchaseId], [PurchaseTime], CAST([PurchaseTime] AS DATE) AS "PurchaseDate"
FROM [Purchases]
WHERE CAST([PurchaseTime] AS DATE) = #targetDate
I'm not sure how efficient this will be (hopefully not bad if the provider is truly clever--which SQL Server likely would be), but you might improve it by bounding the original column value as well:
DECLARE #targetDate DATETIME2 = '2012-09-04' --DATETIME2 so we can adjust by hours
SELECT [PurchaseId], [PurchaseTime], CAST([PurchaseTime] AS DATE) AS "PurchaseDate"
FROM [Purchases]
WHERE CAST([PurchaseTime] AS DATE) = #targetDate --Keep only the local-date matches
AND [PurchaseTime] >= DATEADD(hh, -14, #targetDate) --Up to 14-hour time zone offset
AND [PurchaseTime] <= DATEADD(hh, 38, #targetDate) --24 hours later plus 14
This should efficiently index down to the set of possibilities and then filter properly on the conversion to the local date. Note that time zone offsets can be up to 14 hours (New Zealand is furthest I know at +13:00, but they can go to +/- 14:00 according to MSDN) and the #targetDate will be at the start of the day, so it compares back to 14 hours earlier and 24+14=38 hours later. DATETIME2 has the same range and precision as DATETIMEOFFSET, so it's better for this purpose than the original DATETIME (but it may also work okay with DATETIME instead).