0

I understand some of the limitations of XSLT1.0 with the for-each loop. I need a way of processing a file 52 times so I can allocate an output to each of the 52 weeks of the year. The file is a simple Time Away From Work XML (each node has date / time / code etc...) and I need each week to contain the records of any TAFWs booked for that week, but I must always output 52 sections even if there is no data in the week. The data will be sorted by date, but I need a way of cycling it round 52 times. It must always be 52 weeks from last week, so it always need to be relative otherwise I would have created a temp xml within the XSLT.

For-each only works if there is more than 52 nodes, so I can't use that as there may not be more than 52 nodes.

RAW Data

    <Record>
    <emp_number>73001486</emp_number>
    <paycode>HOLIDAY</paycode>
    <allday_flag>True</allday_flag>
    <halfday_flag>False</halfday_flag>
    <nethours>96.000</nethours>
    <startdate_time>2021-12-14 00:00:00</startdate_time>
    <enddate_time>2021-12-16 00:00:00</enddate_time>
    </Record>

Output:

2021-12-06

2021-12-13

73001486,HOLIDAY,96

Any ideas gratefully received!

Update: Week start is previous week, which I can derive from passed information. Always full weeks. I don't care about holidays which span weeks, I just care about TAFW start dates within the 52 week periods (which I am comfortable with deducing).

  • You need to clarify some details. What exactly do you consider a "week"? What about partial weeks at the beginning and end of a year? How will the stylesheet know which week to start with? Your entries have start and end; how should an entry that spans a week boundary be handled? – michael.hor257k Dec 13 '21 at 17:43
  • Also which processor are we talking about? This might be benefit from some extension functions - if your processor supports them. – michael.hor257k Dec 13 '21 at 17:45
  • Hi, I'm not concerned with partial weeks or TAFWs that span weeks at the moment. It's always 52 weeks from the beginning of the previous week. As for processor, I'm not sure what the processor is, but I've had limited success with extension functions. – Itsallgonepearshaped Dec 14 '21 at 08:58
  • But what is "the previous week"? Previous relative to what? An XSLT stylesheet does not keep history. You must tell it where to start. And (again) what is "a week"? Does it start on Sunday or Monday or ...? Also, you say you are "not concerned with TAFWs that span weeks" - but that doesn't answer the question of how to handle them. -- See here how to identify your processor: https://stackoverflow.com/a/25245033/3016153 – michael.hor257k Dec 14 '21 at 10:17
  • Thanks for the response Michael. It's a Friday the week starts on. I can derive the start date based on information I'm being passed in the xml file, that bit I'm completely comfortable with, it's the loop to get 52 weeks I'm struggling with. The processor is Microsoft, but it's through a cloud application I have no control over. I use c# quite a bit for the more complex stuff, but the xslt is validated using a 3rd party toolset that a) I have no control over and b) seems to be a bit random in it's approach to what is legal code. – Itsallgonepearshaped Dec 14 '21 at 10:27
  • Generating 52 weeks can be accomplished using a recursive named template. But I am afraid I still do not understand how you "derive the start date based on information I'm being passed in the xml file". Please edit your question and show how you do that. – michael.hor257k Dec 14 '21 at 10:35

1 Answers1

1

I think your question should be broken into several separate steps - and neither of these is exactly trivial.

The first step would be to generate 52 weeks from a given start date. Well, actually the first step would be to calculate the start date, but at this point I don't know what to use as the input for this, so let's assume it's a given:

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="/">
    <output>
        <xsl:call-template name="generate-weeks">
            <xsl:with-param name="start-date" select="'2020-11-30'" />
            <xsl:with-param name="n" select="52" />
        </xsl:call-template>
    </output>
</xsl:template>

<xsl:template name="generate-weeks">
    <xsl:param name="start-date"/>
    <xsl:param name="n" select="52"/>
    <xsl:if test="$n > 0">
        <week start-date="{$start-date}"/>
        <xsl:call-template name="generate-weeks">
            <xsl:with-param name="start-date">
                <xsl:variable name="JDN">
                    <xsl:call-template name="JDN">
                        <xsl:with-param name="date" select="$start-date" />
                    </xsl:call-template>
                </xsl:variable>
                <xsl:call-template name="GD">
                    <xsl:with-param name="JDN" select="$JDN + 7" />
                </xsl:call-template>
            </xsl:with-param>
            <xsl:with-param name="n" select="$n - 1" />
        </xsl:call-template>
    </xsl:if>
</xsl:template>

<xsl:template name="JDN">
    <xsl:param name="date"/>
    <xsl:variable name="year" select="substring($date, 1, 4)"/>
    <xsl:variable name="month" select="substring($date, 6, 2)"/>
    <xsl:variable name="day" select="substring($date, 9, 2)"/>
    <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) + 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:variable name="f" select="$JDN + 1401 + floor((floor((4 * $JDN + 274277) div 146097) * 3) div 4) - 38"/>
    <xsl:variable name="e" select="4*$f + 3"/>
    <xsl:variable name="g" select="floor(($e mod 1461) div 4)"/>
    <xsl:variable name="h" select="5*$g + 2"/>
    <xsl:variable name="D" select="floor(($h mod 153) div 5 ) + 1"/>
    <xsl:variable name="M" select="(floor($h div 153) + 2) mod 12 + 1"/>
    <xsl:variable name="Y" select="floor($e div 1461) - 4716 + floor((14 - $M) div 12)"/>
    <xsl:value-of select="$Y" />    
    <xsl:value-of select="format-number($M, '-00')"/>
    <xsl:value-of select="format-number($D, '-00')"/>   
</xsl:template>

</xsl:stylesheet>

This will produce:

Result

<?xml version="1.0" encoding="utf-8"?>
<output>
  <week start-date="2020-11-30"/>
  <week start-date="2020-12-07"/>
  <week start-date="2020-12-14"/>
  <week start-date="2020-12-21"/>
  <week start-date="2020-12-28"/>
  <week start-date="2021-01-04"/>
  <week start-date="2021-01-11"/>
  <week start-date="2021-01-18"/>
  <week start-date="2021-01-25"/>
  <week start-date="2021-02-01"/>
  <week start-date="2021-02-08"/>
  <week start-date="2021-02-15"/>
  <week start-date="2021-02-22"/>
  <week start-date="2021-03-01"/>
  <week start-date="2021-03-08"/>
  <week start-date="2021-03-15"/>
  <week start-date="2021-03-22"/>
  <week start-date="2021-03-29"/>
  <week start-date="2021-04-05"/>
  <week start-date="2021-04-12"/>
  <week start-date="2021-04-19"/>
  <week start-date="2021-04-26"/>
  <week start-date="2021-05-03"/>
  <week start-date="2021-05-10"/>
  <week start-date="2021-05-17"/>
  <week start-date="2021-05-24"/>
  <week start-date="2021-05-31"/>
  <week start-date="2021-06-07"/>
  <week start-date="2021-06-14"/>
  <week start-date="2021-06-21"/>
  <week start-date="2021-06-28"/>
  <week start-date="2021-07-05"/>
  <week start-date="2021-07-12"/>
  <week start-date="2021-07-19"/>
  <week start-date="2021-07-26"/>
  <week start-date="2021-08-02"/>
  <week start-date="2021-08-09"/>
  <week start-date="2021-08-16"/>
  <week start-date="2021-08-23"/>
  <week start-date="2021-08-30"/>
  <week start-date="2021-09-06"/>
  <week start-date="2021-09-13"/>
  <week start-date="2021-09-20"/>
  <week start-date="2021-09-27"/>
  <week start-date="2021-10-04"/>
  <week start-date="2021-10-11"/>
  <week start-date="2021-10-18"/>
  <week start-date="2021-10-25"/>
  <week start-date="2021-11-01"/>
  <week start-date="2021-11-08"/>
  <week start-date="2021-11-15"/>
  <week start-date="2021-11-22"/>
</output>

The next step would be pre-process the input records and assign each one the value of the starting day of its week. This can be then used to assign each record to the corresponding week by matching the two start-dates. I suggest you ask a separate question (or questions) about that.

michael.hor257k
  • 113,275
  • 6
  • 33
  • 51