Search code examples
xmlxpathxpath-1.0

XPath 1.0: convert ss to minutes, seconds


How can I convert seconds to minutes:seconds in XPath 1.0?

For example:

source:

<values>
    <value>80</value>
<values>

output of query should be 1 minute, 20 seconds


Solution

  • Use the XPath 1.0 operators div (combined with floor) and mod. For illustration, there is an XSLT 1.0 stylesheet below. The details depend on the environment you embed XPath into.

    XSLT Stylesheet

    <?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" encoding="UTF-8" indent="yes"/>
        <xsl:strip-space elements="*"/>
    
        <xsl:template match="values">
            <xsl:copy>
                <xsl:apply-templates/>
            </xsl:copy>
        </xsl:template>
    
        <xsl:template match="value">
            <xsl:copy>
                <xsl:value-of select="concat(floor(. div 60),':',. mod 60)"/>
            </xsl:copy>
        </xsl:template>
    
    </xsl:stylesheet>
    

    XML Input

    <?xml version="1.0" encoding="UTF-8"?>
    <values>
        <value>80</value>
        <value>90</value>
    </values>
    

    XML Output

    <?xml version="1.0" encoding="UTF-8"?>
    <values>
        <value>1:20</value>
        <value>1:30</value>
    </values>
    

    EDIT: As @derp has pointed out, this is not helpful if you need to spell out "minutes" and "seconds" and distinguish between plural and singular. Here is another version to handle that, slightly more complicated I'm afraid.

    Also, now you've arrived at a point where the problem can hardly be solved in XPath alone. You need to reach for the higher-level language that makes use of XPath.

    Stylesheet

    <?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" encoding="UTF-8" indent="yes"/>
    
        <xsl:strip-space elements="*"/>
    
        <xsl:template match="values">
            <xsl:copy>
                <xsl:apply-templates/>
            </xsl:copy>
        </xsl:template>
    
        <xsl:template match="value">
    
        <xsl:variable name="mins">
            <xsl:value-of select="concat(floor(. div 60),' ')"/>
            <xsl:value-of select="concat(substring('minute', 1 div boolean(floor(. div 60) = 1)),
           substring('minutes', 1 div boolean(floor(. div 60) != 1)))"/>
        </xsl:variable>
        <xsl:variable name="secs">
            <xsl:value-of select="concat(. mod 60,' ')"/>
            <xsl:value-of select="concat(substring('second', 1 div boolean(. mod 60 = 1)),
           substring('seconds', 1 div boolean(. mod 60 != 1)))"/>
        </xsl:variable>
            <xsl:copy>
                <xsl:value-of select="concat($mins,',',$secs)"/>
            </xsl:copy>
        </xsl:template>
    
    </xsl:stylesheet>
    

    XML Output

    <?xml version="1.0" encoding="UTF-8"?>
    <values>
       <value>1 minute,20 seconds</value>
       <value>1 minute,30 seconds</value>
    </values>
    

    In case you are wondering: if there are 0 minutes or seconds, the correct plural minutes or seconds is output.