Tuesday, July 15

C# : Playing with timespans, and custom calendars

Recently I had to build an improved "event" calendar, that would mark each day that had an event.

Roughly, the event's chronological data were described with a simple startDate + endDate. So in the end we have a collection of such objects.
The problem was with overlapping dates, and determining if the current "month view" displayed correctly the dates with any event occuring during these days. i.e. If the calendar was showing "August 2008", the view should display all events starting prior / during and/or edning during/after the selected month that have at least 1 day within this month.

I searched and searched over and over for a solution to this problem, however nothing was suitable. I also haven't come across any other .net functionality to suit my purpose with less code [obviously there must be a more clever way than the one I'm going to describe].

Ok enough with the blab-bla. The solution proved to be quite simplistic! [most of the times you try hard to think of a complex problem, while the simple answer stares at you with it's tongue out]

First we have to build a hashtable containing a representation of a selected month's days. We need this for the inherent "key-value" nature of a hashtable. Our key will be the day numeric (1,2,3...29/30/31 depending on the current month of the year) and the value shall be a flag showing if at least an event occurs on that day :

for (int i = 1; i <>


Next, we will iterate our event collection (it can be a recordset, another hashtable, a collection of "event" objects -not event as in programming, event as in "meeting", "party" etc - ) and mark each day accordignly :

- Dcevent is custom class holding an event's data (startDate, endDate)
- eventColl is a collection of Dcevent objects

foreach (Dcevent evt in eventColl)
{
tmpTs = evt.endDate.Subtract(evt.startDate); //subtract the start date from the end date to get a temporary timespan object
for (int j = 0; j <= tmpTs.Days; j++) //iterate for the amount of days the timespan represents
{
tmpDt = evt.startDate.AddDays(j); //start from the event's startDate and add days
if (tmpDt.Month == month) //check if the month of the date we have reached is the same as the month we are viewing
{ //if the specified date is within the month specified
if (tmpHt.Contains(tmpDt.Day)) //unnecessary, check if the day is present in our hashtable
{ //if the specified days exists in the hashtable
if (tmpHt[tmpDt.Day].ToString() != "1") //if it hasn't been marked yet
{
tmpHt[tmpDt.Day] = "1"; //mark it as active
}
}
}
}
}

In the end we have a hashtable representing all the days of a month [visual representation can be via a calendar], along with additional information that marks them as an "event day".

All we have to do then is iterate through the hashtable and build our html calendar as we like.

A full function listing :

public Hashtable calcActiveDays(int month, int year, Collection eventColl)
{
TimeSpan tmpTs;
Hashtable tmpHt = new Hashtable();
DateTime tmpDt;
//fill up the hashTable with the days in a month
for (int i = 1; i < DateTime.DaysInMonth(year, month); i++)
{
tmpHt.Add(i, "0");
}
if (eventColl != null) //events were found in the first place
{
foreach (Dcevent evt in eventColl)
{
tmpTs = evt.endDate.Subtract(evt.startDate); //may return 0 ???
for (int j = 0; j <= tmpTs.Days; j++)
{
tmpDt = evt.startDate.AddDays(j);
if (tmpDt.Month == month)
{ //if the specified date is within the month specified
if (tmpHt.Contains(tmpDt.Day))
{ //if the specified days exists in the hashtable
if (tmpHt[tmpDt.Day].ToString() != "1")
{ //if it hasn't been marked yet
tmpHt[tmpDt.Day] = "1"; //mark it as active
}
}
}
}
}
}
return tmpHt;
}


As an added bonus here is a function that determines if a given date is within a specified date-range :

public bool isDayIntimeSpan(DateTime startDate, DateTime endDate, DateTime dayToCheck)
{
TimeSpan ts = endDate.Subtract(startDate);
DateTime td;
for (int j = 0; j <= ts.Days; j++)
{
td = startDate.AddDays(j);
if ((td.Day == dayToCheck.Day) && (td.Month == dayToCheck.Month) && (td.Year == dayToCheck.Year))
{
return true;
}
}
return false;
}



You can alter the code and do whatever you want; however this is provided "as-is" with no guarantees whatsoever. For all I know I might have re-invented the wheel ;b

Below you will find a zipped .cs file containing both functions wrapped under an object , released under the GPL licence,

The .zip file : http://www.mediafire.com/?74nknqn5xgn

Cheers!

Labels: , , , , ,

Tuesday, November 14

Inspiration of the human mind



Just plain amazing ... Attention to detail, and great (midi) music :D

Friday, October 20

A quick postie just to share this!



Amazing!