Reputation: 39
I want to achieve this using XSLT 1.0. Hours is represented in Hours and minutes(for ex: 10:30 is basically 10 hours 30 minutes). I need to convert it into only hours(10.50) and then calculate the sum of all hours nodes.
Here is a source XML document:
<Record>
<hours>10:30</hours>
<hours>20:30</hours>
<hours>10:60</hours>
</Record>
Output:
<Record>
<TotalHours>42.0</TotalHours>
</Record>
Upvotes: 1
Views: 1076
Reputation: 243449
Do note that the solution in Joepie's answer causes compile-time syntax error in any compliant XSLT 1.0 processor (it can only be executed without compilation errors if one uses an XSLT 2.0 processor):
SAXON 6.5.4 from Michael Kay
Java version 1.6.0_31
Error at xsl:value-of on line 11 of file:/(Untitled):
Error in expression sum(hours/number(substring-before(., ':'))) + sum(hours/number(substring-after(., ':'))) div 60 : Unexpected token [<function>] in path expression
Transformation failed: Failed to compile stylesheet. 1 error detected.
Press any key to continue . . .
Here is a truly working, true XSLT 1.0 solution:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common" exclude-result-prefixes="ext">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/*">
<xsl:copy>
<xsl:variable name="vrtfTimes">
<xsl:for-each select="*">
<t>
<xsl:value-of select=
"substring-before(., ':') + substring-after(.,':') div 60"/>
</t>
</xsl:for-each>
</xsl:variable>
<TotalHours>
<xsl:value-of select="floor(sum(ext:node-set($vrtfTimes)/*))"/>
</TotalHours>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the provided XML document:
<Record>
<hours>10:30</hours>
<hours>20:30</hours>
<hours>10:60</hours>
</Record>
the wanted, correct result is produced:
<Record>
<TotalHours>42</TotalHours>
</Record>
Note:
In case you want to get not only the hours from the sum, but also the remaining minute -- as minutes:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common" exclude-result-prefixes="ext">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/*">
<xsl:copy>
<xsl:variable name="vrtfTimes">
<xsl:for-each select="*">
<t>
<xsl:value-of select=
"substring-before(., ':') + substring-after(.,':') div 60"/>
</t>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="vSum" select="sum(ext:node-set($vrtfTimes)/*)"/>
<TotalHours>
<xsl:value-of select="floor($vSum)"/>
<xsl:text>:</xsl:text>
<xsl:value-of select="round(60* ($vSum - floor($vSum)))"/>
</TotalHours>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the following (slightly more complex) XML document:
<Record>
<hours>10:30</hours>
<hours>20:30</hours>
<hours>10:60</hours>
<hours>1:15</hours>
<hours>1:03</hours>
</Record>
the correct result is produced:
<Record>
<TotalHours>44:18</TotalHours>
</Record>
Upvotes: 2
Reputation: 4133
In XSLT 1.0:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output indent="yes"/>
<xsl:template match="Record">
<Record>
<TotalHours>
<xsl:value-of select="
sum(hours/number(substring-before(., ':'))) +
sum(hours/number(substring-after(., ':'))) div 60
"/>
</TotalHours>
</Record>
</xsl:template>
</xsl:transform>
If you also want the minutes in stead of an fraction of an hour:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output indent="yes"/>
<xsl:template match="Record">
<Record>
<TotalHours>
<xsl:value-of select="
concat(
sum(hours/number(substring-before(., ':'))) + floor(sum(hours/number(substring-after(., ':'))) div 60),
'.',
format-number(floor(sum(hours/number(substring-after(., ':'))) mod 60), '00')
)
"/>
</TotalHours>
</Record>
</xsl:template>
</xsl:transform>
Upvotes: 0