Nick B
Nick B

Reputation: 47

XSLT Date range to each day

I would like to seek your expert knowledge on xslt. I have date range coming in XML for e.g.

<data>
<ID>ABC1</ID>
<Start_Date>2018-05-01</Start_Date>
<End_Date>2018-05-10</End_Date>
<data>
<ID>ABC2</ID>
<Start_Date>2018-05-10</Start_Date>
<End_Date>2018-05-12</End_Date>
<data>
<root>

Now I need to send 1 row per date e.g. for ABC1 id I need to send 10 records with startdate enddate as increment.

after transformation the data should look like below

<data>
<ID>ABC1</ID>
<Start_Date>2018-05-01</Start_Date>
<End_Date>2018-05-01</End_Date>
<data>
<data>
<ID>ABC1</ID>
<Start_Date>2018-05-02</Start_Date>
<End_Date>2018-05-02</End_Date>
<data>
<data>
<ID>ABC1</ID>
<Start_Date>2018-05-10</Start_Date>
<End_Date>2018-05-10</End_Date>
<data><root>

till so on so total for ABC1 we need to have 10 data section and for ABC2 2 data section.

Please guide how can I implement the logic in XSLT.

Upvotes: 0

Views: 800

Answers (1)

Martin Honnen
Martin Honnen

Reputation: 167706

As you say you can use at least XSLT 2 you can exploit the xs:date data type and the arithmetic operations like substraction XSLT/XPath 2 and later support to compute the difference in days and then process each item that many times with the right date:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="xs"
    version="3.0">

  <xsl:mode on-no-match="shallow-copy"/>
  <xsl:mode name="increment" on-no-match="shallow-copy"/>

  <xsl:output method="xml" indent="yes"/>

  <xsl:template match="data">
      <xsl:variable name="data" select="."/>
      <xsl:for-each select="0 to days-from-duration(xs:date(End_Date) - xs:date(Start_Date))">
          <xsl:apply-templates select="$data" mode="increment">
              <xsl:with-param name="date" tunnel="yes" select="xs:date($data/Start_Date) + xs:dayTimeDuration('P1D') * ."/>
          </xsl:apply-templates>
      </xsl:for-each>
  </xsl:template>

  <xsl:template match="data/Start_Date | data/End_Date" mode="increment">
      <xsl:param name="date" tunnel="yes"/>
      <xsl:copy>
          <xsl:value-of select="$date"/>
      </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

That is XSLT 3, but only for the xsl:mode stuff you could replace for XSLT 2 with

<xsl:template match="@* | node()" mode="#default increment">
  <xsl:copy>
    <xsl:apply-templates select="@* | node()" mode="#current"/>
  </xsl:copy>
</xsl:template>

XSLT 3 online sample at https://xsltfiddle.liberty-development.net/gWmuiJ6.

Upvotes: 1

Related Questions