Introduction
CustomCal is a .NET-based C++ project that shows how to use the DateTime
class to build and output a calendar on the command line based on a year and month passed in as arguments. This sample project is useful because there aren't a lot of examples that show how to use some of the basic methods of the DateTime
class in C++.
Background
A friend of mine wanted to know how to build a command line calendar in C++, and I thought it was an interesting problem to solve. I decided to use the DateTime
class from the .NET runtime because it did not seem to be limited to UTC (like the old-school ctime
-based functions.)
Using the code
The CustomCal project is pretty simple and only includes three functions, and it does not leverage the object oriented nature of C++. The functions are:
main
buildCalendar(int year, int month)
: The major logic of the program that does all the date calculations and outputs the calendar. showUsage()
: Outputs how the command line program should be used.
The main
function parses the command line arguments and validates that the year and month are within a logical range for the calendar and the DateTime
class.
int main(array<System::String ^> ^args)
{
int year;
int month;
if(args->Length == 2)
{
year = Convert::ToInt32(args[0]);
month = Convert::ToInt32(args[1]);
if((year < 1 || year > 9999) || (month < 1 || month > 12))
{
showUsage();
return -2;
}
}
else
{
showUsage();
return -1;
}
buildCalendar(year, month);
return 0;
}
The DateTime
class, as far as I can tell, doesn't mind the extreme year situations of 1 or 9999. However, it will not magically resolve incorrect month designations by shifting to the next year (for instance, putting in 13 as the month in order to move to the 1st month of the next year.)
Next, the program calls the buildCalendar function, which takes in the month and year, and writes out a calendar.
int buildCalendar(int year, int month)
{
int dayOfWeek;
int weekCount = 6;
int dayCount = 0;
array<String^>^ months = gcnew array<String^>{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
First thing that needs to happen in the function is the declaration of the variables necessary to facilitate the calendar building. The weekCount
variable is used to control the main week loop for the outputting of the calendar, and the months
variable is an array of short names for the month. The DateTime
calendar doesn't have a method for retrieving the names for the months, so they have to be stored in the program itself.
Next, the dateTime
variable is declared and set to the first day of the month and year passed into the function.
DateTime dateTime = DateTime(year, month, 1);
Console::WriteLine(months[month-1] + " - " + year + "\n");
Console::WriteLine("Sun\tMon\tTue\tWed\tThu\tFri\tSat");
In order to start writing out the calendar at the beginning of the first week of the month, the program has to find out what day of the week the first of the month lands on. Then, startDate
is declared and set to the first day of the week (which is not necessarily the first day of the month to be written out.)
dayOfWeek = Convert::ToInt32(dateTime.DayOfWeek)*-1;
DateTime startDate = dateTime.AddDays(dayOfWeek);
Now that the first of the month and week has been set, its time to setup the outer loop to write out 6 weeks.
The inner loop writes out the 7 days for each week. It does this by keeping an integer counter (dayCount
) for the days that have been written to the console, and uses that counter to move to the next day in the month via the AddDays
method. A day is not written out if the month is not the current month (which will probably happen in first or last weeks.)
for(; weekCount != 0; weekCount--)
{
for(int j = 0; j < 7; j++)
{
if(startDate.AddDays(dayCount).Month == month)
{
Console::Write(startDate.AddDays(dayCount).Day + "\t");
}
else
{
Console::Write(" \t");
}
dayCount++;
}
System::Console::WriteLine("");
}
return 0;
}
The last function in the program is showUsage
, which is called by main
if the program is called with invalid arguments.
void showUsage()
{
Console::WriteLine("usage: CustomCal (4 digit year, > 1 and < 10,000) (month number, 1 - 12)");
Console::WriteLine("example: CustomCal 1974 3 <-- March 1974");
}
If all goes well, the program will output one calendar month to the console. Here is the program in action:
Points of Interest
One thing to note about the DateTime
class is that while you can convert the DayOfWeek
output to Int32 in order to get 0 for Sunday, 1 for Monday, etc., you can't convert the integer of the Month
output to a String in order to get the month name (hence the array of month names in the code above.)