add xslt filtering between two dates - xslt

I am trying to filter the list using xslt so that it only shows the item within two dates. But somehow the result is incorrect where only one item displayed. the statement I used is:
<xsl:variable name="Rows"
select="/dsQueryResponse/Rows/Row[(number(concat(substring(ddwrt:FormatDateTime(string(ddwrt:Today()), 1033, 'yyyy'),0,5),substring(ddwrt:FormatDateTime(string(#DateOfBirth), 1033, 'MM'),0,3),substring(ddwrt:FormatDateTime(string(#DateOfBirth),1033,'dd'),1,3))))
>= (number(translate(ddwrt:FormatDateTime(string(ddwrt:Today()),1033,'yyyyMMdd'),' ','')))
and(number(concat(substring(ddwrt:FormatDateTime(string(ddwrt:Today()), 1033, 'yyyy'),0,5),substring(ddwrt:FormatDateTime(string(#DateOfBirth), 1033, 'MM'),0,3),substring(ddwrt:FormatDateTime(string(#DateOfBirth),1033,'dd'),1,3))))
<= (number(translate(ddwrt:FormatDateTime(string(ddwrt:Today()),1033,'yyyyMMdd'),'',''))+7)]"/>
What I am trying to do is to display the employee who is celebrating their birthday this week by concatenate the day and month of their birthdate with today's year and convert this to number. then check if this new date is >= today's date (also convert to number) and <= today's date + 7 days.
I am new in xslt and been trying to solve this problem more than a week now. I am really appreciate if anyone can help me.

I believe you can use an XSLT extension to solve your problem more elegantly.
Some extension functions are provided by the ddwrt namespace, but none of them are very useful for date arithmetic.
Since I have no SharePoint to test any code with, I'm showing a script extension based on VBScript. Here is an IsAnniversary() function along with some supporting code.
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ddwrt="http://schemas.microsoft.com/WebParts/v2/DataView/runtime"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:udf="http://tempuri.org/udf"
exclude-result-prefixes="ddwrt msxsl udf"
>
<xsl:template match="/">
<xsl:for-each select="
/dsQueryResponse/Rows/Row[udf:IsAnniversary(#DateOfBirth, 7)]
">
<xsl:value-of select="#DateOfBirth" />
</xsl:for-each>
</xsl:template>
<msxsl:script language="VBScript" implements-prefix="udf">
<![CDATA[
Option Explicit
Function FirstValue(Input)
If IsObject(Input) Then ' it's an IXMLDOMNodeList
If Input.length > 0 Then FirstValue = Input.item(0).text Else FirstValue = Empty
Else
FirstValue = Input
End If
End Function
Function ParseDate(Input)
Input = FirstValue(Input)
Input = Split(Input & "T", "T")(0) ' handle ISO 8601 datetimes
If IsDate(Input) Then ParseDate = CDate(Input) Else ParseDate = Empty
End Function
Function ParseInt(Input)
Input = FirstValue(Input)
If IsNumeric(Input) Then ParseInt = CInt(Input) Else ParseInt = Empty
End Function
Function IsAnniversary(Input, WithinDays)
Dim TheDate, Diff
IsAnniversary = False
TheDate = ParseDate(Input)
WithinDays = ParseInt(WithinDays)
If Not (IsEmpty(TheDate) Or IsEmpty(WithinDays)) Then
TheDate = DateAdd("yyyy", Year(Now()) - Year(TheDate), TheDate)
Diff = DateDiff("d", Now(), TheDate)
If WithinDays >= 0 Then
IsAnniversary = Diff >= 0 And Diff <= WithinDays
Else
IsAnniversary = Diff <= 0 And Diff >= WithinDays
End If
End If
End Function
]]>
</msxsl:script>
</xsl:stylesheet>
The above quite well-tested and robust and it probably already does what you need(*), still you could re-implement the whole thing as a proper, XSLT-aware SharePoint extension based on the .NET Framework.
Here's an article on how to do that with respect to SharePoint: http://blog.mastykarz.nl/extending-content-query-web-part-xslt-custom-functions/ - it's from 2009 so it might not be a 100% fit with current versions of SharePoint, but it might start you off.
(*) I recommend you keep all your dates in ISO 8601 format (YYYY-MM-DD) so that CDate() recognizes them correctly.

Related

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

Filtering set of rows with XSLT

i have troubles filtering with XSLT, basically i have an xml, where i would like to get only those items where the Due Date falls into interval of 3 months ... i've written the following:
<xsl:variable name="TaskRows" select="/dsQueryResponse/Tasks/Rows/Row[#DueDate > $IsoBeginQuartDate AND #DueDate < $IsoEndQuartDate]"/>
But I get an error: "Failed seting processor stylesheet: expected token ']' found 'NAME' ...
IsoDates are calculated and formated dates in ISO format ...
Any idea, how to do it, or i can't use "AND" when filtering?
PS: i'm using XSLT 1.0
There is case-sensitivity at work here! Try using 'and' instead of 'AND'.
Unfortunately in XSLT 1.0, you can't compare dates to see if a date is between two values.
What you could do is something like this:
<!-- Make sure to format this variables as YYYYMMDD -->
<xsl:variable name="$IsoBeginQuartDate">20130101</xsl:variable>
<xsl:variable name="$IsoBeginQuartDate">20131231</xsl:variable>
<!-- Make sure #DueDate also has the format YYYYMMDD, in this example I assume the DueDate has format YYYY-MM-DD -->
<xsl:variable name="TaskRows" select="/dsQueryResponse/Tasks/Rows/Row[translate(#DueDate, '-', '') > $IsoBeginQuartDate and translate(#DueDate, '-', '') < $IsoEndQuartDate]"/>
You now get errors, because > and < or not supported for strings or dates.

Counting XML based on date conditionals with xslt

I'm working with xslt for the first time. I have 2.0, but that's about the only advantage I have access to with the c# transform library we have. I'm trying to count a number of child nodes in the XML document which contain a date more than 12 years ago and have a certain type attribute.
Sample xml structure:
<xml version=\"1.0\" encoding=\"utf-8\"?>
<... />
<Dependents>
<Dependent><DOB>1964-04-01</DOB><DependentType>Spouse</DependentType></Dependent>
<Dependent><DOB>2000-01-01</DOB><DependentType>Child</DependentType></Dependent>
<Dependent><DOB>2012-01-01</DOB><DependentType>Child</DependentType></Dependent>
</Dependents>
<... />
where <... /> signifies some extra unrelated stuff.
So essentially, I want the number of children younger than 12 years old. (I have the count of dependenttype = child of all ages working, it's just the under 12 that's giving me trouble). The approach that was suggested to me was to construct a variable that stands for 12 years ago from today and use that as a basis for comparison in the count() function. That sounds reasonable, but I'm stuck constructing the date without the use of 3rd party libraries (e.g. exslt) that are so often linked in questions like this for the easy, handy-dandy answers.
The xslt I've gotten so far for this is:
<xsl:variable name="today" select="current-dateTime()" as="xs:dateTime" />
<xsl:variable name="twelveyearsago" select="xs:dateTime(concat(year-from-dateTime($today) - 12, '-', month-from-dateTime($today), '-', day-from-dateTime($today)))" />
<xsl:text>12yearsago=</xsl:text><xsl:value-of select="$twelveyearsago" />
And it's not working because the month-from-dateTime (and presumable day-from-dateTime) don't add leading zeros. For today, March 21 2012 I'm getting back: Saxon.Api.DynamicError : Invalid dateTime value "2000-3-21" (Month must be two digits) (The W3Schools xpath function reference implies that they should, but they don't.)
What I would like to output is:
<xsl:text>&numberofchildren=</xsl:text><xsl:value-of select="count(//InsuranceRequest/HealthInsurance/Dependents/Dependent/DependentType[text() = 'Child'])" />
<xsl:text>&childrenunder12=</xsl:text><xsl:value-of select="children under twelve" />
The more I pound my head against this, the more I feel like there's a simpler approach that I'm just not seeing.
Edit: I cleaned up the xslt syntax so it's valid and not a doubly-quoted c# string.
You can simply substract a duration of 12 years as in <xsl:variable name="twelveyearsago" select="$today - xs:yearMonthDuration('P12Y')"/>, then use e.g. //Dependent[DependentType = 'Child' and xs:date(DOB) >= $twelveyearsago].
[edit]
Here is a template that compiles and executes fine for with Saxon 9.4:
<xsl:template match="/">
<xsl:variable name="today" select="current-date()"/>
<xsl:variable name="twelve-years-ago" select="$today - xs:yearMonthDuration('P12Y')"/>
<xsl:value-of select="count(//Dependent[DependentType = 'Child' and xs:date(DOB) >= $twelve-years-ago])"/>
</xsl:template>

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.

xslt umbraco.library:FormatDateTime and the use of / (slash)

This may not be a real XSLT question but more of an Umbraco question.
This function as written will output another format as expexted:
<xsl:value-of select="umbraco.library:FormatDateTime(data [#alias = 'kursusgangTilmeldingsfrist'], 'dd/MM/yyyy')"/>
When called with these three "second arguments" on the current date
'dd MM yyyy'
'dd-MM-yyyy'
'dd/MM/yyyy'
Then output will be:
10 02 2009
10-02-2009
10-02-2009 (and this should have been with "/" and not a "-")
Br. Anders
Solution is to escape using a backslash so function is called like this:
<xsl:value-of select="umbraco.library:FormatDateTime(data [#alias = 'kursusgangTilmeldingsfrist'], 'dd\/MM\/yyyy')"/>
And this will output correct date as:
10/02/2009