Recipe4.7.Working with the ISO Calendar


Recipe 4.7. Working with the ISO Calendar

Problem

You need to work with dates in the International standard ISO-8601 calendar.[1]

[1] ISO is the International Organization for Standardization. (Not only does ISO roll off the tongue better than IOS but it comes from the Greek isos meaning equal.)

Readers familiar with XML Schema should not confuse the ISO calendar with the ISO format for standard Gregorian dates (e.g., 2002-04-12 or 2002-04-12T09:26:00). Both the calendar and the formatting standard are covered in the 8601 standard. This recipe is concerned with the ISO calendar.


Solution

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="ckbk: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="ckbk: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="ckbk: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="ckbk: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="ckbk:absolute-day-to-iso-date">      <xsl:param name="abs-day"/>            <xsl:variable name="d">        <xsl:call-template name="ckbk: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="ckbk: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="ckbk:iso-week">        <xsl:variable name="a">          <xsl:call-template name="ckbk: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>

Discussion

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 actually started on December 29, 2003![2] To determine the start of the ISO week, then, you need to find the Monday on or before January 4.

[2] And you thought standards were supposed to simplify life!

See Also

You can see the ISO calendar in action at http://personal.ecu.edu/mccartyr/isowdcal.html.




XSLT Cookbook
XSLT Cookbook: Solutions and Examples for XML and XSLT Developers, 2nd Edition
ISBN: 0596009747
EAN: 2147483647
Year: 2003
Pages: 208
Authors: Sal Mangano

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net