To subtract two dates without Using DateAdd Function or DateDiff Function - sql

How i got this output, can you please explain the reason behind it?
declare #a datetime = '2017-06-08 16:02:22.467',
#b datetime = '2017-10-23 00:00:00.000'
select DAY(#a - #b)
select #a - #b
Output:
17
1899-08-17 16:02:22.467

Disclaimer: The information in this answer is relevant only to the DateTime data type. It doesn't apply to the newer data types (DateTime2 , Date and Time).
Well, dates in sql server are stored as the number of days since 1900-01-01.
You can see it if you run this query:
SELECT CAST(0 as datetime)
You'll get 1900-01-01 00:00:00 as the result.
Time is stored as the number of ticks since midnight.
There are 300 ticks per second.
Since the date in #a is before the date in #b, you get a negative result for the date (-137), and that number is then added to 01-01-1900 to give you 1899-08-17. The time in #a is after the time in #b, and since the time in #b is midnight, you get the time of #a in the result.

declare #a datetime = '2017-06-08 16:02:22.467',
#b datetime = '2017-10-23 00:00:00.000'
select datediff(day, #a, #b)
it returns 137, so difference between these two days are 137.
declare #c as datetime = null;
select isnull(#c, 0)
it return 1900-01-01 00:00:00.000, it means the default value for the datetime is 1900-01-01 00:00:00.000
In your case this select #a-#b minus operation is subtracting the actual difference of 137 days with the default value.
select dateadd(day, -137, '1900-01-01 00:00:00.000')
it returns 1899-08-17 00:00:00.000
if it is select #b-#a, it will return 1900-05-17 07:57:37.533 i.e, it will add 137 days from the default value.
and DAY() function simply returns the day of the given date.
So for the 1899-08-17 the DAY() should be 17

Well when you substracting 2 dates zero value for datetime type is 1900-01-01 00:00:00.00.
But from SQL 2008 microsoft created new type datetime2 and as far as you get overflow (negative value) in here it casts result to datetime2 that statrs from 0001-01-01 00:00:00.00
DAY function only get date part from your datetime or datetime2 type.

This will subtract the two date and will add it to Minimum Date in SQL which is '1900-01-01 00:00:00.000'. So after subtracting both dates and getting the difference, it will add it in subsequent minimum date which is '1900-01-01 00:00:00.000'.

Related

Get rows having a datetime less than or equal to today ignoring time

I have a datetime field and would like to select the records having a date less than or equal to today but ignoring the time part of it.
My datetime field is as below:
2019-05-17 13:31:15.900, 2019-05-16 13:32:17.277, 2019-05-24 15:20:03.823
I would like to fetch only the rows with datetime 2019-05-17 13:31:15.900 and 2019-05-16 13:32:17.277 ignoring the time part.
Any idea of how to do that in sql server?
Simply, just cast both, your column and todays date to DATE:
SELECT * FROM MyTable
WHERE CAST(dt AS DATE) <= CAST(GETDATE() AS DATE)
You may try casting GETDATE() to a date type, to compare against tomorrow at midnight:
SELECT *
FROM yourTable
WHERE dt < CAST(DATEADD(day, 1, GETDATE()) AS date);
-- earlier than tomorrow at midnight
The date when this answer was written was 17-June-2019, so the above query would return any record whose datetime is strictly less than 18-June-2019 at midnight. The latest record would therefore be '2019-06-17 23:59:59'.

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)

Extract Date And Hour Only From Date Time Field

I am running SQL Server 2008 and I have a datetime field, from this field I only want to extract the date and hour. I tried to parse out using datepart() but I think I did it incorrectly as when I attempted to string the two back together I got a number value. What I attempted was
Select Datepart(YEAR, '12/30/2015 10:57:00.000') +
Datepart(HOUR, '12/30/2015 10:57:00.000')
And what was returned was 2025 What I want to see returned is
12/30/2015 10:00:00.000
You could use DATEADD and DATEDIFF:
DECLARE #d DATETIME = '12/30/2015 10:57:00.000';
SELECT DATEADD(second,DATEDIFF(second,'1970-01-01',#d)/3600*3600 , '1970-01-01')
-- 2015-12-30 10:00:00
LiveDemo
How it works:
Get seconds difference from 1970-01-01
Divide by 3600 (integer division so the part after decimal point will be skipped)
Multiply by 3600 to get value back to full hours
Add calculated seconds number to 1970-01-01
With SQL Server 2012+ the neat way is to use DATETIMEFROMPARTS:
SELECT DATETIMEFROMPARTS(YEAR(#d), MONTH(#d), DAY(#d), DATEPART(HOUR, #d),0,0,0)
LiveDemo2

How to subtract Time Column from DateTime Column in SQL?

I have Crea_Date column which is a DateTime Column. I want to subtract the value in the IST column from crea_date Column and return a new column with the DateTime Value in it. My sample data is like this:
States crea_date IST
AB 2014-12-30 15:01:00.000 12:30:00.0000000
AK 2014-12-29 16:32:00.000 10:30:00.0000000
AZ 2014-12-18 16:07:00.000 11:30:00.0000000
Thanks in Advance
As strange as it might seem, you can add/subtract datetime values and it seems it's "normal" behavior.
Internally, datetime values are stored as the offset from 1/1/1900. If I add 22/1/2015 and 1/1/2015 I get 22/1/2130 because the second value is actually 115 years after 1900.
When you cast a time value to datetime only the time component is copied and the date component is set to 1/1/1900. In effect, you have an interval equal to your original time value.
This way I can subtract 10:30 hours from a specific datetime:
declare #d datetime='2014-11-04 12:51:00', #t time='10:30:00'
select #d -cast(#t as datetime)
//-----------------------
//2014-11-04 02:21:00.000
This behavior isn't an implementation quirk - it is explicitly permitted only for the datetime type. All other datetime types (eg datetime2, datetimeoffset) return the error Operand data type datetimeoffset is invalid for subtract operator.
If IST is an integer number of seconds:
SELECT DATEADD(s, -IST, crea_date)
FROM yourTable
If IST is of the TIME type:
SELECT DATEADD(ms, DATEDIFF(ms, IST, '00:00:00'), crea_date)
FROM yourTable
Try the below if IST is a character based column.
SELECT
Crea_Date, IST,
dateadd(hh,cast(substring(IST,1,2) as int),
dateadd(mi, cast(substring(IST, 4,2) as int),
dateadd(s, cast(substring(IST,7,2 ) as int), crea_date)
)) Final_Date
from [Yourtable]
You will get the added date in Final_Date column.
With slight modification in #0xF Answer I found the final Solution:
convert(varchar(10),
DATEADD(ms, DATEDIFF(ms, '00:00:00', IST), crea_date), 101) + right(convert(varchar(32
DATEADD(ms, DATEDIFF(ms, '00:00:00', IST), crea_date),100),8)

Confusion over the second argument of the DATEDIFF function

I've referred to this on MSDN but I'm still unsure what the second argument in the DATEDIFF function is doing in the following two examples:
SELECT DATEDIFF(yy,0,getdate()) --run on 14 Aug this returns 112
SELECT DATEDIFF(yy,1000,getdate()) --I chose 1000 arbitrarily and run on 14 Aug this returns 110
Usually I'll use DATEDIFF to find the number of days, or number of years between two months and the second argument is then a date.
Reason I'd like to understand the above is to ultimately understand the following:
SELECT DATEADD(yy, DATEDIFF(yy,0,GETDATE()), 0)
If you use an integer as the second argument (or for any datetime/smalldatetime assignment for that matter), this is interpreted as the number of days since 1900-01-01.
DECLARE #d1 DATETIME = 0, #d2 DATETIME = 1;
SELECT #d1, #d2;
Result:
1900-01-01 00:00:00.000 1900-01-02 00:00:00.000
Note that this doesn't work for new data types like DATE during direct assignment:
DECLARE #d DATE = 0;
Result:
Msg 206, Level 16, State 2, Line 1
Operand type clash: int is incompatible with date
But it can still work using date math, e.g.:
DECLARE #d DATE = DATEADD(YEAR, 0, SYSDATETIME());
SELECT #d;
Result:
2012-08-14
For these inconsistent reasons, I recommend you use proper date literals so that it is clear which date you mean and so that it works regardless of the data type. This is a habit I find hard to break, since typing 0 is so much easier than 19000101...
Consider below example to understand this concept better. 0 is default date "1900-01-01"
Below Query gives output as 2017-10-31 00:00:00.000
SELECT DATEADD(DAY, -1, DATEADD(MONTH, DATEDIFF(MONTH,0,SYSDATETIME())+3, 0))
Dividing the above query into various parts to understand the calculation.
-- 1414 Months since '1900-01-01'
SELECT DATEDIFF(MONTH,0,SYSDATETIME()) + 3
-- adding 1414 Months to Default date 1900 Produces '2017-11-01 00:00:00.000'
SELECT DATEADD(MONTH, DATEDIFF(MONTH,0,SYSDATETIME())+3, 0)
--Subtract 1 day from '2017-11-01 00:00:00:000' gives last day of previous month.
SELECT DATEADD(DAY, -1, DATEADD(MONTH, DATEDIFF(MONTH,0,SYSDATETIME())+3, 0))
The second argument to datediff() is a date.
The first example returns the "start" date of time in the SQL Server world. That would be 112 years before the current date.
The second example is rather non-sensical. As implemented, the dates are represented as number days since the earliest date. This is the number of years since 1000 days after the earliest date.
The last example adds a number of years to the base date. It then adds a number of months. Since the base date is 1/1/1900, this is giving you the first date of the day after the nth month in the yth year.