PDA

View Full Version : دقیق ترین و سریع ترین و کم حجم ترین تابع میلادی به شمسی 2820 ساله گاهشماری حسابی بهروز-بیرشک



aliila
یک شنبه 24 آذر 1392, 16:09 عصر
این دومین تابع تبدیل میلادی به شمسی که نوشتم کل تاریخ مابین ۴۷۴/01/01 تا 3293/12/29 به عبارت دیگر برای دوره ۲۸۲۰ سال اخیر از سال ۴۷۴ تا ۳۲۹۳ هجری خورشیدی رو شامل میشه
سعی کردم از سال ۴۷۴ به بعد باشه تا الگوریتم سبک تر بشه و در ضمن به علت نقض گاه‌شماری ژولینی که از گاه‌شماری رومی گرفته شده بود به دستور گریگوری در تاریخ 1582 یک 10 روز از تاریخ میلادی حذف شده که نخواستم قبل این تاریخ رو لحاظ کنم . چون الگوریتم سنگین تر میشد .
و توصیه آخر : تابع 2 تا جدول متغیر داره چون با هر بار محاسبه تشکیل میشه در کارهای سریع تر بهتره که این 2 تا جدول ثابت بشن تا نرخ الگوریتم پایین بیاد .




create FUNCTION [dbo].[miladi_to_shamsi]

( @year as int , @mounth as int , @day as int )

RETURNS char(10)

BEGIN
--declare @mes as nvarchar(500)
declare @diffrent_rasmi_hesabi_kabise table (num1 int)
declare @birshak_reminder table (num1 int)

insert into @diffrent_rasmi_hesabi_kabise values (9) ,( 113 ),( 17 ),( 21 ),( 42 ),( 46 ),( 50 ),( 54 ),( 71 ),( 75 ),( 79 ),( 83 ),( 87 ),( 104 ),( 108 ),( 112 ),( 116 ),( 137 ),( 141 ),( 145 ),( 149 ),( 170 ),( 174 ),( 178 ),( 182 ),( 203 ),( 207 ),( 211 ),( 215 ),( 236 ),( 240 ),( 244 ),( 269 ),( 273 ),( 277 ),( 302 ),( 306 ),( 310 ),( 331 ),( 335 ),( 339 ),( 343 ),( 364 ),( 368 ),( 372 ),( 397 ),( 401 ),( 405 ),( 430 ),( 434 ),( 438 ),( 463 ),( 467 ),( 471 ),( 475 ),( 496 ),( 500 ),( 504 ),( 529 ),( 533 ),( 537 ),( 558 ),( 562 ),( 566 ),( 570 ),( 591 ),( 595 ),( 599 ),( 603 ),( 924 ),( 628 ),( 632 ),( 661 ),( 665 ),( 690 ),( 694 ),( 698 ),( 723 ),( 727 ),( 731 ),( 756 ),( 760 ),( 789 ),( 793 ),( 822 ),( 824 ),( 855 ),( 859 ),( 888 ),( 921 ),( 954 ),( 983 ),( 987 ),( 1016 ),( 1049 ),( 1082 ),( 1115 ),( 1243 ),( 1275 ),( 1308 ),( 1341 ),( 1403 ),( 1436 ),( 1469 ),( 1473 ),( 1502)
insert into @birshak_reminder values (0 ),( 4 ),( 8 ),(12 ),(16 ),( 20 ),( 24 ),( 29 ),( 33 ),( 37 ),( 41 ),( 45 ),( 49 ),( 53 ),( 57 ),( 62 ),( 66 ),( 70 ),( 74 ),( 78 ),( 82 ),( 86 ),( 90 ),( 95 ),( 99 ),( 103 ),( 107 ),( 111 ),( 115 ),( 119 ),( 124)
--insert into @birshak_reminder_less_than_474 values ( 25) ,( 58 ), ( 91) , ( 120)
declare @kabise_milady as int,@kabise_milady_1 as int ,@kabise_shamsi as int


select @kabise_milady_1= case when case when (case when (@year-1)%100 =0 then 0 else 1 end)=0 then (@year-1)%400 else ( @year-1)%4 end =0 then 1 else 0 end
if @kabise_milady_1=1 select @kabise_milady=0 else select @kabise_milady= case when case when (case when @year %100 =0 then 0 else 1 end)=0 then @year %400 else @year %4 end =0 then 1 else 0 end
select @year = @year-622
select @kabise_shamsi= count(*) from @diffrent_rasmi_hesabi_kabise where num1= @year
if @kabise_shamsi <>1 select @kabise_shamsi= count(*) from @birshak_reminder where num1= (@year%128)
select @day =
case @mounth
when 1 then 0 --31
when 2 then 31 --28 or 29
when 3 then 59 +@kabise_milady--31
when 4 then 90+@kabise_milady --30
when 5 then 120+@kabise_milady --31
when 6 then 151+@kabise_milady --30
when 7 then 181+@kabise_milady --31
when 8 then 212+@kabise_milady --31
when 9 then 243+@kabise_milady --30
when 10 then 273+@kabise_milady --31
when 11 then 304+@kabise_milady --30
when 12 then 334 +@kabise_milady--31
end +@day
--select @mes=' - kabise_milady='+cast(@kabise_milady as nvarchar) +' -kabise_shamsi:'+cast(@kabise_shamsi as nvarchar) + '-day= '+cast(@day as nvarchar) +' - '
if @day>79+@kabise_milady
begin
select @year =@year+1
select @day =@day-79
end
else
select @day =@day+286+@kabise_milady_1
if @day=365 select @day=365+ @kabise_shamsi
--select @mes=@mes+' shamsi day='+cast(@day as nvarchar) +' - '
select @mounth=case when @day> 186 then (@day-187) /30 +7 else (@day-1) /31 +1 end
select @day=case when @day> 186 then (@day-187) %30+1 else (@day-1) %31 +1 end
return -- @mes +
cast(@year as char(4)) + case when @mounth<10 then '/0'+ cast( @mounth as nvarchar(1)) else '/'+ cast( @mounth as nvarchar(2)) end + case when @day<10 then '/0'+ cast( @day as nvarchar(1)) else '/'+ cast( @day as nvarchar(2)) end
END

cherchil_hra
دوشنبه 25 آذر 1392, 13:16 عصر
برای سال 2013، 79 مورد مغایرت هست (01-01-2013 تا 20-03-2013) و 2012 همه روزها

برای اینکه کدت خواناتر بشه-->

به جای کد کبیسه:SELECT @kabise =
CASE
WHEN CASE
WHEN (CASE WHEN @year%100 = 0 THEN 0 ELSE 1 END) = 0 THEN @year%400
ELSE @year%4
END = 0 THEN 1
ELSE 0
END


می تونی از این کد استفاده کنی که ماه فوریه رو برای 29 روز بررسی می کنه :
set @kabise = CASE WHEN ISDATE(CAST(@YEAR AS CHAR(4)) + '0229') = 1 THEN 1
ELSE 0
END

هرچند که از متغیر @kabise استفاده نکردی.

محاسبه تعداد روزهای سال تا تاریخ ورودی:
SELECT @day =
CASE @Month
WHEN 1 THEN 0 --31
WHEN 2 THEN 31 --28 or 29
WHEN 3 THEN @kabise + 59 --31
WHEN 4 THEN @kabise + 90 --30
WHEN 5 THEN @kabise + 120 --31
WHEN 6 THEN @kabise + 151 --30
WHEN 7 THEN @kabise + 181 --31
WHEN 8 THEN @kabise + 212 --31
WHEN 9 THEN @kabise + 243 --30
WHEN 10 THEN @kabise + 273 --31
WHEN 11 THEN @kabise + 304 --30
WHEN 12 THEN @kabise + 334 --31
END + @day
از این کد استفاده کن:DECLARE @DayOfYear INT,
@Date DATETIME
SET @Date = CAST(
(
CAST(@year AS VARCHAR(4)) +'/'+ CAST(@Month AS VARCHAR(2)) +'/'+ CAST(@Day AS VARCHAR(2))
) AS DATETIME
)
SET @DayOfYear = DATEPART(dayofyear, @Date)

و در نهایت برای نمایش خروجیت (مثلا 01):
RETURN CAST(@year AS CHAR(4)) +
CASE
WHEN @Month < 10 THEN '/0' + CAST(@Month AS NVARCHAR(1))
ELSE '/' + CAST(@Month AS NVARCHAR(2))
END
+
CASE
WHEN @day < 10 THEN '/0' + CAST(@day AS NVARCHAR(1))
ELSE '/' + CAST(@day AS NVARCHAR(2))
END
از این کد استفاده کن:
RETURN CAST(@Year AS VARCHAR(4)) + '/'
+ RIGHT('00' + CAST(@Month AS VARCHAR(2)), 2)+ '/'
+ RIGHT('00' + CAST(@day AS VARCHAR(2)), 2)

موفق باشید!

aliila
چهارشنبه 27 آذر 1392, 11:20 صبح
با تشکر از دوست عزیز cherchil_hra
خودم نخواستم از توابع تاریخ sql استفاده کنم به دو دلیل
1- نرخ الگوریتمشون مشخص نیست
2- این تابع رو خیلی راحت بشه تو هر زبان برنامه نویسی پیاده کرد . حتی داخل سیستم عامل های تعبیه شده ( میکرو کنترلر و .. )


در نهایت تابع رو اصلاح کردم یک تستی بکنید .

و یک توصیه اینه که در اکثر موارد سال کبیسه شمسی به سال کبیسه میلادی منطبق است ( فکر کنم تا 100 سال آینده این طوری بشه ) . بنا بر این میتونید قسمت محاسبه کبیسه بودن سال شمسی رو حدف کنید و در نهایت الگوریتم تبدیل به الگوریتم زیر میشه






create FUNCTION [dbo].[miladi_to_shamsi]
( @year as int ,
@mounth as int ,
@day as int
)
RETURNS char(10)
BEGIN
declare @kabise_milady as int,@kabise_milady_1 as int
select @kabise_milady_1= case
when case when (case when (@year-1)%100 =0 then 0 else 1 end)=0 then (@year-1)%400 else ( @year-1)%4 end =0 then 1
else 0
end
if @kabise_milady_1=1
select @kabise_milady=0
else
select @kabise_milady= case
when case when (case when @year %100 =0 then 0 else 1 end)=0 then @year %400 else @year %4 end =0 then 1
else 0
end


select @year = @year-622

select @day =
case @mounth
when 1 then 0 --31
when 2 then 31 --28 or 29
when 3 then 59 +@kabise_milady--31
when 4 then 90+@kabise_milady --30
when 5 then 120+@kabise_milady --31
when 6 then 151+@kabise_milady --30
when 7 then 181+@kabise_milady --31
when 8 then 212+@kabise_milady --31
when 9 then 243+@kabise_milady --30
when 10 then 273+@kabise_milady --31
when 11 then 304+@kabise_milady --30
when 12 then 334 +@kabise_milady--31
end +@day
if @day>79+@kabise_milady
begin
select @year =@year+1
select @day =@day-79
end
else
select @day =@day+286+@kabise_milady_1

if @day=365 select @day=365+ @kabise_milady
select @mounth=case when @day> 186 then (@day-187) /30 +7 else (@day-1) /31 +1 end
select @day=case when @day> 186 then (@day-187) %30+1 else (@day-1) %31 +1 end
return cast(@year as char(4)) + case when @mounth<10 then '/0'+ cast( @mounth as nvarchar(1)) else '/'+ cast( @mounth as nvarchar(2)) end + case when @day<10 then '/0'+ cast( @day as nvarchar(1)) else '/'+ cast( @day as nvarchar(2)) end
END