This is a migrated thread and some comments may be shown as answers.

Regular Expression to parse RecurrenceRule

7 Answers 934 Views
Scheduler
This is a migrated thread and some comments may be shown as answers.
Brian Mackey
Top achievements
Rank 1
Brian Mackey asked on 26 Aug 2009, 06:42 PM
In short, I need to write a regular expression to parse the data from the RuccurenceRule.  I've started it with 1 row of sample data:

DTSTART:(?<StartDate>\d{8}).* DTEND:(?<EndDate>\d{8}).* FREQ=(?<Frequency>[A-Z]{0,10});INTERVAL=(?<Interval>\d{1,3});BYDAY=(?<ByDay>[A-Z]{1,3})

Because this is actually handled inside the scheduler control itself, I assume Telerik already has this pattern nice, pretty, and tested.  If so, would you mind sharing it?


We have the requirement to color code appointments (recurring and not) according to whether a particular file has been received that has been associated with that appointment.  This is extending beyond the functionality a bit, so we need to pretty much duplicate the recurrence handling in order to associate an appointment with a file received via datetime.

7 Answers, 1 is accepted

Sort by
0
Accepted
Peter
Telerik team
answered on 27 Aug 2009, 03:00 PM
Hi Brian,

Yes, you can parse the recurrence rule using RadScheduler. The following resources show how to do this:

http://www.telerik.com/help/aspnet-ajax/schedule_serversideworkingwithrecurringappointments.html

http://www.telerik.com/support/kb/aspnet-ajax/scheduler/how-to-display-all-radscheduler-appointments-in-gridview.aspx

Let us know if you have further questions.


Greetings,
Peter
the Telerik team

Instantly find answers to your questions on the new Telerik Support Portal.
Watch a video on how to optimize your support resource searches and check out more tips on the blogs.
0
Kevin Price
Top achievements
Rank 1
answered on 12 Apr 2010, 03:26 PM
Peter -
I have gained a similar requirement, and am showing a single appt on a page. Without having to load a scheduler, is there an acceptable way to parse the recurrencerule to show as simple text? Ex. Occurs every Monday until ....

Thanks,
Kevin
0
T. Tsonev
Telerik team
answered on 13 Apr 2010, 04:57 PM
Hello Kevin,

We don't have such function ready, but you can use RecurrenceRule.TryParse to obtain the recurrence object and then format the user-friendly string based on the object type and properties.

I hope this helps.

Best wishes,
Tsvetomir Tsonev
the Telerik team

Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items.
0
Kevin Price
Top achievements
Rank 1
answered on 15 Sep 2010, 03:24 AM
Peter,
To avoid duplicating efforts  (again :) ) I'll keep my posts on this thread. Please check the below code (RecRuleParser.cs).
It does a pretty good job of converting the RecurrenceRule values from the database to a plain-text English statement.
Ex:
DTSTART:20100526T040000Z  DTEND:20100527T040000Z  RRULE:FREQ=WEEKLY;INTERVAL=1;BYDAY=WE  EXDATE:20100526T040000Z,20100812T040000Z 

Returns:
Occurs weekly every Wednesday starting on 5/26/2010 at 12:00 AM except on 5/26/2010 12:00:00 AM and 8/12/2010 12:00:00 AM (as it converts to local time) I included code as well to try and spell out the exceptions - source is quite easily modified and full of comments (and commentary)

DTSTART:20100622T160000Z  DTEND:20100622T170000Z  RRULE:FREQ=WEEKLY;COUNT=3;INTERVAL=1;BYDAY=TU  EXDATE:20100622T160000Z,20100706T160000Z 
Returns:
Occurs weekly every Tuesday starting on 6/22/2010 at 12:00 PM for the next 3 weeks ending on 7/13/2010 at 1:00 PM except on 6/22/2010 12:00:00 PM and 7/6/2010 12:00:00 PM

usage: myOutputString = RecRuleParser.ParseRule(string <RecurrenceRuleText>, bool <showExceptions>);

With a little work, and a resource file, probably wouldn't be too hard to support additional language. Hope this helps. It may not

Thanks,
Kevin

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;
//You might want to change this
namespace RRuleParser
{
    public static class RecRuleParser
    {
        public static string ParseRRule(string rRule, bool showExceptions)
        {
            string parsed = string.Empty;//set up the return string
            StringBuilder englishStatement = new StringBuilder();
            //Break it into basic parts
            string[] elements = rRule.Split(' ');
            //it's double spaced in the db, so deal with it accordingly
            string startDate = elements[0];
            string endDate = elements[2];
            string recRule = elements[4];
            string recExcs = string.Empty;
            //check for exceptions
            if (elements.Length > 5)
            {
                recExcs = elements[6];
            }
            //Attempt to parse the zulu dates into something else
            DateTime dtStart = DateTime.ParseExact(getElemValue(startDate), "yyyyMMddTHHmmssZ", System.Globalization.CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal);
            DateTime dtEnd = DateTime.ParseExact(getElemValue(endDate), "yyyyMMddTHHmmssZ", System.Globalization.CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal);
            TimeSpan tsEnd = dtEnd.Subtract(dtStart);
            //Now work with the recurrence rule
            Dictionary<string, string> rruleElems = new Dictionary<string, string>();
            //Convert the string to a dictionary so we can find things easy
            parsed = getElemValue(recRule); //no need having unnecessarily declared strings
            elements = parsed.Split(';');
            for (int i = 0; i < elements.Length; i++)
            {
                string[] tmp = elements[i].Split('=');
                rruleElems.Add(tmp[0], tmp[1]);
            }
            englishStatement.Append("Occurs " + rruleElems["FREQ"].ToLower());
            string calType = string.Empty; //need a scratchpad
            //start translating into English
            int timeToAdd = 0;
            try
            {
                timeToAdd = Convert.ToInt32(rruleElems["COUNT"]);
            }
            catch
            {
                timeToAdd = 0;
            }
            switch (rruleElems["FREQ"].ToLower())
            {
                case "daily":
                    string[] days = rruleElems["BYDAY"].Split(',');
                    englishStatement.Append(parseDayNames(days));
                    dtEnd = dtEnd.AddDays(timeToAdd);
                    calType = "days";
                    break;
                case "weekly":
                    calType = "weeks";
                    dtEnd = dtEnd.AddDays(timeToAdd * 7);
                    try
                    {
                        days = rruleElems["BYDAY"].Split(',');
                        englishStatement.Append(parseDayNames(days));
                    }
                    catch
                    {
                        //just in case we missed something on this one
                        throw new Exception("Error while processing Recurrence Rule");
                    }
                    break;
                case "monthly":
                    calType = "months";
                    dtEnd = dtEnd.AddMonths(timeToAdd);
                    //see if it's positional
                    try
                    {
                        string bsp = getDayEnding(rruleElems["BYSETPOS"]);
                        englishStatement.Append(" on the " + bsp + " " + parseDayNames(rruleElems["BYDAY"].Split(',')).Replace(" every ", ""));
                    }
                    catch
                    {
                        //Ok, no BYSETPOS, let's go for BYMONTHDAY
                        string bsp = getDayEnding(rruleElems["BYMONTHDAY"]);
                        englishStatement.Append(" on the " + bsp + " day of each month");
                    }
                    break;
                case "yearly":
                    calType = "years";
                    dtEnd = dtEnd.AddYears(timeToAdd);
                    //looks a lot like monthly....
                    string mName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(Convert.ToInt32(rruleElems["BYMONTH"]));
                    //see if it's positional
                    try
                    {
                        string bsp = getDayEnding(rruleElems["BYSETPOS"]);
                        englishStatement.Append(" on the " + bsp + " " + parseDayNames(rruleElems["BYDAY"].Split(',')).Replace(" every ", "") + " of " + mName);
                    }
                    catch
                    {
                        //Ok, no BYSETPOS, let's go for BYMONTHDAY
                        string bsp = getDayEnding(rruleElems["BYMONTHDAY"]);
                        englishStatement.Append(" on the " + bsp + " day of " + mName);
                    }
                    break;
                case "hourly":
                    calType = "hours";
                    dtEnd = dtEnd.AddHours(timeToAdd);
                    break;
                default:
                    break;
 
            }
            englishStatement.Append(" starting on " + dtStart.ToLocalTime().ToShortDateString() + " at " + dtStart.ToLocalTime().ToShortTimeString());
 
            if (timeToAdd > 0)
            {
                englishStatement.Append(" for the next " + rruleElems["COUNT"] + " " + calType);
                englishStatement.Append(" ending on " + dtEnd.ToLocalTime().ToShortDateString() + " at " + dtStart.AddHours(tsEnd.Hours).ToLocalTime().ToShortTimeString());
            }
            if (recExcs.Length > 0 && showExceptions)
            {
                string[] excs = recExcs.Split(':')[1].Split(','); string retString = string.Empty;
                englishStatement.Append(" except on ");
                for (int r = 0; r < excs.Length; r++)
                {
                    //we'll use dtEnd, it's not doing anything now
                    dtEnd = DateTime.ParseExact(excs[r], "yyyyMMddTHHmmssZ", System.Globalization.CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal).ToLocalTime();
                    if (r < excs.Length && excs.Length > 2)
                    {
                        retString += dtEnd + ",";
                    }
                    else
                    {
                        if (r < excs.Length - 1 && excs.Length == 2)
                        {
                            retString += dtEnd + " and ";
                        }
                        else
                        {
                            retString += dtEnd;
                        }
                    }
                }
                englishStatement.Append(retString);
            }
            return englishStatement.ToString();
        }
        private static string getElemValue(string elem)
        {
            //just easier than writing split all over the place
            string[] elems = elem.Split(':');
            return elems[1].Trim();
        }
        private static string getDayName(string day)
        {
            //pretty self explanatory
            switch (day)
            {
                case "MO":
                    return "Monday";
                case "TU":
                    return "Tuesday";
                case "WE":
                    return "Wednesday";
                case "TH":
                    return "Thursday";
                case "FR":
                    return "Friday";
                case "SA":
                    return "Saturday";
                case "SU":
                    return "Sunday";
                default:
                    return "";
            }
        }
        private static string parseDayNames(string[] days)
        {
            string retString = string.Empty;
            {
                if (days.Length < 7)
                {
                    retString += " every";
                    for (int d = 0; d < days.Length; d++)
                    {
                        days[d] = getDayName(days[d]);
 
                        if (d == days.Length - 1 && days.Length > 1)
                        {
                            days[d] = " and " + days[d];
                        }
                        else
                        {
                            if (days.Length > 2)
                            {
                                days[d] += ",";
                            }
                        }
                        retString += " " + days[d];
                    }
                }
                return retString;
            }
        }
        private static string getDayEnding(string d)
        {
            //tried to avoid a big ugly if statement
            //handle the events on the "n"th day of the month
            if (d.EndsWith("1") && d != "11")
            {
                d += "st";
            }
            if (d.EndsWith("2") && d != "12")
            {
                d += "nd";
            }
            if (d.EndsWith("3") && d != "13")
            {
                d += "rd";
            }
            if (d.Length < 3)//hasn't been appended yet
            {
                d += "th";
            }
            return d;
        }
    }
}
0
T. Tsonev
Telerik team
answered on 15 Sep 2010, 05:24 PM
Hi Kevin,

Thank you for posting the code here. I'm sure someone will find it helpful.

As a token of gratitude for your involvement your Telerik points have been updated.

Best wishes,
Tsvetomir Tsonev
the Telerik team
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items
0
Gustavo
Top achievements
Rank 1
answered on 05 Feb 2013, 07:37 PM
Hello,
I'm new using asp.net and i want to know how I can implement this code into the my web apps.  right now, I'm using RadScheduler for one of my applications and I have a small problem is that each follows: My application is a work order system which uses a calendar to place recurring orders, these orders are shipped one day before processing in order that they can be addressed. however I am trying to do that every time you mount a recurring type this command is saved in the database independently ie. If an order is placed to run for example every Friday for a month, I need to know how I can send a work order for each Friday of the month that was scheduled.


Thanks you

Gustavo
0
Boyan Dimitrov
Telerik team
answered on 08 Feb 2013, 12:34 PM
Hello Gustavo,

I would recommend using the the RadScheduler AppointmentInsert server-side event to parse the current appointment recurrence rule. This way you could extract the information that would be helpful in order to calculate all appointment occurrences in the future and execute your custom logic to send those orders.
Here you may find more information about AppointmentInsert server-side event.

Regards,
Boyan Dimitrov
the Telerik team
If you want to get updates on new releases, tips and tricks and sneak peeks at our product labs directly from the developers working on the RadControls for ASP.NET AJAX, subscribe to their blog feed now.
Tags
Scheduler
Asked by
Brian Mackey
Top achievements
Rank 1
Answers by
Peter
Telerik team
Kevin Price
Top achievements
Rank 1
T. Tsonev
Telerik team
Gustavo
Top achievements
Rank 1
Boyan Dimitrov
Telerik team
Share this question
or