Handle Julian and Real Times in one query - sql

Hi Below is some sample data
DECLARE #Time TABLE (
[Time] VARCHAR(250),
[Count] VARCHAR(250)
)
INSERT INTO #Time ([Time],[Count])
VALUES
('13:52','2'),
('13:53','2'),
('13:54','5'),
('13:55','3'),
('13:56','3'),
('13:57','1'),
('13:58','4'),
('13:59','1'),
('130','72'),
('1300','61'),
('1301','40'),
('1302','51'),
('1303','53'),
('1304','59'),
('1305','62'),
('1306','36'),
('1307','56'),
('1308','52')
Select * from #Time
Is there a way to handle both data types so that it outputs as a real time
I use a function that partly works but am getting and error each time:
Msg 8116, Level 16, State 1, Line 1
Argument data type varchar is invalid for argument 2 of dateadd function.
The function I use is as follows and wondered if this can be adopted/changed to account for the poor data eg both types of Times (Julian/Regular).
CREATE FUNCTION [dbo].[udf_TR_PROTOS_JulianTimeToSQLDateTime]
(
-- Add the parameters for the function here
#JulianTime INT
)
RETURNS DATETIME
AS
BEGIN
-- Declare the return variable here
DECLARE #Result DATETIME
-- Add the T-SQL statements to compute the return value here
IF #JulianTime >= 0 AND #JulianTime < 1440 -- this ensures that the result will be in the range of a datetime data type
SET #Result = DATEADD(MINUTE, #JulianTime, CAST('00:00' AS TIME))
ELSE
SET #Result = CAST('00:00' AS TIME)
-- Return the result of the function
RETURN #Result
END
GO
ADDITION:
The COMPLETE datetime function is here:
CREATE FUNCTION [dbo].[udf_TR_PROTOS_JulianDateTimeToSQLDateTime] (
-- Add the parameters for the function here
#JulianDate INT,
#JulianTime INT = 0
)
RETURNS DATETIME
AS
BEGIN
-- Declare the return variable here
DECLARE #Result DATETIME
-- Add the T-SQL statements to compute the return value here
IF #JulianDate > 640333 -- this ensures that the result will be in the range of a datetime data type
BEGIN
SET #Result = DATEADD(DAY, (#JulianDate-429), CAST('Jan 1 0001' AS DATETIME2))
IF #JulianTime < 1440 AND #JulianTime >= 0 -- this ensures that the time is between 00:00 and 23:59
SET #Result = DATEADD(MINUTE, #JulianTime, #Result)
END
ELSE
SET #Result = 'Jan 1 1753'
-- Return the result of the function
RETURN #Result
END
GO

I am not sure what exactly are you trying to do from your give information. But I tried to modify the function to return time when I pass it JulianTime as a varchar. Here is the code -- (try to make changes accordingly though)
CREATE FUNCTION [dbo].[udf_TR_PROTOS_JulianTimeToSQLDateTime]
(
-- Add the parameters for the function here
#JulianTime VARCHAR(255)
)
RETURNS TIME(0)
AS
BEGIN
-- Declare the return variable here
DECLARE #Result TIME(0), #HOUR VARCHAR(2), #MINUTES VARCHAR(2)
-- DECLARE #JulianTime VARCHAR(255)
-- SET #JulianTime = '13:02'
SET #HOUR = SUBSTRING(#JulianTime, 1, 2)
IF(LEN(#JulianTime) = 4)
SET #MINUTES = SUBSTRING(#JulianTime, 3, 2)
IF(LEN(#JulianTime) = 5)
SET #MINUTES = SUBSTRING(#JulianTime, 4, 2)
SET #Result = CONCAT(#HOUR, ':', #MINUTES)
-- PRINT #RESULT
-- Return the result of the function
RETURN #Result
END
GO

I've updated the FUNCTION.
CREATE FUNCTION [dbo].[udf_TR_PROTOS_JulianTimeToSQLDateTime]
(
#timeString varchar(250)
)
RETURNS TIME
AS
BEGIN
DECLARE #Result TIME ;
/* Ensure that we're working with a fixed date */
DECLARE #epoch datetime = '19000101' ;
/* Check the string for ':' */
IF CHARINDEX(':',#timeString) > 0
/* Can #timeString be parsed as a valid time? */
SET #Result = TRY_PARSE(#timeString AS time) ; /* Returns NULL if bad time string. */
ELSE
/* No ':' so check if valid julian time. */
IF TRY_CONVERT(int,#timeString) IS NOT NULL AND CONVERT(int,#timeString) BETWEEN 0 AND 1439
SET #Result = CAST( DATEADD(minute, CONVERT(int,#timeString), #epoch) AS time) ;
ELSE
SET #Result = NULL ;
RETURN #Result ;
END
http://dbfiddle.uk/?rdbms=sqlserver_2016&fiddle=1ca82cd76b2932593262601b1742f602
This will only work if you're using something greater than SQL 2012+ because of TRY_PARSE and TRY_CONVERT. If you're lower than 2012, you can modify that bit to do what those functions essentially do.

Select
CASE WHEN CHARINDEX(':',[Time]) > 0 THEN CAST([Time] AS TIME) ELSE DATEADD(MINUTE, TRY_CAST([Time] AS INT), CAST('00:00' AS TIME)) END AS [RealTime],
*
from #Time
This appears to do the trick, thanks for the valuable input

For SQL 2008, use this function:
CREATE FUNCTION [dbo].[udf_TR_PROTOS_JulianTimeToSQLDateTime]
(
#timeString varchar(250)
)
RETURNS TIME
AS
BEGIN
DECLARE #Result time ;
/* Ensure that we're working with a fixed date */
DECLARE #epoch datetime = '19000101' ;
/* Check the string for ':' */
IF CHARINDEX(':',#timeString) > 0
IF ISDATE(#timeString) = 1
/* Is #timeString a valid time object? */
SET #Result = CAST(#timeString AS time) ;
ELSE
SET #Result = NULL ;
ELSE
/* No ':' so check if valid julian time. */
IF ISNUMERIC(#timeString) = 1 AND CONVERT(int,#timeString) BETWEEN 0 AND 1439
SET #Result = CAST( DATEADD(minute, CONVERT(int,#timeString), #epoch) AS time) ;
ELSE
SET #Result = NULL ;
RETURN #Result ;
END

Related

Getting FRACTIONAL difference (YEARS) between two dates in T SQL

I need to detect a difference between two dates, such that when Date_1 = 12-jan-2010 and Date_2 = 01-jan-2016 I would NOT get 6 but a number < 6.
SELECT DATEDIFF(YEAR,'12-jan-2010','01-jan-2016')
DATEDIFF returns 6 in the above case.
It depends on required precision, try following:
SELECT DATEDIFF(DAY, #d1, #d2)/365.25
I guess this resolves,
DECLARE #STARTDATE DATE='12-jan-2010'
DECLARE #ENDDATE DATE='01-jan-2016'
DECLARE #TOTALDAY DECIMAL(18,2)=DATEDIFF(day,#STARTDATE,#ENDDATE)
DECLARE #AVGYEAR DECIMAL(18,2)= ((365*DATEDIFF(YEAR,#STARTDATE,#ENDDATE))+
dbo.LEAP_YEAR(#STARTDATE,#ENDDATE))/CONVERT(DECIMAL(18,2),
DATEDIFF(YEAR,#STARTDATE,#ENDDATE))
SELECT CONVERT(decimal(18,2),#TOTALDAY/#AVGYEAR) AS DiffDate ---This will provide you result in decimal
this the function which return no of leap years between dates.
ALTER FUNCTION LEAP_YEAR(#START DATE,#END DATE)
RETURNS INT
AS BEGIN
DECLARE #COUNT INT = 0,#Z INT = DATEPART(YYYY,#START)
DECLARE #X INT =DATEPART(YYYY,#START)
DECLARE #Y INT =DATEPART(YYYY,#END)
IF (DATEPART(MM,#START) >2)
SET #X=#X+1
IF (DATEPART(MM,#END) <2)
SET #Y=#Y-1
WHILE (#X <= #Y)
BEGIN
SET #COUNT = #COUNT +
(CASE WHEN (#X%4 = 0 AND #X%100 !=0) OR #X%400 = 0
THEN 1
ELSE 0 END)
SET #X = #X + 1
END
RETURN #COUNT
END
If I've got it right. Just Add result of DATEDIFF (6 in this case) to the start date if it > the end date then just subtract 1 year so you will get 5 full years:
DECLARE #StartD DATETIME;
DECLARE #FinalD DATETIME;
SET #StartD = '12-jan-2010';
SET #FinalD = '11-jan-2016';
SELECT DATEDIFF(YEAR,#StartD,#FinalD)
- CASE WHEN DATEADD(YEAR,DATEDIFF(YEAR,#StartD,#FinalD),#StartD)>#FinalD
THEN 1 ELSE 0 END
First of all, I wish to thank all those that spent time trying to provide a simple and reliable solution. Finally, I decided to resolve it as follows:
Supposed that I want to know if #n full years passed between two dates, then:
DECLARE #n INT ;
DECLARE #Old_Date DATETIME ;
DECLARE #New_Date DATETIME ;
SET #n = <some_value> ;
SET #Old_Date = <some_value> ;
SET #New_Date = <some_value> ;
IF (DATEADD(YEAR ,#n , #Old_Date) <= #New_Date)
SET #Result = 'YES' ;
ELSE
SET #Result = 'NO' ;
[of course, a check needs to be included to verify that #Old_Date < #New_Date]
I can't say this can be proved (mathematically) as correct in all possible scenarios, but it provides the needed answer to me.
Thanks to all again.

SQL Function help- Daylight Savings calculation

All,
I wrote a function that essentially takes a timestamp and a shopper ID, and based on that shoppers zipcode work out the UTC offset and correct the time.
The problem I am having is: it's sloooow!
Can anyone see an easy way to speed it up ?
CREATE FUNCTION TimeModifier
(
-- Add the parameters for the function here
#InputDate datetime,#shopperid int
)
RETURNS datetime
AS
BEGIN
--Declare #shopperid int
--set #shopperid=25
-- Declare the return variable here
-- Declare the return variable here
DECLARE #Result datetime
Declare #zip nvarchar(10)
Declare #TimeofYear int
declare #Year int
declare #Winter int
declare #Summer int
select #year=datepart(yyyy,#InputDate)
--If 0 then its outside of the summer hours.
SELECT #timeofyear=count(*) FROM [d].[dbo].[DST-Dates] where #inputdate>=startdate and #inputdate<=enddate
select #zip=zip from d..shopper where shopperid=#shopperid
--Gets the UTC offset for winter and summer
select #winter=winter,#summer=summer FROM [MMD_Feed].[dbo].[ZipCodeZones] where zip=#zip
if(#TimeofYear=0)--IE is it Winter
set #Result=DATEADD(HH,#winter, #Inputdate)
else--Use summer offset
set #Result=DATEADD(HH,#summer, #Inputdate)
--select #Result
-- Return the result of the function
RETURN #Result
END
GO
Thanks
~J
Here is a total shot in the dark for converting this to an iTVF.
CREATE FUNCTION TimeModifier
(
-- Add the parameters for the function here
#InputDate datetime
, #shopperid int
)
RETURNS TABLE WITH SCHEMABINDING
AS RETURN
select DATEADD(Hour, case when t.TimeOfYear = 0 then z.winter else z.summer end, #InputDate) as MyResult
from d.dbo.shopper s
join [MMD_Feed].[dbo].[ZipCodeZones] z on s.zip = z.zip
cross apply
(
SELECT count(*) as TimeOfYear
FROM [d].[dbo].[DST-Dates]
where #inputdate >= startdate
and #inputdate <= enddate
) t
where s.shopperid = #shopperid

With SQL Server add x number of days to a date in sql but also pass in the number of business days in a week

In SQL Server I would like to add x number of business days to a date but also pass in the amount of business days in a week. ie could be 5,6 ,7.
I found this on stack overflow that handles 5 days but not sure how to modify it so that you could specify the number of working days per week.
CREATE FUNCTION[dbo].[AddBusinessDays]
(#Date date, #n INT)
RETURNS DATE AS
BEGIN
DECLARE #d INT;
SET #d = 4 - SIGN(#n) * (4-DATEPART(DW, #Date));
RETURN DATEADD(D, #n + ((ABS(#n) + #d - 2) / 5) * 2 * SIGN(#n) - #d / 7, #Date);
END
This is it, I have converted it to a stored procedure for better understanding.
I have used SQL Server.
Alter procedure [dbo].[AddBusinessDaysP]
(#Date date,#n INT,#wds INT)
as
BEGIN
--exec AddBusinessDaysP '9/25/2014',2,4
Declare #totWeekEnds int
Set #totWeekEnds = (Case When #n > 7 then (#n / 7) else 1 end) * (7-#wds)
Declare #totDays int
Set #totDays = #n + #totWeekEnds
Select #n as DaysToAdd,#wds as DaysInWeek,#totWeekEnds as TotalWeekEnds,#totDays as TotalDaysToAdd,Dateadd(dd,#totDays,#Date) as Answer
END
I have also converted SP into Function as you want.
Alter FUNCTION[dbo].[AddBusinessDays](#Date date,#n INT,#wds INT)
RETURNS DATE AS
BEGIN
--Select [dbo].[AddBusinessDays]('9/25/2014',2,5)
Declare #totWeekEnds int
Set #totWeekEnds = (Case When #n > 7 then (#n / 7) else 1 end) * (7-#wds)
Declare #totDays int
Set #totDays = #n + #totWeekEnds
Return Dateadd(dd,#totDays,#Date)
END

convert date to spanish in sql

I am running a report with multiple fields in english. I have 2 date fields at the end of the report, one has to be in English and the other in Spanish. The format the date needs to be in is ie November 1, 2012. I need the spanish to be Novembre 1, 2012. I cannot get the last field of the report to produce in spanish. I am running in sql server 2005.
Maybe is cumbersome, but I don't see how to do it on an easier way.
First, create a function. This function will make use of system view sys.syslanguages to get the correct name of month in spanish. Parameters are a valid date and language (alias on sys.syslanguage view).
CREATE FUNCTION [dbo].[fn_GetMonthName] (
#Date DATETIME,
#Language NVARCHAR(100)
)
RETURNS NVARCHAR(400)
AS
BEGIN
DECLARE #i INT, #m INT,#mlist NVARCHAR(1000)
SET #m = MONTH(#Date)
SET #mlist = (SELECT months FROM sys.syslanguages WHERE ALIAS = #language)
SET #i = 1
WHILE(#i < #m)
BEGIN
SET #mlist = REPLACE(#mlist, SUBSTRING(#mlist,1,CHARINDEX(',',#mlist)) ,'')
SET #i = #i + 1
END
SET #mlist = (CASE CHARINDEX(',',#mlist) WHEN 0 THEN #mlist ELSE SUBSTRING(#mlist,0,CHARINDEX(',',#mlist) ) END )
RETURN #mlist
END
GO
Then call the function anywhere you need to:
SELECT CONVERT(VARCHAR(20), GETDATE(), 100) AS CurrentDate,
dbo.fn_GetMonthName (GETDATE(), 'Spanish') AS [Mes-Month]
Result:
CurrentDate Mes-Month
May 24 2013 12:02AM Mayo
Taken from Get Language specific Month Name from SQL
Take a look at: http://www.sqlservercurry.com/2010/11/change-default-language-for-sql-server.html
You can temporarily set the language to spanish, not sure how feasible this is for you. The other way to do it would be to write your own months function, and maybe pass it a 2nd parameter that then decides what the output would be.
This function will translate the month within a string it's based on the sys.syslanguages table.
i.e. SELECT dbo.fn_tranMonth(2,0,'1 déc. 2014 10:26:14 UTC+00:00')
Results:
1 Dec. 2014 10:26:14 UTC+00:00
CREATE FUNCTION [dbo].[Split] (#sep char(1), #s varchar(8000))
RETURNS table
AS
RETURN (
WITH Pieces(pn, start, stop) AS (
SELECT 1, 1, CHARINDEX(#sep, #s)
UNION ALL
SELECT pn + 1, stop + 1, CHARINDEX(#sep, #s, stop + 1)
FROM Pieces
WHERE stop > 0
)
SELECT
SUBSTRING(#s, start, CASE WHEN stop > 0 THEN stop-start ELSE 512 END) AS s
FROM Pieces
)
GO
CREATE FUNCTION dbo.fn_tranMonth
(
#fromLan INT
,#toLan INT
,#string VARCHAR(MAX)
)
RETURNS
VARCHAR(50)
AS
BEGIN
DECLARE #TTTT AS TABLE(PK INT IDENTITY(1,1)
,fromMonth VARCHAR(50)
,toMonth VARCHAR(50)
)
DECLARE
#fromMonths VARCHAR(200)
,#toMonths VARCHAR(200)
,#fromMonth VARCHAR(20)
,#toMonth VARCHAR(20)
,#rowNum INT=12;
SELECT #fromMonths=shortmonths
FROM SYS.syslanguages
WHERE langid=#fromLan;
SELECT #toMonths=shortmonths
FROM sys.syslanguages
WHERE langid=#toLan;
INSERT #TTTT(fromMonth)
SELECT S
FROM dbo.Split(',',#fromMonths);
DECLARE #TTTT2 AS TABLE(PK INT IDENTITY(1,1)
,toMonth VARCHAR(50)
)
INSERT #TTTT2(toMonth)
SELECT S
FROM dbo.Split(',',#toMonths);
UPDATE #TTTT
SET toMonth=B.toMonth
FROM
#TTTT A
JOIN #TTTT2 B ON A.PK=B.PK;
DECLARE
#loopPos INT=0
,#returnMonth VARCHAR(50);
WHILE #loopPos<#rowNum
BEGIN
SET #loopPos+=1;
SELECT
#fromMonth=fromMonth
,#toMonth=toMonth
FROM #TTTT
WHERE PK=#loopPos;
SET #string=REPLACE(#string,#fromMonth,#toMonth);
END;
RETURN #string;
END
try this:
SELECT CONVERT(VARCHAR(10),GETDATE(), 103)
or
this code, return a VARCHAR(10) with date EN ESPAÑOL, leches.
IDEA (separator used: '-'):
Get format YYYY-MM-DD NVARCHAR(10).
Get format DD-MM-YYYY nvarchar(10)
Use the function
sample:
select dbo.date2txt(GETDATE ())
function to create:
create function [dbo].[AFecha] (
#fecha NVARCHAR(10)
)
returns NVARCHAR(10)
as
begin
Declare #r nvarchar(10)
if substring(#Fecha,3,1) = '-'
set #r = #Fecha
else
set #r = substring(#fecha,9,2)+'-'+substring(#fecha,6,2)+'-'+left(#fecha,4)
Return #r
End
go
create FUNCTION [dbo].[NTSDate](#fecha DateTime)
RETURNS nVarChar(10) AS
BEGIN
DECLARE #salida nVarChar(10)
set #salida = STR(year(#fecha),4,0) + '-' + RIGHT('0' + LTRIM(month(#fecha)),2) + '-' + RIGHT('0' + LTRIM(day(#fecha)),2)
return (#salida)
End
go
ALTER function [dbo].[Date2Txt](#Fecha as datetime) returns nvarchar(10)
as
begin
return dbo.afecha(dbo.NTSDate(#Fecha))
end
go
You can do the following:
FORMAT(GETDATE(), 'MMMM d,yyyy','es-US')
The last parameter for format datetime is an optional culture parameter that does exactly what you need when passed the right culture.
If you needed to localize to a different language you could find the string to do so from the link below.
https://learn.microsoft.com/en-us/windows-hardware/manufacture/desktop/default-input-locales-for-windows-language-packs?view=windows-11

Try/Catch in UDF not possible? How to "TryCast" a varchar to datetime?

how can i convert a varchar parameter into datetime and if cast fails use GetDate() as default?
I've tried to put it in a Try/Catch but apparently that doesn't work in a UDF. It also does not work to simply check if the datetime is null, because it'll throw an exception('The conversion of a char data type to a datetime data type resulted in an out-of-range datetime value'):
CREATE FUNCTION [dbo].[getRZUInfo]
(
#IMEI varchar(20),
#StrDeliveryDate varchar(20)
)
RETURNS VARCHAR(8000)
AS
BEGIN
DECLARE #Info VARCHAR(8000)
DECLARE #DeliveryDate datetime;
SET #DeliveryDate = Convert(datetime,#StrDeliveryDate,102);
IF #DeliveryDate IS NULL
SET #DeliveryDate=GetDate();
SELECT #Info = COALESCE(#Info + '|', '') + 'TAT_B2B: ' + Convert(varchar,tabData.TAT_B2B) + ', AC' + Convert(varchar,tabData.fimaxActionCode) + ', Diff: ' + Convert(varchar,DateDiff(day,tabData.Received_date,#DeliveryDate))
FROM tabData
WHERE (SSN_Number = #IMEI) AND (Received_Date >= DATEADD(month, -3, #DeliveryDate))
ORDER BY SSN_Number,Received_Date DESC
return #Info
END
SET #DeliveryDate = CASE
WHEN Isdate(#StrDeliveryDate) = 1 THEN
CONVERT(DATETIME, #StrDeliveryDate, 102)
ELSE Getdate()
END
A common flaw with IsDate is that it is unable to take in a date format specifier that CAST/CONVERT can.
See this:
set dateformat dmy
declare #StrDeliveryDate varchar(20) set #StrDeliveryDate = '2011.12.13'
select CASE
WHEN Isdate(#StrDeliveryDate) = 1 THEN
CONVERT(DATETIME, #StrDeliveryDate, 102)
ELSE Getdate()
END
output: 2011-03-21 22:19:54.683
This is a better function for testing 102-formatted dates specifically. Actually 102 is much easier, this is flexible enough to pick up yy/yyyy, m/mm, d/dd.
create function dbo.Is102Date(#any varchar(50))
-- 102 = yyyy.mm.dd
returns bit as begin
set #any = ltrim(rtrim(#any))
declare #theyear varchar(10)
set #TheYear = case
when #any like '%[^0-9.]%' then null
when #any like '[0-9][0-9].%[0-9].%[0-9]' then
case when LEFT(#any,2) >=50
then '19'+LEFT(#any,2)
else '20'+LEFT(#any,2)
end
when #any like '[0-9][0-9][0-9][0-9].%[0-9].%[0-9]' then
LEFT(#any,4)
end
declare #YYYYMMDDToTest varchar(50)
set #YYYYMMDDToTest = case
when #TheYear is not null then
#TheYear
+ -- month
SUBSTRING(#any, charindex('.',#any) +1,
charindex('.',#any,charindex('.',#any)+1)-
charindex('.',#any)-1)
+ -- day
right(#any,charindex('.',reverse(#any))-1)
end
return ISDate(#YYYYMMDDToTest)
end
GO
Use it instead of ISDATE to test for 102-formatted dates in varchar.
Just checking when Null
IF #StrDeliveryDate IS NULL
SET #DeliveryDate = GetDate();
ELSE
SET #DeliveryDate = Convert(datetime, #StrDeliveryDate, 102);