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?

Rene Knop
  • 1,788
  • 3
  • 15
  • 27
Saber
  • 133
  • 1
  • 9
  • For XSLT1, you might want to have a look at [EXSLT date:difference](http://exslt.org/date/functions/difference/index.html). For using EXSLT see [this StackOverflow answer](http://stackoverflow.com/questions/1575111/can-an-xslt-insert-the-current-date#1575134). – halfbit Jul 27 '16 at 06:35
  • @halfbit , But as you may know download for the desired packages are not available. – Saber Jul 27 '16 at 06:49
  • Are you able to use XSLT 2.0? – Tim C Jul 27 '16 at 07:48
  • @TimC I think yes I am. – Saber Jul 27 '16 at 07:52
  • @Saber 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? – michael.hor257k Jul 27 '16 at 07:59
  • @michael.hor257k 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. – Saber Jul 27 '16 at 08:11
  • @Saber 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: http://exslt.org/date/functions/difference/date.difference.template.xsl – michael.hor257k Jul 27 '16 at 11:41
  • @michael.hor257k because I am getting the error: `compilation error: file prepareSummary.xslt element stylesheet` and `xsl:version: only 1.0 features are supported` – Saber Jul 27 '16 at 13:28
  • 1
    What can I say... Did you notice I said "*I suggest you find out for sure, as it makes a big difference here*"? – michael.hor257k Jul 27 '16 at 13:42

4 Answers4

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')
Michael Kay
  • 156,231
  • 11
  • 92
  • 164
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

Daniel Haley
  • 51,389
  • 6
  • 69
  • 95
michael.hor257k
  • 113,275
  • 6
  • 33
  • 51
  • 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! – Sam B Jun 25 '17 at 00:38
  • 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. – JasonWH Apr 12 '19 at 18:04
  • Cannot reproduce your claimed result: https://xsltfiddle.liberty-development.net/6r5Gh3o – michael.hor257k Apr 12 '19 at 18:13
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

Daniel Haley
  • 51,389
  • 6
  • 69
  • 95
michael.hor257k
  • 113,275
  • 6
  • 33
  • 51
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)))"/>
Apurva Singh
  • 4,534
  • 4
  • 33
  • 42