Adding fixed time onto datetime gives unexpected results - sql-server

I have a query that takes two datetime variables (Start date and end date) and appends two differing fixed times to allow for a business trading time offset.
An example of a test query using this logic is:
DECLARE #startdate datetime;
DECLARE #enddate datetime;
SET #startdate = convert(datetime,'2017-01-01')
SET #enddate = convert(datetime,'2017-01-02')
SELECT *
FROM ig_Business..Check_Item_Detail CID (NOLOCK)
JOIN ig_business..Check_Sales_Detail CSD (NOLOCK) ON CSD.transaction_data_id = CID.transaction_data_id
WHERE csd.tendered_date_time BETWEEN DATEADD(m, DATEDIFF(m, 0, convert(date, #STARTDATE)), 0) + '06:00:00'
AND DATEADD(m, DATEDIFF(m, 0, convert(date, #ENDDATE)), 0) + '05:59:59'
However, the result set for this query is empty, and I am unsure why, because when I run
select DATEADD(m, DATEDIFF(m, 0, convert(date, #STARTDATE)), 0) + '06:00:00'
i get back a seemingly valid datetime : 2017-01-01 06:00:00.000
An example of what is returned when I remove the time restriction:

i get back a seemingly valid datetime : 2017-01-01 06:00:00.000
You're not.
You're getting back a date that has been automatically cast to a string, and have glued another string on the end, giving you a string that looks like a datetime.
If you want to add something to the date, use another dateadd(). This will give you a BETWEEN comparison with actual datetimes.
Right now you are doing a "between" with a datetime and a string.
I'm surprised it doesn't throw an error.

If 2012+, you can use format() to append a time to a date/datetime value
Example
Declare #startdate date = '2017-01-01'
Select format(#startdate,'yyyy-MM-dd 06:00:00')
Returns
2017-01-01 06:00:00
This format() can be included in your where
...
Where SomeDateTime between format(#startdate,'yyyy-MM-dd 06:00:00')
and format(#enddate,'yyyy-MM-dd 17:00:00')

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

SQL convert date dmmyyyy and ddmmyy to yyyy-mm-dd

I have the following problem.
I have some dates with the following format '15122019' and I need it in this format 2019-12-15, which I already solved it in the following way.
select convert (date, Stuff(Stuff('15122018',5,0,'.'),3,0,'.'),104)
The real problem is when the dates come like this '3122019' the conversion can not be done because the length is shorter. Is ther e another way to do it? I've been trying to solve it for several hours. And another question, can this query be parameterized?
Try this:
DECLARE #date VARCHAR(20)
SET #date ='3122019'
IF(LEN(#date) = 8)
BEGIN
SET #date = Stuff(Stuff(#date,5,0,'.'),3,0,'.');
SELECT CONVERT(DATE, #date , 103);
END
ELSE IF(LEN(#date) = 7)
BEGIN
SET #date = Stuff(Stuff(#date,4,0,'.'),2,0,'.');
IF(ISDATE(#date)=1)
BEGIN
SELECT CONVERT(DATE, #date , 103);
END
ELSE
BEGIN
SET #date = Stuff(Stuff(#date,4,0,'.'),3,0,'.');
SELECT CONVERT(DATE, #date , 103);
END
END
ELSE IF(LEN(#date) = 6)
BEGIN
SET #date = Stuff(Stuff(#date,3,0,'.'),2,0,'.');
SELECT CONVERT(DATE, #date , 103);
END
You can add 0 to the left and take 8 chars with right. like RIGHT('0'+'15122018',8). it work with 15122018 and 3122018
select convert (date, Stuff(Stuff( RIGHT('0'+'15122018',8) ,5,0,'.'),3,0,'.'),104)
Such conversation can be achieved by:
Casting integer value to DATE using intermediate FORMAT transformation to a recognizable for conversation string pattern.
style 105 applied to match the input as dd-mm-yyyy
style 05 to match dd-mm-yy
SQL:
-- input format: dmmyyyy
SELECT CONVERT(DATE, FORMAT(3012019, '##-##-####'), 105)
-- result: 2019-01-03
-- input format: dmmyy
SELECT CONVERT(DATE, FORMAT(30119, '##-##-##'), 05)
-- result: 2019-01-03
This will work fine with a single (and double) digit day number, however, it indeed requires a double-digit month

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.

How to solve the date comparison issue in SQL Server?

I am using the following way to compare two dates:
if CONVERT(varchar(20), #ScheduleDate, 101) >= CONVERT(varchar(20), #CurrentDateTime, 101)
This is working fine for the current year, but when the comes in yearly like one date is 12/31/2012 and 1/1/2013 then its not working.
Please help me how can I resolve this.
why do you comparing strings?
you can compare dates
if #ScheduleDate >= #CurrentDateTime
but if your date contains time, I usually do
if convert(nvarchar(8), #ScheduleDate, 112) >= convert(nvarchar(8), #CurrentDateTime, 112)
112 datetime format is YYYYMMDD so it's good for compare dates
You have to remember that string comparison is from left to right, so "1/...." is smaller than "12/...".
You need to use DATETIME comparisons, not string comparison.
Something like
DECLARE #ScheduleDate DATETIME = '1/1/2013',
#CurrentDateTime DATETIME = '12/31/2012'
IF (#ScheduleDate >= #CurrentDateTime)
BEGIN
SELECT #ScheduleDate, #CurrentDateTime
END
DECLARE #ScheduleDateString VARCHAR(20) = '1/1/2013',
#CurrentDateTimeString VARCHAR(20) = '12/31/2012'
IF (CONVERT(DATETIME,#ScheduleDateString,101)>=CONVERT(DATETIME,#CurrentDateTimeString,101))
BEGIN
SELECT CONVERT(DATETIME,#ScheduleDateString,101),CONVERT(DATETIME,#CurrentDateTimeString,101)
END
SQL Fiddle DEMO
Note that if the variables are already datetimes, you do not need to convert them.
Assuming that both variables are currently DateTime variables, can't you just compare them without converting to strings?
declare #ScheduleDate DATETIME, #CurrentDateTime DATETIME
SET #ScheduleDate = '1 Jan 2013'
SET #CurrentDateTime = GetDate()
IF (#ScheduleDate >= #CurrentDateTime)
BEGIN
SELECT 'Do Something'
END
ELSE
BEGIN
SELECT 'Do Something Else'
END
when you use CONVERT(nvarchar(8), #ScheduleDate, 112) function it's return string instead of date.
so,
Use "112" DateFormat in Sql Server it's return string in "YMD" format without any sepration.
compare that string in your query and get desire output.
Such as "if CONVERT(nvarchar(8), #ScheduleDate, 112) >= CONVERT(nvarchar(8), #CurrentDateTime, 112)"
I would not use CONVERT to compare formatted strings. It is slow (well, more like microseconds, but still)
I use a UDF for SQL prior to version 2008
CREATE FUNCTION [dbo].[DateOnly] (#Date DateTime)
RETURNS Datetime AS
BEGIN
Return cast (floor (cast (#Date as float)) as DateTime)
END
and for versions >=2008 this approach
select convert(#MyDateTime as DATE)
Of course, you can compare datetime values directly, but to know whether two datetime values are on the same date (ignoring the time component), the above versions have proven to be effectivy.
Date : From and To with following format
from_Date# = #dateformat("#form.from#", "mm/dd/yyyy")
to_Date# = #dateformat("#now()#" + 1, "mm/dd/yyyy")
In SQL Statement
WHERE a.DateCreated >= CAST ('#from_date#' AS DATE) and a.DateCreated <= CAST('#to_date#' AS DATE)
This is working fine without any cast of original date time column

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

Resources