Finding the difference between 2 dates in xslt - xslt

Is there a less then kludgey way of finding the difference in days between 2 dates in xslt? If so can you point me in the right direction. I am receiving dates in the format of mm/dd/yyyy.

A nicer (and shorter) alternative for XSLT 1.0 is to compute the equivalent Julian dates and subtract them.
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"/>
Usage:
<xsl:variable name="dateInv" select="'20120406'" />
<xsl:call-template name="calculate-julian-day">
<xsl:with-param name="year" select="substring($date,1,4)"/>
<xsl:with-param name="month" select="substring($date,5,2)"/>
<xsl:with-param name="day" select="substring($date,7,2)"/>
</xsl:call-template>
Repeat for the second date and you'll have two integers. Then, simply subtract them.

This could be easily done using the following expression:
days-from-duration(xs:date('yyyy-MM-dd')-xs:date('yyyy-MM-dd'))
For example:
days-from-duration(xs:date('2012-06-30')-xs:date('2012-06-18'))
will give result of 12

Use XSLT 2.0 (XPath 2.0) for this:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:my="my:my">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<xsl:variable name="vDate1"
select="my:dateFromUsDate(/*/d1)"/>
<xsl:variable name="vDate2"
select="my:dateFromUsDate(/*/d2)"/>
<xsl:sequence select=
"($vDate1 - $vDate2) div xs:dayTimeDuration('P1D')"/>
</xsl:template>
<xsl:function name="my:dateFromUsDate" as="xs:date">
<xsl:param name="pUsDate" as="xs:string"/>
<xsl:sequence select=
"xs:date(concat(substring($pUsDate,7,4),
'-',
substring($pUsDate,1,2),
'-',
substring($pUsDate,4,2)
)
)
"/>
</xsl:function>
</xsl:stylesheet>
when this transformation is applied on the following XML document:
<t>
<d1>04/06/2011</d1>
<d2>01/11/2010</d2>
</t>
the wanted, correct result (the difference is 450 days) is produced:
450

Related

XSLT Convert milliseconds to timecode

Hello and have a nice day!
There are two steps that I'm trying to figure out:
(But not in priority) to take integer from xml file with tag "duration" in milliseconds
I am trying to convert "duration" milliseconds into timecode that will looks like hh:mm:ss:ff , where h - hours, m - minutes, s - seconds and f - frames (25frames=1second).
As I see the algorithm is:
z=milliseconds
h=z idiv (60*60*25)
m=(z-h*60*60*25) idiv (60*25)
s=(z-h*60*60*25-m*60*25) idiv 25
f=(z-h*60*60*25-m*60*25-s*25)
Have any idea how to make a calculation within XSLT properly?
Consider the following example:
XML
<input>
<milliseconds>45045500</milliseconds>
</input>
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:template match="input">
<output>
<xsl:variable name="f" select="floor(milliseconds div 40) mod 25" />
<xsl:variable name="s" select="floor(milliseconds div 1000) mod 60" />
<xsl:variable name="m" select="floor(milliseconds div 60000) mod 60" />
<xsl:variable name="h" select="floor(milliseconds div 3600000)" />
<timecode>
<xsl:value-of select="format-number($h, '00')"/>
<xsl:value-of select="format-number($m, ':00')"/>
<xsl:value-of select="format-number($s, ':00')"/>
<xsl:value-of select="format-number($f, ':00')"/>
</timecode>
</output>
</xsl:template>
</xsl:stylesheet>
Result
<?xml version="1.0" encoding="UTF-8"?>
<output>
<timecode>12:30:45:12</timecode>
</output>
Note that this truncates fractional frames. If you want to round to the nearest frame, then change the variables to:
<xsl:variable name="totalFrames" select="round(milliseconds div 40)" />
<xsl:variable name="f" select="$totalFrames mod 25" />
<xsl:variable name="s" select="floor($totalFrames div 25) mod 60" />
<xsl:variable name="m" select="floor($totalFrames div 1500) mod 60" />
<xsl:variable name="h" select="floor($totalFrames div 90000)" />
Here the result will be:
<timecode>12:30:45:13</timecode>
In XSLT 2.0 or higher, you can shorten the expression floor($a div $b) to $a idiv $b.

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.

XSLT mapping in SOA transformation to get max date from last month

I am trying to create an XSLT mapping to get the last(max) day of the previous month.
Eg- If I pass a value of 2019-10-17 to the mapping it should return
2019-09-30. The date format that I am using here is YYYY-MM-DD.
tried to get the month from the current data and subtract it with 1 so that it would return the previous month. But I am not able to get the max date of the last month.
xp20:month-from-dateTime (/ns0:ddSelecCorpoMasterOutputCollection/ns0:ddSelecCorpoMasterOutput/ns0:FROM_DATE_FILTER ) - 1
input- sysdate
o/p- maxdate of previous month
eg- i/p-2019-10-18
o/p- 2019-09-30
Thanks in advance.
Finding the last day of previous month in pure XSLT 1.0:
<xsl:template name="end-of-last-month">
<xsl:param name="date"/>
<!-- extract date components -->
<xsl:variable name="year" select="substring($date, 1, 4)"/>
<xsl:variable name="month" select="substring($date, 6, 2)"/>
<!-- go one month back -->
<xsl:variable name="y" select="$year - ($month = 1)"/>
<xsl:variable name="m" select="($month + 10) mod 12 + 1"/>
<!-- get month length -->
<xsl:variable name="cal" select="'312831303130313130313031'"/>
<xsl:variable name="leap" select="not($y mod 4) and $y mod 100 or not($y mod 400)"/>
<xsl:variable name="month-length" select="substring($cal, 2*($m - 1) + 1, 2) + ($m=2 and $leap)" />
<!-- output -->
<xsl:value-of select="$y" />
<xsl:value-of select="format-number($m, '-00')" />
<xsl:text>-</xsl:text>
<xsl:value-of select="$month-length" />
</xsl:template>
Example:
XML
<input>
<date>2019-01-15</date>
<date>2019-02-15</date>
<date>2019-03-15</date>
<date>2019-04-15</date>
<date>2019-05-15</date>
<date>2019-06-15</date>
<date>2019-07-15</date>
<date>2019-08-15</date>
<date>2019-09-15</date>
<date>2019-10-15</date>
<date>2019-11-15</date>
<date>2019-12-15</date>
<date>2020-01-15</date>
<date>2020-02-15</date>
<date>2020-03-15</date>
</input>
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:template match="/input">
<output>
<xsl:for-each select="date">
<end-of-last-month date="{.}">
<xsl:call-template name="end-of-last-month">
<xsl:with-param name="date" select="."/>
</xsl:call-template>
</end-of-last-month>
</xsl:for-each>
</output>
</xsl:template>
<xsl:template name="end-of-last-month">
<xsl:param name="date"/>
<!-- extract date components -->
<xsl:variable name="year" select="substring($date, 1, 4)"/>
<xsl:variable name="month" select="substring($date, 6, 2)"/>
<!-- go one month back -->
<xsl:variable name="y" select="$year - ($month = 1)"/>
<xsl:variable name="m" select="($month + 10) mod 12 + 1"/>
<!-- get month length -->
<xsl:variable name="cal" select="'312831303130313130313031'"/>
<xsl:variable name="leap" select="not($y mod 4) and $y mod 100 or not($y mod 400)"/>
<xsl:variable name="month-length" select="substring($cal, 2*($m - 1) + 1, 2) + ($m=2 and $leap)" />
<!-- output -->
<xsl:value-of select="$y" />
<xsl:value-of select="format-number($m, '-00')" />
<xsl:text>-</xsl:text>
<xsl:value-of select="$month-length" />
</xsl:template>
</xsl:stylesheet>
Result
<?xml version="1.0" encoding="UTF-8"?>
<output>
<end-of-last-month date="2019-01-15">2018-12-31</end-of-last-month>
<end-of-last-month date="2019-02-15">2019-01-31</end-of-last-month>
<end-of-last-month date="2019-03-15">2019-02-28</end-of-last-month>
<end-of-last-month date="2019-04-15">2019-03-31</end-of-last-month>
<end-of-last-month date="2019-05-15">2019-04-30</end-of-last-month>
<end-of-last-month date="2019-06-15">2019-05-31</end-of-last-month>
<end-of-last-month date="2019-07-15">2019-06-30</end-of-last-month>
<end-of-last-month date="2019-08-15">2019-07-31</end-of-last-month>
<end-of-last-month date="2019-09-15">2019-08-31</end-of-last-month>
<end-of-last-month date="2019-10-15">2019-09-30</end-of-last-month>
<end-of-last-month date="2019-11-15">2019-10-31</end-of-last-month>
<end-of-last-month date="2019-12-15">2019-11-30</end-of-last-month>
<end-of-last-month date="2020-01-15">2019-12-31</end-of-last-month>
<end-of-last-month date="2020-02-15">2020-01-31</end-of-last-month>
<end-of-last-month date="2020-03-15">2020-02-29</end-of-last-month>
</output>
Demo: https://xsltfiddle.liberty-development.net/94AbWB5
If you have access to the XPath 2.0 date/time library,
(1) convert to an xs:date
<xsl:variable name="d" select="xs:date($in)"/>
(2) extract the day of the month:
<xsl:variable name="dom" select="day-from-date($d)"/>
(3) subtract this number of days from the date:
<xsl:variable name="result" select="$d - xs:dayTimeDuration('P1D') * $dom"/>

XSLT time zone conversion

I'm working with a system that will only output the server time in Central Time Zone (CT). I need to convert this in XSLT to US Eastern Time.
Is there a built in method to translate this or do I need to use Regex?
<node time="02:14 pm CT" />
Current Output: 02:14 pm CT
Desired Output: 03:14 pm ET
There are at least two main paths to choose between, converting it into a time and using a time based library, or taking it as a string and doing straight string manipulation. The following is string manipulation:
<xsl:variable name="time" select="'11:14 pm CT'"/> <!-- the input value -->
<xsl:variable name="hours" select="number(substring-before($time,':'))"/> <!-- numeric hours -->
<xsl:variable name="mer" select="substring($time,7,2)"/> <!-- the am or pm part -->
<xsl:choose>
<xsl:when test="$hours < 12"> <!-- if we are 01-11 -->
<xsl:value-of select="substring(concat('0', $hours + 1), string-length(concat('0', $hours + 1)) - 1, 2)"/> <!-- add an hour and repad the string with leading zero, messy -->
</xsl:when>
<xsl:otherwise>
<xsl:text>01</xsl:text> <!-- we were 12, so just use 01 -->
</xsl:otherwise>
</xsl:choose>
<xsl:value-of select="substring($time, 3,4)"/> <!-- pull the minutes forward -->
<xsl:choose>
<xsl:when test="not($hours = 11)"> <!-- if we were not 11 for hours we keep the same am/pm -->
<xsl:value-of select="$mer"/>
</xsl:when>
<xsl:otherwise>
<xsl:choose>
<xsl:when test="$mer = 'pm'"> <!-- otherwise we flip it -->
<xsl:text>am</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>pm</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
<xsl:text> ET</xsl:text>
There is no built-in method for this in XSLT 1.0. Regardless, it would have been fairly trivial to do - except for the fact that your time input is in 12-hour format. This makes the process rather tedious, so I have split it off to a processing template:
<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:template match="/">
<node>
<xsl:attribute name="time">
<xsl:call-template name="time-offset">
<xsl:with-param name="time" select="node/#time"/>
</xsl:call-template>
</xsl:attribute>
</node>
</xsl:template>
<xsl:template name="time-offset">
<xsl:param name="time"/>
<xsl:param name="offset" select="1"/>
<xsl:param name="h12" select="substring($time, 1, 2)"/>
<xsl:param name="pm" select="contains($time,'p') or contains($time,'P')"/>
<xsl:param name="h24" select="$h12 mod 12 + 12*$pm"/>
<xsl:param name="newH24" select="($h24 + $offset + 24) mod 24"/>
<xsl:param name="newH12" select="($newH24 + 11) mod 12 + 1"/>
<xsl:param name="am.pm" select="substring('AMPM', 1 + 2*($newH24 > 11), 2)"/>
<xsl:value-of select="concat(format-number($newH12, '00'), substring($time, 3, 4), $am.pm, ' ET')"/>
</xsl:template>
</xsl:stylesheet>
When the above stylesheet is applied to the example input:
<node time="12:14 am CT" />
the result is:
<?xml version="1.0" encoding="UTF-8"?>
<node time="01:14 AM ET"/>

How do I find the number of elements in a <xs:list> using XSLT?

I have an XML schema that contains the following type :
<xs:simpleType name="valuelist">
<xs:list itemType="xs:double"/>
</xs:simpleType>
A sample XML fragment would be:
<values>1 2 3.2 5.6</values>
In an XSLT transform, how do I get the number of elements in the list?
How do I iterate over the elements?
I. XPath 2.0 (XSLT 2.0) solution:
count(tokenize(., ' '))
II. XPath 1.0 (XSLT 1.0) solution:
string-length()
-
string-length(translate(normalize-space(), ' ', ''))
+ 1
As for iteration over the items of this list:
In XPath 2.0 / XSLT 2.0 just use the above XPath 2.0 expression as the value of a select attribute:
--
for $i in tokenize(., ' '),
$n in number($i)
return
yourXPathExpression
--
2. In XSLT 1.0 you need to have some more code for splitting/tokenization. There are several good answers to this question (part of them mine) -- just search for something like "xslt split a string"
If list is strictly spaced, you can count spaces based on string length.
<values>1 2 3.2 5.6</values>
XSLT:
<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:template match="/">
<xsl:value-of select="string-length(values)
- string-length(translate(values, ' ', '')) + 1"/>
</xsl:template>
</xsl:stylesheet>
To iterate over elements you have to split this string. There a lot of examples at SO.
In a schema-aware transformation, use count(data(value)).
With a little imagination you can write your own split function :
<xsl:stylesheet version = '1.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>
<xsl:output method='text'/>
<xsl:template name="split-list">
<xsl:param name="list" />
<xsl:param name="separator"/>
<xsl:variable name="newlist" select="concat(normalize-space($list), $separator)" />
<xsl:variable name="first" select="substring-before($newlist, $separator)" />
<xsl:variable name="remaining" select="substring-after($newlist, $separator)" />
<xsl:message terminate="no">
<xsl:value-of select="$first" />
</xsl:message>
<xsl:if test="$remaining">
<xsl:call-template name="split-list">
<xsl:with-param name="list" select="$remaining" />
<xsl:with-param name="separator" select="$separator"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template match="/">
<xsl:variable name="myList">
1 2 3.2 5.6
</xsl:variable>
<xsl:call-template name="split-list">
<xsl:with-param name="list" select="$myList" />
<xsl:with-param name="separator">
<xsl:text> </xsl:text>
</xsl:with-param>
</xsl:call-template>
</xsl:template>
</xsl:stylesheet>
Output :
[xslt] Loading stylesheet D:\Tools\StackOverFlow\test.xslt
[xslt] 1
[xslt] 2
[xslt] 3.2
[xslt] 5.6