Question

I have some experience with XSLT but now i've got myself a problem: I need to check if a period between a begin- and enddate completely covers an other period.

Here's a part of the xml:

<Parent ID="1">
  <StartDate>20050101</StartDate>
  <EndDate>20060131</EndDate>
  <Child ID="1">
    <StartDate>20050101</StartDate>
    <EndDate>20081231</EndDate>
  </Child>
</Parent>
<Parent ID="2">
  <StartDate>20060201</StartDate>
  <EndDate>20071231</EndDate>
  <Child ID="1">
    <StartDate>20050101</StartDate>
    <EndDate>20081231</EndDate>
  </Child>
</Parent>
<Parent ID="3">
  <StartDate>20080101</StartDate>
  <EndDate>20081231<EndDate>
  <Child ID="1">
    <StartDate>20050101</StartDate>
    <EndDate>20081231</EndDate>
  </Child>
</Parent>

So i need to check if the period between start and end of the Parent is fully covered by the period between start and end of the Child in XSLT and write the Parent and Child ID's to xml for fails.

Can someone give me a head start how to manage this in XSLT...?

I have full control over the structure of the XML so when it's easier with an other XML structure (with the same data) i can change it.

Thanks a lot!

Was it helpful?

Solution

Using simple string comparison this is easy, because your date format is big-endian. Here's a quick XSLT document I wrote up to test it out:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" version="1.0" indent="yes"/>

  <xsl:template match="/">
    <root>
      <xsl:for-each select="//Parent">
        <parent>
          <xsl:attribute name="id">
            <xsl:value-of select="@ID"/>
          </xsl:attribute>
          <xsl:choose>
            <xsl:when test="(Child/StartDate &lt;= StartDate) and 
              (Child/EndDate &gt;= EndDate)">
              <xsl:text>OK</xsl:text>
            </xsl:when>
            <xsl:otherwise>
              <xsl:text>Not OK</xsl:text>
            </xsl:otherwise>
          </xsl:choose>
        </parent>
      </xsl:for-each>
    </root>
  </xsl:template>
</xsl:stylesheet>

Obviously you'll need your own checks to make sure that StartDate is before EndDate for both parent and child.

OTHER TIPS

Here is an example to do it directly in xslt 2.0 and should work with most date delimeters:

    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet version="2.0"
            exclude-result-prefixes="functx xs"
            xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
            xmlns:functx="http://www.functx.com"
            xmlns:xs="http://www.w3.org/2001/XMLSchema">

        <xsl:template match="/">
            <dates>
                <dateBetween>
                    <xsl:choose>
                        <xsl:when test="functx:dateBetween('02-01-2009','01-01-2009','03-01-2009')=true()">
                            <xsl:text>date lays between given dates</xsl:text>
                        </xsl:when>
                        <xsl:otherwise>
                            <xsl:text>date is not between given dates</xsl:text>
                        </xsl:otherwise>
                    </xsl:choose>
                </dateBetween>
                <currentDateBetween>
                    <xsl:choose>
                        <xsl:when test="functx:currentDateBetween('01-01-2000','01-01-2019')=true()">
                            <xsl:text>current date lays between given dates</xsl:text>
                        </xsl:when>
                        <xsl:otherwise>
                            <xsl:text>current date is not between given dates</xsl:text>
                        </xsl:otherwise>
                    </xsl:choose>
                </currentDateBetween>
            </dates>
        </xsl:template>

         <!-- 
            Function:       dateBetween
            Description:    This function will check if a dates is between given dates
            Input:          Input date, Input start date, Input end date
            Output:         Boolean true if beween param 2 and 3
            Created:        21-09-2012 by Raymond Meester
        -->
        <xsl:function name="functx:dateBetween">
            <xsl:param name="param1"/>
            <xsl:param name="param2"/>
            <xsl:param name="param3"/>

            <xsl:variable name ="dateToCheck" select="functx:mmddyyyy-to-date($param1)"/>        
            <xsl:variable name ="startDate" select="functx:mmddyyyy-to-date($param2)"/>
            <xsl:variable name ="endDate" select="functx:mmddyyyy-to-date($param3)"/>    
            <xsl:variable name ="true" as="xs:boolean" select="true()"/>
            <xsl:variable name ="false" as="xs:boolean" select="false()"/>          

            <xsl:choose>
                <xsl:when test="$startDate &lt; $dateToCheck and $dateToCheck &lt; $endDate"><xsl:value-of select="$true"/></xsl:when>
                <xsl:otherwise><xsl:value-of select="$false"/></xsl:otherwise>
            </xsl:choose>

        </xsl:function>

        <!-- 
            Function:       currentDateBetween
            Description:    This function will check if a dates is between given dates
            Input:          Input date, Input start date, Input end date
            Output:         Boolean true if beween param 2 and 3
            Created:        21-09-2012 by Raymond Meester
        -->    
        <xsl:function name="functx:currentDateBetween">
            <xsl:param name="param1"/>
            <xsl:param name="param2"/>

            <xsl:variable name ="startDate" select="functx:mmddyyyy-to-date($param1)"/>
            <xsl:variable name ="endDate" select="functx:mmddyyyy-to-date($param2)"/>    
            <xsl:variable name ="true" as="xs:boolean" select="true()"/>
            <xsl:variable name ="false" as="xs:boolean" select="false()"/>          

            <xsl:choose>
                <xsl:when test="$startDate &lt; current-date() and current-date() &lt; $endDate"><xsl:value-of select="$true"/></xsl:when>
                <xsl:otherwise><xsl:value-of select="$false"/></xsl:otherwise>
            </xsl:choose>

        </xsl:function>

        <!-- 
            Function:       mmddyyyy-to-date
            Description:    The functx:mmddyyyy-to-date function converts $dateString into a valid xs:date value. The order of the digits in $dateString must be MMDDYYYY, but it can contain any (or no) delimiters between the digits.
            Input:          Input string
            Output:         Return date
            Created:        2007-02-26 http://www.xsltfunctions.com/xsl/functx_mmddyyyy-to-date.html
        -->      
        <xsl:function name="functx:mmddyyyy-to-date" as="xs:date?"
                xmlns:functx="http://www.functx.com">
            <xsl:param name="dateString" as="xs:string?"/>
            <xsl:sequence select="if (empty($dateString)) then () else if (not(matches($dateString,'^\D*(\d{2})\D*(\d{2})\D*(\d{4})\D*$'))) then error(xs:QName('functx:Invalid_Date_Format')) else xs:date(replace($dateString,'^\D*(\d{2})\D*(\d{2})\D*(\d{4})\D*$','$3-$1-$2'))"/>
        </xsl:function>

    </xsl:stylesheet>

What's wrong with number(Child/StartDate) <= number(Parent/StartDate) and number(Child/EndDate) >= number(Parent/EndDate)?

This is not going to a complete solution, but you may want to check out the EXSLT extensions for date manipulation, here.

I would however consider creating a couple of XSLT extension functions, using Joda time's Interval abstractions. Probably way easier and faster than trying to do it from XSLT directly.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top