1

I'm using XSLT 1.0 to output the duration between two date fields. The following code (from https://stackoverflow.com/a/38615456/3016153) outputs the Duration in Days, Hours, Minutes, and Seconds as expected, but I need to update it to output Years as well. Does anyone have any suggestions on how I can update this code to output the Year duration? Alternative code is welcomed as well, but it must use XSLT 1.0

Any assistance is greatly appreciated.

<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>

Sample XML:

<events>
<event>
    <name>Alpha</name>
    <start time="2015-02-21T00:59:06+02:00"/>
    <end time="2018-02-22T02:24:38+02:00"/>
</event>
<event>
    <name>Bravo</name>
    <start time="2016-02-21T00:59:06+02:00"/>
    <end time="2020-03-05T02:24:38+02:00"/>
</event>
<event>
    <name>Charlie</name>
    <start time="2016-02-21T00:59:06+02:00"/>
    <end time="2020-02-21T00:59:06+01:00"/>
</event>
enter code here
michael.hor257k
  • 113,275
  • 6
  • 33
  • 51
User1
  • 39
  • 5
  • 2
    There is no correct answer to this question, because the number of days in a year varies. You need to make some assumptions regarding what is a year.. – michael.hor257k Jul 23 '20 at 23:56
  • Hi @michael.hor257k, Can you provide some more details on what type of assumptions that need to be made? – User1 Jul 24 '20 at 00:26
  • You can make whatever assumption you want. For example, you could assume that every 365 days constitute a year. Or that a year elapses on the "anniversary" of the start date. Or any other assumption that fits your purpose for providing the information in this format. The point is that you need to make *some* assumption before you can construct the calculation. And that any calculation will be "correct" only under that assumption and not another. – michael.hor257k Jul 24 '20 at 00:38
  • @michael.hor257k thank you for clarifying. If we assume that every 365 days constitutes a year, can the code be updated to output a Year? – User1 Jul 24 '20 at 00:42
  • If you assume that, all you need to do is calculate the years as `floor($d div 365)` and days as `$d mod 365`. But it seems kind of strange that you need to calculate the difference to the precision of seconds, yet are willing to accept an error of app. 1 day per 4 years. – michael.hor257k Jul 24 '20 at 00:53
  • @michael.hor257k I appreciate the assistance. I added a new variable for . To your second part, should "says as $d mod 365) be another variable?. I only need to calculate to the precision of days. I am just using the minutes and seconds as a test. – User1 Jul 24 '20 at 01:04
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/218487/discussion-between-user1-and-michael-hor257k). – User1 Jul 24 '20 at 01:11
  • Let us not. ----- – michael.hor257k Jul 24 '20 at 08:26

1 Answers1

0

As stated in the comments, if you are willing to assume that every 365 days constitute a year, then all you need to do is change this part:

<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>

to:

<duration>
    <xsl:value-of select="floor($d div 365)"/>
    <xsl:text> years, </xsl:text>
    <xsl:value-of select="$d mod 365"/>
    <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>

As also stated in the comments, it is rather rather strange that you would be willing to accept an error of approximately 1 day per 4 years in a calculation that calculates to seconds precision. If you only need precision of days, then you should save processor time and electricity by removing the parts that calculate the hours, minutes and seconds.

michael.hor257k
  • 113,275
  • 6
  • 33
  • 51