Search code examples
xmldatedatetimexsltformatdatetime

Formatting a date in XSLT


Using format-date($date, 'yyyy-MM-dd') outputs a date as dd/MM/yyyy.

Detail

I'm integrating with a system which uses the below format for dates:

<SomeDateElement>
    <DAY>21</DAY>
    <MONTH>06</MONTH>
    <YEAR>2017</YEAR>
</SomeDateElement>

To convert these values to valid xs:date / xs:datetime elements, I've created the below logic in my XSLT:

Template to match any "fake dates":

<xsl:template match="//*[./YEAR and ./MONTH and ./DAY]">
    <xsl:call-template name="ConvertFakeDateToXmlDateTime">
        <!-- <xsl:with-param name="format">yyyy-MM-DDTHH:mm:ss</xsl:with-param> -->
    </xsl:call-template>
</xsl:template>

Template to format any "fake date" (separate to the above to allow for easy reuse for those dates for which I wish to specify a different format / caught via other matches).

<xsl:template name="ConvertFakeDateToXmlDateTime">
    <xsl:param name="format" select="yyyy-MM-dd" />
    <xsl:variable name="date" select="concat(./YEAR/text(),'-',./MONTH/text(),'-',./DAY/text())" /> <!-- as="xs:datetime" -->

    <!-- debugging code-->
    <xsl:element name="{concat('DEBUG_',name(.))}">
        <xsl:attribute name="IAmADate">
            <xsl:value-of select="$date"/> <!-- show our date variable's content -->
        </xsl:attribute>
        <xsl:apply-templates select="@* | node()" /> <!-- show the original value -->
    </xsl:element>
    <!-- end debugging code -->

    <xsl:element name="{name(.)}" namespace="{namespace-uri(.)}">
        <xsl:value-of select="msxsl:format-date($date, $format)"/>
    </xsl:element>
</xsl:template>

Using the above code & sample input I get the following output:

<DEBUG_SomeDateElement xmlns="" IAmADate="2017-06-21">
    <DAY>21</DAY>
    <MONTH>06</MONTH>
    <YEAR>2017</YEAR>
</DEBUG_SomeDateElement>
<SomeDateElement xmlns="">21/06/2017</SomeDateElement>

Additional Notes

I'm performing the transform with Microsoft .Net's System.Xml.Xsl.XslCompiledTransform.

My stylesheet includes the attribute/namespace: xmlns:msxsl="urn:schemas-microsoft-com:xslt", for the format-date function.

Simple Example

I also tried to simplify the issue by doing this:

<xsl:element name="demo">
    <xsl:value-of select="msxsl:format-date('2017-06-21', 'yyyy-MM-dd')"/>
</xsl:element>

and

<xsl:element name="demo">
    <xsl:variable name="demoDate" select="2017-06-21" />
    <xsl:value-of select="msxsl:format-date($demoDate, 'yyyy-MM-dd')"/>
</xsl:element>
  • The first example (where date & format are literals) gave <demo>2017-06-21</demo>. ✔
  • The second (where date is a variable) gave <demo>1900-01-01</demo>. ✘

As such, I've been unable to reproduce the exact issue I'm seeing above; though I have now discovered a second issue, implying I've misunderstood something about XSLT variables.


Solution

  • You need to quote the values in your selects to make them strings:

    <xsl:variable name="demoDate" select="'2017-06-21'" />
    

    and

    <xsl:param name="format" select="'yyyy-MM-dd'" />