Click here to Skip to main content
15,884,007 members
Please Sign up or sign in to vote.
4.50/5 (2 votes)
Hi, I am working on a calendar project using .NET framework and is running into a horrible performance issue when working with dates. The issue is that whenever I want to retrieve all the events and to-dos occurring on a particular date, I have to transverse the entire list of events and to-dos checking which events and/or to-dos occur on that date. The problem gets worse when I work with project tasks as well. Thus, it is 3 O(n).

Typically, in this kind of situation, I would use a sorted binary tree data structure as storage rather than a list, but I don't know how to sort them when recurrence occurs. Thus, I need some recommendation on how I can improve the performance for retrieving all items occurring on a particular date. Also, this data structure must be able to handle situation when events' and/or to-dos' date is changed.

It is kind of hard to show just only relevant details about the project without overwhelming this question with details, but I will give it a try. I am integrating currently four different components together:
- MonthCalendar class library
- DDay.iCal class library for using iCal file
- Microsoft Office Project 12.0 Object Library
- Plug-In Framework

Microsoft Office Project 12.0 Object Library will be applied via one of the plug-ins. What is events, to-dos, and recurrence is explained in details on this standard. A simple and short version summary can be found on Wikipedia.

Using the event handler, every time I select a date on MonthCalendar, execute the following:
private void calendar_DaySelected(object sender, DaySelectedEventArgs e)
        {
            DateTime date = DateTime.Parse(e.Days[0]);

            schedulePanel.EventList.Clear();
            schedulePanel.EventList.AddRange(calendarPanel.Calendar.GetDateInfo(date));

            schedulePanel.TodoList.Clear();
            schedulePanel.TodoList.AddRange(todoPanel.GetDateInfo(date));

            if (projectPlugins.Count > 0)
            {
                schedulePanel.Tasks.Clear();
                for (int i = 0; i < projectPlugins.Count; i++)
                {
                    schedulePanel.Tasks.AddRange(projectPlugins[i].GetDateInfo(date));
                }
            }

            schedulePanel.Update(date);
        }


Here is the partial class code for SchedulePanel:
C#
public SchedulePanel()
        {
            InitializeComponent();
            eventList = new List<Library.DateItem>();
            taskList = new List<PluginSDK.ITask>();
            todoList = new List<ITodo>();
        }
        public void Update(DateTime date)
        {
            Text = "Schedule for " + date.ToShortDateString();
            eventGridView.Rows.Clear();
            //Display Occurring Events
            for (int i = 0; i < eventList.Count; i++)
            {
                eventGridView.Rows.Add();
                eventGridView.Rows[i].Cells[0].Value = eventList[i].Event.Summary;
            }
            //Display Occurring Project Tasks
            if (taskList.Count != 0)
            {
                if (!this.Controls.Contains(this.taskBox))
                {
                    this.Controls.Add(this.taskBox);
                    this.todoBox.Location = new System.Drawing.Point(3, 232);
                    this.todoBox.Size = new System.Drawing.Size(193, 116);
                }
                taskListBox.Items.Clear();
                for (int j = 0; j < taskList.Count; j++)
                {
                    taskListBox.Items.Add(taskList[j].Name);
                }
            }
            else
            {
                if (this.Controls.Contains(this.taskBox))
                {
                    this.Controls.Remove(this.taskBox);
                    this.todoBox.Location = new System.Drawing.Point(3, 103);
                    this.todoBox.Size = new System.Drawing.Size(193, 236);
                }
            }
            //Display Occurring To-dos
            todoListBox.Items.Clear();
            for (int i = 0; i < todoList.Count; i++)
            {
                todoListBox.Items.Add(todoList[i].Summary);
            }
        }
        public List<Library.DateItem> EventList
        {
            get
            {
                return eventList;
            }
        }
        public List<ITodo> TodoList
        {
            get
            {
                return todoList;
            }
        }
        public List<PluginSDK.ITask> Tasks
        {
            get
            {
                return taskList;
            }
        }


Here is the partial class code for TodoPanel:
C#
public List<ITodo> GetDateInfo(DateTime date)
        {
            List<ITodo> todos = new List<ITodo>();
            for (int i = 0; i < todoList.Count; i++)
            {
                if (todoList[i].Status != TodoStatus.Completed)
                {
                    if (todoList[i].Start != null && todoList[i].Start.Date <= date)
                        todos.Add(todoList[i]);
                    else if (todoList[i].Due != null && todoList[i].Due.Date <= date)
                        todos.Add(todoList[i]);
                }
            }
            return todos;
        }


Here is the partial class code for MSProjectPanel using Microsoft Office Project 12.0 Object Library:
C#
public List<ITask> GetDateInfo(DateTime date)
        {
            List<ITask> tasks = new List<ITask>();
            foreach (Project project in application.Projects)
            {
                foreach (Task task in project.Tasks)
                {
                    if (task.OutlineChildren.Count == 0 && task.Status != PjStatusType.pjComplete)
                    {
                        if (CompletePrerequisite(task) && DateTime.Parse(task.Start.ToString()) <= date)
                        {
                            tasks.Add(new ProjectTask(task));
                        }
                    }
                }
            }
            return tasks;
        }


It takes about 3+ seconds to finish executing after selecting a new date on 2.16GHz computer running Microsoft XP Professional with 512 MB of RAM. Currently, I am using 3 iCal files and 2 MPP files. That should be about 150+ items (events, to-dos, and 100+ project tasks) combined. Without MSProject plugin, it drops down to 1+ second.
Posted
Updated 26-May-11 15:07pm
v3
Comments
Sergey Alexandrovich Kryukov 26-May-11 1:25am    
Sorry, very hard to understand. What's "to-dos", where is the recursion? How did you manage to access anything as slow as O(n)? Some code samples perhaps?
--SA
Kschuler 26-May-11 10:03am    
Could you please provide more details about your project and how you are doing things. Are you sure it's the dates that's causing the performance issue and not your overall design? And like SAKryukov said, please post your relevant code.
YvesDaoust 27-Jul-11 4:49am    
From the code, it is hard to believe that you select only the tasks occurring on the given date. It looks like you consider tasks before-or-on the given date, which can be a much larger amount.

Regardless of running time, do you get the expected list of tasks ?
Que Trac 30-Jul-11 17:33pm    
Yes, you are correct. All currently due tasks including uncompleted and late tasks are shown correctly. But the performance is very slow unfortunately...

1 solution

To handle recurring events that occur on a given day of the week, just keep the day of the week value (day index modulo 7) instead of the full date.

To handle recurring events that occur on a given day of the month, just keep the day of the month value instead of the full date.

And so on.
 
Share this answer
 
v2
Comments
Que Trac 30-Jul-11 18:23pm    
I am not exactly sure if I understand you correctly. I think that you are suggesting that I should truncate the full date of recurring events instead of handling the full date of them when sorting them by different recurrences?

That is very difficult since the recurrences supported in iCal files are rather complicated. For instance, I can have a to-do that occurs on the last Saturday of every 2 months. I can't categorize this to-do as monthly recurrence or weekly recurrence since it is a combination of both. I can, however, push all of these kinds of exception to a list, but I will have yet again another long list of these since most of my events fall in this category. This is basically going back to the drawing board.
YvesDaoust 31-Jul-11 2:43am    
If those recurrences are irregular, than there is little other option than precomputing all relevant dates, say for the next three years, and store them in some data structure. Why not just an array of linked lists (one entry per day).

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900