RE: [xsl] Date Difference

Subject: RE: [xsl] Date Difference
From: "Michael Kay" <mike@xxxxxxxxxxxx>
Date: Wed, 21 Dec 2005 00:56:51 -0000
> Questions:
> 
> A)
> Is there a reason the operators described within the op: 
> namespace in the CR are not available?  I began with statements like:
> 
> op:subtract-dates($earlier, $later)

It might be better to ask, is there any reason that the operators of the
language are specified by reference to a notional function library that
exists only for the purpose of specifying operators? The answer is that
someone thought that would be an elegant way of doing it. Readers may
disagree.

> 
> The functional notation seems much more intuitive to me than using
> arithmetic operations on dates. 

That's a separate question: why are some things operators and some things
functions. Again, there's no objective answer. When such aesthetic issues
get debated in a working group, the status quo usually prevails, and the
status quo is often something decided quite arbitrarily in an early draft.

> 
> B)
> This statement:
> 
>     <xsl:variable name="later" select="dateStandard[2]" as="xs:date"/>
> 
> seemed to work fine, but this one later:
> 
>     <xsl:variable name="regexE" 
>       select="fn:replace($earlierPadded, '(.*)/(.*)/(.*)', '$3-$1-$2')
> 	as="xs:date"/>

The result of replace() is a string (there's no need to use the fn: prefix,
by the way), and you'll therefore get a type error because the required type
of the variable is xs:date. If you want to turn a string into a date, you
have to cast it (as below). The "as" attribute declares that you expect the
"select" expression to deliver a date, which it doesn't.

> Stating it in the 
> following manner
> corrected the error:
> 
>     <xsl:variable name="regexE" 
>       select="xs:date(fn:replace($earlierPadded, '(.*)/(.*)/(.*)',
> '$3-$1-$2'))"/>
> 
> I suspect I am missing an implicit cast somewhere that makes the first
> statement work and renders the as= attribute moot.

"moot" in the English sense (controversial, debatable) or the American sense
(irrelevant)? (Sorry, Tommie, this is an international list and we can't
make assumptions).

The "as" attribute is an assertion about the type of the result of the
select expression, and your code failed because the assertion was false.
> 
> C)
> Any idiomatic corrections to the stylesheet or notational shortcuts I
> missed?

You've used essentially the same date(replace() expression twice, which to
me calls for a function.

I tend to do this kind of thing

<xsl:variable name="earlierPadded">
      <xsl:choose>
        <xsl:when test="fn:string-length(substring-before(date[1], '/')) =
1">
          <xsl:value-of select="concat('0',date[1])"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="date[1]"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>

as

<xsl:variable name="earlierPadded" select="
  if (string-length(substring-before(date[1], '/') = 1)
  then concat('0', date[1])
  else date[1]"/>

(and this is another expression that you use twice which should therefore be
a function)

You can use xsl:choose and still avoid creating a temporary tree, however,
by writing it as

<xsl:variable name="earlierPadded" as="xs:string">
      <xsl:choose>
        <xsl:when test="string-length(substring-before(date[1], '/')) =1">
          <xsl:sequence select="concat('0',date[1])"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:sequence select="date[1]"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>

Simpler still is to use format-number().

Michael Kay
http://www.saxonica.com/

Current Thread