ورود

View Full Version : تابع تبدیل تاریخ میلادی به شمسی ( سریع و تست شده )



SabaSabouhi
سه شنبه 05 اسفند 1393, 08:39 صبح
سلام
چون دوستان زیادی دنبال تابع تبدیل تاریخ میلادی به شمسی می‌گشتن من این تابع رو مجدد ارسال می‌کنم. چون ارسال قبلی مربوط به چند سال
پیش و داخل یه تاپیک طولانی بود و تو جستجو راحت پیدا نمی‌شد، دوباره می‌فرستم به این امید که به درد دوستان بخوره.
1. تو این UDF غیر از DATEPART از هیچ تابعی استفاده نشده ( برای حداکثر سرعت )
2. هیچ حلقه‌ای وجود نداره
3. الگوریتم محاسبه شکسته شده، ( با خوندن متن شاید متوجه منطق برنامه نشین )
4. تو تمام دامنه تعریف یعنی از سال 1900 تا 2100 میلادی ( محدوده‌ی SmallDateTime ) تست شده
5. این رو حدود 15 سال پیش نوشتم، اما هنوز هیچ تابعی سریع‌تر و بهینه‌تر از این ندیدم.
6. لایسنس از کد Free هست. استفاده کنید و به دوستانتون هم بدین.



CREATE FUNCTION [dbo].[SDate]( @Date as SmallDateTime)
RETURNS int
AS
BEGIN

-- Convert English Calendar to Persian Calendar
-- Created by Saba Sabouhi
DECLARE @Year as int, @Month as int, @Day as int
DECLARE @Year2 as int, @Month2 as int, @Day2 as int
DECLARE @Period as int
DECLARE @pIdx as int, @MonLen as int
DECLARE @n as int


SET @Year = DatePart( yyyy, @Date)
SET @Month = DatePart( m, @Date)
SET @Day = DatePart( d, @Date)
IF @Year < 1970
SET @Period = (@Year - 1897) / 32
ELSE
SET @Period = (@Year - 1897 - 4) / 32
SET @pIdx = (@Year - 1) - 4 * ((@Year - 1) / 4)
SET @Day2 = @Day + CASE
WHEN @Month in (1, 5, 6) THEN 10
WHEN @Month in (2, 4) THEN 11
WHEN @Month in (3, 7, 8, 9, 11, 12) THEN 9
WHEN @Month = 10 THEN 8 END
SET @Day2 = @Day2 - CASE
WHEN @Period < @pIdx - 1 THEN 1
WHEN @Period > @pIdx + 2 THEN -1
ELSE 0 END
SET @MonLen = CASE
WHEN @Month in (1, 2, 10, 11, 12) THEN 30
WHEN @Month = 3 THEN 29
ELSE 31 END
IF @Month > 2 AND @Year = 4 * (@Year / 4)
SET @Day2 = @Day2 + 1
SET @n = (@Year - 1899) - 33 * ( (@Year - 1899) / 33 )
IF @n in (3, 7, 11, 15, 19, 23, 27, 32)
BEGIN
IF @Month > 3
SET @Day2 = @Day2 - 1
IF @Month = 3
SET @MonLen = @MonLen + 1
END
SET @n = @Month
IF @Date < '1900/3/1'
SET @Day2 = @Day2 + 1
IF @Day2 > @MonLen
SET @Day2 = @Day2 - @MonLen
ELSE
SET @n = @n - 1
SET @Month2 = 1 + ( @n + 9 ) - 12 * (( @n + 9) / 12)
IF @Month2 > @Month
SET @Year2 = @Year - 622
ELSE
SET @Year2 = @Year - 621
RETURN @Year2 * 10000 + @Month2 * 100 + @Day2
END


صبا صبوحی

Ali_M.Eghbaldar
یک شنبه 08 شهریور 1394, 19:12 عصر
با تشکر از کد صحیح شما :قلب:
اگه کد برعکسشو هم بزارید که عالی میشه :)

SabaSabouhi
دوشنبه 09 شهریور 1394, 15:07 عصر
با تشکر از کد صحیح شما :قلب:
اگه کد برعکسشو هم بزارید که عالی میشه :)

سلام
بله دارم. اگه تاریخ شمسی رو به صورت عدد داشته باشی ( مثلاً 13930519 )


CREATE FUNCTION [dbo].[MDate]( @SDate as int)
RETURNS smalldatetime
AS
BEGIN
DECLARE @TempDate as datetime
DECLARE @Year2 as int
DECLARE @Month as int
DECLARE @Day as int, @Day2 as int
DECLARE @Period as int, @Index as int


IF @SDate < 12781011 OR @SDate > 14580316
RETURN NULL
SET @Year2 = @SDate / 10000 + 621
SET @Period = (@Year2 - 1897) / 33 - 2 -- Base Period [ 1342 .. 1374 ] Equ: [ 1963 .. 1995 ]]
SET @Index = @Year2 - 4 * ( @Year2 / 4)


SET @Day = @SDate - 100 * (@SDate / 100)
SET @Month = (@SDate - 10000 * (@SDate / 10000 )) / 100


SET @Day2 = @Day + (@Month - 1) * 30 + CASE WHEN @Month > 7 THEN 6 ELSE @Month - 1 END
IF @SDate < 12790000
SET @Day2 = @Day2 - 1
SET @Day2 = @Day2 + ( (@Index + 4 - @Period) / 4) - 1
SET @TempDate = '3/20/' + STR( @Year2)
RETURN Convert( smalldatetime, DateAdd( Day, @Day2, @TempDate))
END


و اگه به صورت رشته باشه ( مثلاً '1393/03/08' )


CREATE FUNCTION [dbo].[MDateStr]( @SDateStr as varchar( 20) )
RETURNS smalldatetime
AS
BEGIN
DECLARE @TempDate as datetime
DECLARE @Year as int, @Year2 as int
DECLARE @Month as int
DECLARE @Day as int, @Day2 as int
DECLARE @Period as int, @Index as int


IF Len(@SDateStr) <> 10
RETURN NULL
SET @Year = CONVERT( int, SUBSTRING( @SDateStr, 1, 4 ) )
SET @Month = CONVERT( int, SUBSTRING( @SDateStr, 6, 2 ) )
SET @Day = CONVERT( int, SUBSTRING( @SDateStr, 9, 2 ) )


SET @Year2 = @Year + 621
SET @Period = (@Year2 - 1897) / 33 - 2 -- Base Period [ 1342 .. 1374 ] Equ: [ 1963 .. 1995 ]]
SET @Index = @Year2 - 4 * ( @Year2 / 4)


SET @Day2 = @Day + (@Month - 1) * 30 + CASE WHEN @Month > 7 THEN 6 ELSE @Month - 1 END
IF @Year < 1279
SET @Day2 = @Day2 - 1
SET @Day2 = @Day2 + ( (@Index + 4 - @Period) / 4) - 1
SET @TempDate = '3/20/' + STR( @Year2)
RETURN Convert( smalldatetime, DateAdd( Day, @Day2, @TempDate))
END



صبا صبوحی

Ali_M.Eghbaldar
شنبه 14 شهریور 1394, 16:21 عصر
سلام دوستان
با استفاده از تابع SDate که SabaSabouhi زحمت کشید قرار داد من یک تابع نوشتم که تاریخ شمسی هم فرمت yyyy/mm/yy بر میگرداند و هم روز هفته بهم می چسباند
میدونم که بهتر هم میشه نوشت و شاید از راه های دیگه.




CREATE FUNCTION [dbo].[CDate](@Date datetime)
RETURNS nvarchar(200)
AS
BEGIN


declare @Sdate_ varchar(8)
declare @y char(4)-- year of shamsi date
declare @m char(2)-- month of shamsi date
declare @d char(2)-- day of shamsi day
declare @dow nvarchar(10)

set @Sdate_= dbo.SDate (@date)


set @y=(left(cast(@Sdate_ as varchar),4))
set @m=(substring(cast(@Sdate_ as varchar),5,2))
set @d=(right(cast(@Sdate_ as varchar),2))


set @dow=(select
case datepart(dw,@date)
when 1 then 'ِیکشنبه'
when 2 then 'دوشنبه'
when 3 then 'سه شنبه'
when 4 then 'چهارشنبه'
when 5 then 'پنجشنبه'
when 6 then 'جمعه'
when 7 then 'شنبه' end )


return @y+'/'+@m+'/'+@d+' '+@dow


END

SabaSabouhi
یک شنبه 15 شهریور 1394, 09:14 صبح
سلام دوستان
با استفاده از تابع SDate که SabaSabouhi زحمت کشید قرار داد من یک تابع نوشتم که تاریخ شمسی هم فرمت yyyy/mm/yy بر میگرداند و هم روز هفته بهم می چسباند
میدونم که بهتر هم میشه نوشت و شاید از راه های دیگه.



سلام
منظور من از SDate در واقع ShamsiDate بود که یه جورایی مخففش کردم
اگه می‌خوای این تابع رو گسترش بدی بجای کاری که کردی از تابع من یه کپی بگیر، اسمش رو مثلاً بکن SDate2 و کارهای جدید رو بهش
اضافه کن، نه این که تابع جدیدی بنویسی که تابع قبلی رو صدا کنه، این باعث کند شدن این تابع می‌شه. ضمن این که مقادیر روز و ماه و سال
توی تابع من وجود داره و نیازی نخواهی داشت که از توابع left, right, cast استفاده کنی.
چون این تابع می‌تونه توی queryها استفاده بشه، خیلی مهمه که سرعت خوبی داشته باشه و تو queryهای سنگین باعث کندی کل قضیه نشه.

صبا صبوحی

Ali_M.Eghbaldar
یک شنبه 15 شهریور 1394, 16:52 عصر
سلام
منظور من از SDate در واقع ShamsiDate بود که یه جورایی مخففش کردم
اگه می‌خوای این تابع رو گسترش بدی بجای کاری که کردی از تابع من یه کپی بگیر، اسمش رو مثلاً بکن SDate2 و کارهای جدید رو بهش
اضافه کن، نه این که تابع جدیدی بنویسی که تابع قبلی رو صدا کنه، این باعث کند شدن این تابع می‌شه. ضمن این که مقادیر روز و ماه و سال
توی تابع من وجود داره و نیازی نخواهی داشت که از توابع left, right, cast استفاده کنی.
چون این تابع می‌تونه توی queryها استفاده بشه، خیلی مهمه که سرعت خوبی داشته باشه و تو queryهای سنگین باعث کندی کل قضیه نشه.

صبا صبوحی


ممنون.
ولی پروژه ای که دارم مینوسیم اونقدر هم بزرگ نیست که نیاز به این کار باشه.
کلا خواستم قطعه کد جدید آموزشی برای گسترش بزارم.
قطعا روش های بهینه تر بهتری وجود داره.

موفق باشید.