Re: [xsl] Getting years from duration

Subject: Re: [xsl] Getting years from duration
From: Wolfgang Laun <wolfgang.laun@xxxxxxxxx>
Date: Sat, 8 Jun 2013 17:09:41 +0200
Here's a simple function that avoids all problems arising from the
conversion of two dates to a duration. Age in completed years can be
computed by a simple comparison of months and days:

<xsl:function name="wl:age-in-years" as="xs:integer">
  <xsl:param name="birth" as="xs:date"/>
  <xsl:param name="death" as="xs:date"/>
  <xsl:variable name="birthYear"  as="xs:integer"
select="year-from-date($birth)"/>
  <xsl:variable name="birthMonth" as="xs:integer"
select="month-from-date($birth)"/>
  <xsl:variable name="birthDay"   as="xs:integer"
select="day-from-date($birth)"/>
  <xsl:variable name="deathYear"  as="xs:integer"
select="year-from-date($death)"/>
  <xsl:variable name="deathMonth" as="xs:integer"
select="month-from-date($death)"/>
  <xsl:variable name="deathDay"   as="xs:integer"
select="day-from-date($death)"/>
  <xsl:choose>
    <xsl:when test="$deathMonth > $birthMonth">
      <xsl:sequence select="$deathYear - $birthYear"/>
    </xsl:when>
    <xsl:when test="$deathMonth &lt; $birthMonth">
      <xsl:sequence select="$deathYear - $birthYear - 1"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:choose>
        <xsl:when test="$deathDay >= $birthDay">
          <xsl:sequence select="$deathYear - $birthYear"/>
        </xsl:when>
        <xsl:otherwise><!-- $deathDay &lt; $birthDay -->
          <xsl:sequence select="$deathYear - $birthYear - 1"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>


On 08/06/2013, Martin Holmes <mholmes@xxxxxxx> wrote:
> We came up against what looked like a very simple XPath issue today, and
> hit a brick wall with it. Given data that looks like this:
>
>          <person role="author">
>            <persName>Milton, John</persName>
>            <birth when="1600-12-09"/>
>            <death when="1674-11-08"/>
>          </person>
>
> we want to calculate the age of the person at death. So we thought:
> subtract the death date from the birth date to get a duration, then
> extract the years from the duration:
>
>    <xsl:template match="person">
>
>      <xsl:variable name="life" select="xs:date(death/@when) -
> xs:date(birth/@when)"/>
>
>      <xsl:variable name="age" select="years-from-duration($life)"/>
>
>      <xsl:text>Age at death: </xsl:text>
>      <xsl:value-of select="$age"/>
>
>    </xsl:template>
>
>
> However, the only value we were able to get back, after trying all
> manner of permutations and casts, was zero. It appears that what comes
> back from the date subtraction (which I think uses the
> op:subtract-dates() operator) is always an xs:dayTimeDuration, and that
> cannot AFAIKS be manipulated into anything from which a year can be
> extracted.
>
> It's always possible to get the number of days and divide by 365.25, but
> it seems strange to have to do that. Given the range of date- and
> duration-related functions, I'm sure there must be some better way of
> getting the result. Does anyone know?
>
> This is using Saxon 9.4.0.6.
>
> Cheers,
> Martin

Current Thread