Get the the last ms before tomorrow in SQL Server? - sql-server

I'm using the Between command in SQL Server.
I need to find :
select * from MyTable where myDate
between getdate() and [1ms before tomorrow = 2012-02-26 :23:59:59:999]
I DON'T want [ 2012-02-27 :00:00:00:000] because future queries should use that value .
So I need 1 ms before tomorrow.
However - this is what I've tried but for some reason it refuse to give me the desire value !
and give me : 2012-02-26 23:59:59.997 + unpredictable results!
Why is that ? What am I missing ?
I want to get 2012-02-26 :23:59:59:999 !

Why not specify an exclusive range instead?
SELECT * FROM `MyTable`
WHERE `myDate` >= GETDATE() AND `myDate` < (tomorrow)
(I can't be bothered to figure out how to get tomorrow's DATETIME as I'm usually a MySQL guy, but I believe you already know how to do it.)
Otherwise you're stuck messing around with floating-point values of questionable accuracy.

The SQL Server DATETIME has an accuracy of 3.33ms - you'll always get the .997 as the closest value to a full hour. That's just the way it is, and you cannot change it in SQL Server 2005. Read all about it at Demystifying the SQL Server DATETIME datatype.
In SQL Server 2008, you can use the DATETIME2 datatype which has an accuracy of 100ns - so there you have up to 7 exact digits after the "seconds" decimal point.
Update: if you want to get .999 with a DATETIME2, you need to use:
DECLARE #dt2 DATETIME2
-- you need to cast GETDATE() to DATETIME2 - otherwise it's a DATETIME !
SET #dt2 = CAST(GETDATE() AS DATETIME2)
DECLARE #dt2_Added DATETIME2
SET #dt2_Added = DATEADD(d, DATEDIFF(d, 0, #dt2) + 1, 0)
SELECT DATEADD(ms, -1, #dt2_added)
Result:
2012-02-26 23:59:59.9990000
Update #2: things get stranger still.....
If I use SYSDATETIME() instead of GETDATE(), it gives me a DATETIME2 right from the get to - but if I do the calculation in one step:
DECLARE #dt2 DATETIME2
SET #dt2 = SYSDATETIME()
SELECT DATEADD(ms, -1, DATEADD(d, DATEDIFF(d, 0, #dt2) + 1, 0) )
I get the result of :
2012-02-27 00:00:00.000
but if I do the same calculation in two steps:
DECLARE #dt2 DATETIME2
SET #dt2 = SYSDATETIME()
DECLARE #dt2_Added DATETIME2
SET #dt2_Added = DATEADD(d, DATEDIFF(d, 0, #dt2) + 1, 0)
SELECT DATEADD(ms, -1, #dt2_added)
I get the expected result:
2012-02-26 23:59:59.9990000
This is indeed quite weird......

Related

Adding fixed time onto datetime gives unexpected results

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')

Convert Datetime format in SQL Server 2008

I need to convert the Long value to a date time format.
Eg., Long value - 20080506015600658
datetime format - Tue May 06 01:56:00 IST 2008
Here's the ugly way to do it via string manipulation:
declare #start bigint
set #start = 20080506015600658
select CONVERT(datetime,
STUFF(STUFF(STUFF(STUFF(STUFF(STUFF(
t,15,0,'.'),
13,0,':'),
11,0,':'),
9,0,'T'),
7,0,'-'),
5,0,'-'))
from (select CONVERT(varchar(20),#start) as t) n
Which basically forces it to conform to the pattern YYYY-MM-DD'T'hh:mm:ss.mil before doing the conversion.
And here's the ugly as sin way to do it with maths:
declare #start bigint
set #start = 20080506015600658
select
DATEADD(year, (#start/10000000000000) - 1, --Because we already have 1 on starting date
DATEADD(month, (#start/100000000000)%100 - 1, --Because we already have 1 on starting date
DATEADD(day, (#start/1000000000)%100 - 1, --Because we already have 1 on starting date
DATEADD(hour, (#start/10000000)%100,
DATEADD(minute,(#start/100000)%100,
DATEADD(second,(#start/1000)%100,
DATEADD(millisecond,#start%1000,CONVERT(datetime2,'0001-01-01'))))))))

What is the equivalent of Oracle's LAST_DAY() function in SQL Server 2008?

I have used the LAST_DAY() function in Oracle like this:
Last_Day( to_date( '$pay_first_day' , 'YYYY-mm-dd') )
What do i have to do in SQL server 2008 R2 database to achieve the same result?
Try this one -
DECLARE #Date DATETIME
SELECT #Date = GETDATE()
SELECT DATEADD(MONTH, DATEDIFF(MONTH, 0, #Date) + 1, 0) - 1
SQL Server 2012 introduces a new date function called EOMONTH which returns the last day of the month that contains the specified date, with an optional offset.
EOMONTH ( <start_date> [, <month_to_add> ] )
For instance
select EOMONTH('01/07/2018')
Would return
2018-01-31
-- Wrapping the answer in a function for seamless transition.
CREATE FUNCTION [dbo].[LAST_DAY]
(
#inDate DATETIME
)
RETURNS DATETIME
AS
BEGIN
RETURN DATEADD(MONTH, DATEDIFF(MONTH, 0, #inDate) + 1, 0) - 1
END
-- TO TEST: SELECT dbo.LAST_DAY(getDate()) as LastDayOfThisMonth
-- note: Please mark as answer if this helps you better then the one before.
Could you please try this, it should work as expected :
SELECT DATEADD (DAY, -1, DATEADD (MONTH, DATEDIFF (MONTH, 0, '$pay_first_day') + 1, 0))
It would return the last day equivalent of the given date in this format : '2013-02-28 00:00:00.000' if your $pay_first_day is in February, and it would return '2013-04-30 00:00:00.000' if your $pay_first_day is in April.
Here are two UDF for begin and end of month.
CREATE FUNCTION dbo.End_of_month (#Date datetime)
RETURNS datetime AS
BEGIN
DECLARE #ret datetime
SET #Ret=DATEADD(DAY,-DAY(#Date)+1,#Date);
SET #Ret=DATEADD(Month,1,#Ret);
SET #Ret=DATEADD(Day,-1,#Ret);
RETURN(#Ret)
END
CREATE FUNCTION dbo.Begin_of_month (#Date datetime)
RETURNS datetime AS
BEGIN
DECLARE #ret datetime
SET #Ret=DATEADD(DAY,-DAY(#Date)+1,#Date);
RETURN(#Ret)
END

Convert UTC Milliseconds to DATETIME in SQL server

I want to convert UTC milliseconds to DateTime in SQL server.
This can easily be done in C# by following code:
DateTime startDate = new DateTime(1970, 1, 1).AddMilliseconds(1348203320000);
I need to do this in SQL server. I found some script here, but this was taking initial ticks from 1900-01-01.
I have used the DATEADD function as below, but this was giving an arithmetic overflow exception by supping milliseconds as difference:
SELECT DATEADD(MILLISECOND,1348203320000,'1970-1-1')
How can I do the conversion properly?
DECLARE #UTC BIGINT
SET #UTC = 1348203320997
SELECT DATEADD(MILLISECOND, #UTC % 1000, DATEADD(SECOND, #UTC / 1000, '19700101'))
Below the function that converts milliseconds to datetime
IF object_id('dbo.toDbTimeMSC', 'FN') IS NOT NULL DROP FUNCTION dbo.toDbTimeMSC
GO
CREATE FUNCTION [dbo].[toDbTimeMSC] (#unixTimeMSC BIGINT) RETURNS DATETIME
BEGIN
RETURN DATEADD(MILLISECOND, #unixTimeMSC % 1000, DATEADD(SECOND, #unixTimeMSC / 1000, '19700101'))
END
GO
-- select dbo.toDbTimeMSC(1348203320000)
I had problems with using answers given here (especially that the system was counting ticks form 0001-01-01) - so I did this:
CONVERT(DATETIME,[Time]/ 10000.0/1000/86400-693595)
--explanation for [Time_in_Ticks]/ 10000.0/1000/86400-693595
--Time is in "ticks"
--10000 = number of ticks in Milisecond
--1000 = number of milisecons in second
--86400 = number of seconds in a day (24hours*60minutes*60second)
--693595= number of days between 0001-01-01 and 1900-01-01 (which is base
-- date when converting from int to datetime)
Using SQL Server 2008R2 this produced the required result:
CAST(SWITCHOFFSET(CAST(dateadd(s, convert(bigint, [t_stamp]) / 1000, convert(datetime, '1-1-1970 00:00:00')) AS DATETIMEOFFSET), DATENAME (TZoffset, SYSDATETIMEOFFSET())) AS DATETIME)
The DATEADD requires an integer as a second argument. Your number 1348203320000 is very large for integer therefore it produce an error in runtime. Your should use bigint type instead and provide DATEADD with correct int values by splitting your milliseconds to seconds and milliseconds. That is sample you could use.
DECLARE #total bigint = 1348203320000;
DECLARE #seconds int = #total / 1000
DECLARE #milliseconds int = #total % 1000;
DECLARE #result datetime = '1970-1-1';
SET #result = DATEADD(SECOND, #seconds,#result);
SET #result = DATEADD(MILLISECOND, #milliseconds,#result);
SELECT #result
Right now, you can use dateadd with division on minutes and not seconds.
The code will be like this:
DATEADD(MILLISECOND, epoch% 60000, DATEADD(MINUTE, epoch/ 60000, '19700101'));
=dateadd("d",INT((Fields!lastLogon.Value / 864000000000)- 134774),"1970-01-01 00:00:00")
That's what I used in SSRS to get around the INT error, use days instead of seconds. Is it wrong?

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