4/15/2008 3:48:43 PM
I figured coming up with a solution to Jacob Carpenter's HTML Calendar Challenge would be a good entry to resurface with ;) I have been extremely busy with work and haven't had a whole lot of time to code-for-fun. Coding for fun / free is something that I love to do. This challenge was the perfect opportunity for this.
My solution presupposes that the actual days of the week were hard-coded into the calendar. If this isn't the case I may lose some points ;)
Here is my HTML.
<
table cellpadding="3" cellspacing="2">
<tr style="font-weight: bold; text-transform: uppercase;">
<td>Sunday</td>
<td>Monday</td>
<td>Tuesday</td>
<td>Wednesday</td>
<td>Thursday</td>
<td>Friday</td>
<td>Saturday</td>
</tr>
<asp:PlaceHolder ID="ph" runat="server" />
</table>
The first thing I do is create the first day of the current month. Pretty basic. I then need to find the start of that week (whether or not it is the current month or previous). We can do this with DateTime.AddDays(). I created a static int that takes the DayOfWeek as a parameter. Depending on what the first day of the current month is, we now know how far to go back to get the date for Sunday of that week. Again, I am presupposing the week starts on Sunday. Some folks like Monday, etc...
I then have a _continue bool. While this is true I keep adding weeks (WebControls.TableRow). I just add 7*counter to the AddDays (there is no AddWeeks so 7 days = 1 week). While I am creating my TableRow I check to see if any of the cells contain the previous or next month. I compare the current date to my _firstOfMonth.Date.Month. This lets me set the class of "prevMonth". I do the same if the end of the month falls midweek and I have to buffer out the week with next month dates. This also sets _continue to false. Easy.
I could probably stream-line this (and probably will here-and-there over the next couple of days) but when all was said and done it took me a little over 45 minutes from reading Jacob's post to create a solution, test it, blog about it, etc...
Here is the code-behind.
private
bool _continue = true;
private DateTime _firstOfMonth;
protected void Page_Load(object sender, EventArgs e)
{
DateTime dt = new DateTime(DateTime.Now.Year, DateTime.Now.Month, 1);
ph.Controls.Add(Week(month.AddDays(GoBack(dt.DayOfWeek))));
int counter = 1;
_firstOfMonth = dt;
while (_continue)
{
DateTime current = month.AddDays(7*counter);
ph.Controls.Add(Week(current.AddDays(GoBack(current.DayOfWeek))));
++counter;
}
}
private static int GoBack(DayOfWeek dayOfWeek)
{
switch (dayOfWeek)
{
case DayOfWeek.Sunday: return 0;
case DayOfWeek.Monday: return -1;
case DayOfWeek.Tuesday: return -2;
case DayOfWeek.Wednesday: return -3;
case DayOfWeek.Thursday: return -4;
case DayOfWeek.Friday: return -5;
case DayOfWeek.Saturday: return -6;
default: return 0;
}
}
private TableRow Week(DateTime startDate)
{
bool containsNextMonth = false;
TableRow row = new TableRow();
for (int i = 0; i < 7; ++i)
{
TableCell cell = new TableCell();
DateTime cellDate = startDate.AddDays(i);
cell.Text = cellDate.Date.Day.ToString();
if (cellDate.Date.CompareTo(DateTime.Today.Date) == 0)
cell.Attributes.Add("class", "today");
if (cellDate.Date.Month < _firstOfMonth.Date.Month)
cell.Attributes.Add("class", "prevMonth");
if (cellDate.Date.Month > _firstOfMonth.Date.Month)
{
cell.Attributes.Add("class", "nextMonth");
containsNextMonth = true;
}
row.Cells.Add(cell);
}
_continue = !containsNextMonth;
return row;
}
I love little projects like this because I get to hack in the weeds of low-level stuff. Of course I could've used the asp:Calendar but the fact of the matter is it sucks. Have you ever tried to roll your own JavaScript handlers into one of those? Postbacks are a given, a cost of doing business. I'm sure there are others like me out there that would take from this a little more intimate understanding of what goes into creating a calendar control.
If anyone who reads this teaches a web programming course this would be a perfect weekly assignment. It could even be used as an interview question. Not necessarily having to create a solution on the spot, but more of thinking out loud as to how you would go about creating a solution.
Thanks Jacob for the idea and creating a fun little distraction.
Finished product
HTML source
<table cellpadding="3" cellspacing="2">
<tr style="font-weight: bold; text-transform: uppercase;">
<td>Sunday</td>
<td>Monday</td>
<td>Tuesday</td>
<td>Wednesday</td>
<td>Thursday</td>
<td>Friday</td>
<td>Saturday</td>
</tr>
<tr>
<td class="prevMonth">30</td><td class="prevMonth">31</td><td>1</td><td>2</td><td>3</td><td>4</td>
<td>5</td>
</tr>
<tr>
<td>6</td><td>7</td><td>8</td><td>9</td><td>10</td><td>11</td>
<td>12</td>
</tr><tr>
<td>13</td><td>14</td><td class="today">15</td><td>16</td><td>17</td><td>18</td><td>19</td>
</tr>
<tr>
<td>20</td><td>21</td><td>22</td><td>23</td><td>24</td>
<td>25</td><td>26</td>
</tr>
<tr>
<td>27</td><td>28</td><td>29</td><td>30</td><td class="nextMonth">1</td><td class="nextMonth">2</td><td class="nextMonth">3</td>
</tr>
</table>
If this were for a project I would have added proper tabs and newlines to make the HTML look neat and more organic.
UPDATE
I realized this morning that both previous and next months can be less than the current month (December). November (11) is less than December (12) and January (1) is less than December. I will update the code with _previousMonth and _nextMonth as int.
Easy.
C#,
Code
