Search code examples
xmlxsltjulian-date

XSL how to find the Julian day for a specific date?


I have this simple XML-file

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="faan.xsl"?>
<datum>2016-05-17</datum>

I want to use XSL (XSL 2.0 and extensions is not an option) to calculate Julian day for this date (correct answer is 2457526, this is just practice!) and came up with this style sheet that seems to be copy-pasted in many places. But, when a open the file in Explorer it just gives me the date 2016-05-17, and when I open it in Excel it says I can't use xsl:with-param in this place. I have obviously missed something very simple, but what?

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>

<xsl:template match="/">
    <xsl:for-each select="datum">
    <xsl:sort select="datum"/>
            <dateline>
                <datum><xsl:value-of select="datum"/></datum>
            </dateline>
    </xsl:for-each>
</xsl:template>

<xsl:template match="datum">
    <xsl:call-template name="calculate-julian-day" />
        <xsl:with-param name="year" select="substring(datum,1,4)" />
        <xsl:with-param name="month" select="substring(datum,6,2)" />
        <xsl:with-param name="day" select="substring(datum,9,2)" />
</xsl:template>

<xsl:template name="calculate-julian-day">
    <xsl:param name="year"/>
    <xsl:param name="month"/>
    <xsl:param name="day"/>

    <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:value-of select="$day + floor((153 * $m + 2) div 5) + $y * 365 + floor($y div 4) - floor ($y div 100) + floor($y div 400) - 32045"/>
</xsl:template>

</xsl:stylesheet>

Solution

  • Here is one possible solution:

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     <xsl:output omit-xml-declaration="yes" indent="yes"/>
    
    
      <xsl:template match="datum">
        <xsl:call-template name="date-to-julian-day">
          <xsl:with-param name="date" select="."/>
        </xsl:call-template>
      </xsl:template>
    
    <!--
    ==========================================================================
        Template: date-to-julian-day
     Description: Convert a date to julian day
     Parameters:-
        <year> <month> <day>
       or
        <date> (format: yyyymmdd or yyyy-mm-dd)
    ========================================================================== -->
        <xsl:template name="date-to-julian-day">
            <xsl:param name="year"/>
            <xsl:param name="month"/>
            <xsl:param name="day"/>
            <!-- or -->
            <xsl:param name="date" select="''"/>
            <!-- trim down date -->
            <xsl:variable name="tdate" select="translate($date,'-','')"/>
            <!-- decide which params were passed -->
            <xsl:variable name="yyyy">
               <xsl:choose>
                 <xsl:when test="string-length($date) &gt; 0">
                   <xsl:value-of select="substring($tdate,1,4)"/>
                 </xsl:when>
                 <xsl:otherwise><xsl:value-of select="$year"/></xsl:otherwise>
                </xsl:choose>
            </xsl:variable>
            <xsl:variable name="mm">
                <xsl:choose>
                  <xsl:when test="string-length($date) &gt; 0">
                    <xsl:value-of select="substring($tdate,5,2)"/>
                  </xsl:when>
                  <xsl:otherwise><xsl:value-of select="$month"/></xsl:otherwise>
                </xsl:choose>
            </xsl:variable>
            <xsl:variable name="dd">
                <xsl:choose>
                    <xsl:when test="string-length($date) &gt; 0">
                       <xsl:value-of select="substring($tdate,7,2)"/>
                    </xsl:when>
                    <xsl:otherwise><xsl:value-of select="$day"/></xsl:otherwise>
                </xsl:choose>
            </xsl:variable>
            <!-- pre-calcs -->
            <xsl:variable name="j0" select="ceiling(($mm - 14) div 12)"/>
            <xsl:variable name="j1" select="floor((1461 * ($yyyy + 4800 + $j0)) div 4)"/>
            <xsl:variable name="j2" select="floor((367 * ($mm - 2 - (12 * $j0))) div 12)"/>
            <xsl:variable name="j3" 
                          select="floor((3 * floor(($yyyy + 4900 + $j0) div 100)) div 4)"/>
            <!-- final calc -->
            <xsl:value-of select="$j1 + $j2 - $j3 + $dd - 32075"/>
        </xsl:template>
    
    </xsl:stylesheet>
    

    When this transformation is applied on the provided XML document:

    <datum>2016-05-17</datum>
    

    the wanted, correct result is produced:

    2457526
    

    Attribution:

    The provided code is part of the the datetime_lib.xsl XSLT 1.0 library of templates for date-time arithmetics and conversions, written by my friend Martin Rowlinson -- the author of XSelerator -- the best ever XSLT IDE, which is still a shining example of user-friendly and powerful UI.

    Sometimes around 2010 the XSelerator was freely available on sourceforge.net and this is how people would typically get hold of these valuable template libraries..