سالهاست كه براي ذخيره تاريخ در SQL Server
يك فيلد رشتهاي ده كاراكتري در نظر ميگيرم
و باقي تغيير و تحوّلات را
ميگذارم بر عهده كدهاي برنامه
ديروز اما قصد داشتم گزارشي بنويسم از يكسري لاگ
و تاريخها همه ميلادي ذخيره ميشوند
به صورت خودكار و با تابع GETDATE داخل SQL Server
مبدّلهايي كه داشتم
مربوط به كدهاي داخل Net.
در اينترنت جستجو كردم و براي SQL Server
اينكه در داخل آن تبديل را انجام دهم
اين موارد را يافتم:
GETTING SHAMSI DATE FROM GREGORIAN DATE
How to convert Persian Or Jalali Calendar to Gregorian Calendar & vice versa In Sql Server
Creating a CLR Persian Date Convertor Function for SQL Server
Persian Date/Time support for MsSQL
وقتي تبديل در قالب يك تابع در وسطهاي SELECT انجام ميشود
كار سريعتر و سادهتر به انجام ميرسد
ديگر نياز نيست ركورد به ركورد بگيري و داخل كدهاي برنامه تبديل كني
يكسره ديتا را ميگيري و ميفرستي براي كلاينت
تاريخها هم همه شمسي شده
اما با كدهاي فوق به مشكلاتي برخوردم؛
دو مورد اول نتايج اشتباهي ميدادند
ظاهراً در محاسبه سال كبيسه ايراد داشتند
و يك روز جلوتر حساب كردند
مورد سوم و چهارم هم رفته بودند سراغ كدهاي داخل Net. اساساً
از قابليتي استفاده كرده
كه اجازه ميدهد از داخل SQL Server به توابع Net. ارجاع شود
دنبال اين پيچيدگيها نبودم
يك كد ساده و روان ميخواستم
كه در قالب UDF مثلاً قابل اجرا باشد
ولي اتفاق جالبي افتاد
كد بسيار سادهاي در كامنتهاي اين مطلب يافتم
كد مذكور از منطق سادهاي پيروي ميكرد
ولي ايراد داشت
تصميم گرفتم و كمي وقت گذاشتم
كد را يكبار به صورت كامل بررسي كردم
و بر اساس همان منطق سادهاي كه داشت بازنويسي نمودم
CREATE FUNCTION [G2J] ( @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 = datepart(dw, "01/02/" + CONVERT(CHAR(4), @intYY))
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 ( ( @intYY - 1992 ) % 4 = 0) SET @Kabiseh1 = 1 ELSE SET @Kabiseh1 = 0
IF ( ( @shYear - 1371 ) % 4 = 0) SET @Kabiseh2 = 1 ELSE SET @Kabiseh2 = 0
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
SET @DayDate = REPLACE(RIGHT(STR(@shYear, 4), 4), " ", "0") + "/"+ REPLACE(STR(@shMonth, 2), " ", "0") + "/" + REPLACE(( STR(@shDay,2) ), " ", "0")
--1394/02/17
*/
RETURN @DayDate
END
منطق آن چنين است:
از يك مبدأ مشترك ميان تقويم شمسي و ميلادي استفاده ميكند
روزهاي سال را ميشمرد
يكبار براي ميلادي
و يكبار براي شمسي
سر هر ماه كه ميرسد ميپرد
كبيسهها را هم به خوبي حساب مينمايد
در نهايت وقتي تاريخ ميلادي به روز مورد نظر رسيد
همانجا ميايستد
و تاريخ شمسي را به عنوان خروجي عرضه مينمايد
روش بسيار روشن و تميزي به نظرم رسيد
وقتي اصلاحات تمام شد
پاسخ خيلي دقيقي داد
درست همان پاسخي كه انتظار داشتم
كد را گذاشتم كه مورد استفاده مردمانم قرار گيرد
كافيست آن را در قالب يك تابع تعريفشده توسط كاربر (UDF)
استفاده كنيد
مانند اين:
SELECT CreateDate, G2J(CreateDate) AS Tarikh FROM MyTable
CreateDate Tarikh
- - - - - - - - - - - - - - - - - - - - - - -
06/19/2015 جمعه 29 خرداد 1394
06/23/2015 سهشنبه 2 تير 1394
پينوشت اول: (4 شهريور 94)
در يكي از نظرات
پيشنهادي طرح شد
مبني بر اينكه فرمت خروجي تابع
از طريق پارامترهاي ارسالي به آن تعيين شود
و ديگر نيازي به دستكاري كدها نباشد
اين شد كه تابع را به شكل زير توسعه دادم:
CREATE FUNCTION dbo.[G2J] ( @intDate DATETIME, @Format NVARCHAR(max))
RETURNS NVARCHAR(max)
BEGIN
/* Format Rules: (پنجشنبه 7 ارديبهشت 1394)
ChandShanbe -> پنجشنبه (روز هفته به حروف)
ChandShanbeAdadi -> 6 (روز هفته به عدد)
Rooz -> 7 (چندمين روز از ماه)
Rooz2 -> 07 (چندمين روز از ماه دو كاراكتري)
Maah -> 2 (چندمين ماه از سال)
Maah2 -> 02 (چندمين ماه از سال دو كاراكتري)
MaahHarfi -> ارديبهشت (نام ماه به حروف)
Saal -> 1394 (سال چهار كاراكتري)
Saal2 -> 94 (سال دو كاراكتري)
Saal4 -> 1394 (سال چهار كاراكتري)
SaalRooz -> 38 (چندمين روز سال)
Default Format -> "ChandShanbe Rooz MaahHarfi Saal"
*/
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, @YearDay 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
IF (@Format IS NULL) OR NOT LEN(@Format)>0 SET @Format = "ChandShanbe Rooz MaahHarfi Saal"
SET @m1 = 1
SET @d1 = 1
SET @shMonth = 10
SET @shDay = 11
SET @DayCnt = datepart(dw, "01/02/" + CONVERT(CHAR(4), @intYY))
SET @YearDay = 276
IF ( ( @intYY - 1993 ) % 4 = 0 ) SET @shDay = 12
SET @YearDay = @YearDay + @shDay
WHILE ( @m1 != @intMM ) OR ( @d1 != @intDD )
BEGIN
SET @d1 = @d1 + 1
SET @DayCnt = @DayCnt + 1
IF ( ( @intYY - 1992 ) % 4 = 0) SET @Kabiseh1 = 1 ELSE SET @Kabiseh1 = 0
IF ( ( @shYear - 1371 ) % 4 = 0) SET @Kabiseh2 = 1 ELSE SET @Kabiseh2 = 0
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
SET @YearDay = @YearDay + 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
SET @YearDay = 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 = REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(@Format,"MaahHarfi",@shMaah),"SaalRooz",LTRIM(STR(@YearDay,3))),"ChandShanbeAdadi",@DayCnt),"ChandShanbe",@shRooz),"Rooz2",REPLACE(STR(@shDay,2), " ", "0")),"Maah2",REPLACE(STR(@shMonth, 2), " ", "0")),"Saal2",SUBSTRING(STR(@shYear,4),3,2)),"Saal4",STR(@shYear,4)),"Saal",LTRIM(STR(@shYear,4))),"Maah",LTRIM(STR(@shMonth,2))),"Rooz",LTRIM(STR(@shDay,2)))
/* Format Samples:
Format="ChandShanbe Rooz MaahHarfi Saal" -> پنجشنبه 17 ارديبهشت 1394
Format="Rooz MaahHarfi Saal" -> ـ 17 ارديبهشت 1394
Format="Rooz/Maah/Saal" -> 1394/2/17
Format="Rooz2/Maah2/Saal2" -> 94/02/17
Format="Rooz روز گذشته از MaahHarfi در سال Saal2" -> ـ 17 روز گذشته از ارديبهشت در سال 94
*/
RETURN @DayDate
END
اكنون بايد اينطور استفاده كنيد:
SELECT CreateDate, G2J(CreateDate,"ChandShanbe Rooz MaahHarfi Saal") AS Tarikh FROM MyTable
CreateDate Tarikh
- - - - - - - - - - - - - - - - - - - - - - -
06/19/2015 جمعه 29 خرداد 1394
06/23/2015 سهشنبه 2 تير 1394
اگر پارامتر دوم را خالي بفرستيد
دو آپستروف مثلاً: ("",G2J(CreateDate
همان پيشفرض قبلي را خروجي ميدهد
ولي يازده كلمه كليدي وجود دارد
كه با ارسال هر كدام
يا تركيبشان
ميتوانيد خروجيهاي متنوعي بگيريد.
پينوشت دوم: (4 شهريور 94)
شايد شما هم متوجه شده باشيد
قبلاً هم در سايتهاي معرفي شده فوق ديده بودم
اينكه از كندي چنين توابعي سخن به ميان آمده
بالاخره يك فرآيند است
و اين الگوريتم
منابع زيادي از سرور را مصروف ميدارد
هر بار اجراي اين كد كه در سرور شخصي من حدود 40 ميليثانيه زمان ميبرد
ولي وقتي حجم كار بالا ميرود
خودم هم با اين مشكل مواجه شدم
وقتي خواستم حدود يازدههزار ركورد را براساس تاريخ شمسي گروهبندي نمايم
در حالي كه تاريخ ميلادي در جدول ذخيره شده بود
من هم با تايماوت مواجه شدم!
ديده بودم كساني ميآيند و جدول ميسازند
براي تبديل ميلادي به شمسي
يا بالعكس
به نظر ميرسد سرعت را بسيار بيشتر ميكند
يك پروسيجر نوشتم
سال ابتدا و سال انتها را وارد ميكنيد
(در اين نمونه 2015 و 2016)
تمام روزها را استخراج كرده
ميلادي و شمسي و حتي به تفكيك
در يك جدول ميريزد (Miladi_Shamsi)
البته براي اينكار از همان تابع بالا استفاده ميكند
ببينيد:
CREATE PROCEDURE dbo.Make_Convert_Date_Table
AS
BEGIN
DECLARE @AzYear AS INT, @TaYear AS INT
SET @AzYear = 2015
SET @TaYear = 2016
IF OBJECT_ID("dbo.Miladi_Shamsi", "U") IS NOT NULL
DROP TABLE [dbo].[Miladi_Shamsi]
CREATE TABLE [dbo].[Miladi_Shamsi] (
[Miladi] smalldatetime NOT NULL,
[ShamsiSlash] char(10) NULL,
[ShamsiFull] nvarchar(30) COLLATE Persian_100_CI_AI NULL,
[ShamsiRooz] int NULL,
[ShamsiMaah] int NULL,
[ShamsiSaal] int NULL,
[ShamsiWeekDay] int NULL,
PRIMARY KEY CLUSTERED ([Miladi])
)
DECLARE @CurDate AS DATETIME, @LastDate AS DATETIME
SET @CurDate = CONVERT(DATETIME, "1/1/" + CONVERT(CHAR(4),@AzYear)) --MM/DD/YYYY
SET @LastDate = CONVERT(DATETIME, "12/31/" + CONVERT(CHAR(4),@TaYear))
DECLARE @Shamsi AS NVARCHAR(max), @Saal AS CHAR(4), @Maah AS CHAR(2), @Rooz AS CHAR(2), @ShanbeInt AS CHAR(1), @Shanbe AS NVARCHAR(10), @MaahH AS NVARCHAR(10)
WHILE @CurDate <= @LastDate
BEGIN
SET @Shamsi = dbo.G2J(@CurDate,"Saal4Maah2Rooz2ChandShanbeAdadiChandShanbe|MaahHarfi") --139402175.$.
SET @Saal = SUBSTRING(@Shamsi,1,4)
SET @Maah = SUBSTRING(@Shamsi,5,2)
SET @Rooz = SUBSTRING(@Shamsi,7,2)
SET @ShanbeInt = SUBSTRING(@Shamsi,9,1)
SET @Shanbe = SUBSTRING(@Shamsi,10,CHARINDEX("|",@Shamsi)-10)
SET @MaahH = SUBSTRING(@Shamsi,CHARINDEX("|",@Shamsi)+1,10)
INSERT INTO [dbo].[Miladi_Shamsi] ([Miladi],[ShamsiSlash],[ShamsiFull],[ShamsiRooz],[ShamsiMaah],[ShamsiSaal],[ShamsiWeekDay])
VALUES (@CurDate,@Saal+"/"+@Maah+"/"+@Rooz,@Shanbe+" "+LTRIM(STR(CONVERT(INT,@Rooz),2))+" "+@MaahH+" "+@Saal,@Rooz,@Maah,@Saal,@ShanbeInt)
SET @CurDate = @CurDate + 1
END
END
پس از اينكه اين جدول را ساختيد
جدولي شبيه به اين:
Miladi ShamsiSlash ShamsiRooz ShamsiMaah ShamsiSaal ShamsiWeekDay ShamsiFull
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1/1/2015 1393/10/11 11 10 1393 6 پنجشنبه 11 دي 1393
1/2/2015 1393/10/12 12 10 1393 7 جمعه 12 دي 1393
و در اختيار گرفتيد
ميتوانيد يك تابع مياني بسازيد
مانند زير:
CREATE FUNCTION dbo.[G2J_Fast] ( @intDate DATETIME)
RETURNS NVARCHAR(max)
BEGIN
DECLARE @Shamsi AS NVARCHAR(max)
SELECT @Shamsi=ShamsiFull FROM [Miladi_Shamsi]
WHERE Miladi=@intDate
IF @Shamsi IS NULL SET @Shamsi=dbo.G2J(@intDate,"")
RETURN @Shamsi
END
حالا يك تابع جديد به نام G2J_Fast داريم
كه سريعتر از قبلي عمل مينمايد
زيرا ابتدا به سراغ جدول Miladi_Shamsi ميرود
اگر تاريخ ميلادي را يافت كه هيچ
شمسي را ميگيرد و تقديم مينمايد
اما اگر تاريخ مذكور در گستره ذخيره شده نبود
آن را از تابع G2J فراخواني ميكند
بدينترتيب ما يك روش هيبريدي در اختيار داريم
ميتوانيم جدول را در گسترهاي كه نياز معمولمان است بسازيم
و براي حالتهاي خاص
گاهي كه تاريخ مربوط به گذشته يا آينده دور است
از تابع تبديل استفاده كنيم
از نظرات ارزشمندتان بهرهمندم سازيد
و اگر اصلاحي به نظرتان رسيد
بفرماييد تا اعمال شود.
پينوشت سوم: (4 شهريور 94)
فراموش نكنيد كه به جاي كوتيشنماركها ("...") در كدها
بايد آپستروف بگذاريد (")
پارسيبلاگ به جهات امنيتي اجازه درج آپستروف در متن نميدهد
اين است كه آپستروفها را خودبهخود تبديل به كوتيشن ميكند
روي كدهاي فوق قبل از درج در ديتابيس بايد يك جستجوجايگزيني انجام دهيد
تا مشكل حل شود!
پينوشت چهارم: (5 شهريور 94)
براي آنان كه جدول آماده معادلسازيشده ميلادي_شمسي را ميخواهند
اين فايل را آماده كردم (http://movashah.id.ir/o/MiladiShamsi.txt)
با همين پروسيجر فوق ساخته شده
از سال 2010 تا 2020 را خروجي گرفتم (دي 1388 تا دي 1399)
يازده سال كه ميشود 4018 روز
(با احتساب سه روز اضافه به خاطر سه سال كبيسه)
ميتوانيد با ديدن اين نمونه صحّت تبديل تابع G2J را مشاهده و بررسي بفرماييد
و يا به ديتابيس خود منتقل نموده و براي تبديل استفاده كنيد
پينوشت پنجم: (17 آبان 94)
اشكالي در كد تابع وجود داشت
كه در شماره روز هفته و نام روز اشتباه ميكرد
يكي از بازديدكنندگان وبلاگ تذكّر دادند
و مشكل مزبور مرتفع گرديد
پينوشت ششم: (17 آبان 94)
فراموش نكنيم كه به دليل مبتني بودن تابع فوق بر دورههاي چهارساله كبيسه
و عدم لحاظ كبيسه پنجساله
تنها در يك بازه زماني حدوداً 33 ساله صحيح عمل ميكند
يعني از 1371 تا تقريباً 1404 هـ ش
در قبل و بعد اين دوره
احتمال بروز يك روز خطا وجود دارد
مگر اينكه تابع اصلاح شود و كبيسههاي پنجساله نيز در آن محاسبه گردند!
پينوشت هفتم: (18 آبان 94)
يك كد سريع نوشته شده است
قطعاً بسيار سريعتر از كد فوق
زيرا اين حلقه يكساله را ندارد
يك لوپ كه هميشه از نخستين روز سال آغاز ميشود
تا به روز مورد نظر برسد
توسط يكي از برنامهنويسان
كه در نشاني http://mamehdi.parsiblog.com/Posts/1 قابل دسترسيست
تست كردم
خروجيهاي كاملاً صحيحي ميداد
فرمت ورودي و خروجي آن نيز كاملاً شبيه به همين تابع G2J است
كافيست هنگام نياز به تبديل تاريخ و استفاده از تابع
به جاي dbo.G2J بنويسيد dbo.SDAT
همان پاسخ را
البته كه بسيار سريعتر
خواهد داد
با تشكر از اين برادر بزرگوار!