You need to work with dates in the International standard ISO-8601 calendar.[5]
A basic facility you need to work with ISO dates (and, later, for determining certain holidays) is a function for finding the absolute day of the kth day on or before a specific absolute day. For example, the first Monday (k = 1) on or before January 4, 2004 (absolute day 731,584) is December 29, 2003 (absolute day 731,578):
<xsl:template name="date:k-day-on-or-before-abs-day"> <xsl:param name="abs-day"/> <xsl:param name="k"/> <xsl:value-of select="$abs-day - (($abs-day - $k) mod 7)"/> </xsl:template>
You can now convert ISO dates to absolute days, which is a simple matter of determining the number of absolute days in prior years and adding in the remaining days in the given ISO date:
<xsl:template name="date:iso-date-to-absolute-day"> <xsl:param name="iso-week"/> <xsl:param name="iso-day"/> <xsl:param name="iso-year"/> <xsl:variable name="jan-4-of-year"> <xsl:call-template name="date:date-to-absolute-day"> <xsl:with-param name="year" select="$iso-year"/> <xsl:with-param name="month" select="1"/> <xsl:with-param name="day" select="4"/> </xsl:call-template> </xsl:variable> <xsl:variable name="days-in-prior-yrs"> <xsl:call-template name="date:k-day-on-or-before-abs-day"> <xsl:with-param name="abs-day" select="$jan-4-of-year"/> <xsl:with-param name="k" select="1"/> </xsl:call-template> </xsl:variable> <xsl:variable name="days-in-prior-weeks-this-yr" select="7 * ($iso-week - 1)"/> <xsl:variable name="prior-days-this-week" select="$iso-day - 1"/> <xsl:value-of select="$days-in-prior-yrs + $days-in-prior-weeks-this-yr + $prior-days-this-week"/> </xsl:template>
To convert from absolute
days to an ISO date, the code will first try to establish the year by
making a guess that it is the same as the Gregorian minus 3 days.
This guess can be wrong only if the absolute day is actually on Jan 1
to Jan 3 of the following year. To correct for the possible
off-by-one mistake, a comparison is made to Jan 1 of the following
year using the iso-date-to-absolute-day
code
already on hand. Having firmly established the ISO year, the week and
day follow by computing the offset from Jan 1 of that year. We return
the ISO date formatted as
year
-week-day
.
This format is an ISO convention to prevent ISO dates from being
confused with Gregorian dates:
<xsl:template name="date:absolute-day-to-iso-date"> <xsl:param name="abs-day"/> <xsl:variable name="d"> <xsl:call-template name="date:absolute-day-to-date"> <xsl:with-param name="abs-day" select="$abs-day - 3"/> </xsl:call-template> </xsl:variable> <xsl:variable name="approx" select="substring-before($d,'/')"/> <xsl:variable name="iso-year"> <xsl:variable name="a"> <xsl:call-template name="date:iso-date-to-absolute-day"> <xsl:with-param name="iso-week" select="1"/> <xsl:with-param name="iso-day" select="1"/> <xsl:with-param name="iso-year" select="$approx + 1"/> </xsl:call-template> </xsl:variable> <xsl:choose> <xsl:when test="$abs-day >= $a"> <xsl:value-of select="$approx + 1"/> </xsl:when> <xsl:otherwise> <xsl:value-of select="$approx"/> </xsl:otherwise> </xsl:choose> </xsl:variable> <xsl:variable name="date:iso-week"> <xsl:variable name="a"> <xsl:call-template name="date:iso-date-to-absolute-day"> <xsl:with-param name="iso-week" select="1"/> <xsl:with-param name="iso-day" select="1"/> <xsl:with-param name="iso-year" select="$iso-year"/> </xsl:call-template> </xsl:variable> <xsl:value-of select="1 + floor(($abs-day - $a) div 7)"/> </xsl:variable> <xsl:variable name="iso-day"> <xsl:variable name="a" select="$abs-day mod 7"/> <xsl:choose> <xsl:when test="not($a)"> <xsl:value-of select="7"/> </xsl:when> <xsl:otherwise> <xsl:value-of select="$a"/> </xsl:otherwise> </xsl:choose> </xsl:variable> <xsl:value-of select="concat($iso-year,'-W',$iso-week,'-',$iso-day)"/> </xsl:template>
In European commercial and industrial applications, reference to a week of a year is often required. The ISO calendar specifies dates by using the Gregorian year, the week number (1-53) within the year, and the ordinal day of the week (1-7, where ISO mandates the first day of the week is Monday). A week that overlaps successive years is assigned to the year with the most days in that week. According to this rule, the first week of the ISO calendar year can begin as late as January 4 and as early as December 29 of the previous year. Likewise, the last week of the ISO calendar year can end as early as December 28 and as late as January 3 of the following year. For example, in 2004, ISO week 1 will actually start on December 29, 2003![6] To determine the start of the ISO week, then, you need to find the Monday on or before January 4.
You can see the ISO calendar in action at http://personal.ecu.edu/mccartyr/isowdcal.html.