Search code examples
xmlxsltmappingxslt-2.0xslt-3.0

Get list of Dates and map values based on Day XSLT2.0/XSLT3.0


I have this scenario wherein:

Need to list the dates between the Start Date and End Date and based on Type being 'Weekly' or 'Bi-Weekly' need to map the Day Nodes (Monday,Tuesday...) to the corresponding dates.

Week1 is the week of StartDate and Week2 is the second week of StartDate (the week after Week1)

If Type is Weekly: Need to map Day node values from Week1 node to corresponding dates of that week and keep repeating until EndDate

If Type is Bi-Weekly: Need to map node values from Week1 node and Week2 node for the first two weeks and keep repeating until End Date

StartDate can be any day of the week and not necessarily be on beginning of week (Monday).

Below is my XML:

<Data>
    <Root>
        <Type>Bi-Weekly</Type>
        <StartDate>2022-02-22</StartDate>
        <EndDate>2022-12-31</EndDate>
        <Week1>
            <Monday>4</Monday>
            <Tuesday>0</Tuesday>
            <Wednesday>4</Wednesday>
            <Thursday>0</Thursday>
            <Friday>4</Friday>
        </Week1>
        <Week2>
            <Monday>0</Monday>
            <Tuesday>4</Tuesday>
            <Wednesday>0</Wednesday>
            <Thursday>4</Thursday>
            <Friday>0</Friday>
        </Week2>
    </Root>
    <Root>
        <Type>Weekly</Type>
        <StartDate>2023-02-12</StartDate>
        <EndDate>2023-12-15</EndDate>
        <Week1>
            <Monday>4</Monday>
            <Tuesday>0</Tuesday>
            <Wednesday>4</Wednesday>
            <Thursday>0</Thursday>
            <Friday>4</Friday>
        </Week1>
        <Week2>
            <Monday>0</Monday>
            <Tuesday>4</Tuesday>
            <Wednesday>0</Wednesday>
            <Thursday>4</Thursday>
            <Friday>0</Friday>
        </Week2>
    </Root>
</Data>

My desired output for Bi-Weekly is:

22-Feb-22|Tuesday|0
23-Feb-22|Wednesday|4
24-Feb-22|Thursday|0
25-Feb-22|Friday|4
28-Feb-22|Monday|0
1-Mar-22|Tuesday|4
2-Mar-22|Wednesday|0
3-Mar-22|Thursday|4
4-Mar-22|Friday|0
.
.
.
20-Dec-22|Tuesday|4
21-Dec-22|Wednesday|0
22-Dec-22|Thursday|4
23-Dec-22|Friday|0
26-Dec-22|Monday|4
27-Dec-22|Tuesday|0
28-Dec-22|Wednesday|4
29-Dec-22|Thursday|0
30-Dec-22|Friday|4

My desired output for Weekly is:

22-Feb-22|Tuesday|0
23-Feb-22|Wednesday|4
24-Feb-22|Thursday|0
25-Feb-22|Friday|4
28-Feb-22|Monday|4
1-Mar-22|Tuesday|0
2-Mar-22|Wednesday|4
3-Mar-22|Thursday|0
4-Mar-22|Friday|4
7-Mar-22|Monday|4
8-Mar-22|Tuesday|0
.
.
.
19-Dec-22|Monday|4
20-Dec-22|Tuesday|0
21-Dec-22|Wednesday|4
22-Dec-22|Thursday|0
23-Dec-22|Friday|4
26-Dec-22|Monday|4
27-Dec-22|Tuesday|0
28-Dec-22|Wednesday|4
29-Dec-22|Thursday|0
30-Dec-22|Friday|4

Additionally . . Getting rid of Dates with value as '0' in the output will help.

The structure of input file can be modified if it will make the process easy such as

<Data>
    <Root>
        <Type>Bi-Weekly</Type>
        <StartDate>2022-02-21</StartDate>
        <EndDate>2022-12-31</EndDate>
        <Week>
            <W1Monday>4</W1Monday>
            <W1Tuesday>0</W1Tuesday>
            <W1Wednesday>4</W1Wednesday>
            <W1Thursday>0</W1Thursday>
            <W1Friday>4</W1Friday>
            <W2Monday>0</W2Monday>
            <W2Tuesday>4</W2Tuesday>
            <W2Wednesday>0</W2Wednesday>
            <W2Thursday>4</W2Thursday>
            <W2Friday>0</W2Friday>
        </Week>
    </Root>
</Data>

Thanks!

Current Code : https://xsltfiddle.liberty-development.net/nbspVb7/4


Solution

  • See if this can help:

    XSLT 2.0

    <xsl:stylesheet version="2.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="xs">
    <xsl:output method="text" encoding="UTF-8"/>
    
    <xsl:key name="week1" match="Week1/*" use="name()" />
    <xsl:key name="week2" match="Week2/*" use="name()" />
    
    <xsl:template match="Root">
        <xsl:variable name="root" select="." />
        <xsl:variable name="startDate" select="xs:date(StartDate)" />
        <xsl:variable name="n" select="(xs:date(EndDate) - $startDate) div xs:dayTimeDuration('P1D')" />
        <xsl:for-each select="0 to xs:integer($n)">
            <xsl:variable name="week2" select=". idiv 7 mod 2" />
            <xsl:variable name="date" select="$startDate + xs:dayTimeDuration('P1D') * ." />
            <xsl:variable name="day-of-week" select="format-date($date, '[F]')" />      
            <xsl:variable name="value" select="key(if ($week2) then 'week2' else 'week1', $day-of-week, $root)" />      
            <xsl:if test="$value > 0">
                <xsl:value-of select="format-date($date, '[D01]-[M01]-[Y0001]'), $day-of-week, $value" separator="|"/>
                <xsl:text>&#10;</xsl:text>
            </xsl:if>
        </xsl:for-each>
    </xsl:template>
    
    </xsl:stylesheet>
    

    This needs adjustment to accommodate <Type>Weekly</Type> - but that shouldn't be too difficult. Also, if I am not mistaken, it assumes that StartDate is always a Monday.


    Demo: https://xsltfiddle.liberty-development.net/nbspVb7/5