Recipe 4.4. Calculating Julian and Absolute Day Numbers from a Specified Date
Problem
You have a date and would like to know
the corresponding Julian day number and/or absolute day number.
Do not make the mistake of confusing the Julian day with the Julian
calendar. Joseph J. Scaliger has invented the Julian period so
that every year could be associated with a positive number without
having to worry about BC/AD. The period starts on January 1, 4713 BC
(according to the Julian calendar) and lasts for 7980 years.
Astronomers have used the Julian period to assign a unique number to
every day since 1 January 4713 BC. This is the Julian
day. As I write this chapter, the Julian day is 2,452,376.
The curious can use the code in this section to determine the actual
date. Another absolute numbering scheme used by N.
Dershowitz
and E. Reingold
in their calendar algorithms begins on January 1, 1 AD. I refer to a
number in their system as an absolute day
number. Absolute day 1 corresponds to Julian Day
1,721,426.
|
Solution
XSLT 1.0
This 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.0
The 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>
Discussion
The 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.
|