Neil Ang

Developer

A stunning likeness of Neil Ang
Hello world

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.