ISDATE Function for different date formats in TSQL - sql-server

I need to convert VARCHAR values into DATETIME in multiple columns of a view for sorting and formatting (displaying in locale format) purposes in another application on SQL Server 2008.
There are currently two problems.
The input format of the VARCHAR values differ (but consistent at
column level)
Also there may be faulty values (e.g.: 20..05.2015)
Unfortunately the TRY_CONVERT function is available just for SQL Server 2012 and later.
ISDATE does not work because the view contains different date formats and I can neither set the language inside user defined functions nor in views, which would cause ISDATE to work with german date formats for example.
Is there any easier solution for my problem?
My first thought was to write a function like
FUNCTION TryConvertStringAsDatetime ( #value VARCHAR(MAX),
#format INT
)
that uses the format numbers of the CONVERT function, but checking for every possible format manually scares me a bit.
Example: TryConvertStringAsDatetime('20.05.2015', 104) (with some pseudocode)
SET #day = character 1 and 2
SET #month = character 4 and 5
SET #year = character 7, 8, 9 and 10
SET #dateODBCFormat = #year - #month - #day (concatenated with hyphen and not subtracted :)
IF ISDATE(#dateODBCFormat ) = 1
RETURN CONVERT(DATETIME, #dateODBCFormat, 120)
ELSE
RETURN CONVERT(DATETIME, 0) (does the job)

This is the function I now came up with:
CREATE
FUNCTION TryConvertStringAsDatetime ( #value VARCHAR(MAX),
#format INT
)
RETURNS DATETIME
AS
/*
Tries to convert a given VARCHAR value to DATETIME.
Returns NULL if no value was specified or the value is not in the correct format.
*/
BEGIN
DECLARE #length INT = LEN(#value)
IF #length IS NULL OR #length < 10 OR #length > 23
RETURN NULL
DECLARE #day VARCHAR(2),
#month VARCHAR(2),
#year VARCHAR(4),
#time VARCHAR(9)
IF #format = 104 --dd.mm.yyyy hh:mi:ss(24h)
BEGIN
SET #day = SUBSTRING(#value, 1, 2)
SET #month = SUBSTRING(#value, 4, 2)
SET #year = SUBSTRING(#value, 7, 4)
END
ELSE IF #format IN (120, 121) --yyyy-mm-dd hh:mi:ss(24h)
BEGIN
SET #year = SUBSTRING(#value, 1, 4)
SET #month = SUBSTRING(#value, 6, 2)
SET #day = SUBSTRING(#value, 9, 2)
END
ELSE
RETURN NULL -- currently only german and ODBC supported
IF #length > 11
SET #time = SUBSTRING(#value, 12, #length - 11)
SET #value = #year + '-' + #month + '-' + #day + ISNULL(' ' + #time, '')
IF ISDATE(#value) = 1
RETURN CONVERT(DATETIME, #value, 121)
RETURN NULL
END

I would probably go with something like this:
CREATE FUNCTION TryConvertToDate
(
#InputString varchar(20)
)
RETURNS Datetime
BEGIN
DECLARE #DateTime datetime = NULL
SET #DateTime =
CASE
WHEN LEN(#InputString) = 10 AND PATINDEX('[0-9][0-9].[0-9][0-9].[0-9][0-9][0-9][0-9]', #InputString)=1 THEN
CONVERT(DateTime, #InputString, 104) -- German
WHEN LEN(#InputString) = 10 AND PATINDEX('[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]', #InputString)=1 THEN
CONVERT(DateTime, #InputString, 120) -- ODBC
ELSE
NULL -- unsuported format
END
RETURN #DateTime
END
Note: Testing for length and using patindex ensures only general format, so you need the call this function inside a try block in case the days and months are inverted and will cause a conversion error.
On the other hand, adding supported formats to this function is very easy - all you have to do is add a when clause with the correct patindex and length and the correct convert style.
Another option is to ensure the string can actually be converted to date.
This will make your function more complicated and thus harder to write, but will be easier to work with as it will reduce to minimum the chance of raising a conversion error:
CREATE FUNCTION TryConvertToDate
(
#InputString varchar(20)
)
RETURNS Datetime
BEGIN
DECLARE #DateValue date, #Days int, #Months int, #Years int
IF LEN(#DateString) = 10 AND PATINDEX('[0-9][0-9].[0-9][0-9].[0-9][0-9][0-9][0-9]', #InputString)=1 -- German format
BEGIN
SELECT #Days = CAST(LEFT(#InputString, 2) As int),
#Months = CAST(SUBSTRING(#InputString, 4, 2) as int),
#Years = CAST(RIGHT(#InputString, 4) as int)
-- NOTE: you will need to add a condition for leap years
IF (#Days < 31 AND #Months IN(4,6,9,12)) OR (#Days < 30 AND #Months = 2)
SET #DateValue = convert(date, #InputString, 104)
END
IF LEN(#InputString) = 10 AND PATINDEX('[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]', #InputString)=1 -- ODBC format
BEGIN
SELECT #Days = CAST(RIGHT(#InputString, 2) As int),
#Months = CAST(SUBSTRING(#InputString, 6, 2) as int),
#Years = CAST(LEFT(#InputString, 4) as int)
-- NOTE: you will need to add a condition for leap years
IF (#Days < 31 AND #Months IN(4,6,9,12)) OR (#Days < 30 AND #Months = 2)
SET #DateValue = convert(date, #InputString, 120)
END
RETURN #DateValue
END

You might have better luck in terms of both speed and functionality doing this in SQLCLR (as noted by #Tab and #Zohar in various comments).
.NET / C# code:
using System;
using System.Data.SqlTypes;
using System.Globalization;
using Microsoft.SqlServer.Server;
public class TryConvertStuff
{
[SqlFunction(IsDeterministic = true, IsPrecise = true)]
public static SqlDateTime TryConvertDateTime([SqlFacet(MaxSize = 50)] SqlString
StringDate, [SqlFacet(MaxSize = 10)] SqlString Culture)
{
CultureInfo _Culture = CultureInfo.CurrentCulture;
if (!Culture.IsNull && Culture.Value.Trim() != String.Empty)
{
_Culture = CultureInfo.GetCultureInfo(Culture.Value);
}
DateTime _RealDate;
if (DateTime.TryParse(StringDate.Value, _Culture,
DateTimeStyles.None, out _RealDate))
{
return _RealDate;
};
return SqlDateTime.Null;
}
}
Tests:
SELECT dbo.TryConvertDateTime(N'2019-04-20', N'en'); -- 2019-04-20 00:00:00.000
SELECT dbo.TryConvertDateTime(N'2019-04-20f', N'en'); -- NULL
SELECT dbo.TryConvertDateTime(N'2019.04.20', N'en'); -- 2019-04-20 00:00:00.000
SELECT dbo.TryConvertDateTime(N'20.04.2019', N'en'); -- NULL
SELECT dbo.TryConvertDateTime(N'20.04.2019', N'de'); -- 2019-04-20 00:00:00.000
SELECT dbo.TryConvertDateTime(N'20.04.2019', NULL); -- NULL

Related

"Conversion failed when converting date and/or time from character string." error

I have the following query:
DECLARE #Month int
DECLARE #Year int
set #Month = 2
set #Year = 2004
Declare #MonthStartDate datetime
declare #MonthEndDate datetime
set #MonthStartDate = 'select DATEADD(month,#Month-1,DATEADD(year,#Year-1900,0))'
set #MonthEndDate = 'select DATEADD(day,-1,DATEADD(month,#Month,DATEADD(year,#Year-1900,0)))'
return #MonthStartDate , #MonthEndDate
But it returns:
"Conversion failed when converting date and/or time from character string."
What's wrong here?
Alternatively, you can also use as follow..
select #MonthStartDate = DATEADD(month,#Month-1,DATEADD(year,#Year-1900,0))
select #MonthEndDate = DATEADD(day,-1,DATEADD(month,#Month,DATEADD(year,#Year-1900,0)))
You should use DateTime expression instead of string literal. Just remove quotes:
DECLARE #Month int
DECLARE #Year int
set #Month = 2
set #Year = 2004
Declare #MonthStartDate datetime
declare #MonthEndDate datetime
set #MonthStartDate = DATEADD(month,#Month-1,DATEADD(year,#Year-1900,0))
set #MonthEndDate = DATEADD(day,-1,DATEADD(month,#Month,DATEADD(year,#Year-1900,0)))
Looking at your Query (Since you don't have enough Description on the Question ) What I understood is that you are trying to get the First and Last day of a Given month. If you are using a SQL Server Version 2012 or Above, then you have an Inbuild Function called EOMONTH() which can be used to calculate the End of any given month. Otherwise, you may try the below method on any Version on SQL Server
Declare #MonthStartDate datetime,
#MonthEndDate datetime,
#Year int,
#Month int --It's Better to Declare all the variables in the same space for easy handling
SELECT
#Month = 2,
#Year = 2004 -- Common Assignments can be done together
;WITH MNT
AS
(
SELECT
MonthStartDate = CAST(#Month AS VARCHAR(20))+'/01/'+CAST(#Year AS VARCHAR(20)) -- Hardcoded Day as 1 since Month Start always at 1
)
SELECT
#MonthStartDate = MonthStartDate,
#MonthEndDate = DATEADD(DAY,-1,DATEADD(MONTH,1,MonthStartDate))
FROM MNT

Convert Epoch to DateTime SQL Server (Exceeds Year 2038)

How to convert Epoch to DateTime SQL Server if epoch exceeds the year 2038?
Answer in Convert Epoch to DateTime SQL Server will not work.
Example:
SELECT DATEADD(ss, 2713795200000 / 1000, '19700101')
Thu, 30 Dec 2055 16:00:00 GMT
DATEADD function assumes an INT as an increment to your date, to bypass the limitation of INT you can either reduce the precision of your epoch, or do a slightly complex code to retain the precision of your epoch.
This reduces the precision to minutes:
SELECT DATEADD(MINUTE,#YourEpoch/60/1000, '1/1/1970')
This one splits your epoch to days and milliseconds and then combines them in a datetime
CREATE FUNCTION [dbo].[fn_EpochToDatetime] (#Epoch BIGINT)
RETURNS DATETIME
AS
BEGIN
DECLARE #Days AS INT, #MilliSeconds AS INT
SET #Days = #Epoch / (1000*60*60*24)
SET #MilliSeconds = #Epoch % (1000*60*60*24)
RETURN (SELECT DATEADD(MILLISECOND, #MilliSeconds, DATEADD(DAY, #Days, '1/1/1970')))
END;
However, I'm not quite sure why the 2nd solution is not as precise as I expect it to be.
Building on the response above, the solution provided works but does not protect from trying to convert to a date that is out of bounds for SQL server.
create function dbo.unixTimestampConversion (
#unixTime bigInt
)
returns dateTime2(7)
as
begin
declare
#output dateTime2(7)
, #days int
, #ms int
, #x int = (1000 * 60 * 60 * 24)
;
set #days = #unixTime / #x
;
set #ms = #unixTime % #x
;
if (#unixTime < 32503593600000 and #unixTime > -2208988800000)
begin
set #output = dateAdd (millisecond, #ms, dateAdd (day, #days, '1/1/1970'))
;
end
;
else if (#unixTime <= -2208988800000)
begin
set #output = '1/1/1900'
;
end
;
else if (#unixTime >= 32503593600000)
begin
set #output = '12/31/2999'
;
end
;
return #output
;
end
;
You can assign the epoch time to your datetime directly (I tried this on SQL Server 15.0). Although it considers the number as the number of days since 1900-1-1 00:00:00 so you have to add 2208988800 (the number of seconds in 70 years) and then divide by 86400(number of seconds in a day).
DECLARE #time DATETIME = (2208988800.0 + [your epoch time in seconds])/86400;
However, it seems to be 0.007s or 0.003s behind the given epoch. Also, I'm not sure if this is faster than the DATEADD() function.
create a function to convert epoch to datetime and use them in your query like
below
create FUNCTION [dbo].[from_unixtime] (#Datetime BIGINT)
RETURNS DATETIME
AS
BEGIN
DECLARE #LocalTimeOffset BIGINT
,#AdjustedLocalDatetime BIGINT;
SET #LocalTimeOffset = DATEDIFF(second,GETDATE(),GETUTCDATE())
SET #AdjustedLocalDatetime = #Datetime - #LocalTimeOffset
RETURN (SELECT DATEADD(second,#AdjustedLocalDatetime, CAST('1970-01-01 00:00:00' AS datetime)))
END;
and then use this function in your query

VARCHAR comparison in the FUNCTION

I have the following function:
CREATE FUNCTION ISRELKDVG(#WUNSCH_LIEFERTERMIN VARCHAR) RETURNS int AS
BEGIN
DECLARE #sTerminNone VARCHAR(8)
DECLARE #sTerminFrom VARCHAR(8)
SET #sTerminNone = '00000000'
SET #sTerminFrom = CONVERT(VARCHAR(8), GETDATE() - 100, 112) -- 100 days in the past
IF (#WUNSCH_LIEFERTERMIN <> #sTerminNone AND #WUNSCH_LIEFERTERMIN >= #sTerminFrom)
RETURN 1
RETURN 0
END
The variable given as parameter to the function is a date and has a form "YYYYMMDD". The problem is that the function never returns 1.
I have found the link which says that comparing varchar's with unspecified length may lead to errors. Does it also refer to varchar's passed to the functions?
Do not rely on implicit conversions, using unknown dateformats. Convert to dates or datetimes, specifying a dateformat, and compare those values.
Specify the size of function parameter, e.g. varchar(8). Otherwise it is 1 character long by default:
declare #c as varchar
set #c = '20130101'
select #c -- returns 2
Try this out:
CREATE FUNCTION ISRELKDVG(#WUNSCH_LIEFERTERMIN VARCHAR(8)) RETURNS int AS
BEGIN
DECLARE #sTerminNone VARCHAR(8)
DECLARE #sTerminFrom DATE
SET #sTerminNone = '00000000'
IF (#WUNSCH_LIEFERTERMIN = #sTerminNone)
RETURN 0
SET #sTerminFrom = DATEADD(day,-100,GETDATE()) -- 100 days in the past
IF (CAST(#WUNSCH_LIEFERTERMIN AS DATE) >= #sTerminFrom)
RETURN 1
RETURN 0
END
select dbo.ISRELKDVG('00000000') --0
select dbo.ISRELKDVG('20030901') --0
select dbo.ISRELKDVG('20130901') --1
select dbo.ISRELKDVG('20130730') --1
Yes that is the main problem.
MSDN - Remarks
When n is not specified in a data definition or variable declaration
statement, the default length is 1. When n is not specified when using
the CAST and CONVERT functions, the default length is 30.
But (even in case of Cast/Convert), you should always define the length to avoid such troubles.
Change
#WUNSCH_LIEFERTERMIN VARCHAR --<--Length is 1 by default
To
#WUNSCH_LIEFERTERMIN VARCHAR(8) --Or length as appropriate
Secondly, you should be comparing Dates not Strings.
DECLARE #sTerminNone DATE = CONVERT(DATETIME, 0) --<--DATE (Assign correct date)
DECLARE #sTerminFrom DATE = GETDATE() - 100
Finally,
IF (CONVERT(DATE, #WUNSCH_LIEFERTERMIN, 112) <> #sTerminNone AND --Not sure if you need this
CONVERT(DATE, #WUNSCH_LIEFERTERMIN, 112) >= #sTerminFrom)
RETURN 1
ELSE
RETURN 0
Ideally, you should pass/define a DateType parameter to the function.

How to convert datetime in Persian in SQL Server

I want to convert my datetime into Persian datetime in SQL Server. My datetime is in MM/DD/YYYY format. Is there any function in SQL Server to do this as when I want hijri datetime I use this
SELECT CONVERT(VARCHAR(40), GETDATE(), 131) -- Output is 14/08/1432 5:02:01:197PM
I'm using SQL Server 2008.
Best method in SQL Server 2016
Example:
SELECT FORMAT(GETDATE(), 'yyyy/MM/dd-HH:mm:ss', 'fa')
Answer:
1398/10/08-05:37:59
I know it is too late for answering this question, but I've submitted the function that I'm using for a long time without any bug, all of other methods which I've ever seen have problem with intercalary years:
CREATE FUNCTION [CalculatePersianDate] ( #intDate DATETIME )
RETURNS NVARCHAR(max)
BEGIN
DECLARE #shYear AS INT ,#shMonth AS INT ,#shDay AS INT ,#intYY AS INT ,#intMM AS INT ,#intDD AS INT ,#Kabiseh1 AS INT ,#Kabiseh2 AS INT ,#d1 AS INT ,#m1 AS INT, #shMaah AS NVARCHAR(max),#shRooz AS NVARCHAR(max),#DayCnt AS INT
DECLARE #DayDate AS NVARCHAR(max)
SET #intYY = DATEPART(yyyy, #intDate)
IF #intYY < 1000 SET #intYY = #intYY + 2000
SET #intMM = MONTH(#intDate)
SET #intDD = DAY(#intDate)
SET #shYear = #intYY - 622
SET #DayCnt = 5
IF ( ( #intYY - 1992 ) % 4 = 0) SET #Kabiseh1 = 0 ELSE SET #Kabiseh1 = 1
IF ( ( #shYear - 1371 ) % 4 = 0) SET #Kabiseh2 = 0 ELSE SET #Kabiseh2 = 1
SET #m1 = 1
SET #d1 = 1
SET #shMonth = 10
SET #shDay = 11
IF ( ( #intYY - 1993 ) % 4 = 0 ) SET #shDay = 12
WHILE ( #m1 != #intMM ) OR ( #d1 != #intDD )
BEGIN
SET #d1 = #d1 + 1
SET #DayCnt = #DayCnt + 1
IF
(#d1 = 32 AND (#m1 = 1 OR #m1 = 3 OR #m1 = 5 OR #m1 = 7 OR #m1 = 8 OR #m1 = 10 OR #m1 = 12))
OR
(#d1 = 31 AND (#m1 = 4 OR #m1 = 6 OR #m1 = 9 OR #m1 = 11))
OR
(#d1 = 30 AND #m1 = 2 AND #Kabiseh1 = 1)
OR
(#d1 = 29 AND #m1 = 2 AND #Kabiseh1 = 0)
BEGIN
SET #m1 = #m1 + 1
SET #d1 = 1
END
IF #m1 > 12
BEGIN
SET #intYY = #intYY + 1
SET #m1 = 1
END
IF #DayCnt > 7 SET #DayCnt = 1
SET #shDay = #shDay + 1
IF
(#shDay = 32 AND #shMonth < 7)
OR
(#shDay = 31 AND #shMonth > 6 AND #shMonth < 12)
OR
(#shDay = 31 AND #shMonth = 12 AND #Kabiseh2 = 1)
OR
(#shDay = 30 AND #shMonth = 12 AND #Kabiseh2 = 0)
BEGIN
SET #shMonth = #shMonth + 1
SET #shDay = 1
END
IF #shMonth > 12
BEGIN
SET #shYear = #shYear + 1
SET #shMonth = 1
END
END
IF #shMonth=1 SET #shMaah=N'فروردین'
IF #shMonth=2 SET #shMaah=N'اردیبهشت'
IF #shMonth=3 SET #shMaah=N'خرداد'
IF #shMonth=4 SET #shMaah=N'تیر'
IF #shMonth=5 SET #shMaah=N'مرداد'
IF #shMonth=6 SET #shMaah=N'شهریور'
IF #shMonth=7 SET #shMaah=N'مهر'
IF #shMonth=8 SET #shMaah=N'آبان'
IF #shMonth=9 SET #shMaah=N'آذر'
IF #shMonth=10 SET #shMaah=N'دی'
IF #shMonth=11 SET #shMaah=N'بهمن'
IF #shMonth=12 SET #shMaah=N'اسفند'
IF #DayCnt=1 SET #shRooz=N'شنبه'
IF #DayCnt=2 SET #shRooz=N'یکشنبه'
IF #DayCnt=3 SET #shRooz=N'دوشنبه'
IF #DayCnt=4 SET #shRooz=N'سه‌شنبه'
IF #DayCnt=5 SET #shRooz=N'چهارشنبه'
IF #DayCnt=6 SET #shRooz=N'پنجشنبه'
IF #DayCnt=7 SET #shRooz=N'جمعه'
--SET #DayDate = #shRooz + " " + LTRIM(STR(#shDay,2)) + " " + #shMaah + " " + STR(#shYear,4)
--پنجشنبه 17 اردیبهشت 1394
/*
SET #DayDate = LTRIM(STR(#shDay,2)) + " " + #shMaah + " " + STR(#shYear,4)
--17 اردیبهشت 1394
SET #DayDate = STR(#shYear,4) + "/"+LTRIM(STR(#shMonth,2)) + "/" + LTRIM(STR(#shDay,2))
--1394/2/17
--1394/02/17
*/
SET #DayDate = REPLACE(RIGHT(STR(#shYear, 4), 4), ' ', '0') + '/'+ REPLACE(STR(#shMonth, 2), ' ', '0') + '/' + REPLACE(( STR(#shDay,2) ), ' ', '0')
RETURN #DayDate
END
It is really easy to customize the result of the function.
adopted from: this page
I know it is too late but maybe useful for others like me having this trouble.
You should write a SQL Function for this conversion like this:
Converting Gregorian to Persian Date
and then use it like this:
SELECT dbo.[UDF_Gregorian_To_Persian]('2013-08-24')
Try this:
select format(getdate() , 'yyyy/MM/dd', 'fa-ir')
You can use the following code to convert the date. This practical and important method has been added to the 2012 version of SQL and can be used.
SELECT FORMAT(GETDATE(), 'yyyy/MM/dd-HH:mm:ss', 'fa')
Result: 1400/02/08-05:08:51
SELECT cast( FORMAT(GETDATE(), 'yyyyMMdd', 'fa') as int)
Result: 14000208
And you can use Format as Follow to get Higri Date:
SELECT FORMAT(GETDATE(), N'yyyy/MM/dd', N'ar')
Result: 1443/06/19
Out of the box, no.
You'd have to write your own UDF, however there is one on CodePlex and another
I believe the best available solution is to use SQLCLR-Jalali-Date-Utility. It has a straightforward installation guide and easy to use functions. Moreover, you can define the format of the converted date without any limitation. in fact, you can use the standard time formatting to define the shape of converted dates.
There are several examples provided inside the GitHub page.
select dbo.GregorianToJalali(GETDATE(),'yyyy/MM/dd hh:mm:ss tt') -- returns 1395/07/01 03:04:33 ب ظ
this is persian Calendar function in SQL 2016+
ALTER FUNCTION [dbo].[PCalendar](#date datetime)
RETURNS #ret TABLE (
ly int,
y int,
m int,
mname nvarchar(15),
d int,
dy int,
dw int,
dname nvarchar(10),
hh int,
mm int,
ss int,
mss int,
dt datetime,
t nvarchar(3))
as
BEGIN
DECLARE #format varchar(19);
set #format = 'yyyy/MM/dd HH:mm:ss';
DECLARE #y int;
DECLARE #m int;
DECLARE #d int;
DECLARE #dy int;
DECLARE #dw int;
DECLARE #hh int;
DECLARE #mm int;
DECLARE #ss int;
DECLARE #ms int;
DECLARE #ldt varchar(8);
set #y = DATEPART(YEAR, FORMAT(#date, #format, 'fa')) ;
set #m = DATEPART(MONTH, FORMAT(#date, #format, 'fa'));
set #d = DATEPART(DAY, FORMAT(#date, #format, 'fa')) ;
set #dy = DATEPART(DAYOFYEAR, FORMAT(#date, #format, 'fa'));
set #dw = DATEPART(WEEKDAY, FORMAT(#date,#format, 'fa'));
set #hh = DATEPART(HOUR, #date) ;
set #mm = DATEPART(MINUTE, #date) ;
set #ss = DATEPART(SECOND, #date);
set #ms = DATEPART(MILLISECOND, #date);
set #ldt =DATEPART(year, FORMAT(#date, #format, 'en'));
DECLARE #_w nvarchar(10);
set #_w = CASE
WHEN #dw=1 THEN N'جمعه'
WHEN #dw=2 THEN N'شنبه'
WHEN #dw=3 THEN N'یکشنبه'
WHEN #dw=4 THEN N'دوشنبه'
WHEN #dw=5 THEN N'سه شنبه'
WHEN #dw=6 THEN N'چهارشنبه'
ELSE N'پنج شنبه'
END;
DECLARE #_m nvarchar(15);
set #_m = CASE
WHEN #m=1 THEN N'فروردین'
WHEN #m=2 THEN N'اردیبهشت'
WHEN #m=3 THEN N'خرداد'
WHEN #m=4 THEN N'تیر'
WHEN #m=5 THEN N'مرداد'
WHEN #m=6 THEN N'شهریور'
WHEN #m=7 THEN N'مهر'
WHEN #m=8 THEN N'آبان'
WHEN #m=9 THEN N'آذر'
WHEN #m=10 THEN N'دی'
WHEN #m=11 THEN N'بهمن'
ELSE N'اسفند'
END;
set #_m = #_m+N' ماه';
INSERT INTO #ret
SELECT
IIF(#y % 33 in (1,5,9,13,17,22,26,30) , 1 , 0) as ly,
#y as y,
#m as m,
#_m as mname,
#d as d,
#dy as dy,
#dw as dw,
#_w as dname,
#hh as hh,
#mm as mm,
#ss as ss,
#ms as mss,
#date as dt,
IIF(#hh > 12 , N'ب.ظ','ق.ظ') as t;
RETURN;
END
You can convert it to shamsi using this functions.
The first function.
CREATE FUNCTION [dbo].[ToPersianDate](#dt [datetime])
RETURNS [nvarchar](10) WITH EXECUTE AS CALLER
AS
EXTERNAL NAME [PersianSQLFunctions].[UserDefinedFunctions].[ToPersianDate]
GO
second function.
SET ANSI_NULLS OFF
GO
SET QUOTED_IDENTIFIER OFF
GO
CREATE FUNCTION [dbo].[ToPersianDate](#dt [datetime])
RETURNS [nvarchar](10) WITH EXECUTE AS CALLER
AS
EXTERNAL NAME [PersianSQLFunctions].[UserDefinedFunctions].[ToPersianDate]
GO
third function.
CREATE FUNCTION [dbo].[fnToShamsiDate]
(
#d DateTime
)
RETURNS NVARCHAR(10)
AS
BEGIN
-- Declare the return variable here
DECLARE #RV NVARCHAR(10)
IF (#d) IS NULL RETURN NULL;
ELSE SELECT #RV = DBO.ToPersianDate(#D);
-- Return the result of the function
RETURN #RV
END
GO
And also you can find shamsi months from this function
create function [dbo].[fnGetShamsiMonth]
(
#GregorianDate date
)
returns nvarchar(2)
as
begin
declare #ShamsiMonth nvarchar(2), #ShamsiDate nvarchar(10);
set #ShamsiDate = confsys.dbo.fnToShamsiDate(#GregorianDate);
set #ShamsiMonth = SUBSTRING(#ShamsiDate,6,2);
return #ShamsiMonth
end
GO
examples
select confsys.dbo.fnToShamsiDate(getdate())
result is 1397/12/29
get shamsi months
select confsys.dbo.fnGetShamsiMonth(GETDATE());
Function : Full Convert Persian (Shamsi / Jalali ) String to Gregorian (miladi) Datetime in sql server :
> create or ALTER Function [dbo].[Func_ShamsiToMiladi] (#Date
> Varchar(23) ) RETURNS DateTime BEGIN
> -- ==============================================================
> -- SELECT [dbo].[Func_ShamsiToMiladi] ('1356-09-20 05:35:00.000')
> --
> -- Output : '1977-12-11 02:05:00.000'
> -- ==============================================================
> -- BY: Shahrokh Vazifedan Hobabname#Gmail.COM DECLARE #PersianDate Varchar(23) SET #PersianDate = #Date
>
> DECLARE #Year INT = SUBSTRING(#PersianDate, 1, 4)
> DECLARE #Month INT = SUBSTRING(#PersianDate, 6, 2)
> DECLARE #Day INT = SUBSTRING(#PersianDate, 9, 2)
> DECLARE #DiffYear INT = #Year - 1350
> DECLARE #Time varchar(13) = SUBSTRING(#PersianDate, 11, 13)
>
>
> DECLARE #Days INT = #DiffYear * 365.24 +
> CASE WHEN #Month < 7 THEN (#Month - 1) * 31
> ELSE 186 + (#Month - 7) * 30 END + #Day
>
> DECLARE #StartDate DATETIME = '03/21/1971'
> DECLARE #ResultDate DATE = #StartDate + #Days
>
> DECLARE #TempDate varchar(23) = Convert( Nvarchar(10) , #ResultDate ,120) + #Time DECLARE #OffSET_First_half_in_Year
> INT; SET #OffSET_First_half_in_Year = iif( Substring(Convert(
> Nvarchar(50), #TempDate,120) ,6,16) Between '03-20 20:30' and '09-22
> 20:30' , -60 ,0)
> RETURN dateadd(MINUTE, #OffSET_First_half_in_Year + (-1)*datediff(MINUTE, getutcdate(), getdate()),#TempDate ) END
Created By : Shahrokh Vazifedan -Sari # Iran :)
Email: HobabName#Gmail.com
CREATE FUNCTION [dbo].[MITSH] (#MDate DateTime)
RETURNS Varchar(10)
AS
BEGIN
DECLARE #SYear as Integer
DECLARE #SMonth as Integer
DECLARE #my_mah varchar(2)
declare #my_day varchar(2)
DECLARE #SDay as Integer
DECLARE #AllDays as float
DECLARE #ShiftDays as float
DECLARE #OneYear as float
DECLARE #LeftDays as float
DECLARE #YearDay as Integer
DECLARE #Farsi_Date as Varchar(100)
SET #MDate=#MDate-CONVERT(char,#MDate,114)
SET #ShiftDays=466699 +2
SET #OneYear= 365.24199
SET #SYear = 0
SET #SMonth = 0
SET #SDay = 0
SET #AllDays = CAst(#Mdate as Real)
SET #AllDays = #AllDays + #ShiftDays
SET #SYear = (#AllDays / #OneYear) --trunc
SET #LeftDays = #AllDays - #SYear * #OneYear
if (#LeftDays < 0.5)
begin
SET #SYear=#SYear+1
SET #LeftDays = #AllDays - #SYear * #OneYear
end;
SET #YearDay = #LeftDays --trunc
if (#LeftDays - #YearDay) >= 0.5
SET #YearDay=#YearDay+1
if ((#YearDay / 31) > 6 )
begin
SET #SMonth = 6
SET #YearDay=#YearDay-(6 * 31)
SET #SMonth= #SMonth+( #YearDay / 30)
if (#YearDay % 30) <> 0
SET #SMonth=#SMonth+1
SET #YearDay=#YearDay-((#SMonth - 7) * 30)
end
else
begin
SET #SMonth = #YearDay / 31
if (#YearDay % 31) <> 0
SET #SMonth=#SMonth+1
SET #YearDay=#YearDay-((#SMonth - 1) * 31)
end
SET #SDay = #YearDay
SET #SYear=#SYear+1
if #SMonth <10 begin
set #my_mah='0'+str(#SMonth,1)
end else begin
set #my_mah = str(#SMonth,2)
end
if #sday <10 begin
set #my_day='0'+str(#Sday,1)
end else begin
set #my_day = str(#Sday,2)
end
SET #Farsi_Date = CAST (#SYear as VarChar(10)) + '/' + #my_mah + '/' + #my_day
Return #Farsi_Date
END
AN FOR EXEC FUNCTION
SELECT DBO.MITSH(GETDATE())
for example date is 2020-09-25
resualt =>>>> 1399/07/04
Complete Function For Shamsi date for SQL 2008 and 2008 R2 and below versions:
CREATE FUNCTION [dbo].[PersToJul](#iYear int,#iMonth int,#iDay int)
RETURNS bigint
AS
Begin
Declare #PERSIAN_EPOCH as int
Declare #epbase as bigint
Declare #epyear as bigint
Declare #mdays as bigint
Declare #Jofst as Numeric(18,2)
Declare #jdn bigint
Set #PERSIAN_EPOCH=1948321
Set #Jofst=2415020.5
If #iYear>=0
Begin
Set #epbase=#iyear-474
End
Else
Begin
Set #epbase = #iYear - 473
End
set #epyear=474 + (#epbase%2820)
If #iMonth<=7
Begin
Set #mdays=(Convert(bigint,(#iMonth) - 1) * 31)
End
Else
Begin
Set #mdays=(Convert(bigint,(#iMonth) - 1) * 30+6)
End
Set #jdn =Convert(int,#iday) + #mdays+ Cast(((#epyear * 682) - 110) / 2816 as int) + (#epyear - 1) * 365 + Cast(#epbase / 2820 as int) * 1029983 + (#PERSIAN_EPOCH - 1)
RETURN #jdn
End
--------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------
GO
CREATE FUNCTION [dbo].[GrToPers] (#date datetime)
Returns nvarchar(50)
AS
Begin
Declare #depoch as bigint
Declare #cycle as bigint
Declare #cyear as bigint
Declare #ycycle as bigint
Declare #aux1 as bigint
Declare #aux2 as bigint
Declare #yday as bigint
Declare #Jofst as Numeric(18,2)
Declare #jdn bigint
Declare #iYear As Integer
Declare #iMonth As Integer
Declare #iDay As Integer
Set #Jofst=2415020.5
Set #jdn=Round(Cast(#date as int)+ #Jofst,0)
Set #depoch = #jdn - [dbo].[PersToJul](475, 1, 1)
Set #cycle = Cast(#depoch / 1029983 as int)
Set #cyear = #depoch%1029983
If #cyear = 1029982
Begin
Set #ycycle = 2820
End
Else
Begin
Set #aux1 = Cast(#cyear / 366 as int)
Set #aux2 = #cyear%366
Set #ycycle = Cast(((2134 * #aux1) + (2816 * #aux2) + 2815) / 1028522 as int) + #aux1 + 1
End
Set #iYear = #ycycle + (2820 * #cycle) + 474
If #iYear <= 0
Begin
Set #iYear = #iYear - 1
End
Set #yday = (#jdn - [dbo].[PersToJul](#iYear, 1, 1)) + 1
If #yday <= 186
Begin
Set #iMonth = CEILING(Convert(Numeric(18,4),#yday) / 31)
End
Else
Begin
Set #iMonth = CEILING((Convert(Numeric(18,4),#yday) - 6) / 30)
End
Set #iDay = (#jdn - [dbo].[PersToJul](#iYear, #iMonth, 1)) + 1
Return Convert(nvarchar(50),#iDay) + '-' + Convert(nvarchar(50),#iMonth) +'-' + Convert(nvarchar(50),#iYear)
End
--------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------
GO
CREATE FUNCTION [dbo].[JulToGre] (#jdn bigint)
Returns nvarchar(11)
AS
Begin
Declare #Jofst as Numeric(18,2)
Set #Jofst=2415020.5
Return Convert(nvarchar(11),Convert(datetime,(#jdn- #Jofst),113),110)
End
--------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------
GO
CREATE FUNCTION [dbo].[COnvertOToN](#StrMyNum NVARCHAR(2))
RETURNS NVARCHAR(2)
AS
BEGIN
DECLARE #MyNunInStr NVARCHAR(10)
SET #MyNunInStr = #StrMyNum
IF LEN(#MyNunInStr) < 2
BEGIN
SET #MyNunInStr = '0' + #MyNunInStr
END
RETURN #MyNunInStr
END
--------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------
GO
-- Changing Date Format
CREATE FUNCTION [dbo].[RevDateShm](#StrDateShamsi NVARCHAR(10), #Seperator CHAR(1))
RETURNS NVARCHAR(10)
AS
BEGIN
DECLARE #StrDayOfMotn NVARCHAR(10)
DECLARE #StrMothOfYear NVARCHAR(10)
DECLARE #StrYearOfYear NVARCHAR(10)
SET #StrDayOfMotn = dbo.COnvertOToN(REPLACE(SUBSTRING(#StrDateShamsi , 1 , ((SELECT CHARINDEX('-' , #StrDateShamsi , 0)))), '-' , ''))
SET #StrMothOfYear = dbo.COnvertOToN(REPLACE(SUBSTRING(#StrDateShamsi , ((CHARINDEX('-' , #StrDateShamsi , 0) )) , 3) , '-' , ''))
SET #StrYearOfYear = RIGHT(#StrDateShamsi , 4)
return (#StrYearOfYear + #Seperator + #StrMothOfYear + #Seperator + #StrDayOfMotn)
END
--------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------
GO
CREATE FUNCTION [dbo].[ConvertShamsiToMiladiDate](#InputShamsiDateString nvarchar(10))
RETURNS datetime
AS
BEGIN
declare #InputShamsiDateString1 nvarchar(10)
declare #yearm int
declare #monthm int
declare #daym int
set #yearm = CONVERT(int , SUBSTRING(#InputShamsiDateString , 1 , 4))
set #monthm = CONVERT(int , SUBSTRING(#InputShamsiDateString , 6 , 2))
set #daym = CONVERT(int , SUBSTRING(#InputShamsiDateString , 9 , 2))
return (select dbo.[JulToGre](dbo.[PersToJul](#yearm,#monthm ,#daym )))
END
--------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------
GO
-- The Latest And Main Function
CREATE FUNCTION [dbo].[GetShmsiDate](#InputMiladiDate DateTime , #MySeperatorChar char(1))
RETURNS NVARCHAR(10)
AS
BEGIN
return (select dbo.RevDateShm(dbo.GrToPers(#InputMiladiDate), #MySeperatorChar) AS ShamsiDateOfLog)
END
GO
How to use:
SELECT dbo.GetShmsiDate(GETDATE() , N'/') AS ShamsiDate1,
dbo.GetShmsiDate(GETDATE() , N'-') AS ShamsiDate2
Result:
|ShamsiDate1|ShamsiDate2|
|-----------|-----------|
|1400/11/03 | 1400-11-03|
To convert a date to persian, try this code:
DECLARE #DateString NVARCHAR(200)='2022/09/07';
SELECT FORMAT(CAST(#DateString AS DATE),'yyyy/MM/dd','fa');

TSQL UDF To Split String Every 8 Characters

Someone decided to stuff a bunch of times together into a single column, so the column value might look like this:
08:00 AM01:00 PM
And another column contains the date in the following format;
20070906
I want to write a UDF to normalize this data in a single SQL query, so I can get back 2 rows of datetime type for the above example
2007-09-06 08:00:00.000
2007-09-06 13:00:00.000
The conversion to datetime type is simple...but I need to split the time part every 8 characters to get the individual time out.
Anyone know of an existing UDF to do this?
Try this, it'll split your string into chunks of the specified lenth:
create function SplitString
(
#str varchar(max),
#length int
)
returns #Results table( Result varchar(50) )
AS
begin
declare #s varchar(50)
while len(#str) > 0
begin
set #s = left(#str, #length)
set #str = right(#str, len(#str) - #length)
insert #Results values (#s)
end
return
end
For example:
select * from dbo.SplitString('08:00 AM01:00 PM', 8)
Will give this result:
Result
08:00 AM
01:00 PM
There is a bug in the query above, the below query fixes this.
Also, I have made the returned table contain a sequence column so that it is possible to determine what sequence the split is in:
CREATE function SplitString
(
#str varchar(max),
#length int
)
RETURNS #Results TABLE( Result varchar(50),Sequence INT )
AS
BEGIN
DECLARE #Sequence INT
SET #Sequence = 1
DECLARE #s varchar(50)
WHILE len(#str) > 0
BEGIN
SET #s = left(#str, #length)
INSERT #Results VALUES (#s,#Sequence)
IF(len(#str)<#length)
BREAK
SET #str = right(#str, len(#str) - #length)
SET #Sequence = #Sequence + 1
END
RETURN
END

Resources