Recipe 4.4. Calculating Julian and Absolute Day Numbers from a Specified DateProblemYou have a date and would like to know the corresponding Julian day number and/or absolute day number.
SolutionXSLT 1.0This template will give you the Julian day, given the year, month, and day: <xsl:template name="ckbk:calculate-julian-day"> <xsl:param name="year"/> <xsl:param name="month"/> <xsl:param name="day"/> <xsl:variable name="a" select="floor((14 - $month) div 12)"/> <xsl:variable name="y" select="$year + 4800 - $a"/> <xsl:variable name="m" select="$month + 12 * $a - 3"/> <xsl:value-of select="$day + floor((153 * $m + 2) div 5) + $y * 365 + floor($y div 4) - floor($y div 100) + floor($y div 400) - 32045"/> </xsl:template> Once you have a way to calculate the Julian day number, it is easy to create a template for determining the number of days between any two dates: <xsl:template name="ckbk:date-difference"> <xsl:param name="from-year"/> <xsl:param name="from-month"/> <xsl:param name="from-day"/> <xsl:param name="to-year"/> <xsl:param name="to-month"/> <xsl:param name="to-day"/> <xsl:variable name="jd1"> <xsl:call-template name="ckbk:calculate-julian-day"> <xsl:with-param name="year" select="$from-year"/> <xsl:with-param name="month" select="$from-month"/> <xsl:with-param name="day" select="$from-day"/> </xsl:call-template> </xsl:variable> <xsl:variable name="jd2"> <xsl:call-template name="ckbk:calculate-julian-day"> <xsl:with-param name="year" select="$to-year"/> <xsl:with-param name="month" select="$to-month"/> <xsl:with-param name="day" select="$to-day"/> </xsl:call-template> </xsl:variable> <xsl:value-of select="$jd1 - $jd2"/> </xsl:template> The following templates convert from a Julian day to a Gregorian date in the form YYYY/MM/DD. Use substring-before, substring-after, and TRanslate to parse or convert to the conventions of a particular locale: <xsl:template name="ckbk:julian-day-to-julian-date"> <xsl:param name="j-day"/> <xsl:call-template name="ckbk:julian-or-gregorian-date-elem"> <xsl:with param name="b" select="0"/> <xsl:with param name="c" select="$j-day + 32082"/> </xsl:call-template> </xsl:template> <xsl:template name="ckbk:julian-day-to-gregorian-date"> <xsl:param name="j-day"/> <xsl:variable name="a" select="$j-day + 32044"/> <xsl:variable name="b" select="floor((4 * $a + 3) div 146097)"/> <xsl:variable name="c" select="$a - 146097 * floor($b div 4)"/> <xsl:call-template name="ckbk:julian-or-gregorian-date-elem"> <xsl:with param name="b" select="$b"/> <xsl:with param name="c" select="$c"/> </xsl:call-template> </xsl:template> <!-- A utility that is used for both Gregorian and Julian calendars. --> <xsl:template name="ckbk:julian-or-gregorian-date-elem"> <xsl:param name="b"/> <xsl:param name="c"/> <xsl:variable name="d" select="floor((4 * $c + 3) div 1461)"/> <xsl:variable name="e" select="$c - floor((1461 * $d) div 4)"/> <xsl:variable name="m" select="floor((5 * $e + 2) div 153)"/> <xsl:variable name="day" select="$e - floor((153 * $m + 2) div 5) + 1"/> <xsl:variable name="month" select="$m + 3 - (12 * floor($m div 10))"/> <xsl:variable name="year" select="100 * $b + $d - 4800 + floor($m div 10)"/> <xsl:value-of select="concat($year,'/',$month,'/',$day)"/> </xsl:template> You can easily convert between Julian days and absolute days with the following templates: <xsl:template name="ckbk:julian-day-to-absolute-day"> <xsl:param name="j-day"/> <xsl:value-of select="$j-day - 1721425"/> </xsl:template> <xsl:template name="ckbk:absolute-day-to-julian-day"> <xsl:param name="abs-day"/> <xsl:value-of select="$abs-day + 1721425"/> </xsl:template> You can then express absolute day/Gregorian conversions in terms of the existing Julian day/Gregorian conversions: <xsl:template name="ckbk:date-to-absolute-day"> <xsl:param name="year"/> <xsl:param name="month"/> <xsl:param name="day"/> <xsl:call-template name="ckbk:julian-day-to-absolute-day"> <xsl:with-param name="j-day"> <xsl:call-template name="ckbk:date-to-julian-day"> <xsl:with-param name="year" select="$year"/> <xsl:with-param name="month" select="$month"/> <xsl:with-param name="day" select="$day"/> </xsl:call-template> </xsl:with-param> </xsl:call-template> </xsl:template> <xsl:template name="ckbk:absolute-day-to-date"> <xsl:param name="abs-day"/> <xsl:call-template name="ckbk:julian-day-to-date"> <xsl:with-param name="j-day"> <xsl:call-template name="ckbk:absolute-day-to-julian-day"> <xsl:with-param name="abs-day" select="$abs-day"/> </xsl:call-template> </xsl:with-param> </xsl:call-template> </xsl:template> XSLT 2.0The need for Julian and absolute day functions in XSLT 2.0 is diminished because date math is directly supported. For example, <dateDiff> <xsl:value-of select="xs:date('2005-02-21') - xs:date('2005-01-01')"/> </dateDiff> results in: <dateDiff>P51D</dateDiff> However, some applications may need these values for other purposes. Here we choose to implement the 1.0 templates in terms of functions that take an xs:date rather than the individual components: <xsl:function name="ckbk:calculate-julian-day"> <xsl:param name="date" as="xs:date"/> <xsl:variable name="year" select="year-from-date($date)" as="xs:integer"/> <xsl:variable name="month" select="month-from-date($date)" as="xs:integer"/> <xsl:variable name="day" select="month-from-date($date)" as="xs:integer"/> <xsl:variable name="a" select="(14 - $month) idiv 12" as="xs:integer"/> <xsl:variable name="y" select="$year + 4800 - $a" as="xs:integer"/> <xsl:variable name="m" select="$month + 12 * $a - 3" as="xs:integer"/> <xsl:sequence select="$day + ((153 * $m + 2) idiv 5) + $y * 365 + floor($y div 4) - ($y idiv 100) + ($y idiv 400) - 32045"/> </xsl:function> DiscussionThe Julian day and absolute day are useful because they greatly simplify other date algorithms. Other examples in this chapter reuse these conversions extensively. These numbering schemes act as a common currency for all the calendar systems in this chapter. Should you ever find yourself needing to convert a Hebrew date to a Muslim date, the sequence Muslim to Absolute to Hebrew will do the trick. |