'Format a date in XML via XSLT

When I use the XML serializer to serialize a DateTime, it is written in the following format:

<Date>2007-11-14T12:01:00</Date>

When passing this through an XSLT stylesheet to output HTML, how can I format this? In most cases I just need the date, and when I need the time I of course don't want the "funny T" in there.



Solution 1:[1]

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.

Solution 2:[2]

John Workman discusses this issue at length and gives several solutions in this discussion[1] on his blog. Basically, parse the individual date components and recombine in whatever order you wish. For your case, a pure XSLT 1.0+ version would be:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="date">
<!-- converts FROM <date>2001-12-31T12:00:00</date> TO some new format (DEFINED below) -->
<xsl:template name="FormatDate">
<xsl:param name="DateTime" />

<xsl:variable name="year" select="substring($DateTime,1,4)" />
<xsl:variable name="month-temp" select="substring-after($DateTime,'-')" />
<xsl:variable name="month" select="substring-before($month-temp,'-')" />
<xsl:variable name="day-temp" select="substring-after($month-temp,'-')" />
<xsl:variable name="day" select="substring($day-temp,1,2)" />
<xsl:variable name="time" select="substring-after($DateTime,'T')" />
<xsl:variable name="hh" select="substring($time,1,2)" />
<xsl:variable name="mm" select="substring($time,4,2)" />
<xsl:variable name="ss" select="substring($time,7,2)" />

<!-- EUROPEAN FORMAT -->
<xsl:value-of select="$day"/>
<xsl:value-of select="'.'"/> <!--18.-->
<xsl:value-of select="$month"/>
<xsl:value-of select="'.'"/> <!--18.03.-->
<xsl:value-of select="$year"/>
<xsl:value-of select="' '"/> <!--18.03.1976 -->
<xsl:value-of select="$hh"/>
<xsl:value-of select="':'"/> <!--18.03.1976 13: -->
<xsl:value-of select="$mm"/>
<xsl:value-of select="':'"/> <!--18.03.1976 13:24 -->
<xsl:value-of select="$ss"/> <!--18.03.1976 13:24:55 -->
<!-- END: EUROPEAN FORMAT -->

</xsl:template>

Another format (REPLACEs the EUROPEAN FORMAT section):

<!-- Long DATE FORMAT -->
<xsl:choose>
<xsl:when test="$month = '1' or $month= '01'">January</xsl:when>
<xsl:when test="$month = '2' or $month= '02'">February</xsl:when>
<xsl:when test="$month= '3' or $month= '03'">March</xsl:when>
<xsl:when test="$month= '4' or $month= '04'">April</xsl:when>
<xsl:when test="$month= '5' or $month= '05'">May</xsl:when>
<xsl:when test="$month= '6' or $month= '06'">June</xsl:when>
<xsl:when test="$month= '7' or $month= '07'">July</xsl:when>
<xsl:when test="$month= '8' or $month= '08'">August</xsl:when>
<xsl:when test="$month= '9' or $month= '09'">September</xsl:when>
<xsl:when test="$month= '10'">October</xsl:when>
<xsl:when test="$month= '11'">November</xsl:when>
<xsl:when test="$month= '12'">December</xsl:when>
</xsl:choose> 
<xsl:value-of select="' '"/> <!--January -->
<xsl:value-of select="$day"/> <!--January 12 -->
<xsl:value-of select="','"/> <!--January 12,-->
<xsl:value-of select="' '"/> <!--January 12, -->
<xsl:value-of select="$year"/> <!--January 12, 2001-->
<!-- END: Long DATE FORMAT -->

You can recombine the elements in any way you choose.

[1] http://geekswithblogs.net/workdog/archive/2007/02/08/105858.aspx @@ http://archive.is/4Hjep

Solution 3:[3]

Apologies for commenting on this old thread but for others finding it like me you could also use javascript if you are using an MS transformer:

Declare the "msxsl" namespace:

xmlns:msxsl="urn:schemas-microsoft-com:xslt" 

Declare a namespace for your script:

xmlns:js="urn:custom-javascript" 

(Optional) Omit the prefixes from the output:

exclude-result-prefixes="msxsl js" 

So you end up with an xsl declaration like this:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
  xmlns:msxsl="urn:schemas-microsoft-com:xslt"
  xmlns:js="urn:custom-javascript"
  exclude-result-prefixes="msxsl js">

Write the JavaScript in the msxsl:script element:

<msxsl:script language="JavaScript" implements-prefix="js"> 
<![CDATA[ 
function javascriptFunction(dateValue){
  var date = new Date(dateValue);
  if(!isNaN(date)) return date.toLocaleString();
  return dateValue;
}
]]>
</msxsl:script>

Call your JavaScript function (using the XPath syntax '.' denoting 'this node'):

<xsl:value-of select="js:javascriptFunction(string(.))"/>

NB: As of writing there doesn't seem to be an (xsl) way to include external js files (eg. jquery library). This could be done by parsing the xsl file server side before the transformation and adding the js file contents as a string into a CDATA section. I started to go down this route myself but concluded that if you need this level of functionality it might be better placed in a different part of the pipeline.

source: http://dev.ektron.com/kb_article.aspx?id=482
ref: http://www.ibm.com/developerworks/xml/library/x-tipxsltjs/index.html

Solution 4:[4]

correction to roy's post: the day from the function will always get the month value. Use the following:

<xsl:variable name="year" select="substring($dateTime,1,4)" />
<xsl:variable name="month-temp" select="substring-after($dateTime,'-')" />
<xsl:variable name="month" select="substring-before($month-temp,'-')" />
<xsl:variable name="day-temp" select="substring-after($month-temp,'-')" />
<xsl:variable name="day" select="substring($day-temp,1,2)" />
<xsl:variable name="time" select="substring-after($dateTime,'T')" />
<xsl:variable name="hh" select="substring($time,1,2)" />
<xsl:variable name="mm" select="substring($time,4,2)" />
<xsl:variable name="ss" select="substring($time,7,2)" />

<xsl:value-of select="concat($month,'/',$day,'/',$year,' ',$hh,':',$mm,':',$ss)" />

Solution 5:[5]

Thanks, this post helped a lot.

I was transforming an RSS feed which uses the following date format: Mon, 04 Apr 2011 23:18:00 -0700. Here is the named template I used to parse it.

<!--Parse date format: Mon, 04 Apr 2011 23:18:00 -0700-->
<xsl:template name="formatDate">

    <xsl:param name="dateIn" />

    <xsl:variable name="day" select="substring($dateIn, 0, 3)" />
    <xsl:variable name="date" select="substring($dateIn, 6, 2)" />
    <xsl:variable name="month" select="substring($dateIn, 9, 3)" />
    <xsl:variable name="year" select="substring($dateIn, 13, 4)" />

    <xsl:variable name="hour" select="substring($dateIn, 18, 2)" />
    <xsl:variable name="min" select="substring($dateIn, 21, 2)" />
    <xsl:variable name="sec" select="substring($dateIn, 24, 2)" />

    <xsl:value-of select="concat($date, ' ', $month, ' ', $year, ' ', $hour, ':', $min, ':', $sec)" />

</xsl:template>

Solution 6:[6]

<xsl:template match="date">
     <xsl:copy>
         <xsl:call-template name="formatdate">
             <xsl:with-param name="DateTimeStr" select="."/>
        </xsl:call-template>
     </xsl:copy>
  </xsl:template>

  <xsl:template name="formatdate">
     <xsl:param name="DateTimeStr" />

     <!-- input format xslt datetime string -->
     <!-- output format mm/dd/yyyy -->

     <xsl:variable name="datestr">
         <xsl:value-of select="substring-before($DateTimeStr,'T')" />
     </xsl:variable>

     <xsl:variable name="mm">
         <xsl:value-of select="substring($datestr,6,2)" />
     </xsl:variable>

     <xsl:variable name="dd">
        <xsl:value-of select="substring($datestr,9,2)" />
     </xsl:variable>

     <xsl:variable name="yyyy">
        <xsl:value-of select="substring($datestr,1,4)" />
     </xsl:variable>

     <xsl:value-of select="concat($mm,'/', $dd, '/', $yyyy)" />
  </xsl:template>

This worked for me. You can check other options at :

https://blog.fpmurphy.com/2008/05/xslt-datetime-formatting.html

Solution 7:[7]

With XSLT2 lib, you can

  • parse the text : get the dateTime format with xs:dateTime(text())
  • format the dateTime format with the XSLT 2 function : format-dateTime [*]

[*] https://www.w3.org/TR/xslt20/#function-format-dateTime

With your XML sample

<Date>2007-11-14T12:01:00</Date>

The XSL snippet

<p>My date : <xsl:value-of select="format-dateTime(xs:dateTime(text()), '[Y0001]-[M01]-[D01] [H01]:[m]:[s]')"/></p>

The result is

<p>My date : 2007-11-14 12:01:00</p>

Solution 8:[8]

Me and my brother wrote this, and it helped me. I figured I would post for the next person. Enjoy!

Example Image

<xsl:template name="timestamp">
    <!-- transform an ISO 8601 timestamp into a human readable representation -->
    <xsl:param name="iso-timestamp" />
    <!-- converts FROM <date>2001-12-31T12:00:00</date> TO some new format (DEFINED below) -->

    <xsl:variable name="year" select="substring($iso-timestamp,1,4)" />
    <xsl:variable name="month-temp" select="substring-after($iso-timestamp,'-')" />
    <xsl:variable name="month" select="substring-before($month-temp,'-')" />
    <xsl:variable name="day-temp" select="substring-after($month-temp,'-')" />
    <xsl:variable name="day" select="substring($day-temp,1,2)" />
    <xsl:variable name="time" select="substring-after($iso-timestamp,'T')" />
    <xsl:variable name="hh" select="substring($time,1,2)" />
    <xsl:variable name="mm" select="substring($time,4,2)" />
    <xsl:variable name="ss" select="substring($time,7,2)" />
    
    <xsl:choose>
        <xsl:when test="$month = '1' or $month= '01'">January</xsl:when>
        <xsl:when test="$month = '2' or $month= '02'">February</xsl:when>
        <xsl:when test="$month= '3' or $month= '03'">March</xsl:when>
        <xsl:when test="$month= '4' or $month= '04'">April</xsl:when>
        <xsl:when test="$month= '5' or $month= '05'">May</xsl:when>
        <xsl:when test="$month= '6' or $month= '06'">June</xsl:when>
        <xsl:when test="$month= '7' or $month= '07'">July</xsl:when>
        <xsl:when test="$month= '8' or $month= '08'">August</xsl:when>
        <xsl:when test="$month= '9' or $month= '09'">September</xsl:when>
        <xsl:when test="$month= '10'">October</xsl:when>
        <xsl:when test="$month= '11'">November</xsl:when>
        <xsl:when test="$month= '12'">December</xsl:when>
    </xsl:choose>       

    <xsl:value-of select="' '"/>
    <xsl:value-of select="$day"/> 
    <xsl:value-of select="','"/> 
    <xsl:value-of select="' '"/> 
    <xsl:value-of select="$year"/> 
    <xsl:value-of select="' '"/>
    <xsl:choose>
        <xsl:when test="$hh = '0' or $hh= '12'">01</xsl:when>
        <xsl:when test="$hh = '1' or $hh= '01'">01</xsl:when>
        <xsl:when test="$hh = '2' or $hh= '02'">02</xsl:when>
        <xsl:when test="$hh= '3' or $hh= '03'">03</xsl:when>
        <xsl:when test="$hh= '4' or $hh= '04'">04</xsl:when>
        <xsl:when test="$hh= '5' or $hh= '05'">05</xsl:when>
        <xsl:when test="$hh= '6' or $hh= '06'">06</xsl:when>
        <xsl:when test="$hh= '7' or $hh= '07'">07</xsl:when>
        <xsl:when test="$hh= '8' or $hh= '08'">08</xsl:when>
        <xsl:when test="$hh= '9' or $hh= '09'">09</xsl:when>
        <xsl:when test="$hh= '10'">10</xsl:when>
        <xsl:when test="$hh= '11'">11</xsl:when>
        <xsl:when test="$hh= '12'">12</xsl:when>
        <xsl:when test="$hh= '13'">01</xsl:when>
        <xsl:when test="$hh= '14'">02</xsl:when>
        <xsl:when test="$hh= '15'">03</xsl:when>
        <xsl:when test="$hh= '16'">04</xsl:when>
        <xsl:when test="$hh= '17'">05</xsl:when>
        <xsl:when test="$hh= '18'">06</xsl:when>
        <xsl:when test="$hh= '19'">07</xsl:when>
        <xsl:when test="$hh= '20'">08</xsl:when>
        <xsl:when test="$hh= '21'">09</xsl:when>
        <xsl:when test="$hh= '22'">10</xsl:when>
        <xsl:when test="$hh= '23'">11</xsl:when>
    </xsl:choose>
    <xsl:value-of select="':'"/>
    <xsl:value-of select="$mm"/>
    <xsl:value-of select="':'"/>
    <xsl:value-of select="$ss"/>
    <xsl:value-of select="'  '"/>
    <xsl:choose>
        <xsl:when test="$hh = '0' or $hh = '00' or $hh= '1' or $hh= '01' or $hh = '2' or $hh = '02' or $hh = '3' or $hh = '03' or $hh = '4' or $hh = '04' or $hh = '5' or $hh = '05' or $hh = '6' or $hh = '06' or $hh = '7' or $hh = '07' or $hh = '8' or $hh = '08' or $hh = '9' or $hh = '09' or $hh = '10' or $hh = '11'">AM</xsl:when>
        <xsl:when test="$hh = '12' or $hh= '13' or $hh = '14' or $hh = '15' or $hh = '16' or $hh = '17' or $hh = '18' or $hh = '19' or $hh = '20' or $hh = '21' or $hh = '22' or $hh = '23'">PM</xsl:when>
    </xsl:choose>
    <!-- <xsl:value-of select="concat(substring($iso-timestamp, 0, 11), ' ', substring($iso-timestamp, 12, 5))" /> -->
</xsl:template>

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2
Solution 3 Rui Jarimba
Solution 4 Andy
Solution 5 Jibran
Solution 6 Yogesh Sanchihar
Solution 7 Fred Laurent
Solution 8 Blake Drumm