Recipe 4.8. Working with the Islamic CalendarProblemYou need to work with dates in the Islamic system.
SolutionThe last day of an Islamic month can be estimated quite accurately by assigning 30 days to odd months and 29 days to even months, except during a leap year: <xsl:template name="ckbk:last-day-of-islamic-month"> <xsl:param name="month"/> <xsl:param name="year"/> <xsl:variable name="islamic-leap-year" select="(11 * $year + 14) mod 30 < 11"/> <xsl:choose> <xsl:when test="$month mod 2 or ($month = 12 and $islamic-leap-year)"> <xsl:value-of select="30"/> </xsl:when> <xsl:otherwise> <xsl:value-of select="29"/> </xsl:otherwise> </xsl:choose> </xsl:template> The Islamic calendar began with the Hijra, Muhammad's emigration to Medina. For most Muslims, this date occurred on sunset of July 15, 622 AD (Julian calendar). This corresponds to absolute day 227,015, hence the offset of 227,014 in the otherwise straightforward calculation of the absolute day from the Islamic date: <xsl:template name="ckbk:islamic-date-to-absolute-day"> <xsl:param name="year"/> <xsl:param name="month"/> <xsl:param name="day"/> <xsl:value-of select="$day + 29 * ($month - 1) + floor($month div 2) + 354 * ($year - 1) + floor((11 * $year + 3) div 30) + 227014"/> </xsl:template> The absolute day to Islamic date conversion is different from the original Lisp code from which it was adapted. I use floating-point math to avoid a search technique employed in the Lisp implementation. The authors of the Lisp code desired to keep all calculations within 24-bit integer limits while retaining the greatest accuracy. However, their techniques did not translate well to XSLT. Given that XSLT 1.0 uses only floating-point math, it does not make sense to go through pains to avoid it. The numbers used stem from the average lunar month having 29.530555... days. The month number is approximated and then adjusted if it would result in a day with value less than 1. Once the year and month are established, the day can be computed as an offset from the first day of the year: <xsl:template name="ckbk:absolute-day-to-islamic-date"> <xsl:param name="abs-day"/> <xsl:variable name="year" select="floor(($abs-day - 227014) div 354.36667) + 1"/> <xsl:variable name="month"> <xsl:variable name="a" select="$abs-day - 227014 - floor((11 * $year + 3) div 30) - 354 * ($year - 1)"/> <xsl:variable name="approx" select="floor($a div 29.53056)+1"/> <xsl:choose> <xsl:when test="(29 * ($approx - 1) + floor($approx div 2)) - $a < 1"> <xsl:value-of select="$approx - 1"/> </xsl:when> <xsl:otherwise> <xsl:value-of select="$approx"/> </xsl:otherwise> </xsl:choose> </xsl:variable> <xsl:variable name="day"> <xsl:variable name="a"> <xsl:call-template name="ckbk:islamic-date-to-absolute-day"> <xsl:with-param name="year" select="$year"/> <xsl:with-param name="month" select="$month"/> <xsl:with-param name="day" select="1"/> </xsl:call-template> </xsl:variable> <xsl:value-of select="$abs-day - $a + 1"/> </xsl:variable> <xsl:value-of select="concat($year,'/',$month,'/',$day)"/> </xsl:template> DiscussionThe Hijri or Islamic calendar is interesting because it is based on purely lunar cycles, and thus the Muslim months are not fixed within the seasons. An Islamic year is approximately 354.36 days. The first year of the Islamic calendar is denoted 1 A.H. (After Hijra), Muhammad's flight from Mecca to Medina. The Islamic calendar has deep religious significance to devout Muslims and is almost always based on visual observations of the moon. Approximate calendars can be computed in advance by using algorithms, but they should generally be used only for rough planning. See AlsoYou can see the Muslim calendar in action at http://www.sufisattari.com/calendar.html. Differences in convention among various Islamic counties are described at http://www.math.nus.edu.sg/aslaksen/calendar/islamic.shtml. |