Сравните даты-периоды с помощью XSLT
Вопрос
У меня есть некоторый опыт работы с XSLT, но теперь у меня возникла проблема:Мне нужно проверить, полностью ли период между начальной и конечной датой охватывает другой период.
Вот часть 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>
Поэтому мне нужно проверить, полностью ли период между началом и концом родительского элемента покрывается периодом между началом и концом дочернего элемента в XSLT, и записать родительский и дочерний идентификаторы в xml для сбоев.
Может кто-нибудь подсказать мне, как управлять этим в XSLT ...?
У меня есть полный контроль над структурой XML, поэтому, когда это проще с другой структурой XML (с теми же данными), я могу изменить ее.
Большое спасибо!
Решение
Используя простое сравнение строк, это несложно, потому что ваш формат даты имеет порядковый номер.Вот краткий документ XSLT, который я написал, чтобы протестировать его:
<?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 <= StartDate) and
(Child/EndDate >= 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>
Очевидно, вам понадобятся ваши собственные проверки, чтобы убедиться, что StartDate
находится перед EndDate
как для родителя, так и для ребенка.
Другие советы
Вот пример, позволяющий сделать это непосредственно в xslt 2.0 и должен работать с большинством разделителей дат:
<?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 < $dateToCheck and $dateToCheck < $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 < current-date() and current-date() < $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>
Что плохого в number(Child/StartDate) <= number(Parent/StartDate) and number(Child/EndDate) >= number(Parent/EndDate)
?
Это не полное решение, но вы можете захотеть проверить расширения EXSLT для манипулирования датами, здесь.
Однако я бы подумал о создании пары Функции расширения XSLT, используя Абстракции интервалов времени Джоды.Вероятно, намного проще и быстрее, чем пытаться сделать это напрямую из XSLT.