en-UShe-IL
You are here:   Blog
Register   |  Login

Blog Archive:

Maximize
* Can be used in order to search for older blogs Entries

Search in blogs


Blog Categories:

Maximize
* Can be used in order to search for blogs Entries by Categories

Blog Tags:

Maximize
* Can be used in order to search for blogs by keywords

TNWikiSummit


Awared MVP

 


Microsoft® Community Contributor 


Microsoft® Community Contributor


 Read this before you use the blog! Maximize

Recent Entries

Minimize
אוג9

Written by: ronen ariely
09/08/2021 16:32 RssIcon

מה זה לוח שנה היג'רי?

לוח שנה היג'רי הוא לוח השנה המוסלמי, אשר קובע את חגי האסלאם ומועדיו. ברוב העולם היום, נעשה שימוש בלוח השנה הגרגוריאני אשר קרוי גם לוח השנה האזרחי או הלוח הלועזי. ההגדרות של לוח השנה המוסלמי יוצרות אתגר גדול מאוד במיפוי של תאריכים מלוח השנה הגרגוריאני (זה הנהוג באירופה) אל תאריכים בלוח השנה ההיג'רי, והפוך. 

שרתי SQL Server מנסים לתת מענה, אם כי בהצלחה מוגבלת מאוד, למיפוי בין לוחות השנה השונים, בעזרת מספר ואריאציות שונות ללוח השנה המוסלמי. כפי שנראה היום בפוסט זה המימוש של שימוש בתאריכים בלוח השנה המוסלמי אינו טריוויאלי.

מיפוי בין תאריכים בלוח השנה הגריגוריאני לתאריכים בלוח השנה המוסלמי יכולים להעשות בעזרת הפונקציה CONVERT תוך שימוש בסטייל 130 או 131. על מנת להמיר מחרוזת עם ערך תאריך גרגוריאני לתאריך בסגנון היג'רי, SQL Server משתמש באלגוריתם הכוויתי (Kuwaiti algorithm).

-- CONVERT from Hijri date to Gregorian Date
SELECT CONVERT(DATETIME2, '01/01/0001', 131) -- 0622-07-18 00:00:00.0000000 ; first date in Hijri
GO
-- CONVERT from Gregorian date to Hijri Date text
DECLARE @DateTime AS DATETIME2
SET @DateTime = '0622-07-18'
SELECT @DateTime AS [Gregorian Date], CONVERT(VARCHAR(11),@DateTime,131) AS [Hijri date]
GO

היום בתאריך 2021-08-09 חוגגים המוסלמים את ראש השנה של שנת 1443

DECLARE @DateTime AS DATETIME2
SET @DateTime = '2021-08-09'
SELECT
    @DateTime AS [Gregorian Date],
    CONVERT(VARCHAR(11),@DateTime,131) AS [Hijri date]
GO

בוא נבדוק למשל מתי יהיה יום ההולדת הבא שלי, כדי שאף אחד לא ישכח לשלוח לי מתנות :-) 

על פי לוח השנה הגריגוריאני יום ההולדת הבא שלי יהיה בתאריך 27 בפברואר 2022

DECLARE @DateTime AS DATETIME2
SET @DateTime = '2022-02-27' -- My next birthday
SELECT
    @DateTime AS [Gregorian Date],
    CONVERT(VARCHAR(11),@DateTime,131) AS [Hijri date],
    CONVERT(NVARCHAR(11),@DateTime,130) AS [Hijri date]
GO

כפי שניתן לראות, יום ההולדת הבא שלי לפי הלוח המוסלמי יערך בתאריך 26/07/1443

אורך השנה:

לוח השנה המוסלמי הוא לוח שנה ירחי, כלומר מבוסס על מספר קבוע של פעמים שבהן הירח משלים הקפה שלמה סביב כדור הארץ. בלוח המוסלמי לא נהוג עיבור שנים וכל חודש מוסלמי הוא בן 29 או 30 יום. מכאן יוצא ששנה מוסלמית קצרה יותר משנה בלוח הגריגוריאני.

בואו נראה התנהגות מוזרה הקשורה להגדרות אלו. ניבדוק מתי חגגתי יום הולדת האחרון שלי השנה ובכמה אני אזדקן עד יום ההולדת הבא שלי.

DECLARE @DateTime AS DATETIME2
SET @DateTime = '2021-02-27' -- My next birthday
SELECT
    @DateTime AS [Gregorian Date],
    CONVERT(VARCHAR(11),@DateTime,131) AS [Hijri date],
    CONVERT(NVARCHAR(11),@DateTime,130) AS [Hijri date]
GO

האם זכרת לשלוח לי מתנות השנה ביום ההולדת שלי בתאריך 16/07/1442 ?!?

שמתם לב שלפי הלוח הנוצרי עברה בדיוק שנה אחת בין יום ההולדת שלי השנה בתאריך 2021-02-27 ועד יום ההולדת הבא שלי בתאריך 2022-02-27, אבל לפי הלוח המוסלמי אני אזדקן בשנה ועוד 10 ימים. טוב, אז המסקנה המיידיתי היא שאם אתם רוצים להיות צעירים יותר, כדאי להמשיך לעבוד בלוח השנה הגריגוריאני - ככה "תרוויחו" בערך 10 ימים בכל שנה :-)

דרך אגב, יכול להיות גרוע יותר - אם נולדת בשנה מעוברת ביום 29 בפברואר אז יש לך יום הולדת רק פעם ב 4 שנים.

out-of-range

השנה הראשונה בלוח השנה האיסלאמי נקבעה לפי הזמן בו מוחמד היגר ממכה למדינה בשנת 622 לספירה. היום הראשון בלוח השנה המוסלמי נקבע לתאריך הגרגייוני '0622-07-18'.

DECLARE @DateTime AS DATETIME2
SET @DateTime = '0622-07-18'
SELECT @DateTime AS [Gregorian Date], CONVERT(VARCHAR(11),@DateTime,131) AS [Hijri date]
GO

אז מה קורה אם אתה רוצה לעבוד עם נתונים לפני תאריך זה?

DECLARE @DateTime AS DATETIME2
SET @DateTime = '0622-07-17'
SELECT @DateTime AS [Gregorian Date], CONVERT(VARCHAR(11),@DateTime,131) AS [Hijri date]
GO

Error! The date provided is before the start of the Hijri calendar which in Microsoft's 'Kuwaiti Algorithm' is July 15th, 622 C.E. (Julian calendar) or July 18th, 622 C.E (proleptic Gregorian calendar).

טוב... נשמע הגיוני שלא נוכל לבצע המרה לתאריך שלא קיים

בשאילתה מעל אנחנו תחילה עושים שימוש בשרשרת הטקסט '0622-07-18' אשר מופיעה בפורמט ISO8601. טקסט בפורמט זה ניתן להמיר לתאריך ללא צורך בהגדרת ה style (אם כי אני תמיד ממלי. לעשות שימוש ב style באופן מפורש)

יתרה מכך, יום בלוח מוסלמי מתחיל בערב עם שקיעת השמש ומסתיים בשקיעתה הבאה ואינו תואם ליום בלוח השנה הגרגוריאני אשר מתחיל מחצות עד חצות... וזו רק ההתחלה. 

שאלה: האם השרת תומך בכך שיום מתחיל משעות הערב?!?

התשובה הקצרה היא לא!

קודם כל, אין בהגדרה של יום בלוח המוסלמי זמן מסוים לתחילת היום. היום נקבע לפי רדת החושך (אז מה אתם אומרים... תמיד אפשר להפעיל פנסים כדי להמשיך את היום או לשבת בחדר מואר כדי לדחות את סיום יום העבודה. לא 🤣)

פרט לכך, אין לנו שום דרך לבצע את חישובים על תאריכים בלוח השנה המוסלמי כמו למשל שימוש בפונקציה DATEDIFF. ההסבר לכך מתקשר לנקודה עליה נדון בהמשך

האם השרת באמת מאחסן תאריכים על פי הלוח המוסלמי?

ממש לא! 

שרתי SQL לא מאפשרים שמירה של תאריך לפי לוח שנה מסוים אלא רק מספקים את התמיכה בעבודה עם לוחות שנה שונים, אם על ידי שימוש באובייקטים שונים כגון בפונקיות CONVERT וכן FORMAT.

ניתן לאשר את הדבר על דיי שימוש בפקודה DBCC PAGE ולבדוק מה הערך הבינארי שהשרת שומר על הדיסק. מה שנגלה הוא שבמסד הנתונים הנתונים נשמרים לפי הלוח הגריגוריאני בפורמט מספרי אשר אינו קשור לפורמט טקסט של תאריך.

השימוש בפונקציה FORMAT

Inconsistency between using CONVERT and using FORMAt when working with Hijri Dates

השימוש בפונקציה FORMAT מאפשר לנו לבצע המרה של תאריך לפי הגדרות שפה מסוימת

SET LANGUAGE us_english
declare @DT datetime = '2013-01-12'
select FORMAT (@DT, 'yyyy-MM-dd', 'ar-SA' ) --Returns 1434-02-30
select FORMAT (@DT, 'yyyy-MM-dd', 'he-IL' ) --Returns 2013-12-01
select FORMAT (@DT, 'yyyy-MM-dd', 'at-KW' ) --Returns 2013-12-01
GO


במבט ראשוני נראה שזה פתרון פשוט לממש על מנת לבצע המרה של תאריך מלוח שנה גריגוריאני לתאריך המוסלמי, אבל מעשית ההתנהגות של FORMAT שונה מההתנהגות של CONVERT כי שהראתי לפני כמה חודשים בבלוג הבא:

https://ariely.info/Blog/tabid/83/EntryId/266/SQL-Server-Bugs-and-issues-when-working-with-Hijri-dates.aspx

זהירות! עבודה עם מנועי המרה שונים באינטרנט יכולים להוביל לתוצאות לא מצופות. יתרה מזה בהרבה כלי המרה בין תאריכם אתם יכולים למצו שגיות כדוגמה הבאה הלקוה מהאתר של islamicity. שימו לב לצילומי המסך הבאים אשר מראים כיצד אני ממיר שני תאריכים שונים ומקבל את אותה תוצאה! אני מניח שמקור הבעיה זהה לזה שבהבדל בין עבודה עם FORMAT לעבודה עם CONVERT - שימוש באלגוריתים שונה

-- Error since date not exists
SELECT CONVERT(DATETIME2, '30/2/1434', 131)
GO
-- Using CONVERT we shoudl use the date
SELECT CONVERT(DATETIME2, '1/3/1434', 131)
GO
-- and we see inconsistent result
-- but the site fits the using of FORMAT
DECLARE @DateTime AS DATETIME2
SET @DateTime = '2013-01-12' -- My next birthday
SELECT
    @DateTime AS [Gregorian Date],
    CONVERT(VARCHAR(11),@DateTime,131) AS [Hijri date]
GO
 


אז מה שנשאר לנו כרגע זה לאכל לכולם שנה טובה ולצאת לחגיגות