Finding the difference between two dateTimes in XSLT
Asked Answered
C

4

6

I have an XML file which includes some dates with start and end points, like shown as follows:

<start time="2016-02-21T00:59:06+02:00"/>
.....
.....
<end time="2016-02-22T02:24:38+02:00"/>

Question:

How to calculate the difference between two time attributes?

Cribbing answered 27/7, 2016 at 5:29 Comment(9)
For XSLT1, you might want to have a look at EXSLT date:difference. For using EXSLT see this StackOverflow answer.Sanfo
@Sanfo , But as you may know download for the desired packages are not available.Cribbing
Are you able to use XSLT 2.0?Pyemia
@TimC I think yes I am.Cribbing
@Cribbing I suggest you find out for sure, as it makes a big difference here. Also clarify what exactly you mean by "the difference between two dates/times". The difference expressed as what?Watson
@Watson I checked and found out that I can use XSLT version 2. Also by times/dates I meant different in dates, which means how many days, hours, minutes ans seconds. I will use them for presenting as the results in my code.Cribbing
@Cribbing An XSLT 1.0 solution is not as trivial. Why are you asking? -- P.S. You have been already directed to a possible solution. I don't particularly like it myself, but it is available: exslt.org/date/functions/difference/…Watson
@Watson because I am getting the error: compilation error: file prepareSummary.xslt element stylesheet and xsl:version: only 1.0 features are supportedCribbing
What can I say... Did you notice I said "I suggest you find out for sure, as it makes a big difference here"?Watson
W
6

I am getting the error: ... xsl:version: only 1.0 features are supported

Here's a purely XSLT 1.0 solution:

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:template match="event">
    <xsl:variable name="start">
        <xsl:call-template name="dateTime-to-seconds">
            <xsl:with-param name="dateTime" select="start/@time" />
        </xsl:call-template>
    </xsl:variable> 
    
    <xsl:variable name="end">
        <xsl:call-template name="dateTime-to-seconds">
            <xsl:with-param name="dateTime" select="end/@time" />
        </xsl:call-template>
    </xsl:variable>
    
    <xsl:variable name="duration" select="$end - $start" />
    <xsl:variable name="d" select="floor($duration div 86400)"/>
    <xsl:variable name="t" select="$duration mod 86400"/>
    <xsl:variable name="h" select="floor($t div 3600)"/>
    <xsl:variable name="r" select="$t mod 3600"/>
    <xsl:variable name="m" select="floor($r div 60)"/>
    <xsl:variable name="s" select="$r mod 60"/>
    
    <xsl:copy>
        <xsl:copy-of select="name"/>
        <duration>
            <xsl:value-of select="$d"/>
            <xsl:text> days, </xsl:text>
            <xsl:value-of select="$h"/>
            <xsl:text> hours, </xsl:text>
            <xsl:value-of select="$m"/>
            <xsl:text> minutes and </xsl:text>
            <xsl:value-of select="$s"/>
            <xsl:text> seconds</xsl:text>
        </duration>
    </xsl:copy>
</xsl:template>

<xsl:template name="dateTime-to-seconds">
    <xsl:param name="dateTime"/>

    <xsl:variable name="date" select="substring-before($dateTime, 'T')" />
    <xsl:variable name="time" select="substring-after($dateTime, 'T')" />

    <xsl:variable name="local-time" select="substring($time, 1, string-length($time) - 6)" />
    <xsl:variable name="offset" select="substring-after($time, $local-time)" />

    <xsl:variable name="year" select="substring($date, 1, 4)" />
    <xsl:variable name="month" select="substring($date, 6, 2)" />
    <xsl:variable name="day" select="substring($date, 9, 2)" />

    <xsl:variable name="hour" select="substring($local-time, 1, 2)" />
    <xsl:variable name="minute" select="substring($local-time, 4, 2)" />
    <xsl:variable name="second" select="substring($local-time, 7)" />

    <xsl:variable name="offset-sign" select="1 - 2 * starts-with($offset, '-')" />
    <xsl:variable name="offset-hour" select="substring($offset, 2, 2) * $offset-sign" />
    <xsl:variable name="offset-minute" select="substring($offset, 5, 2) * $offset-sign" />

    <xsl:variable name="a" select="floor((14 - $month) div 12)"/>
    <xsl:variable name="y" select="$year + 4800 - $a"/>
    <xsl:variable name="m" select="$month + 12*$a - 3"/>    
    <xsl:variable name="jd" select="$day + floor((153*$m + 2) div 5) + 365*$y + floor($y div 4) - floor($y div 100) + floor($y div 400) - 32045" />

    <xsl:value-of select="86400*$jd + 3600*$hour + 60*$minute + $second - 3600*$offset-hour - 60*$offset-minute" />
</xsl:template> 

</xsl:stylesheet>

Demo: http://xsltfiddle.liberty-development.net/aiyndK

Watson answered 27/7, 2016 at 14:12 Comment(3)
oh my god, why isn't this answer come up first on google searches for XSLT date diff. Thanks so much dude. I am going to copy this link everywhere!Engelhart
I know this is an old thread, but I'm trying this method and finding odd results. When the start time is an even hour and the end time is 30 minutes later, it always shows a duration of 7 hours. If the start time is on a 30minute and the end time is an even hour, I get a duration of -6 hours. I also have similar abnormalities to 1.5 hours and 45 minute duration.Propylite
Cannot reproduce your claimed result: xsltfiddle.liberty-development.net/6r5Gh3oWatson
T
6

Your dates are in ISO format so in XSLT 2.0 you can do

xs:dateTime(end/@time) - xs:dateTime(start/@time)

The result is an xs:dayTimeDuration, which you can convert to seconds (for example) by doing

(xs:dateTime(end/@time) - xs:dateTime(start/@time)) div xs:dayTimeDuration('PT1S')
Trimetric answered 27/7, 2016 at 8:12 Comment(0)
W
6

I am getting the error: ... xsl:version: only 1.0 features are supported

Here's a purely XSLT 1.0 solution:

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:template match="event">
    <xsl:variable name="start">
        <xsl:call-template name="dateTime-to-seconds">
            <xsl:with-param name="dateTime" select="start/@time" />
        </xsl:call-template>
    </xsl:variable> 
    
    <xsl:variable name="end">
        <xsl:call-template name="dateTime-to-seconds">
            <xsl:with-param name="dateTime" select="end/@time" />
        </xsl:call-template>
    </xsl:variable>
    
    <xsl:variable name="duration" select="$end - $start" />
    <xsl:variable name="d" select="floor($duration div 86400)"/>
    <xsl:variable name="t" select="$duration mod 86400"/>
    <xsl:variable name="h" select="floor($t div 3600)"/>
    <xsl:variable name="r" select="$t mod 3600"/>
    <xsl:variable name="m" select="floor($r div 60)"/>
    <xsl:variable name="s" select="$r mod 60"/>
    
    <xsl:copy>
        <xsl:copy-of select="name"/>
        <duration>
            <xsl:value-of select="$d"/>
            <xsl:text> days, </xsl:text>
            <xsl:value-of select="$h"/>
            <xsl:text> hours, </xsl:text>
            <xsl:value-of select="$m"/>
            <xsl:text> minutes and </xsl:text>
            <xsl:value-of select="$s"/>
            <xsl:text> seconds</xsl:text>
        </duration>
    </xsl:copy>
</xsl:template>

<xsl:template name="dateTime-to-seconds">
    <xsl:param name="dateTime"/>

    <xsl:variable name="date" select="substring-before($dateTime, 'T')" />
    <xsl:variable name="time" select="substring-after($dateTime, 'T')" />

    <xsl:variable name="local-time" select="substring($time, 1, string-length($time) - 6)" />
    <xsl:variable name="offset" select="substring-after($time, $local-time)" />

    <xsl:variable name="year" select="substring($date, 1, 4)" />
    <xsl:variable name="month" select="substring($date, 6, 2)" />
    <xsl:variable name="day" select="substring($date, 9, 2)" />

    <xsl:variable name="hour" select="substring($local-time, 1, 2)" />
    <xsl:variable name="minute" select="substring($local-time, 4, 2)" />
    <xsl:variable name="second" select="substring($local-time, 7)" />

    <xsl:variable name="offset-sign" select="1 - 2 * starts-with($offset, '-')" />
    <xsl:variable name="offset-hour" select="substring($offset, 2, 2) * $offset-sign" />
    <xsl:variable name="offset-minute" select="substring($offset, 5, 2) * $offset-sign" />

    <xsl:variable name="a" select="floor((14 - $month) div 12)"/>
    <xsl:variable name="y" select="$year + 4800 - $a"/>
    <xsl:variable name="m" select="$month + 12*$a - 3"/>    
    <xsl:variable name="jd" select="$day + floor((153*$m + 2) div 5) + 365*$y + floor($y div 4) - floor($y div 100) + floor($y div 400) - 32045" />

    <xsl:value-of select="86400*$jd + 3600*$hour + 60*$minute + $second - 3600*$offset-hour - 60*$offset-minute" />
</xsl:template> 

</xsl:stylesheet>

Demo: http://xsltfiddle.liberty-development.net/aiyndK

Watson answered 27/7, 2016 at 14:12 Comment(3)
oh my god, why isn't this answer come up first on google searches for XSLT date diff. Thanks so much dude. I am going to copy this link everywhere!Engelhart
I know this is an old thread, but I'm trying this method and finding odd results. When the start time is an even hour and the end time is 30 minutes later, it always shows a duration of 7 hours. If the start time is on a 30minute and the end time is an even hour, I get a duration of -6 hours. I also have similar abnormalities to 1.5 hours and 45 minute duration.Propylite
Cannot reproduce your claimed result: xsltfiddle.liberty-development.net/6r5Gh3oWatson
W
4

I checked and found out that I can use XSLT version 2. Also by times/dates I meant different in dates, which means how many days, hours, minutes ans seconds.

Then I would suggest you do it along the lines of:

<xsl:variable name="duration" select="xs:dateTime(end/@time) - xs:dateTime(start/@time)" />
<xsl:value-of select="days-from-duration($duration)"/>
<xsl:text> days, </xsl:text>
<xsl:value-of select="hours-from-duration($duration)"/>
<xsl:text> hours, </xsl:text>
<xsl:value-of select="minutes-from-duration($duration)"/>
<xsl:text> minutes and </xsl:text>
<xsl:value-of select="seconds-from-duration($duration)"/>
<xsl:text> seconds</xsl:text>

Full demo: http://xsltfiddle.liberty-development.net/aiyndK/1

Watson answered 27/7, 2016 at 9:2 Comment(0)
T
0

Using XSLT3.0 compliant Saxon processor, I use this (should work for 2.0 also):- xs is xsd schema

<xsl:variable name="to" select="...to node value"/>
<xsl:variable name="from" select"...from node value"/>
<xsl:value-of select="days-from-duration(xs:dayTimeDuration(xs:date($to) - xs:date($from)))"/>
Takishatakken answered 3/10, 2017 at 17:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.