Here are two solutions:
I. XPath 1.0
This is one pair of XPath 1.0 expressions that select the required nodes:
/*/*
[translate(@time, ':','')
>
translate('15:59',':','')
][1]
selects the first sub
node with time later than 15:59
.
/*/*
[translate(@time, ':','')
<
translate('15:59',':','')
][last()]
selects selects the first sub
node with the previous than 15:59
sub
time.
We can include these in an XSLT transformation and check that the really wanted result is produced:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes"/>
<xsl:template match="/">
First time after 15:59:
<xsl:copy-of select=
"/*/*
[translate(@time, ':','')
>
translate('15:59',':','')
][1]
"/>
First time before 15:59:
<xsl:copy-of select=
"/*/*
[translate(@time, ':','')
<
translate('15:59',':','')
][last()]
"/>
</xsl:template>
</xsl:stylesheet>
When the above transformation is applied on the originally provided XML document:
<mainNode>
<sub time="08:00">
<status id="2">On</status>
<status id="3">Off</status>
</sub>
<sub time="13:00">
<status id="4">On</status>
<status id="7">On</status>
</sub>
<sub time="16:00">
<status id="5">On</status>
<status id="6">On</status>
<status id="7">Off</status>
<status id="8">On</status>
</sub>
<sub time="20:00">
<status id="4">Off</status>
<status id="7">On</status>
</sub>
<sub time="23:59">
<status id="4">On</status>
<status id="7">On</status>
</sub>
</mainNode>
the wanted result is produced:
First time after 15:59:
<sub time="16:00">
<status id="5">On</status>
<status id="6">On</status>
<status id="7">Off</status>
<status id="8">On</status>
</sub>
First time before 15:59:
<sub time="13:00">
<status id="4">On</status>
<status id="7">On</status>
</sub>
Do note the following:
The use of the XPath translate()
function to get rid of the colons
The use of the last()
function in the second expression
There is no need to convert the time to seconds before the comparison
When used as part of an XML document (such as an XSLT stylesheet, the <
operator must be escaped.
II. XPath 2.0
In XPath 2.0 we can use the following two expressions to produce select the desired nodes:
/*/*[xs:time(concat(@time,':00'))
gt
xs:time('15:59:00')
][1]
selects the first sub
node with time later than 15:59
.
/*/*[xs:time(concat(@time,':00'))
lt
xs:time('15:59:00')
][last()]
selects selects the first sub
node with the previous than 15:59
sub
time.
We can include these in an XSLT 2.0 transformation and check that the really wanted result is produced:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output omit-xml-declaration="yes"/>
<xsl:template match="/">
First time after 15:59:
<xsl:copy-of select=
"/*/*[xs:time(concat(@time,':00'))
gt
xs:time('15:59:00')
][1]
"/>
First time before 15:59:
<xsl:copy-of select=
"/*/*[xs:time(concat(@time,':00'))
lt
xs:time('15:59:00')
][last()]
"/>
</xsl:template>
</xsl:stylesheet>
When the above transformation is applied on the originally provided XML document (the same as in the first solution), the same wanted result is produced.
Do note the following:
- In XPath 2.0
xs:time
is a native data type. However, in order to construct an xs:time()
from the values in the xml document, we have to concat to them the missing seconds part.
- In XPath 2.0
xs:time
values can be compared with the "atomic-value comarison operators" such as lt
or gt
.