به نام خدا

در پارسي‌بلاگتبديل ساده تاريخ ميلادي به شمسي در SQL Server

پنج‌شنبه ۴ تير ۹۴ - ۹:۲۱ صبح

سال‌هاست كه براي ذخيره تاريخ در 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
همان پاسخ را
البته كه بسيار سريع‌تر
خواهد داد
با تشكر از اين برادر بزرگوار!


مطلب بعدي: دفترچه راهنماي خودروي پژوي آردي مطلب قبلي: دردسر شفافيّت

نظرات

hossein: عالي بود


خيلي ممنون پنج‌شنبه ۱۰ آبان ۹۷ - ۹:۱۳ عصر
پاسخ: در پناه خدا.
محمد: عاااااااااالي بود ممنونچهارشنبه ۹ خرداد ۹۷ - ۸:۴۵ صبح
پاسخ: تشكر. إن‌شاءالله موفق باشيد.
محسن: لطفا خروجي اين تاريخ رو ببينين چي ميده
("1988-09-23 00:00:00.000")
سه‌شنبه ۱۱ ارديبهشت ۹۷ - ۱۰:۱۸ صبح
پاسخ: :) ظاهراً شما پي‌نوشت ششم را مطالعه نفرموديد! پس يك نظري به آن بياندازيد. سالي كه شما نوشتيد خارج از دامنه محاسبه اين تابع است. اين تابع بر اساس يك فرمول مطلق عمل نمي‌نمايد. بلكه از يك روز خاصي شروع مي‌كند به شمارش روزها و ماه‌ها و سال‌ها. بنابراين بازه محدودي دارد. همان بازه‌اي كه در آن لحاظ شده است. ولي يك تابعي را يكي از مخاطبين طراحي نموده‌اند كه به نظر مي‌رسد بازه وسيع‌تري داشته باشند. لينك آن را در پي‌نوشت هفتم مي‌يابيد. موفق باشيد و در پناه خدا.
ميثم محمودي: با سلام.
جالب اينجاست بعد اين همه سال فقط همين يك مطلب در مورد تاريخ شمسي در sql توي اينترنت موجود بود. ميخواستم خدمت تون عرض كنم بنده خودم براي مشكلاتي كه داشتم ، اسكريپتي مشابه اسكريپت شما نوشته ام و الان چندين ساله كه دارم ازش استفاده مي كنم و بدون هيچ مشكلي داره كار مي كنه.
موردي كه الان پيش اومده اينكه نياز بنده پيشرفت كرده و من نياز به چندمين روز سال و چندمين هفته سال و ديتايي از اين دست دارم.
اسكريپتي بابت اين موضوع داريد؟ ممنون ميشم ازتون. دوشنبه ۲۰ فروردين ۹۷ - ۱۰:۰۸ صبح
پاسخ: سلام. در اين سايت: https://jdf.scr.ir/jdf كدهاي خوبي پيدا مي‌شود. زحمات زيادي هم كشيده‌اند. شايد بتوانيد قسمت‌هايي از كدهاي آن‌ها را تبديل به SQL نماييد. موفق باشيد.
نصير: بسيار عالي كار ميكنه
ممنون
خانواده اي را از نگراني درآورديد با اين فانكشن
خدا به شما خير بده
سه‌شنبه ۵ دي ۹۶ - ۳:۰۲ عصر
پاسخ: سلام. خدا را شكر. من هم سال‌هاست دارم از همين تابع استفاده مي‌كنم. تا به حال راضي بودم. تشكر.
مسعود نيك كار: سلام و احترام . ممكنه بفرمائيد كدهائي كه نوشته ايد را چگونه در sql server پياده كنم . ببخشيد مبتدي هستم .
يكشنبه ۱۷ ارديبهشت ۹۶ - ۱:۰۶ عصر
پاسخ: سلام. مي‌توانيد از آموزش‌هايي مانند: http://www.taktemp.com/1388/08/%D8%A2%D9%85%D9%88%D8%B2%D8%B4-sql-server-%D8%AC%D9%84%D8%B3%D9%87-%D9%87%D9%81%D8%AA%D9%85-2/ و موارد مشابهي مانند: http://barnamenevis.org/showthread.php?222041-%D8%A7%D8%B3%D8%AA%D9%81%D8%A7%D8%AF%D9%87-%D8%A7%D8%B2-function-%D8%AF%D8%B1-SQL يا http://www.hozhan.ir/Article/71/%D8%AA%D9%81%D8%A7%D9%88%D8%AA-Stored-Procedure-%D9%88-Function-%D8%AF%D8%B1-SQL-Server/ استفاده بفرماييد. در اينترنت اطلاعات زيادي در اين زمينه به زبان فارسي پيدا مي‌:كنيد. موفق باشيد.
سيد ضياء: سلام
خيلي خيلي ممنون بابت انتشار اين اسكريپ كاربردي و پي‌نوشت‌هاي بعد از اون!

تابع SDAT كه سريع‌تر خروجي رو توليد مي‌كنه و آقاي مهدي‌زاده لطف كردند و منتشرش كردند، در مورد «چندمين روز سال» يعني همون فرمت «SaalRooz» مشكل داره. به نظر مي‌رسه اين مشكل براي تاريخ‌هاي بين 11 دي تا 29 اسفند (مثلا 2015-01-01 تا 2015-03-20) ايجاد مي‌شه.
يك نمونه از مقايسه خروجي تابع G2J و SDAT:
http://s9.picofile.com/file/8273810250/DateDimension.PNG

به هر حال تشكر بابت اشتراك‌گذاري هر دو تابع.
موفق باشيد
دوشنبه ۱۷ آبان ۹۵ - ۹:۱۵ عصر
پاسخ: سلام. تشكر از اين كه به اين نكته اشاره فرموديد. اميدوارم ايشان مطلب شما را مشاهده كرده و تابع خود را اصلاح نمايند. تابع ايشان فرآيند به ظاهر بسيار پيچيده‌اي دارد كه فهم آن نياز به دقت و بررسي فراوان دارد و من نيز هنوز از آن سر در نياورده‌ام. اما اين تابعي كه بنده منبع اصلي آن را از كامنتي در سايت آقاي فعال‌خواه يافته‌ام، نوشته شده توسط «روح‌اله» در 29 آذر 1393، الگوريتم بسيار ساده‌اي دارد كه به راحتي درك مي‌شود. اگر چه كبيسه‌هاي پنج‌ساله در آن ديده نشده است، ولي هر زمان چنين كبيسه‌هايي رخ داد، به دليل سادگي فرآيند، با اضافه كردن يك IF مي‌توان مشكل را حل نمود. موفق باشيد.
بنده: الگوريتم تاريخ اشتباه استيكشنبه ۲۵ مهر ۹۵ - ۴:۲۹ عصر
پاسخ: همين الآن تابع G2J را مجدداً بر اساس همين كد كه در صفحه فوق آمده است در ديتابيس ساختم و با ورودي 2016/10/16 تست كردم و جواب گرفتم: يكشنبه 25 مهر 1395 كه كاملاً صحيح مي‌باشد. اين‌كه مي‌فرماييد الگوريتم اشتباه است، لطفاً بفرماييد كجاي آن غلط است تا اصلاح كنم. فعلاً خروجي تابع كه صحيح است. تشكر.
علي: سلام؛
با تشكر از كدهايي كه در اختيار بقيه گذاشتيد
نكته اي رو مي خواستم بگم كه به نظر مهم اومد و البته اشتباه خيلي از برنامه نويس هاي عزيز هست. داخل كد آخر براي خروجي از nvarchar(max) استفاده شده كه واقعا نبايد اين اتفاق بيوفته. همه ميدونن كه max بودن نوع متغير يعني فضاي هارد اشغال شده كه البته هم ارزون هست و هم فراوان. اما توجه داشته باشيد كه مموري و كش مورد استفاده SQL نه ارزون هست و نه فراوان. اگر از اين پروسيجر براي مثلا يك جدول Log استفاده كنيد ( مثلا براي 10000 ركورد ) اين قضيه خودش رو سرعت و خيلي چيزهاي ديگه نشون ميده.
nvarchar(30) براي خروجي اين پروسيجر كافي هست.
موفق باشيد
شنبه ۱۶ مرداد ۹۵ - ۲:۴۵ عصر
پاسخ: تشكر از نكته خوب و ارزشمندي كه فرموديد. به ميزان منابعي كه به اين طريق مصرف مي‌شود توجه نداشتم. خيلي ممنون.
احمدي: باسلام
كد معرفي شده با sql 2005 كار نميكنهپنج‌شنبه ۲۶ فروردين ۹۵ - ۲:۰۳ عصر
پاسخ: سلام. ولي كدي كه در داخل همين پست وبلاگي نوشته‌ام و جديداً ويرايش و اصلاح كردم به نظرم با هر نگارشي از SQL كار كند، زيرا از دستورات عمومي و ساده‌اي استفاده مي‌كند. چند روز پيش تست كردم و تاريخ درست را خروجي داد. راستي، فراموش نفرماييد كه ما براي اولين بار در SQL 2008 حروف فارسي به صورت مستقل داريم (Persian-100) در حالي كه در 2005 از عربي استفاده مي‌شود. دو حرف ي و ك در عربي با فارسي تفاوت دارند. وقتي حروف «ي» و «ك» فارسي بخواهند مورد استفاده قرار بگيرند معمولاً دچار مشكل مي‌شوند. اين احتمال را هم بررسي بفرماييد، با توجه به اين‌كه در ابتداي تابع از حروف فارسي استفاده شده است. براي تست، مي‌توانيد تمام بخش‌هاي فارسي داخل كد را موقتاً به انگليسي بر گردانيد و تست كنيد. اگر جواب داد مشكل از همين تفاوت فارسي و عربي‌ست. موفق باشيد.
احمدي: مشكل كوتيشن و تك كوتيشن نيست
شنبه ۲۱ فروردين ۹۵ - ۲:۴۵ عصر
پاسخ: من همين الآن كل كد موجود در لينك داده شده را كپي كردم در نوت‌پد، كوتيشن‌ها را با آپستروف جايگزين كردم، بردم داخل اديتور SQL Server 2008 اجرا كردم. تابع SDAT ساخته شد. با كد ( SELECT [dbo].[SDAT] ("2016/04/09" , "") ) تست كردم، پاسخ داد ( شنبه 21 فروردين 1395 ) كه درست بود. شايد نسخه SQL شما متفاوت است يا دليل ديگري. موفق باشيد.
احمدي:

سلام مجدد

كدي كه در لينك معرفي شده ، در سيستم من خطا ميگيره!

آيا لازمه كه كد ايشون اصلاح بشه؟ يا همين كد، كد نهايي هست؟

كه در اينصورت روي تعاريف متغير ها داراي اشكال هست

پنج‌شنبه ۱۹ فروردين ۹۵ - ۱:۰۵ عصر
پاسخ: سلام. احتمال مي‌رود فراموش فرموده باشيد كه كوتيشن‌مارك‌هاي داخل اين كد را با نشان آپستروف جايگزين فرماييد. كوتيشن‌ها دوتايي هستند، ولي آپستروف تكي‌ست. اگر اين جايگزيني انجام نشود، كد خطا مي‌دهد. يك بررسي بفرماييد.
احمدي:

سلام

اين كد را سال گذشته استفاده ميكردم درست كار ميكرد و لي امثال تاريخ ميلادي را كه به شمسي تغيير ميده تاريخ دو روز پيش را نشون ميدهو لطفا يه نگاه بهش بندازيد

سه‌شنبه ۱۷ فروردين ۹۵ - ۹:۲۹ صبح
پاسخ: سلام. صحيح مي‌فرماييد. اين به دليل محاسبه «كبيسه‌»هاست. سال‌هاي ميلادي كبيسه‌هاي خاص خود را دارند و سال‌هاي شمسي نيز. غير از اين‌كه هر چهار سال يك‌بار ما كبيسه داريم، در يك بازه زماني كبيسه‌هاي پنج‌ساله اتفاق مي‌افتند. كد فوق را چند ماه پيش اصلاح كردم كه مشكل كبيسه آن حل شده است. يك كد جديد هم يك بزرگواري نوشته‌اند كه خيلي سريع‌تر از كد فوق است. نشاني آن را در پي‌نوشت هفتم مي‌يابيد. مي‌توانيد از آن استفاده كنيد كه قطعاً بسيار بهتر است. موفق باشيد.
mir asghar mehdizadeh: كد در آدرس زير است جهت تست و استفاده ساير علاقمندان
اينجا
دوشنبه ۱۸ آبان ۹۴ - ۴:۳۵ عصر
پاسخ: تشكر.
MAMEHDI: با سلام و خسته نباشيد خدمت شما كدي را كه گذاشتيد بعلت اينكه روز به روز اضافه ميكند داراي حلقه طولاني است و روز هفته چه عددي و چه حرفي را اشتباه چاپ ميكند مثلا امروز كه يكشنبه 17 آبان 94 و دومين روز هفته است را ميگويد شنبه و اولين روز من داخل كد شما دست بردم و بسيار سريعتر شد و چون تقريبا 90% كد عوض شد هم اسم متغييرها را عوض كردم و هم تعداد آنها را كاهش دادم اگر خواستيد كدش را ميگزارم تا يك تست هم از لحاظ درستي و هم از لحاظ سرعت در ركوردهاي بالا با كد قبلي تست شود تا شايد نياز به جدول ديگر نباشد با تشكر
يكشنبه ۱۷ آبان ۹۴ - ۶:۱۲ عصر
پاسخ: سلام. متغيّر DayCnt را تغيير دادم و مشكل روز يكشنبه حل شد. اما نسبت به الگوريتم آن، بله صحيح مي‌فرماييد. الگوريتم خيلي كندي دارد، ولي قابل فهم‌تر نسبت به ساير الگوريتم‌ها. اگر لطف بفرماييد و كد را ايميل كنيد، آن را در همين پست وبلاگ معرفي خواهم كرد. من كدهاي مختلفي را ديده‌ام تا به حال. ولي علّت اين‌كه اين كد را انتخاب كردم، سادگي بي‌حدّ آن بود كه مي‌‌شود دقيقاً از اتفاقاتي كه در لوپ و حلقه مي‌افتد اطمينان حاصل كرد. البته كد فوق مربوط به بنده نيست و من نيز آن را از كامنت‌هاي وبلاگي ديگر برداشته و در آن دست برده‌ام كه نشاني‌اش در پست ذكر شده است. تشكر.
بازم علي: سلام.بايد بگم مشكل قبلي بنده حل شد اما يه ايراد و اون اينكه توي تابع دوم(G2J_Fast)نميتونيد فرمت خروجي را تعيين كنيم!
جمعه ۱ آبان ۹۴ - ۱۰:۱۶ عصر
پاسخ: سلام. آيا جاي كوتيشن‌مارك‌ها را با آپستروف عوض كرديد؟ من به جهت اين‌كه در وبلاگ نمي‌شد از آپستروف استفاده كرد، كوتيشن‌مارك قرار دادم. تابع به نحوي طراحي شده كه حتي اگر رشته اشتباهي هم در فرمت قرار دهيد، متوقف نشده و كارش را انجام مي‌دهد. يعني تنها كاري كه با رشته فرمت انجام مي‌پذيرد اين است كه پس از توليد تاريخ شمسي، با نمادهاي استفاده شده در رشته فرمت جايگزين مي‌گردد. پس منطقي نيست و نبايد درج فرمت مشكلي در اجراي تابع اصلي ايجاد نمايد. يك اشتباهي هم ممكن است صورت داده باشد و آن اين‌:كه: من دو عدد تابع G2J‌ نوشته‌ام؛ اولي بدون فرمت است و اصلاً چنين پارامتري ندارد و دومي دارد. شما ممكن است تابع اول را ساخته باشيد و معلوم است كه حالا نمي‌توانيد پارامتري به عنوان فرمت براي آن ارسال كنيد. تابع را باز كنيد و ببينيد كدام را ساخته‌ايد. اگر در سطر اول آن پارامتر فرمت ثبت نشده، آن تابع را حذف كنيد؛ DROP و يك بار ديگر تابع دوم G2J كه داراي دو پارامتر و آرگومان است @intDate DATETIME, @Format NVARCHAR را بسازيد.
علي: سلام
اولا كه خدا خير بهتون بده.
من يه سري مشكل دارم راهنمايي بفرماييد ممنون ميشم:
آخرين كد مربوط به ايجاد پروسيجر (كه داخلش يه جدول ايجاد ميكنه)پروسيجر ايجاد شده ولي جدول Miladi_Shamsi نه.چون وقتي تابع بعدي رو ميخوام اجرا كنم جدوله رو نميشناسه.چرا؟؟
و اينكه تابع اولي ايجاد شده ولي وقتي برا استفاده چند كاراكتر اسمشو ميزنم نمياردش كامل هم كه بنويسم جواب ميده ولي زير اسمش خط قرمز موج دار گذاشته چرا؟
ممنون
جمعه ۱ آبان ۹۴ - ۸:۵۸ عصر
پاسخ: الحمدلله كه اين مشكل حل شد. گاهي كمي با تأخير شناسايي توابع و جداول صورت مي‌پذيرد. من خودم معمولاً معطل نمي‌شوم تا نام جداول و توابع را خود ابزار مديريت ديتابيس كامل كند، اسم‌ها را كامل مي‌نويسم و اجرا مي‌كنم.
مرتضي: با سلام در خروجي نمايش زمان هم نياز دارم لطفا كمك كنيد تشكر
سه‌شنبه ۲۸ مهر ۹۴ - ۳:۲۰ عصر
پاسخ: زمان در SQL نياز به تبديل ندارد، مگر اين‌كه بخواهيد آن را به وقت تهران 3:30+ نمايش دهيد كه تبديل بخواهد. كافيست در آخرين خط كه خروجي ساخته مي‌شود (متغيّر DayDate@) ساعت و دقيقه را اضافه كنيد. تابع ()GETDATE تاريخ و زمان فعلي را مي‌دهد، حالا با تابع CONVERT و استفاده از كد 108 تنها ساعت و دقيقه و ثانيه را مي‌گيريد. شرح كامل آن و كدهاي مورد استفاده را در نشاني http://www.w3schools.com/sql/func_convert.asp مشاهده بفرماييد. به اين صورت: (CONVERT(VARCHAR(8), getdate(), 108
احمدي: الگوريتم كامل بود ، ممنون
سه‌شنبه ۳ شهريور ۹۴ - ۸:۴۹ صبح
پاسخ: موفق باشيد.
احمدي: در توضيح سوالم بايد بگم: اگه من تاريخ مبدا را 1/1/1394 در نظر بگيرم وقتي يكي يكي به اين تاريخ اضافه بكنم ،بخوام 1/2/1394 را ثبت كنم چكار بايد بكنم؟پنج‌شنبه ۲۹ مرداد ۹۴ - ۱۰:۴۸ صبح
پاسخ: روش‌هاي متنوعي براي اين كار وجود دارد. اولاً مي‌توانيد اين كار را در لايه برنامه‌نويسي با زباني كه كار مي‌كنيد، Net. يا php مثلاً انجام دهيد كه در آن صورت بايد يك مجموعه INSERT‌ بسازيد و به يك‌باره يا بخش‌بخش اجرا بفرماييد. يا اين‌كه برويد در لايه SQL و يك تابع به زبان T-SQL بنويسيد؛ مبدأ و مقصد را بگيرد و در يك لوپ انداخته، تمام ركوردها را مستقيم بسازد. از سوي ديگر نحوه ذخيره داده تاريخ هم متنوع است. ممكن است بخواهيد يك فيلد (CHAR(10 بسازيد و تاريخ شمسي را در قالب يك رشته ذخيره كنيد، يا ممكن است برويد سراغ smalldatetime و به صورت ميلادي تاريخ را ذخيره كرده و سپس هر بار موقع SELECT‌ كردن آن را دوباره به شمسي باز گردانيد. روشي هم وجود دارد كه با استفاده از كدهاي آماده‌اي كه در اينترنت يافت مي‌شود يك فيلد از نوع تاريخ شمسي در ديتابيس ايجاد كنيد. هر كدام از اين‌ها تكنيك خاص خود را دارد. موارد فوق را بررسي نموده، روش مناسب براي كار خود را بيابيد. موفق باشيد.
احمدي:

خيلي خيلي ممنون

فقط يه سوال:

من ميخوام يك تاريخ مبدا و يك تاريخ نهايي از كاربر بگيرم و به ازاي هر روز بين اين دوتا تاريخ يك ركورد ثبت كنم چطور ميتونم اين كارو انجام بدم؟

پنج‌شنبه ۲۹ مرداد ۹۴ - ۱۰:۴۴ صبح
پاسخ: به اين ترتيب: 1. تاريخ را به اجزاي تشكيل‌دهنده خود تقسيم كنيد؛ روز، ماه، سال، به عدد كانورت كرده و در سه متغير عددي قرار دهيد. 2. حالا يك حلقه بسازيد كه تكرار شود. 3. در هر تكرار يك واحد به متغير روز اضافه نمايد. 4. كنترل كند ماه بين 0 تا 7 است (31 روزه) يا بين 6 تا 12 (30 روزه) يا 12. 5. اگر ماه 12 است آيا سال كبيسه است (از يك سال كبيسه‌اي به عنوان مبدأ منها شده و بر 4 تقسيم گردد و اگر باقي‌مانده صفر شد كبيسه است). 6. اگر روز از مقدار ممكن در ماه بيشتر شد، يكي به ماه اضافه شده و روز يك شود. 7. مقدار ماه اگر از 12 بيشتر شده يكي به سال اضافه شده و ماه يك شود. 8. اكنون تاريخ فعلي به دست آمده و بايد يك INSERT ساخته شده و ركورد ايجاد گردد. 9. با تاريخ مقصد مقايسه شود و اگر به مقصد رسيده است از لوپ خارج گردد. 10. كار تمام است، مي‌تواند يك پيغام مناسب بازگرداند. اين الگوريتم به نظرم كامل و روشن باشد. موفق باشيد.
امير: سلام برادر عزيزم من كد شما رو نگاه كردم بسيار عالي بود و جاي تشكر فراوان داره من يك متغيير ديگه هم بهش اضافه كردم جهت تعيين خروجي يعني در زمان فراخواني تابع تعيين ميكنيد كه كدام نوع از خروجي هاي تابع براي شما برگردانده شود
بجاي اينكه تابع را ويرايش كنيد
ALTER FUNCTION [dbo].[G2J] ( @intDate DATETIME ,@OutPut_Mod as tinyint)
-----------------------------------------------------------------------------
IF @OutPut_Mod = 0
SET @DayDate = @shRooz + " " + LTRIM(STR(@shDay,2)) + " " + @shMaah + " " + STR(@shYear,4)
--پنجشنبه 17 ارديبهشت 1394

IF @OutPut_Mod = 1
SET @DayDate = LTRIM(STR(@shDay,2)) + " " + @shMaah + " " + STR(@shYear,4)
--17 ارديبهشت 1394

IF @OutPut_Mod = 2
SET @DayDate = STR(@shYear,4) + "/"+LTRIM(STR(@shMonth,2)) + "/" + LTRIM(STR(@shDay,2))
--1394/2/17

IF @OutPut_Mod = 3
SET @DayDate = REPLACE(RIGHT(STR(@shYear, 4), 4), " ", "0") + "/"+ REPLACE(STR(@shMonth, 2), " ", "0") + "/" + REPLACE(( STR(@shDay,2) ), " ", "0")
--1394/02/17
يكشنبه ۲۵ مرداد ۹۴ - ۱۰:۲۸ صبح
پاسخ: سلام. بله بسيار عالي‌ست. تشكر.
amir: اين كد اشتباه جواب نميده اشتباه چاپ مي كنهدوشنبه ۱۲ مرداد ۹۴ - ۱:۱۲ عصر
پاسخ: اگر منظور شما از «اشتباه چاپ كردن» اين است كه ترتيب قرارگيري روز و ماه و سال منظم نيست و در هنگام چاپ جابه‌جا قرار مي‌گيرد، اين كاملاً بستگي دارد به نرم‌افزار يا مرورگر مورد استفاده و كدهاي HTML به كاررفته؛ RTL بودن يا LTR‌ بودن. شما براي متناسب‌سازي خروجي تابع فوق با نياز خود كافيست آخرين بخش تابع را تغيير دهيد. وقتي مقدار به خروجي داده مي‌شود، بنده چند نوع تركيب را امتحان كرده و به صورت كامنت‌شده (غيرفعال) قرار داده‌ام. شما سه مقدار سال و ماه و روز را مي‌توانيد به هر ترتيبي كه مايل بوديد يا هستيد به خروجي ارسال بفرماييد. موفق باشيد.
بازگشتنسخه محلّي از نوشته‌هاي وبلاگ شايد سخن حق سال نشر13نوشته‌هاي وبلاگ بر اساس سال برچسب‌ها33نوشته‌هاي وبلاگ بر اساس برچسب بيشترين نظر32نوشته‌هاي وبلاگ با بيشترين تعداد نظر
صفحه اصليبازگشت به صفحه نخست سايت نوشته‌ها721طرح‌ها، برنامه‌ها و نوشته‌ها مكان‌ها65براي چه جاهايي نوشتم زمان‌ها22همه سال‌هايي كه نوشتم جستجودستيابي به نوشته‌ها از طريق جستجو وبلاگ846با استفاده از سامانه پارسي‌بلاگ نماها1چند فيلم كوتاه از فعاليت‌ها آواها7تعدادي فايل صوتي براي شنيدن سايت‌ها23معرفي سايت‌هاي طراحي شده نرم‌افزارها36سورس نرم‌افزارهاي خودم معرفي6معرفي طراح سايت و آثار و سوابق كاري او فونت‌هاي فارسي60تعدادي قلم فارسي كه معمولاً در نوشته‌هايم استفاده شده است بايگاني وبلاگ792نسخه محلّي از نوشته‌هاي وبلاگ
با اسكن باركد صفحه را باز كنيد
تماس پيامك ايميل ذخيره
®Movashah ©2018 - I.R.IRAN