Reference/citation for xslt 1.0 Julian day calculations - xslt

Having seen
Date operations on xsl 1.0
<xsl:template name="JDN">
<xsl:param name="date"/>
<xsl:param name="year" select="substring($date, 1, 4)"/>
<xsl:param name="month" select="substring($date, 6, 2)"/>
<xsl:param name="day" select="substring($date, 9, 2)"/>
<xsl:param name="a" select="floor((14 - $month) div 12)"/>
<xsl:param name="y" select="$year + 4800 - $a"/>
<xsl:param name="m" select="$month + 12*$a - 3"/>
<xsl:value-of select="$day + floor((153*$m + 2) div 5) + 365*$y + floor($y div 4) - floor($y div 100) + floor($y div 400) - 32045" />
I'd like to have a reference back to something a bit more concrete, rather than a stack overflow answer...it seems to work!....and I believe its based on some standard mathematical model for datetimes...is there some sort of canonical XSLT 1.0 implementation? published in a book or a library? i.e. how do I know there isn't a typo? I think implementing the model in XSLT is non trivial, I don't want to have to reinvent the wheel.

I wrote the above template based on the algorithm published in the Wikipedia's entry on Julian Day. They have since changed to another algorithm. If you want the source of the original algorithm, you will need to browse the article's revision history.
There are no "canonical" or "authoritative" sources for XSLT, other than the XSLT specifications published by the World Wide Web Consortium (W3C). Anyone with access to the algorithm can check the correctness of the implementation for themselves (and are strongly encouraged to do so, for anything they find on the Internet).

I have seen this template in an O'Reilly's book: https://www.oreilly.com/library/view/xslt-cookbook-2nd/0596009747/ch04.html (this of course doesn't guarantee that it's correct, but there can also be bugs in more formal datetime libraries.)
If you are stuck with XSLT 1.0, you can also defer to the EXSLT date module if your processor supports it (http://exslt.org/date/). MSXML allows you to insert Javascript or C# code depending on the version.

Related

code to get DATE in which output should be generated with the DATE which is exactly 2 days later [duplicate]

I need to get 3 days before and after a given date defined in a variable, and store each one of them in a new individual variable in xsl 1.0.
i can't use any extensions or third party tools.
Looking trough the answers in the forums, i found this: Expanding datetime ranges in XSLT 1.0 for a similar problem, but i dont fully understand if and how it would aply to my code.
Mi date variable is in standard dateTime format, like this:
<xsl:variable name="Date" select="2014-05-13T00:00:00"/>
And i would need to output an html similar to this:
<table>
<tr>
<td>
2014-05-10
<td>
</tr>
<!---some rows with pricing information -->
</table>
<table>
<tr>
<td>
2014-05-11
<td>
</tr>
<!---some rows with pricing information -->
</table>
<table>
<tr>
<td>
2014-05-12
<td>
</tr>
<!---some rows with pricing information -->
</table>
<!-- etc -->
In the rows with pricing information I will have to use each individual date to perform other operations, so each day must be stored in a variable for further use.
Is there a way to accomplish this, using just xslt 1.0?
Thanks in advance.
Adding/subtracting number of days to/from date in pure XSLT 1.0:
<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:param name="givenDate" select="'2014-05-13T00:00:00'"/>
<xsl:param name="daysDiff" select="-3"/>
<xsl:variable name="JDN">
<xsl:call-template name="JDN">
<xsl:with-param name="date" select="$givenDate" />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="newDate">
<xsl:call-template name="GD">
<xsl:with-param name="JDN" select="$JDN + $daysDiff" />
</xsl:call-template>
</xsl:variable>
<xsl:template match="/">
<output>
<GivenDate><xsl:value-of select="$givenDate"/></GivenDate>
<NewDate><xsl:value-of select="$newDate"/></NewDate>
</output>
</xsl:template>
<xsl:template name="JDN">
<xsl:param name="date"/>
<xsl:param name="year" select="substring($date, 1, 4)"/>
<xsl:param name="month" select="substring($date, 6, 2)"/>
<xsl:param name="day" select="substring($date, 9, 2)"/>
<xsl:param name="a" select="floor((14 - $month) div 12)"/>
<xsl:param name="y" select="$year + 4800 - $a"/>
<xsl:param name="m" select="$month + 12*$a - 3"/>
<xsl:value-of select="$day + floor((153*$m + 2) div 5) + 365*$y + floor($y div 4) - floor($y div 100) + floor($y div 400) - 32045" />
</xsl:template>
<xsl:template name="GD">
<xsl:param name="JDN"/>
<xsl:param name="f" select="$JDN + 1401 + floor((floor((4 * $JDN + 274277) div 146097) * 3) div 4) - 38"/>
<xsl:param name="e" select="4*$f + 3"/>
<xsl:param name="g" select="floor(($e mod 1461) div 4)"/>
<xsl:param name="h" select="5*$g + 2"/>
<xsl:param name="D" select="floor(($h mod 153) div 5 ) + 1"/>
<xsl:param name="M" select="(floor($h div 153) + 2) mod 12 + 1"/>
<xsl:param name="Y" select="floor($e div 1461) - 4716 + floor((14 - $M) div 12)"/>
<xsl:param name="MM" select="substring(100 + $M, 2)"/>
<xsl:param name="DD" select="substring(100 + $D, 2)"/>
<xsl:value-of select="concat($Y, '-', $MM, '-', $DD)" />
</xsl:template>
</xsl:stylesheet>
Result:
<?xml version="1.0" encoding="UTF-8"?>
<output>
<GivenDate>2014-05-13T00:00:00</GivenDate>
<NewDate>2014-05-10</NewDate>
</output>
--
Note that the givenDate's parameter value is a string and as such must be wrapped in single quotes.
SAXON 6.5.5 supports the EXSLT extensions, but the date:add-duration() from dates and times module — which would solve your problem elegantly — is not implemented.
However, you can directly use Java objects from within XSLT with Saxon:
You can also use a short-cut technique of binding external Java classes, by making the class name part of the namespace URI.
With the short-cut technique, the URI for the namespace identifies the class where the external function will be found. The namespace URI must either be "java:" followed by the fully-qualified class name (for example xmlns:date="java:java.util.Date") ...
Quoting from this post, the method for adding days to dates in Java is
String dt = "2008-01-01"; // Start date
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Calendar c = Calendar.getInstance();
c.setTime(sdf.parse(dt));
c.add(Calendar.DATE, 1); // number of days to add
dt = sdf.format(c.getTime()); // dt is now the new date
the XSLT version could look something like this (untested):
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:simple-date-format:="java:java.text.SimpleDateFormat"
xmlns:calendar="java:java.util.Calendar"
>
<xsl:template name="date-add-days">
<xsl:param name="input" />
<xsl:param name="days" />
<xsl:if test="
function-available('simple-date-format:new')
and function-available('calendar:get-instance')
">
<xsl:variable name="sdf" select="simple-date-format:new('yyyy-MM-dd')" />
<xsl:variable name="cal" select="calendar:get-instance()" />
<xsl:variable name="time" select="simple-date-format:parse($sdf, $input)" />
<xsl:variable name="tmp1" select="calendar:set-time($cal, $time)" />
<xsl:variable name="tmp2" select="calendar:add($cal, calendar:DATE(), number($days))" />
<xsl:variable name="res" select="calendar:get-time($cal)" />
<xsl:value-of select="simple-date-format:format($sdf, $res)" />
</xsl:if>
</xsl:template>
</xsl:stylesheet>
With regard to interacting with Java classes and objects from XPath:
From: http://saxon.sourceforge.net/saxon6.5.5/extensibility.html
The system function function-available(String name) returns true if there appears to be a method available with the right name.
If the method is a constructor, the XPath value is of type "wrapped Java object". The only way of using this is by passing it to another external function [...]
If the result is any other Java object (including null), it is returned as a "wrapped Java object".
From http://www.saxonica.com/documentation/extensibility/functions/staticmethods.html
Static methods can be called directly.
From http://www.saxonica.com/documentation/extensibility/functions/instance-methods.html)
Instance-level methods (that is, non-static methods) are called by supplying an extra first argument of type Java Object which is the object on which the method is to be invoked.
I'm not sure of the method name format. The Saxon 6.5.5 documentation seems to imply dashed format (toString() becomes to-string()), so I've been using this here. Maybe calendar:set-time() must actually called calendar:setTime(), try it out & fix my answer.

getting dynamic substring xslt

I am pretty new to all of developing / using XSLT but for work we have to use this now.
I am busy mapping messages from application A to B and i run into something I cannot find the answer to.
as input field i have <sys_external_id>201000077_G001_S20_H10</sys_external_id>
now have i created below:
<stopNumber>
<xsl:value-of select="substring(sys_external_id, 16, 4)" />
</stopNumber>
<stopHandlingNumber>
<xsl:value-of select="substring(sys_external_id, 20, 4)" />
</stopHandlingNumber>
this gives me at the moment the correct answer ( S20 and H10 )
now is the "problem" that the value of S20 and H10 can differ.
for example the value can be S2100 or H110. also the G001 can also differ.
A substring-after does not work because i cannot determine where my end is ( as far as I know )
In XSLT 2.0 you could do:
<xsl:variable name="tokens" select="tokenize(sys_external_id, '_')" />
<stopNumber>
<xsl:value-of select="$tokens[3]" />
</stopNumber>
<stopHandlingNumber>
<xsl:value-of select="$tokens[4]" />
</stopHandlingNumber>
If you're limited to XSLT 1.0, then it could be:
<xsl:variable name="tail" select="substring-after(substring-after(sys_external_id, '_'), '_')" />
<stopNumber>
<xsl:value-of select="substring-before($tail, '_')" />
</stopNumber>
<stopHandlingNumber>
<xsl:value-of select="substring-after($tail, '_')" />
</stopHandlingNumber>
This is assuming that the input string is simply a sequence of tokens delimited by an underscore.
Note also that some XSLT 1.0 processors support the EXSLT str:tokenize() extension function.

How can I trim a decimal value to four places behind the decimal point using XSLT

In my ORACLE SOA BPEL process, we are receiving a value as string # 10.2345678956 but for target system we just need to send upto 4 places in decimal like 10.2345. Could you please tell me how we can achieve that using xslt.
also if source system send value as 10 even those in that case, to target system we need to send 10.0000(always 4 digit in decimal)
To truncate (i.e. round down) a number to 4 decimal digits precision, you can do:
floor($number * 10000) div 10000
To format the result to always display 4 decimal digits:
format-number(floor($number * 10000) div 10000, '0.0000')
This works in XSLT 1.0 and XSLT 2.0 alike.
If you don't care about the direction of rounding, then do simply:
format-number($number, '0.0000')
and be done with it.
You don't specify which version of XSLT you are using, but in XSLT 1.0 you should do this "by hand", like as follows:
Note: as Michael says in the comments, you can use format-number in XSLT 1.0. However, the chosen rounding is unclear. While this works, see Michael's answer for a more trivial solution.
<xsl:template match="/">
<xsl:variable name="input" select="10.23456789" />
<xsl:call-template name="truncate">
<xsl:with-param name="val" select="$input" />
</xsl:call-template>
</xsl:template>
<xsl:template name="truncate">
<xsl:param name="val" />
<xsl:variable name="adj">
<xsl:value-of select="$val"/>
<xsl:if test="not(contains($val, '.'))">.</xsl:if>
<xsl:text>0000</xsl:text>
</xsl:variable>
<xsl:variable name="before" select="substring-before($adj, '.')" />
<xsl:variable name="after" select="substring-after($adj, '.')" />
<xsl:value-of select="$before" />
<xsl:text>.</xsl:text>
<xsl:value-of select="substring($after, 1, 4)" />
</xsl:template>
And in XSLT 2.0 and 3.0, you can simply do as follows:
<xsl:value-of select="format-number($input, '#.0000')" />
But this will round up (halves to even), i.e. with your input it would give you 10.2346. If that is undesirable, you could use the trick suggested by fvu:
<xsl:value-of select="
format-number(xs:integer(number($input) * 10000) div 10000, '#.0000')" />
Both the XSLT 1.0 and XSLT 2.0/3.0 versions above will append the necessary zeroes up to four.

How can I format pubDate to HH:MM AM/PM?

Is there an easy way to format an RSS pubDate
<pubDate>Thu, 28 May 2015 08:00:00 -0400</pubDate>
into:
8:00AM
I'm not all that familiar with date formatting, and I can't for the life of me recall what that time format is, so Google isn't helping me any.
Try it this way:
XSLT 1.0
<xsl:template match="pubDate">
<xsl:variable name="h" select="substring(., 18, 2)"/>
<xsl:variable name="m" select="substring(., 21, 2)"/>
<xsl:variable name="h12" select="($h + 11) mod 12 + 1"/>
<xsl:variable name="am.pm" select="substring('AMPM', 1 + 2*(number($h) > 11), 2)"/>
<xsl:value-of select="concat($h12, ':', $m, $am.pm)"/>
</xsl:template>
General date parsing is quite complex. There were some discussions to add the opposite of fn:format-date() to the standard XPath library 3.0, but did not succeed because of the complexity of the task in the generic case.
I am afraid you are limited to string manipulation here, which in your case is not too bad. For instance:
<xsl:variable name="hours" select="xs:integer(substring($input, 18, 2))"/>
<xsl:variable name="minutes" select="substring($input, 21, 2)"/>
<xsl:sequence select="
if ( $hours ge 12 ) then
concat($hours - 12, ':', $minutes, 'PM')
else
concat($hours, ':', $minutes, 'AM')"/>
Error management has not been added, up to you to add some, depending on how good and homogeneous is your input.

Implementing product() in XSLT

XPath 2.0 (and 3.0) has convenient aggregate functions sum() and avg(), but nothing that returns the product of a sequence of numeric atomic values.
Although implementing such a function is trivial in a language that allows assignment statements, it seems not to be that easy in XSLT. The only way I've found to get an aggregate product is to use recursion:
<xsl:function name="fn:products">
<xsl:param name="input" />
<xsl:param name="total" />
<xsl:if test="exists($input)">
<xsl:variable name="x" select="$input[1] * $total"/>
<xsl:sequence select="$x"/>
<xsl:sequence select="fn:products(subsequence($input,2),$x)"/>
</xsl:if>
</xsl:function>
But the above is actually my adaptation of a function that appears on p.994 of Michael Kay's book XSLT 2.0 and XPath 2.0 4th Edition. The original function outputs a sequence of running balances by recursively adding a sequence of numbers.
My version just multiplies the numbers, instead of adding them. The result is then a sequence of "running products". For example, fn:products((4, 0.75, 10, 0.7), 1) returns the sequence 4, 3, 30, 21.
Since I'm only interested in the product of all numbers, I just take the last item of the output with a filter expression: fn:products((4, 0.75, 10, 0.7), 1)[last()].
It works. But just for the sake of making good code, I wonder if there is a way to make the function to just directly return the last item as the aggregate product (i.e. just get 21, in the example above).
I tried a couple of things, but they just broke the function. Is there a way to achieve this? Is it possible also to implement it in a better, leaner way (the original function was not meant to return a singleton sequence)?
If you use one of the fancy processors, you can use dyn:evaluate.
<?xml version="1.0" encoding="UTF-8"?>
<test>
<num>4</num>
<num>0.75</num>
<num>10</num>
<num>0.7</num>
</test>
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:dyn="http://exslt.org/dyn/dyn.xml"
extension-element-prefixes="dyn"
>
<xsl:template match="/">
<xsl:variable name="run">
<xsl:for-each select="test/num"><xsl:value-of select="."/><xsl:if test="position() != last()"> * </xsl:if></xsl:for-each>
</xsl:variable>
<xsl:value-of select="dyn:evaluate(string($run))"/>
</xsl:template >
</xsl:stylesheet>