XSLT Convert milliseconds to timecode - xslt

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.

Related

How to calculate time difference between two given time in xslt?

Given below is the xml.
I want to display time difference like: "2h 52m".
START TIME - 09:02
STOP TIME - 11:45
TOTAL -2h 52m
<Surgery>
<SURGERY_START_TIME>9:02</SURGERY_START_TIME>
<SURGERY_STOP_TIME>11:45</SURGERY_STOP_TIME>
</Surgery>
I tried the below code but that doesn't work,
<xsl:variable name="surgStartTime">
<xsl:value-of select="SURGERY_START_TIME/."/>
</xsl:variable>
<xsl:variable name="surgStopTime">
<xsl:value-of select="SURGERY_STOP_TIME/."/>
</xsl:variable>
<xsl:value-of select="$surgStartTime -$surgStopTime)/>
There is no support for date/time arithmetic in XSLT 1.0; you need to do the calculation yourself - e.g:
<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="Surgery">
<!-- calculate the duration in minutes -->
<xsl:variable name="duration" select="60 * substring-before(SURGERY_STOP_TIME, ':') + substring-after(SURGERY_STOP_TIME, ':') - 60 * substring-before(SURGERY_START_TIME, ':') - substring-after(SURGERY_START_TIME, ':')" />
<xsl:copy>
<xsl:copy-of select="*"/>
<DURATION>
<!-- output whole hours -->
<xsl:value-of select="floor($duration div 60)"/>
<xsl:text>h </xsl:text>
<!-- output remaining minutes -->
<xsl:value-of select="$duration mod 60"/>
<xsl:text>m</xsl:text>
</DURATION>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Applied to your input example, the result will be:
<?xml version="1.0" encoding="UTF-8"?>
<Surgery>
<SURGERY_START_TIME>9:02</SURGERY_START_TIME>
<SURGERY_STOP_TIME>11:45</SURGERY_STOP_TIME>
<DURATION>2h 43m</DURATION>
</Surgery>
and not 2h 52m as stated in your question.

xquery or xslt to convert time hours to PT*H*M format

I am new to XQuery and XSLT.
I'm looking for an XQuery or XQuery function to convert the timetotal i.e. 5 Hours to PT5H or 5.5 hours to PT5H30M
Not sure if XQuery can do that. If not any XSLT 1.0 function is ok too.
You can't define an xs:dayTimeDuration with a fractional number of hours, or minutes, but you can use a fractional number of seconds, so just multiply the hours by 3600 and create a duration in seconds:
xs:dayTimeDuration(
concat(
'PT',
$hours * 3600,
'S'
)
)
e.g.
let
$hours:= 5.551245
return
xs:dayTimeDuration(
concat(
'PT',
$hours * 3600,
'S'
)
)
returns: PT5H33M4.482S
Consider the following example:
XML
<input>
<hours>5</hours>
<hours>5.5</hours>
<hours>5.555</hours>
</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="hours">
<xsl:variable name="seconds" select=". * 3600" />
<xsl:variable name="h" select="floor($seconds div 3600)"/>
<xsl:variable name="m" select="floor($seconds div 60) mod 60"/>
<xsl:variable name="s" select="$seconds mod 60"/>
<duration>
<xsl:text>PT</xsl:text>
<xsl:value-of select="$h"/>
<xsl:text>H</xsl:text>
<xsl:value-of select="$m"/>
<xsl:text>M</xsl:text>
<xsl:value-of select="$s"/>
<xsl:text>S</xsl:text>
</duration>
</xsl:for-each>
</output>
</xsl:template>
</xsl:stylesheet>
Result
<?xml version="1.0" encoding="UTF-8"?>
<output>
<duration>PT5H0M0S</duration>
<duration>PT5H30M0S</duration>
<duration>PT5H33M18S</duration>
</output>
This assumes the input value cannot be negative.
Do note that PT5H30Mand PT330M are equally valid expressions of the same duration - so you could just multiply the input value by 60 (assuming you only need precision to a minute).

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"/>

XML to Fixed Length File Using XSLT (Complex Position Logic)

We have a requirement in which need to convert XML into Fixed Length File.
First record is as header and after that we have actual records..From 2 record onwards we need to apply the logic which is mentioned below:
1.After length 45, consider 10 numbers 0000001000, what ever be the
last digit we need to check and replace by following the below
table:
"For Positive Amount: (0000001000) - (000000100{)
{= 0
A = 1
B = 2
c = 3
D = 4
E = 5
F = 6
G = 7
H = 8
I = 9
I have not that much idea so created the small XSLT , request anyone please help on the same.
Input:
<?xml version='1.0' encoding='utf-8'?>
<ZR>
<INPUT>
<I_FIL>ERES</I_FIL>
</INPUT>
<TABLES>
<T_ER>
<item>
<DATA> HEADER1111111122222222333333344456</DATA>
</item>
<item>
<DATA>778944 D4E2 EA 1234567891 2018-11-060000001000EA
0000000000000100001020D04YA30TRE0000000XXXYYY 300{ P 2018-11-05</DATA>
</item>
<item>
<DATA>987654 D4E2 EA 1987654321 2018-11-060000002001EA
0000000000000100001020D04YA30UUU0000000XXXLRB 100{ P 2018-11-05</DATA>
</item>
.
.
.
.
.
.
.
.
</T_ER>
</TABLES>
</ZR>
XSLT:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fn="http://www.w3.org/2005/xpath-functions" >
<xsl:output omit-xml-declaration="yes"/>
<xsl:param name="break" select="'
'" />
<xsl:template match="/">
<xsl:value-of select="ZR/TABLES/T_ER/item[1]/DATA"/>
<xsl:value-of select="$break" />
</xsl:template>
</xsl:stylesheet>
Expected Output:
HEADER1111111122222222333333344456
778944 D4E2 EA 1234567891 2018-11-06000000100{EA
0000000000000100001020D04YA30TRE0000000XXXYYY 300{ P 2018-11-05
987654 D4E2 EA 1987654321 2018-11-06000000200AEA
0000000000000100001020D04YA30UUU0000000XXXLRB 100{ P 2018-11-05
.
.
.
.
It looks like you just want to replace the 55th character based on your map, so you could do this...
<xsl:template match="/">
<xsl:value-of select="ZR/TABLES/T_ER/item[1]/DATA"/>
<xsl:value-of select="$break" />
<xsl:for-each select="ZR/TABLES/T_ER/item[position() > 1]/DATA">
<xsl:variable name="char" select="substring('{ABCDEFGHI', number(substring(., 55, 1)) + 1, 1)" />
<xsl:value-of select="concat(substring(., 1, 54), $char, substring(., 56))" />
<xsl:value-of select="$break" />
</xsl:for-each>
</xsl:template>
This would work in XSLT 1.0.
An XSLT 2.0 solution could be this...
<xsl:template match="/">
<xsl:value-of select="ZR/TABLES/T_ER/item[1]/DATA,
ZR/TABLES/T_ER/item[position() > 1]/DATA/concat(substring(., 1, 54), substring('{ABCDEFGHI', number(substring(., 55, 1)) + 1, 1), substring(., 56))"
separator="
" />
</xsl:template>
In XSLT 3.0, you could potentially make use of a map with has the advantage of easily being extended if you wanted to consider two or more characters instead:
<xsl:stylesheet version="3.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:map="http://www.w3.org/2005/xpath-functions/map"
xmlns:fn="http://www.w3.org/2005/xpath-functions" >
<xsl:output omit-xml-declaration="yes"/>
<xsl:param name="break" select="'
'" />
<xsl:variable name="chars" as="map(xs:string, xs:string)">
<xsl:map>
<xsl:map-entry key="'0'" select="'{'"/>
<xsl:map-entry key="'1'" select="'A'"/>
<xsl:map-entry key="'2'" select="'B'"/>
<xsl:map-entry key="'3'" select="'C'"/>
<xsl:map-entry key="'4'" select="'D'"/>
<xsl:map-entry key="'5'" select="'E'"/>
<xsl:map-entry key="'6'" select="'F'"/>
<xsl:map-entry key="'7'" select="'G'"/>
<xsl:map-entry key="'8'" select="'H'"/>
<xsl:map-entry key="'9'" select="'I'"/>
</xsl:map>
</xsl:variable>
<xsl:template match="/">
<xsl:value-of select="ZR/TABLES/T_ER/item[1]/DATA,
ZR/TABLES/T_ER/item[position() > 1]/DATA/concat(substring(., 1, 54), $chars(substring(., 55, 1)), substring(., 56))" separator="
" />
</xsl:template>
</xsl:stylesheet>
There's probably a much nicer way in XSLT 3.0, so hopefully Martin Honnen will be along soon to say....
Well, using functions string-length, substring and translate for your specifications it can be achieved as:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fn="http://www.w3.org/2005/xpath-functions">
<xsl:output omit-xml-declaration="yes" />
<xsl:param name="break" select="'
'" />
<xsl:template match="/">
<xsl:value-of select="ZR/TABLES/T_ER/item[1]/DATA" />
<xsl:value-of select="$break" />
<xsl:for-each select="ZR/TABLES/T_ER/item[position() != 1]">
<xsl:variable name="length" select="string-length(substring(DATA,0,46))" />
<xsl:variable name="tenNumbers" select="substring(DATA, ($length + 1), 10)"/>
<xsl:variable name="charToReplace" select="translate(substring($tenNumbers, string-length($tenNumbers), 1),'0123456789','{ABCDEFGHI')" />
<xsl:value-of select="concat(substring(DATA,0,46), substring(DATA, ($length + 1), 9), $charToReplace, substring(DATA,($length+11),(string-length(DATA) + 1)))"/>
<xsl:value-of select="$break" />
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>

Finding the difference between 2 dates in 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