Make a Negative number Positive - XSLT - xslt

I was wondering whether there is a way to make a negative value, positive? Here is a snippet of my xslt document:
<NumberOfLinesAtRate>
<xsl:value-of select="number(/xsales:Qty)" />
</NumberOfLinesAtRate>
The problem is, sometimes this Qty value may be negative, -1, -2 etc.. in my original XML document. Is there a way I can make this always be positive in my transformed document?

If you are using XSLT1.0 you can use this formula (I've left out the namespace for brevity)
<xsl:value-of select="Qty * (Qty >= 0) - Qty * not(Qty >= 0)" />
In XSLT2.0 there is a dedicated abs operator you can use
<xsl:value-of select="abs(Qty)" />

This would be a fairly concise way to do it:
<xsl:value-of select="Qty * (1 - 2 * (Qty < 0))" />

Related

XSLT 1.0 - Padding 0's with Math Equation

I am trying to pad an output in XSLT with variables being multiplied in XSLT 1.0. I cannot use the format-number because it will auto-round the numbers but I need to get the decimals in place. I can get this to work when I use column formatting within the XML, but since this has multiple column formats within the value-of-select, it does not seem to work here and obey the 7 in the code below:
<xsl:value-of select="substring(E_BaseRate * E_NormalHours * 2, 1, 7)" disable-output-escaping="yes"/>
The output I am getting is: 1601.93 translated to 160193 but I need to get the result of 0160193. Is there a way to do this?
I broke this out to give you an idea. You could use as is or put it all together into one select...
<xsl:variable name="numToString" select="string(160193)"/>
<xsl:variable name="numLen" select="string-length($numToString)"/>
<xsl:variable name="value" select="concat(substring('0000000', 1, 7 - $numLen), $numToString)"/>

XSLT format-number with comma for indian price

I want to display a price for India, like this:
5,55,555
And not
555,555
There should be no decimal. There should be a comma, like this:
1,000 one thousand
10,000 ten thousand
1,00,000 one lakh
My code:
<Price>555555</Price>
<xsl:decimal-format name="Format_INR" grouping-separator="," />
<xsl:value-of select="format-number(Price, '#,##,###', 'Format_INR')" />
But it displays
555,555
What did I do wrong?
Thank you for your help.
The XSLT 2.0 specification of format-number() allows irregular grouping separators as in your example.
The XSLT 1.0 specification is based on the Java specification of DecimalFormat, which requires regular intervals between grouping separators.
(To be more precise: the JDK 7 spec requires regular intervals, or at any rate, it treats the last interval as the one to be used: (the interval between the last one and the end of the integer is the one that is used. So "#,##,###,####" == "######,####" == "##,####,####". But the XSLT 1.0 spec refers specifically to JDK 1.1.8, which is pretty-well unobtainable nowadays; my recollection is that it was very vague on such questions, and later versions of the JDK specification essentially documented the bugs in the initial implementation. To the extent that JDK 1.1.8 was vague, XSLT 1.0 implementations are free to do their own thing.)
As already mentioned, in XSLT 2.0 you can use:
<xsl:value-of select="format-number(Price, '#,##,###')" />
This will accommodate numbers up to 9,999,999. Above that, you need to add more separators, e.g.:
<xsl:value-of select="format-number(Price, '##,##,##,###')" />
will work for numbers up to 999,999,999 and so on.
In XSLT 1.0 you can do:
<xsl:choose>
<xsl:when test="Price >= 1000">
<xsl:value-of select="format-number(floor(Price div 1000), '#,##')" />
<xsl:text>,</xsl:text>
<xsl:value-of select="format-number(Price mod 1000, '000')" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="format-number(Price, '#,###')" />
</xsl:otherwise>
</xsl:choose>
This will work for any magnitude of Price. If you need to reuse this, consider making it a named template.
Note that neither method requires you to define a xsl:decimal-format.

How can I shorten this XSLT snippet?

I have to repeat the following XSLT snippet like 100 times and I would like it to be as small as possible. Is there a way to make an equivalent XSLT snippet that is shorter?
<xslo:variable name="myVariable" select="//This/that/anotherthing" />
<xslo:choose>
<xslo:when test="string($myVariable) != 'NaN'">
<xslo:text>1</xslo:text>
</xslo:when>
<xslo:otherwise>
<xslo:text>0</xslo:text>
</xslo:otherwise>
</xslo:choose>
I'm basically setting the state of a checkbox based on whether or not a value exists in //This/that/anotherthing in the source xml.
Can be XSLT 1.0 or XSLT 2.0, doesn't matter.
You can use an if instead of xsl:choose (XSLT 2.0 only)...
<xsl:value-of select="if (string(number(//This/that/anotherthing)) = 'NaN') then 0 else 1"/>
I also dropped the xsl:variable, but if you need it for some other reason, you can put it back.
You could also create a function...
<xsl:function name="local:isNumber">
<xsl:param name="context"/>
<xsl:value-of select="if (string(number($context)) = 'NaN') then 0 else 1"/>
</xsl:function>
usage...
<xsl:value-of select="local:isNumber(//This/that/anotherthing)"/>
<xslo:variable name="myVariable" select="//This/that/anotherthing" />
<xslo:value-of select="number(boolean($myVariable))"/>
If I understand the purpose correctly - that is return 1 if the value in question can be successfully expressed as a number, 0 otherwise - then I believe:
<xsl:value-of select="number(//This/that/anotherthing castable as xs:double)"/>
would be the most straightforward way (in XSLT 2.0) to achieve it.
Edit
In view of your change of purpose:
I'm basically setting the state of a checkbox based on whether or not
a value exists in //This/that/anotherthing
That's even simpler:
<xsl:value-of select="number(boolean(string(//This/that/anotherthing)))"/>

Is there a functionality in XSLT through which i can put a test condition on a range of values

For an exampple i want to test if the time (customized variable here) is between 1345:59 - 1400:00 Hrs
I am not sure if this is the right way of doing this.
Is there a way i can apply a range for the test condition. Like its true if the current time is between this range otherwise not.
Please let me know if there is any functionality in XSLT 1.0
<xsl:when test="(($curHour = '13' and $curMin >= '45' and $curMin <= '59' ) or ($curHour = '14' and $curMin >= '00' and $curMin <= '30' ))">
Thanks!
If what you're given is already broken into hour and minute, you could do:
<xsl:variable name="minutes" select="60 * $curHour + $curMin" />
<xsl:choose>
<xsl:when test="826 <= $minutes and $minutes < 840">
...
This returns true when the time given by the two variables is between 13:46 (inclusive) and 14:00 (exclusive).
Edit:
If not, then you could streamline the process by calculating the $minutes variable directly from the $curTime parameter as:
<xsl:variable name="minutes" select="60 * substring($curTime,12,2) + substring($curTime,15,2)" />
(based on what you showed in the comments).
The easiest way would be to treat the whole HHmmss time value as a single number, e.g.
<xsl:variable name="timeNum" select="number(concat(substring($curTime, 12, 2),
substring($curTime, 15, 2), substring($curTime, 18, 2))" />
(offsets taken from your comment, which are consistent with an ISO8601 timestamp format YYYY-MM-DDTHH:mm:ssZ). This number can then be directly compared with the target times
<xsl:when test="$timeNum > 134559 and $timeNum <= 140000">

date minus another date in xslt

Hope someone can help. I am trying to compare 2 dates inside an XML file and using a XSLT to do some calculation:
For example I have 2 dates in XML: 2011-05-23 and 2011-04-29. I want to do calculation inside XSLT like below:
('2011-05-23'-'2011-04-29')*30 = 24*30= 720
Can anyone shed any light?
An XSLT 2.0 solution
<?xml version="1.0"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:template match="/">
<xsl:value-of select="days-from-duration(
xs:date('2011-05-23')
- xs:date(xs:date('2011-04-29'))
)*30"/>
</xsl:template>
</xsl:stylesheet>
Yields: 720
xs:date() function evaluates the dates, which can be used to perform date operations
subtracting the second date from the first yields the xdt:dayTimeDuration P24D (24 days)
days-from-duration() extracts the days component from the xdt:dayTimeDuration (24)
then you can use that number to perform normal arethmatic (e.g. 24*30=720)
It might be worth looking at the EXSLT - date:difference solution. I believe that should do what you want, and there's even a straight XSLT implementation available.
Be aware though that the returned value is in duration format as specified in the XML Schema Part 2: Datatypes Second Edition, so it's likely you will need to process the result to derive the unit you wish to use in calculation (for instance in your example above you're expecting a result detailing the number of days difference - so you would need to pull out the relevant unit you want to work with, the result from date:difference in that case would most likely be "P24D").
Here's two templates I sometimes use for date calculations:
<xsl:template name="calcseconds">
<xsl:param name="date" />
<xsl:value-of select="(((substring($date,1,4) - 1970) * 365)+floor((substring($date,1,4) - 1970) div 4)+substring('000,031,059,090,120,151,181,212,243,273,304,334,365',substring($date,6,2)*4-3,3)+(substring($date,9,2)-1)+(1-floor(((substring($date,1,4) mod 4) + 2) div 3))*floor((substring($date,6,2)+17) div 20))*86400+(substring($date,12,2)*3600)+(substring($date,15,2)*60)+substring($date,18,2)" />
</xsl:template>
<xsl:template name="calcdays">
<xsl:param name="date" />
<xsl:value-of select="(((substring($date,1,4) - 1970) * 365)+floor((substring($date,1,4) - 1970) div 4)+substring('000,031,059,090,120,151,181,212,243,273,304,334,365',substring($date,6,2)*4-3,3)+(substring($date,9,2)-1)+(1-floor(((substring($date,1,4) mod 4) + 2) div 3))*floor((substring($date,6,2)+17) div 20))" />
</xsl:template>
They're a bit of a mouthful, but they'll calculate the number of seconds/days since midnight 1st January 1970 as an integer, which you can then do straight arithmetic on. They rely on the date format being yyyy-mm-dd hh:mm:ss, but manipulation of the parameters of the substring calls should allow you to process dates in any format you need.