How to validate if a DateTime includes time in SQL Server - sql

I have a stored procedure that takes a datetime parameter. I need to determine if the given datetime parameter includes the time.
Now, DATEPART(hour, #datetimeValue) = 0 doesn't work in my case because the datetime can be provided as 3/14/2019 0:00 which refers to as 12 AM and valid.
Return true if input is :
'3/14/2019 0:00'
'3/14/2019 15:00'
Return false only if input has no time :
'3/14/2019'
Thanks everyone for their input. It looks there is no solution to this other than changing the stored procedure parameter to varchar.

The requirement is not technically possible to handle in the Stored Procedure.
The DateTime parameter in your stored procedure will always contain a time aspect which is either explicitly passed to it or defaults to midnight of the passed in date. There is no way to know if the caller explicitly passed in a time aspect or not.
You have 2 options:
Change the incoming data type of the parameter to varchar and have the stored procedure parse that into a DateTime and handle validation.
Make the caller handle any validation having to do with time and drop this requirement from your code.

By default, a DATETIME includes a time, set to midnight (e.g. '00:00:00'), even if you aren't using the time portion. If your datetime's are strings, then here is a way to see if a time is part of the string (though it's a little hacky):
SELECT CASE WHEN CHARINDEX(':', '3/14/2019 0:00') > 0 THEN 1 ELSE 0 END -- Returns 1
SELECT CASE WHEN CHARINDEX(':', '3/14/2019 15:00') > 0 THEN 1 ELSE 0 END -- Returns 1
SELECT CASE WHEN CHARINDEX(':', '3/14/2019') > 0 THEN 1 ELSE 0 END -- Returns 0
Basically it's just searching the string and looking to see if a colon is in the string. It meets your criteria though.

The only solution I can think of is to ALTER your SP and use two (2) parameters instead, one is DATE and the second is TIME datatype as:
CREATE PROCEDURE HasTime(
#MyDate DATE = NULL,
#MyTime TIME = NULL
)
AS
BEGIN
IF #MyTime IS NULL
SELECT 'There is no time'
ELSE
SELECT 'There is time';
END;
EXEC dbo.HasTime '2019-01-01', NULL; --Or EXEC dbo.HasTime '2019-01-01';
EXEC dbo.HasTime '2019-01-01', '00:00:00';
Live Demo

This is crude, but it does what you need. If its throw away, eg part of some import it might be good enough for your needs.
I am sure this approach could be improved with more understanding on how to retreive details of the current executing SQL from within the PROC, I had a quick look at using query plans etc.
You could just fail back to this check if the time is midnight this makes it more efficient
ALTER PROCEDURE Hastime(#d AS DATETIME)
AS
BEGIN
-- Is there a easier way to get from DBCC INPUTBUFFER to a SQL variable?
CREATE TABLE #temp
(
spid INT,
eventtype NVARCHAR(30),
parameters INT,
eventinfo NVARCHAR(4000)
)
INSERT INTO #temp
(eventtype,
parameters,
eventinfo)
EXEC ('DBCC INPUTBUFFER(' + ##spid +') WITH NO_INFOMSGS')
-- Yes, we could do this better
IF EXISTS (SELECT *
FROM #temp
WHERE eventinfo LIKE '%:%')
SELECT 'Time'
ELSE
SELECT 'No Time'
END
go
EXEC dbo.Hastime
'2000/06/01'
go
EXEC dbo.Hastime
'2000/06/01 00:00:00'

You can do a check like this, but if the time is actually midnight, it will return not valid time:
-- returns 'NOTime'
SELECT CASE WHEN CAST('1/1/2019' AS TIME) = '00:00:00.000' THEN 'NOTime' ELSE 'TIME' END
-- returns 'TIME'
SELECT CASE WHEN CAST(GetDate() AS TIME) = '00:00:00.000' THEN 'NOTime' ELSE 'TIME' END

I would cast as date:
select (case when convert(date, #datetimeValue) = #datetimeValue then 'NoTime'
else 'HasTime'
end)

The only way is to pass a varchar type to teh procedure instead of datetime
otherwise we can not make difference between a date without time and a date at midnight
here you can check this restriction
The follwoing script illustrates that restriction:
declare #d as datetime
declare #d1 as datetime
set #d='01/01/2019'
set #d1='01/01/2019 00:00:00:00'
if #d=#d1 print 'equal' else print 'not equal'

Related

SQL Scalar Function with Date Convert?

I am trying to make a scalar function, that will allow me to deal with null dates, which show as 1/1/1900. I can write out the logic in a query, but when I try to make it a function, whether I return a varchar or a date, it makes the format wrong.
This is the CASE for a column line in a select:
CASE WHEN CONVERT(VARCHAR(10), CAST(f.[DateStart] AS DATE),101) = '01/01/1900'
THEN ''
ELSE CONVERT(VARCHAR(10), CAST(f.[DateStart] AS DATE),101)
END AS 'DateStart'
If I make that a function, like this:
ALTER FUNCTION dbo.ConvertDate(#d datetime)
RETURNS VARCHAR
AS
BEGIN
RETURN (SELECT CASE WHEN CONVERT(VARCHAR(10), CAST(#d AS DATE),101) = '01/01/1900'
THEN ''
ELSE CONVERT(VARCHAR(10), CAST(#d AS DATE),101)
END);
END
GO
I returns a 0 for a legitimate date. If I return as a date, it returns a good date as 2019-10-08 and returns a null date as 1900-01-01. Anyone know how to get a function to return the way the case statement runs in the select, the way I want it to? Just trying to minimize repeating the same long logic throughout all my stored procs.
Thanks!

SQL Server DateDiff between two dates returns inaccurate values

I am trying to convert a string into DateTimeOffset (in SQL Server) through a ETL job. Basically, my string would look something like '2017-10-15' and I want this to be converted into a DatetimeOffset (from the current DB server).
SELECT
SWITCHOFFSET(DATEADD(mi, DATEDIFF(MI, GETDATE(), GETUTCDATE()), CAST(#DateInString + ' 00:00:00' AS DATETIMEOFFSET)), DATENAME(tzoffset, SYSDATETIMEOFFSET()))
I have been getting some weird issues with this statement as the final output would fall either +1 / -1 minute than the expected ones. This happens for at least every 10 records/million. I tried to nail down the issue and I could see the problem was with the DATEDIFF() method returning +/-1 minute.
SELECT DATEDIFF(MI, GETDATE(), GETUTCDATE())
This should exactly return -600 (since my DB server UTC is +10). However, it returns either -599 or 601 for few records. I execute them as a single select statement in my Stored Procedure and return it as a parameter.
This is weird on how SQL could detect two different datetime values for GETDATE() and GETUTCDATE() on the same select statement.
Is there a way to force SQL to get exactly same dates in those DATEDIFF parameters or am I missing something here? Thanks in advance
I am using SQL Server 2014 (v12.0).
Stored procedure:
CREATE PROCEDURE dbo.SPConvertDateTimeOffset
#DateInString VARCHAR(10),
#DateTimeOffset_Value DATETIMEOFFSET OUTPUT,
#Datediff_Value INT OUTPUT
AS
BEGIN
-- This line returns +/- 1
SELECT #Datediff_Value = DATEDIFF(MI, GETDATE(), GETUTCDATE())
SELECT #DateTimeOffset_Value = SWITCHOFFSET(DATEADD(mi, #Datediff_Value, CAST(#DateInString + ' 00:00:00' AS DATETIMEOFFSET)), DATENAME(tzoffset, SYSDATETIMEOFFSET()))
END
#GordonLinoff has explained why this happens: since functions are executed at slightly different times, they may return a different minute.
To work around, try this:
DECLARE #DateTimeOffset_Value Datetimeoffset
DECLARE #Datediff_Value INT, #DateInString VARCHAR( 10 )
SET #DateInString = CONVERT( VARCHAR, GETDATE(), 112 )
SET #DateTimeOffset_Value = TODATETIMEOFFSET( #DateInString, DATENAME(tzoffset,SYSDATETIMEOFFSET()))
SET #Datediff_Value = DATEDIFF( MI, #DateInString, #DateTimeOffset_Value )
SELECT #DateInString, #DateTimeOffset_Value, #Datediff_Value
It does not use current date comparisons.
Note: that during the time when day light saving changes you may get a different value from the expected, depending on when exactly the code was run.
Have a look at https://dba.stackexchange.com/questions/28187/how-can-i-get-the-correct-offset-between-utc-and-local-times-for-a-date-that-is for more solutions about how to handle DTS changes.
The two functions are not executed simultaneously. So about 1 time in 100,000 (in your test) the times are on opposite sides of a minute boundary.
If you just want the timezone, you could try:
select datepart(tz, SYSDATETIMEOFFSET())

SQL: converting datetime from character string when inserting date

I have the following procedure:
ALTER PROCEDURE [dbo].[myProcedute]
#mydate VARCHAR(8000) = NULL
IF #mydate = ''
BEGIN
SET #mydate = NULL
END
ELSE
BEGIN
SET #mydate = CONVERT(DATETIME, #mydate, 103) -- dd/mm/yy
END
INSERT INTO dbo.myTable(theDate) VALUES(#mydate)
And I execute it like this:
EXEC [dbo].[myProcedure] '02/02/2012'
After execution I get this error:
Conversion failed when converting datetime from character string.
I execute it from my vb6 code and the date can vary. This time is
'02/02/2012' but next time it may be ''(empty).
When it comes as '', I need it to insert a NULL as you can see in the IF clause.
Why am I getting this error?
Thanks a lot !
Use ISDATE() to check if it is a valid date.
Conversion seems fine. And as comments suggests, it would solve a lot if you could change parameter to DATE
ALTER PROCEDURE [dbo].[myProcedute]
#mydate VARCHAR(8000) = NULL
IF ISDATE(#mydate)
BEGIN
SET #mydate = CONVERT(DATETIME, #mydate, 103) -- dd/mm/yy
END
ELSE
BEGIN
SET #mydate = NULL
END
INSERT INTO dbo.myTable(theDate) VALUES(#mydate)
It is better to format your date string to ISO format (yyyymmdd) which guaranteed to work on any sql-server. Assuming you vb code passing it like dd/mm/yyyy (as per your data). Try this;
--Format #mydate to yyyymmdd
SELECT #mydate = CASE LEN(#mydate) WHEN 10
THEN RIGHT(#mydate,4)+ SUBSTRING(#mydate,4,2)+ LEFT(#mydate,2)
ELSE NULL END
--Insert
INSERT INTO dbo.myTable(theDate) VALUES(#mydate)

convert strings of type mm/dd/yyyy h to date time

I have a function which converts all of the strings to datetime.
ALTER FUNCTION formatit(
#fromtime VARCHAR(50) -- varchar
)
RETURNS DATETIME
AS
BEGIN
DECLARE #from datetime
IF (CHARINDEX('NOON',#fromtime,0)) = 0
SET #from = CONVERT(DATETIME, #fromtime)
ELSE
SET #from =CONVERT(DATETIME, '01/01/2000 12pm')
RETURN(#from)
END
SELECT dbo.formatit('04/12/2011 12 ')
So when u see the last select stmt it throws an error saying:
The conversion of a char data type to a datetime data type
resulted in an out-of-range datetime value.
It works fine if i give time 4 pm or 4:00. But it gives an error if i give just 4. Kindly let me know how i can handle this?
You could add a lot of code to look at the string and see if there is a ':' (like you have code to look for NOON). The problem is that if the string ends in 12PM the conversion will work too. So you would have to do something like
ALTER FUNCTION formatit(
#fromtime VARCHAR(50) -- varchar
)
RETURNS DATETIME
AS
BEGIN
DECLARE #from datetime
IF (CHARINDEX('NOON',#fromtime,0)) <> 0
BEGIN
SET #from =CONVERT(DATETIME, '01/01/2000 12pm')
END
ELSE IF (CHARINDEX('PM',#fromtime,0)) = 0 AND (CHARINDEX('AM',#fromtime,0)) = 0 AND (CHARINDEX(':',#fromtime,0)) = 0
BEGIN
SET #fromtime = #fromtime+':00'
SET #from =CAST(#fromtime AS datetime)
END
ELSE
BEGIN
SET #from =CAST(#fromtime AS datetime)
END
return(#from)
END
It looks like whitespace is ignored - I tried these and they all worked...
SELECT dbo.formatit('04/12/2011 12 ')
SELECT dbo.formatit('04/12/2011 12:00')
SELECT dbo.formatit('04/12/2011 12PM')
SELECT dbo.formatit('04/12/2011 12AM')
SELECT dbo.formatit('NOON')
Really you should look at the data you are using to feed this function with something other than TSQL so you have a better string parser.
Your call to dbo.formatit('04/12/2011 12 ') results in a call to convert(datetime, '04/12/2011 12 '). This string is not a valid input to convert to a date/time value.
See the list of formats in the CAST and CONVERT reference in MSDN. If you need to convert this string, you will need to change the time portion into "12:00PM" or just "12:00" (which is assumed to be in the 24-hour format).
First, I would find all the unique time occurrences in your table, something like:
select distinct rtrim(substring(#timeField,charindex(' ',#timeField)+1,99))
as TimePortion
This will give you a sense of what you are dealing with.
Then, in your function, break apart the date and time portions...
Now, based on what you found from the query, you need to spell out what to do with the times you find. For example, if the time portion is numeric, you might simply append :00 to it (i.e. 4 becomes 4:00). If you see NOON, you might replace it with 12:00pm, etc.
ALTER FUNCTION formatit( #fromtime VARCHAR(50) )
RETURNS DATETIME
AS
BEGIN
declare #dtPortion VARCHAR(12)
declare #tmPortion VARCHAR(32)
declare #x INT
SET #x = charindex(' ',#fromTime)
SET #dtPortion = left(#fromTime,#x-1)
SET #tmPortion = rtrim(substring(#fromTime,#x+1,99))
-- Figure out what to do
SELECT #FromTime =
CASE
WHEN IsNumeric(#tmPortion) = 1 THEN #dtPortion+' '+#tmPortion+':00'
WHEN #tmPortion = 'NOON' THEN #dtPortion+' 12:00pm'
ELSE
#dtPortion+' 12:00pm'
END
SET #from =CONVERT(DATETIME, '01/01/2000 12pm')
RETURN(#from)
END
Go
Hope this gets you started

SQL 2008 CASE statement aggravation

Why does this fail:
DECLARE #DATE VARCHAR(50) = 'dasf'
SELECT CASE WHEN ISDATE(#DATE) = 1 THEN CONVERT(date,#DATE) ELSE #DATE END
Msg 241, Level 16, State 1, Line 2
Conversion failed when converting date
and/or time from character string.
Why is it trying to convert dasf to date when it clearly causes ISDATE(#DATE) = 1 to evaluate to false...
If I do:
SELECT ISDATE(#DATE)
The return value is 0.
CASE returns a single type. In this case, the type is Date, found from your THEN clause. It is implicitly converting the ELSE clause result to Date to match.
You must choose a single type to be returned by CASE. It cannot be used to return sometimes Date and sometimes varchar.
from MSDN:
http://msdn.microsoft.com/en-us/library/ms181765.aspx
Return Types
Returns the highest
precedence type from the set of types
in result_expressions and the optional
else_result_expression. For more
information, see Data Type Precedence
(Transact-SQL).
and then following that link: http://msdn.microsoft.com/en-us/library/ms190309.aspx
8) date
27) varchar
It's not clear what you want, so it's hard to offer alternatives (I don't know if the CASE is part of a larger query or script), but here's a couple things you can do:
-- choose a single return type per CASE expression
SELECT
CASE
WHEN IsDate(#Date) = 1
THEN convert(date, #Date)
ELSE null
END as [Date],
CASE
WHEN IsDate(#Date) = 1
THEN null
ELSE #Date
END as [VarChar]
--use control flow to select what you want.
IF IsDate(#Date) = 1
THEN
SELECT convert(date, #Date)
ELSE
SELECT #Date
try this:
DECLARE #DATE VARCHAR(50) = 'dasf'
SELECT CASE
WHEN ISDATE(#DATE)=1 THEN CONVERT(char(23),CONVERT(date,#DATE),121)
ELSE #DATE
END
It will basically format your valid date and leave the non-dates alone. Is that what you are after?
actual working sample:
DECLARE #YourTable table (DATE VARCHAR(50))
INSERT #YourTable VALUES ('dasf')
INSERT #YourTable VALUES ('1/1/2010')
SELECT
CASE
WHEN ISDATE(DATE)=1 THEN CONVERT(char(23),CONVERT(datetime,DATE),121)
ELSE DATE
END AS DATE
FROM #YourTable
OUTPUT:
DATE
--------------------------------------------------
dasf
2010-01-01 00:00:00.000
(2 row(s) affected)
In the working example, I made a substitute from date data type to datetime because I'm on SQL Server 2005 and date datatype is SQL Server 2008 only.