Format a date in XML via XSLT

Date formatting is not easy in XSLT 1.0. Probably the most elegant way is to write a short XSLT extension function in C# for date formatting. Here's an example:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
                xmlns:msxsl="urn:schemas-microsoft-com:xslt"
                xmlns:myExtension="urn:myExtension"
                exclude-result-prefixes="msxsl myExtension">
  <xsl:output method="xml" indent="yes"/>

  <msxsl:script implements-prefix="myExtension" language="C#">
    <![CDATA[
      public string FormatDateTime(string xsdDateTime, string format)
      {
          DateTime date = DateTime.Parse(xsdDateTime);
          return date.ToString(format); 
      }

    ]]>
  </msxsl:script>

  <xsl:template match="date">
    <formattedDate>
      <xsl:value-of select="myExtension:FormatDateTime(self::node(), 'd')"/>
    </formattedDate>
  </xsl:template>
</xsl:stylesheet>

With this input document

<?xml version="1.0" encoding="utf-8"?>
<date>2007-11-14T12:01:00</date>

you will get

<?xml version="1.0" encoding="utf-8"?>
<formattedDate>14.11.2007</formattedDate> 

The function formatting the date takes a date value as string and a format as described in DateTime.ToString Method. Using .NET's DateTime struct gives you parsing arbitrary XSD datetime values (including time zone specifiers), timezone calculation and localized output for free.

However, be aware that there is one caveat (http://support.microsoft.com/kb/316775) with msxml script extensions: Each time you load the XSLT an assembly containing the script code is generated dynamically and loaded into memory. Due to the design of the .NET runtime, this assembly cannot be unloaded. That's why you have to make sure that your XSLT is only loaded once (and then cached for further re-use). This is especially important when running inside IIS.


Here are a couple of 1.0 templates that you can use:-

<xsl:template name="formatDate">
    <xsl:param name="dateTime" />
    <xsl:variable name="date" select="substring-before($dateTime, 'T')" />
    <xsl:variable name="year" select="substring-before($date, '-')" />
    <xsl:variable name="month" select="substring-before(substring-after($date, '-'), '-')" />
    <xsl:variable name="day" select="substring-after(substring-after($date, '-'), '-')" />
    <xsl:value-of select="concat($day, ' ', $month, ' ', $year)" />
</xsl:template>

<xsl:template name="formatTime">
    <xsl:param name="dateTime" />
    <xsl:value-of select="substring-after($dateTime, 'T')" />
</xsl:template>

Call them with:-

    <xsl:call-template name="formatDate">
        <xsl:with-param name="dateTime" select="xpath" />
    </xsl:call-template>

and

    <xsl:call-template name="formatTime">
        <xsl:with-param name="dateTime" select="xpath" />
    </xsl:call-template>

where xpath is the path to an element or attribute that has the standard date time format.