Displaying Data Node Items with Recurring Dates

Author:G .Eggold
Last Updated:August 25, 2015 11:21 AM

Sample XML Snippet

Here is an example of the XML that will be passed into your Data Detail or Listing XSL.  Note the Ocurrence nodes. There will be one of these for each future date projected out by the recurrence pattern. For example, if you create an "event" the recurrs every Tuesday for 6 month, there will be one Occurrence for eqach projected date.

<Event_Date>     
     <Occurrences>       
          <Occurrence>2015-05-23T15:00:00</Occurrence>       
          <Occurrence>2015-05-24T15:00:00</Occurrence>       
          <Occurrence>2015-05-25T15:00:00</Occurrence>     
     </Occurrences>   
</Event_Date>   
<Event_Date_Config>     
     <RecurrencePattern ID="39">       
          <Frequency>1</Frequency>       
          <Type>0</Type>       
          <Interval>1</Interval>       
          <PatternDOW>0</PatternDOW>       
          <PatternNth>0</PatternNth>       
          <Count>3</Count>       
          <Dates>         
               <Start>2015-05-23T15:00:00.000</Start>         
               <End />       
          </Dates>
          <DateTimeNow> 2015-05-06T11:07:52.213</DateTimeNow >     
     </RecurrencePattern>   
</Event_Date_Config>   
<End_Date>2015-05-26T17:00:00</End_Date>
 
Generating Sample XML
 
SELECT * FROM {schema}.Latest{DataNodeName}
 
Displaying Four Types of Event Date Strings
 
SAME DAY EVENTS:                        Monday June 29, 2015 12:00 PM - 2:30 PM
MULTI-DAY EVENTS:                       Saturday May 23, 2015 3:00 PM - Tuesday May 26, 2015 5:00 PM
ALL DAY EVENT:                          All Day
RECURRING EVENTS:                       Saturday May 23, 2015 3:00 PM
RECURRING EVENTS (concurrent Days):     (First Occurrence Date and Time) - (Last Occurrence Date and END_DATE TIME)
 
Same Day and Multi-Day events are the easier to account for because the do not involve recurrence.
 
With recurring events, there are two big "types" that you need to account for and display properly: recurring events on concurrent days and recurring events on non-concurrent days.  Also, you need to properly identify the next future occurrence of an event (and display it) in many cases.
 

     <xsl:choose>
       <xsl:when test="count(./Event_Date/Occurrences/Occurrence) > 1 and ./Event_Date_Config/RecurrencePattern/Frequency != '1'">
          <!-- Recurring event -->
          <!--DEBUG: <xsl:apply-templates select="./Event_Date_Config/RecurrencePattern" mode="results"/><br/> -->
          <!-- CUSTOM HANDLER : Show only the next future occurrence -->
          <xsl:apply-templates select="./Event_Date/Occurrences/Occurrence" mode="compare">
            <xsl:with-param name="DateTimeNow">
               <xsl:value-of select="./Event_Date_Config/RecurrencePattern/DateTimeNow" />
            </xsl:with-param>
          </xsl:apply-templates>
       </xsl:when>

       <xsl:when test="count(./Event_Date/Occurrences/Occurrence) > 1 and ./Event_Date_Config/RecurrencePattern/Frequency = '1'">
          <!-- Recurring event - consecutive days -->
          <!--DEBUG: <xsl:apply-templates select="./Event_Date_Config/RecurrencePattern" mode="results"/><br/> -->
          <!-- CUSTOM HANDLER : Show FIRST OCCURENCE - LAST OCCURENCE DATE + END_DATE TIME-->
          <xsl:apply-templates select="./Event_Date/Occurrences/Occurrence" mode="firstLast" />
          <xsl:if test="ms:format-time(./End_Date, $TimeMask) != '12:00 AM'" >
            <xsl:value-of select="ms:format-time(./End_Date, $TimeMask)" disable-output-escaping="yes"/>
          </xsl:if>
       </xsl:when>

       <xsl:otherwise>
          <xsl:value-of select="ms:format-date(./Event_Date_Config/RecurrencePattern/Dates/Start, $DateMask)" disable-output-escaping="yes"/>
          <xsl:text> </xsl:text>

          <xsl:choose>
            <xsl:when test="ms:format-time(./Event_Date_Config/RecurrencePattern/Dates/Start, $TimeMask) = '12:00 AM'">
               <xsl:text> - All Day</xsl:text>
            </xsl:when>
            <xsl:otherwise>
               <xsl:value-of select="ms:format-time(./Event_Date_Config/RecurrencePattern/Dates/Start, $TimeMask)" disable-output-escaping="yes"/>
               <xsl:text> - </xsl:text>
            </xsl:otherwise>
          </xsl:choose>

          <xsl:if test="ms:format-date(./Event_Date_Config/RecurrencePattern/Dates/Start, $DateMask) != ms:format-date(./End_Date, $DateMask)" >
            <xsl:value-of select="ms:format-date(./End_Date, $DateMask)" disable-output-escaping="yes"/>
            <xsl:text> </xsl:text>
          </xsl:if>
          <xsl:if test="ms:format-time(./End_Date, $TimeMask) != '12:00 AM'" >
            <xsl:value-of select="ms:format-time(./End_Date, $TimeMask)" disable-output-escaping="yes"/>
          </xsl:if>
       </xsl:otherwise>
     </xsl:choose>

  <xsl:template match="Occurrence" mode="compare">
    <xsl:param name="DateTimeNow" />
    <xsl:variable name="DateMask" select="'dddd MMMM d, yyyy'" />
    <xsl:variable name="TimeMask" select="'h:mm tt'" />

    <!-- This template handles recurring events and displays the NEXT event date in the sequence.
                    This is done by comparing datetime strings and looking for the Occurence in which the current node is in the future
                    and the preceeding node is in the past
          -->
    <xsl:choose>
      <xsl:when test="ms:string-compare(.,$DateTimeNow) = 1 and ms:string-compare(preceding-sibling::Occurrence[1],$DateTimeNow) = -1">
        <xsl:value-of select="ms:format-date(., $DateMask)" disable-output-escaping="yes"/>
        <xsl:text> </xsl:text>
        <xsl:choose>
          <xsl:when test="ms:format-time(., $TimeMask) = '12:00 AM'">
            <xsl:text> - All Day</xsl:text>
          </xsl:when>
          <xsl:otherwise>
            <xsl:value-of select="ms:format-time(., $TimeMask)" disable-output-escaping="yes"/>
          </xsl:otherwise>
        </xsl:choose>
        <br/>
      </xsl:when>
    </xsl:choose>

  </xsl:template>

< xsl:template match =" Occurrence" mode= "firstLast ">
     < xsl:variable name =" DateMask" select= "'dddd MMMM d, yyyy' " />
     < xsl:variable name =" TimeMask" select= "'h:mm tt' " />

     <!-- This template handles recurring events and displays the FIRST and LAST event date in the sequence.
                  -->
     < xsl:choose >
       < xsl:when test =" position() = 1 ">
          < xsl:value-of select =" ms:format-date(., $DateMask) " disable-output-escaping =" yes" />

          < xsl:if test =" ms:format-time(., $TimeMask) != '12:00 AM' " >
            < xsl:text > </xsl:text >
            < xsl:value-of select =" ms:format-time(., $TimeMask) " disable-output-escaping =" yes" />
          </ xsl:if >

          < xsl:text > - </xsl:text >
       </ xsl:when >
       < xsl:when test =" position() = last() ">
          < xsl:value-of select =" ms:format-date(., $DateMask) " disable-output-escaping =" yes" />
          < xsl:text > </xsl:text >
       </ xsl:when >
     </ xsl:choose >
</ xsl:template>

Built-in Date/Time Formatting

In order to properly display recurring events, we need to know the current DateTime.  This requires (as of this writing) a modification to a base function:
 
ALTER FUNCTION [dbo].[udf_ReadRecurrencePattern] (@patternID INT, @forCopy BIT)
RETURNS XML
AS
BEGIN
     IF (@patternID IS NULL) OR NOT EXISTS (SELECT 1 FROM RecurrencePattern WHERE PatternID = @patternID)
     BEGIN
          RETURN NULL;
     END;
    
    RETURN
       (SELECT CASE @forCopy WHEN 0 THEN PatternID ELSE -1 END AS '@ID',
                  Frequency,
                  Type,
                  Interval,
                  PatternDOW,
                  PatternNth,
                  Count,
                  Dates AS '*',
                  GETDATE() as DateTimeNow
          FROM RecurrencePattern
         WHERE PatternID = @patternID
           FOR XML PATH('RecurrencePattern'), TYPE);
END;
 
The XSL code uses the DateTimeNow field to determine which NEXT FUTURE EVENT to show.

 

top