user6353351
user6353351

Reputation: 29

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.

Upvotes: 1

Views: 528

Answers (2)

Sebastien
Sebastien

Reputation: 2714

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

Upvotes: 0

Martin Honnen
Martin Honnen

Reputation: 167706

You could use

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

  <xsl:mode on-no-match="shallow-copy"/>
  
  <xsl:output indent="yes"/>

  <xsl:template match="Record">
      <xsl:apply-templates select="(0 to days-from-duration(xs:date(EndDate) - xs:date(StartDate))) ! current()" mode="split"/>
  </xsl:template>
  
  <xsl:template match="Record" mode="split">
      <xsl:copy>
          <xsl:apply-templates select="* except (StartDate, EndDate)"/>
          <EventDate>{xs:date(StartDate) + xs:dayTimeDuration('P1D') * (position() - 1)}</EventDate>
      </xsl:copy>
  </xsl:template>
  
</xsl:stylesheet>

Upvotes: 1

Related Questions