How to save Persian date time in SQL server?

datetime2 in SQL Server uses the Gregorian calendar.

If you want to use dates based on the Persian calendar, then you either need to make and use some functions that you can call in your SQL code to do the conversion for you, or you need to make a User Defined Type that can store the data.

Here's an example of some functions that can convert between Persian and Gregorian. I haven't tried them out so I have no idea if they work, or to their quality.

I couldn't find an example of creating a UDT for Persian dates, but this is the documentation for UDTs in general.

Personally, I would store the dates in SQL Server as UTC Gregorian datetime2. And I'd either have conversion code in the application data layer that converts them when saving to, and loading from SQL Server, or I'd do the conversion when the DateTime is displayed to/retrieved from the user.

UTC is the date and time standard for the world, especially for science and engineering, and as such SQL Server and .Net have been built using it. It makes sense to also use UTC so that you can get the most benefit from the existing SQL Server and .Net code. Of course your users still want to be able to write and read a DateTime using the Persian calendar, so it makes sense to me that you'd perform that conversion just before one will be displayed to the user, or just after the user has provided one.


You can use the below functions to convert calendar to Persian and vice versa.

Create FUNCTION [dbo].[UDF_Persian_To_Julian](@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

and the second function is:

Create Function dbo.[UDF_Gregorian_To_Persian] (@date datetime)
Returns NvarChar(10) AS
begin

Declare @gd As int
Declare @gm As int
Declare @gm2 As int
Declare @gy As int
Declare @gy2 As int
Declare @jy as int
Declare @jd as int
Declare @jm as int
Declare @days as int
Declare @g_d_m as int

Declare @Result As NVarChar(10)

set @gy = YEAR(@date)
set @gm = MONTH(@date)
set @gd = DAY(@date)

if @gy <= 1600 
    set @jy = 0
else
    set @jy = 979

if @gy <= 1600 
    set @gy = @gy - 621
else
    set @gy = @gy - 1600

if @gm > 2 
    set @gy2 = @gy + 1
else
    set @gy2 = @gy

set @gm2 = @gm - 1

if @gm2 = 0
    set @g_d_m = 0
else if @gm2 = 1
    set @g_d_m = 31
else if @gm2 = 2
    set @g_d_m = 59
else if @gm2 = 3
    set @g_d_m = 90
else if @gm2 = 4
    set @g_d_m = 120
else if @gm2 = 5
    set @g_d_m = 151
else if @gm2 = 6
    set @g_d_m = 181
else if @gm2 = 7
    set @g_d_m = 212
else if @gm2 = 8
    set @g_d_m = 243
else if @gm2 = 9
    set @g_d_m = 273
else if @gm2 = 10
    set @g_d_m = 304
else if @gm2 = 11
    set @g_d_m = 334

set @days = (365 * @gy) + ((@gy2 + 3) / 4) - ((@gy2 + 99) / 100) + ((@gy2 + 399) / 400) - 80 + @gd + @g_d_m 
set @jy = @jy +  33 * (@days / 12053);
set @days = @days % 12053
set @jy = @jy + 4 * (@days / 1461)
set @days = @days % 1461
set @jy = @jy + ((@days - 1) / 365);

if @days > 365
    set @days = (@days - 1) % 365;

if @days < 186
    set @jm = 1 + (@days / 31) 
else
    set @jm = 7 + ((@days - 186) / 30);

if @days < 186
    set @jd = 1 + (@days % 31) 
else
    set @jd = 1 + ((@days - 186) % 30)

Return Convert(nvarchar(50),@jy) + '/' + RIGHT('0' + CAST(@jm AS VARCHAR(2)), 2) +'/' + RIGHT('0' + CAST(@jd AS VARCHAR(2)), 2)
end

Example of using this function is:

SELECT [dbo].[UDF_Gregorian_To_Persian] ('2018-2-5') as PersianDate

Result is:

1396/11/16

You can use in SQL SERVER 2016 above Persian format like this function :

ALTER FUNCTION [Fun].[PersianDateFormat]
(
    @Date   datetime ,      --تاریخ میلادی ورودی 
    @How    tinyint =1      --0.numberFull and time 1.numbers   2.Month latter  3.Day latter    4.Month and Day latter  5.just Month    6.Just Day
)
RETURNS Nvarchar(30)
AS
BEGIN
    DECLARE @PersianDate Nvarchar(30)
    if @How = 0
        SELECT @PersianDate=FORMAT(@Date, 'yyyy/MM/dd-hh:mm:ss', 'fa')
    if @How = 1
        SELECT @PersianDate=FORMAT(@Date, 'yyyy/MM/dd', 'fa')
    if @How = 2
        SELECT @PersianDate=FORMAT(@Date, 'yyyy/MMM/dd', 'fa')
    if @how = 3
        SELECT @PersianDate=FORMAT(@Date, 'yyyy/MM/ddd', 'fa')      
    if @how = 4
        SELECT @PersianDate=FORMAT(@Date, 'yyyy/MMM/ddd', 'fa') 
    if @how = 5
        SELECT @PersianDate=FORMAT(@Date, 'MMM', 'fa')  
    if @how = 6
        SELECT @PersianDate=FORMAT(@Date, 'ddd', 'fa')  
    RETURN @PersianDate

END

You can use directly in query for change date to Persian like this :

Select FORMAT(Getdate(), 'yyyy/MM/dd-hh:mm:ss', 'fa')

I hope you wish the best