Solution 1
This conversion is :
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="text"/> <xsl:key name="krowsInHalfHour" match= "row[not((substring-after(time,':')+30) mod 30 = 0)]" use="generate-id( preceding-sibling::row [60*substring-before(time,':') + substring-after(time,':') >= 60*substring-before(current()/time,':') + substring-after(current()/time,':') - substring-after(current()/time,':') mod 30 ] [1] ) "/> <xsl:template match= "row[(substring-after(time,':')+30) mod 30 = 0 or not( 60*substring-before(preceding-sibling::row[1]/time,':') + substring-after(preceding-sibling::row[1]/time,':') >= 60*substring-before(time,':') + substring-after(time,':') - substring-after(time,':') mod 30 ) ] "> <xsl:variable name="vPrevStartMins" select= "60*substring-before(time,':') + substring-after(time,':') - substring-after(time,':') mod 30 "/> <xsl:value-of select= "concat('
',floor($vPrevStartMins div 60), ':', concat(substring('0',($vPrevStartMins mod 60 >0)+1), $vPrevStartMins mod 60 ) ) "/> <xsl:text> $</xsl:text> <xsl:value-of select= "sum(sales | key('krowsInHalfHour',generate-id())/sales)"/> </xsl:template> <xsl:template match="text()"/> </xsl:stylesheet>
when applied to the provided XML document:
<root> <row> <time>08:00</time> <sales>800</sales> </row> <row> <time>08:15</time> <sales>815</sales> </row> <row> <time>08:30</time> <sales>830</sales> </row> <row> <time>08:45</time> <sales>845</sales> </row> <row> <time>11:00</time> <sales>1100</sales> </row> <row> <time>11:45</time> <sales>1145</sales> </row> <row> <time>14:15</time> <sales>1415</sales> </row> <row> <time>14:30</time> <sales>1430</sales> </row> </root>
creates the desired, correct result:
8:00 $1615 8:30 $1675 11:00 $1100 11:30 $1145 14:00 $1415 14:30 $1430
Solution 2 :
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:my="my:my" > <xsl:output method="text"/> <xsl:strip-space elements="*"/> <my:halfHours> <t>00:00</t><t>00:30</t><t>01:00</t><t>01:30</t> <t>02:00</t><t>02:30</t><t>03:00</t><t>03:30</t> <t>04:00</t><t>04:30</t><t>05:00</t><t>05:30</t> <t>06:00</t><t>06:30</t><t>07:00</t><t>07:30</t> <t>08:00</t><t>08:30</t><t>09:00</t><t>09:30</t> <t>10:00</t><t>10:30</t><t>11:00</t><t>11:30</t> <t>12:00</t><t>12:30</t><t>13:00</t><t>13:30</t> <t>14:00</t><t>14:30</t><t>15:00</t><t>15:30</t> <t>16:00</t><t>16:30</t><t>17:00</t><t>17:30</t> <t>18:00</t><t>18:30</t><t>19:00</t><t>19:30</t> <t>20:00</t><t>20:30</t><t>21:00</t><t>21:30</t> <t>22:00</t><t>22:30</t><t>23:00</t><t>23:30</t> <t>24:00</t> </my:halfHours> <xsl:variable name="vhalfHrs" select= "document('')/*/my:halfHours/*"/> <xsl:template match="row"> <xsl:variable name="vStart" select= "$vhalfHrs[translate(.,':','') > translate(current()/time,':','') ][1] /preceding-sibling::*[1] "/> <xsl:variable name="vprecRow" select= "preceding-sibling::*[1]"/> <xsl:if test= "not(translate($vprecRow/time,':','') >= translate($vStart,':','') )"> <xsl:value-of select="concat('
',$vStart, ' $')"/> <xsl:value-of select= "sum(sales|following-sibling::* [not(translate(time,':','') >= translate($vStart/following-sibling::*,':','') ) ] /sales ) "/> </xsl:if> </xsl:template> </xsl:stylesheet>
when this conversion is applied to the same XML document, the desired, correct result is again obtained :
08:00 $1615 08:30 $1675 11:00 $1100 11:30 $1145 14:00 $1415 14:30 $1430
Explanation
The variable $vhalfHrs contains elements whose values โโare the starting time of each half-hour period during the day.
In the template that corresponds to each row , the $vStart variable is set at this half-hour start time, at which the current node ( row )'s time falls.
The $vprecRow set to the previous sibling ( row ) of the current row .
If the time of the previous row no later than the beginning of half an hour (in $ vStart ), then the current row` is the first for this half hour period.
We display the initial time of half an hour.
We calculate and display the sum of all row elements whose time is in the same half-hour period. They follow the brothers and sisters of the current row , and their time not greater than or equal to the beginning of the next half-hour period.
Solution 3 (XSLT 2.0):
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xsl:output method="text"/> <xsl:template match="/*"> <xsl:for-each-group select="row" group-adjacent= "(xs:integer(substring-before(time,':'))*60 + xs:integer(substring-after(time,':')) ) idiv 30 "> <xsl:variable name="vStartMinutes" select="current-grouping-key()*30"/> <xsl:value-of separator="" select= "'
', format-number($vStartMinutes idiv 60, '00'), ':', format-number($vStartMinutes mod 60,'00'), ' $', sum(current-group()/sales/xs:integer(.)) "/> </xsl:for-each-group> </xsl:template> </xsl:stylesheet>
when this conversion is applied to the same XML document as above, the same desired, correct result is obtained :
08:00 $1615 08:30 $1675 11:00 $1100 11:30 $1145 14:00 $1415 14:30 $1430
Explanation
We use <xsl:for-each-group> with the group-adjacent attribute as an expression evaluating the position of the 1/2 hour period in which a row/time .
Heavy use of standard functions current-group() and current-grouping-key() .