1

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!

  • Why is the child node's data a superset of the parent's? That seems backward. Anyway, to use XML date functions you should be using the XML date format instead of your own: http://www.w3schools.com/Schema/schema_dtypes_date.asp – Welbog Jan 04 '10 at 17:04
  • In this part of the xml they are a superset but in onther cases in my data it's the other way around. The dates are part of the key/relation and i need to check if the child records match one or more of the parent periods. I'll change the format to the official date format and have a look at the different XML date functions, thnx! – A.W.J. van Ede Jan 04 '10 at 17:18

4 Answers4

3

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>
skin27
  • 31
  • 1
2

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.

Welbog
  • 59,154
  • 9
  • 110
  • 123
  • A few nitpicking remarks :) Doing `'//Parent'` is unnecessary, `'Parent'` is enough. `` is redundant in favor of ``. Also, parentheses in the `` test are not strictly necessary, though they increase readability. – Tomalak Jan 06 '10 at 12:37
  • @Tomalak: Like I usually tell you when you nitpick, I'm trying to explain something simply, to decrease the chances of getting people confused over syntax shorthand like `{@id}`. Though I'll give you the `//Parent` part. That was because I altered the original format slightly. – Welbog Jan 06 '10 at 12:58
0

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

Krab
  • 2,118
  • 12
  • 23
0

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.

Wilfred Springer
  • 10,869
  • 4
  • 55
  • 69