Search code examples
xsltxslt-2.0xslt-3.0

XSLT - How do I Create an Element for Each Date Between Two Dates


I am working with the following XML structure.

<File>
<Record>
    <ID>01234</ID>
    <Description>Sample description</Description>
    <StartDate>2021-01-29</StartDate>
    <EndDate>2021-02-02</EndDate>
</Record>
<Record>
    <ID>56789</ID>
    <Description>Sample description</Description>
    <StartDate>2021-02-03</StartDate>
    <EndDate>2021-02-06</EndDate>
</Record>

I need to create a Record element for each date in between, and including, the given StartDate and EndDate. My resulting XML needs to look like the following.

<File>
<Record>
    <ID>01234</ID>
    <Description>Sample description</Description>
    <EventDate>2021-01-29</EventDate>
</Record>
<Record>
    <ID>01234</ID>
    <Description>Sample description</Description>
    <EventDate>2021-01-30</EventDate>
</Record>
<Record>
    <ID>01234</ID>
    <Description>Sample description</Description>
    <EventDate>2021-01-31</EventDate>
</Record>
<Record>
    <ID>01234</ID>
    <Description>Sample description</Description>
    <EventDate>2021-02-01</EventDate>
</Record>
<Record>
    <ID>01234</ID>
    <Description>Sample description</Description>
    <EventDate>2021-02-02</EventDate>
</Record>
<Record>
    <ID>56789</ID>
    <Description>Sample description</Description>
    <EventDate>2021-02-03</EventDate>
</Record>
<Record>
    <ID>56789</ID>
    <Description>Sample description</Description>
    <EventDate>2021-02-04</EventDate>
</Record>
<Record>
    <ID>56789</ID>
    <Description>Sample description</Description>
    <EventDate>2021-02-05</EventDate>
</Record>
<Record>
    <ID>56789</ID>
    <Description>Sample description</Description>
    <EventDate>2021-02-06</EventDate>
</Record>

Research I've done thus far hasn't given me much and, given my XSLT knowledge is severely lacking, I'd greatly appreciate any assistance. My preference would be not to use a recursive function (possibly XSLT 3.0??) but at this point I'll go with anything.

Thank you.


Solution

  • Here's a way this could be done:

    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xmlns:xs="http://www.w3.org/2001/XMLSchema"
        exclude-result-prefixes="#all"
        version="3.0">
    
      <xsl:mode on-no-match="shallow-copy"/>
    
      <xsl:output method="xml"/>
    
      <xsl:template match="Record">
        <xsl:variable name="currentRec" select="."/>
        <xsl:variable name="numberDays" select="xs:integer((xs:date(EndDate) - xs:date(StartDate)) div xs:dayTimeDuration('P1D'))"/>
        <xsl:for-each select="0 to $numberDays">
            <Record>
                <xsl:copy-of select="$currentRec/ID"/>
                <xsl:copy-of select="$currentRec/Description"/>
                <EventDate><xsl:value-of select="xs:date($currentRec/StartDate) + (xs:dayTimeDuration('P1D')*(position()-1))"/></EventDate>
            </Record>
        </xsl:for-each>
      </xsl:template>
      
    </xsl:stylesheet>
    

    See it working here : https://xsltfiddle.liberty-development.net/ei5R4uW/1