Neil Ang

Bersonal Plog

A stunning likeness of Neil Ang
Super Nerd

Generating a calendar using XSLT

Posted on .

Recently I was tasked to build an interactive calendar in XSLT. I wanted the calendar to always start on a Sunday as well as have clean markup. I couldn't find any good examples on the web while I was building it, so I decided to post the base of my solution.

Picture of final product

Insert the following variables and templates into your stylesheet:

<xsl:variable name="DisplayDate" select="date:date()"/> 
<xsl:variable name="Year" select="date:year($DisplayDate)"/> 
<xsl:variable name="Month" select="date:month-in-year($DisplayDate)"/> 
<xsl:variable name="MonthName" select="date:month-name($DisplayDate)" /> 
<xsl:variable name="NumberOfDaysInMonth"> 
  <xsl:call-template name="DaysInMonth"> 
    <xsl:with-param name="month" select="$Month" /> 
    <xsl:with-param name="year" select="$Year" /> 
  </xsl:call-template> 
</xsl:variable> 
<xsl:variable name="FirstDayInWeekForMonth">
  <xsl:choose>
    <xsl:when test="$Month &lt; 10">
      <xsl:value-of select="date:day-in-week(date:date(concat($Year,'-0', $Month, '-01')))" />
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="date:day-in-week(date:date(concat($Year,'-', $Month, '-01')))" />
    </xsl:otherwise>
  </xsl:choose>
</xsl:variable> 
<xsl:variable name="WeeksInMonth"><xsl:value-of select="($NumberOfDaysInMonth + $FirstDayInWeekForMonth - 1) div 7" /></xsl:variable> 

<xsl:template name="DaysInMonth"> 
  <xsl:param name="month"><xsl:value-of select="$Month" /></xsl:param> 
  <xsl:param name="year"><xsl:value-of select="$Year" /></xsl:param> 
  <xsl:choose> 
    <xsl:when test="$month = 1 or $month = 3 or $month = 5 or $month = 7 or $month = 8 or $month = 10 or $month = 12">31</xsl:when> 
    <xsl:when test="$month=2"> 
      <xsl:choose> 
        <xsl:when test="$year mod 4 = 0">29</xsl:when> 
        <xsl:otherwise>28</xsl:otherwise> 
      </xsl:choose> 
    </xsl:when> 
    <xsl:otherwise>30</xsl:otherwise> 
  </xsl:choose> 
</xsl:template> 

<xsl:template name="Calendar"> 
  <table summary="Monthly calendar"> 
    <caption> 
      <xsl:value-of select="$MonthName" /> 
      <xsl:text> </xsl:text> 
      <xsl:value-of select="$Year" /> 
    </caption> 
    <tr> 
      <th abbr="Sunday">Sun</th> 
      <th abbr="Monday">Mon</th> 
      <th abbr="Tuesday">Tue</th> 
      <th abbr="Wednesday">Wed</th> 
      <th abbr="Thursday">Thu</th> 
      <th abbr="Friday">Fri</th> 
      <th abbr="Saturday">Sat</th> 
    </tr> 
    <xsl:call-template name="CalendarWeek"/> 
  </table> 
</xsl:template> 

<xsl:template name="CalendarWeek"> 
  <xsl:param name="week">1</xsl:param> 
  <xsl:param name="day">1</xsl:param> 
  <tr> 
    <xsl:call-template name="CalendarDay"> 
      <xsl:with-param name="day" select="$day" /> 
    </xsl:call-template> 
  </tr> 
  <xsl:if test="$WeeksInMonth &gt; $week"> 
    <xsl:call-template name="CalendarWeek"> 
      <xsl:with-param name="week" select="$week + 1" /> 
      <xsl:with-param name="day" select="$week * 7 - ($FirstDayInWeekForMonth - 2)" /> 
    </xsl:call-template> 
  </xsl:if> 
</xsl:template> 

<xsl:template name="CalendarDay"> 
  <xsl:param name="count">1</xsl:param> 
  <xsl:param name="day" /> 
  <xsl:choose> 
    <xsl:when test="($day = 1 and $count != $FirstDayInWeekForMonth) or $day &gt; $NumberOfDaysInMonth"> 
      <td class="empty"><xsl:text disable-output-escaping="yes">&amp;nbsp;</xsl:text></td> 
      <xsl:if test="$count &lt; 7"> 
        <xsl:call-template name="CalendarDay"> 
          <xsl:with-param name="count" select="$count + 1" /> 
          <xsl:with-param name="day" select="$day" /> 
        </xsl:call-template> 
      </xsl:if> 
    </xsl:when> 
    <xsl:otherwise> 
      <td> 
        <xsl:value-of select="$day" /> 
      </td> 
      <xsl:if test="$count &lt; 7"> 
        <xsl:call-template name="CalendarDay"> 
          <xsl:with-param name="count" select="$count + 1" /> 
          <xsl:with-param name="day" select="$day + 1" /> 
        </xsl:call-template> 
      </xsl:if> 
    </xsl:otherwise> 
  </xsl:choose> 
</xsl:template>

For this code to work, you need to be using exslt.

<xsl:stylesheet version="1.0" 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
  xmlns:exslt="http://exslt.org/common" 
  xmlns:date="http://exslt.org/dates-and-times">

Once you have added the templates you can call the calendar like this:

<xsl:call-template name="Calendar" />

Finally add some CSS to the page so your calendar looks more presentable:

<style type="text/css" media="screen"> 
  table{ 
    border:1px solid #CCC; 
    border-collapse: collapse; 
  } 
  caption{ 
    font-weight:bold; 
    font-size:120%; 
    margin-bottom:8px; 
  } 
   
  td,th{ 
    text-align:center; 
    vertical-align:middle; 
    width:36px; 
    height:34px; 
    border:1px solid #CCC; 
    padding:0; 
  } 
   
  th{ 
    height:inherit; 
  } 
   
  .empty{ 
    background-color:#EEE; 
  } 
</style>

This is only the base of the calendar I built, and you can expand this to make it browse-able. But I will leave that for another post.