Using and formatting DateTime and TimeSpan data types

One of the most useful constructs in the .NET Framework is the ease of use for date/time manipulation. I need to manipulate dates and time frequently, either displaying them or parsing strings containing them.

Let me show you the 5 easy ways to get a DateTime variable.

DateTime dt;
dt = DateTime.MinValue;
Console.WriteLine(dt.ToString());
dt = DateTime.MaxValue;
Console.WriteLine(dt.ToString());
dt = DateTime.Today;
Console.WriteLine(dt.ToString());
dt = DateTime.Now;
Console.WriteLine(dt.ToString());
dt = DateTime.UtcNow;
Console.WriteLine(dt.ToString());

The DateTime.MinValue represents year 0001, January 1st, on the 0th hour, the 0th minute and 0th second.

The DateTime.MaxValue represents year 9999, December 31st, on the 23rd hour, the 59th minute and 59th second.

The DateTime.Today represents today. Taking the date of this post, it’ll be year 2007, November 2nd (zeroes for the other values).

The DateTime.Now represents today with the time component. Taking the date of this post again, it’ll be year 2007, November 2nd. And (for example) the time is 9th hour, 1st minute and 34th second.

The DateTime.UtcNow represents today with the time component, in Universal Coordinated Time. Taking the date of this post again, it’ll be year 2007, November 2nd. And (for example) the time is 1st hour, 1st minute and 34th second. I’m in Singapore, so the time zone offset is 8 hours ahead of UTC.

DateTime.UtcNow is particularly useful when you deal with international data, where the dates and times can span across many countries. In this case, having one standard time offset makes manipulating and displaying dates and times easy. Then people won’t keep asking “Is this American time, or London time, or Singapore time?” Just display, and let the users add or subtract their own time zone offset to obtain data in their local time.

Specific date/time values

What if you need to create DateTime variables with a specific date and time?

DateTime dt;
// 2nd Nov 2007, 1:15 pm, 45th second, 853rd millisecond
dt = new DateTime(2007, 11, 2, 13, 15, 45, 853);
Console.WriteLine(dt.ToString());
// get the same date by parsing a string
dt = DateTime.ParseExact("20071102131545", "yyyyMMddHHmmss", null);
Console.WriteLine(dt.ToString());
// get the same date by parsing a string formatted differently
dt = DateTime.ParseExact("02/11/2007 13:15:45", "dd/MM/yyyy HH:mm:ss", null);
Console.WriteLine(dt.ToString());

I prefer using the ParseExact() function rather than Parse(). I like to have exact control over how a date/time string is interpreted, and I want any culture-specific information to be left out of the parsing. If you know exactly what you have and what you need, it’s better to do it yourself rather than letting the code do implicit conversions. I’m paranoid that way…

Displaying dates and times

Then there’s displaying them

DateTime dt;
dt = new DateTime(2007, 11, 2, 13, 15, 45, 853);
// get 2 Nov 2007 01:15 PM
Console.WriteLine(dt.ToString("d MMM yyyy hh:mm tt"));
// get November 02, 2007 13:15
Console.WriteLine(dt.ToString("MMMM dd, yyyy HH:mm"));
// get 2007-11-02T13:15:45.8+08:00
Console.WriteLine(dt.ToString("yyyy-MM-ddTHH:mm:ss.fzzz"));

The last one is in the format of the one submitted for discussion in W3 Consortium. Refer to the Microsoft documentation of date and time formats for more information.

Obtaining first and last days of the month

In my work, I’ve often had to deal with special dates, like the first and last days of the month. So how do you accomplish this?

DateTime dtStart, dtEnd;
int iDaysInMonth = 0;
dtStart = DateTime.Now;
dtStart = new DateTime(dtStart.Year, dtStart.Month, 1, 0, 0, 0, 0);
dtEnd = DateTime.Now;
iDaysInMonth = DateTime.DaysInMonth(dtEnd.Year, dtEnd.Month);
dtEnd = new DateTime(dtEnd.Year, dtEnd.Month, iDaysInMonth, 23, 59, 59, 999);
Console.WriteLine("The first day of the month is {0}", dtStart.ToString("dd/MM/yyyy HH:mm:ss"));
Console.WriteLine("The last day of the month is {0}", dtEnd.ToString("dd/MM/yyyy HH:mm:ss"));

The trick to obtaining the last day of the month is the DateTime.DaysInMonth() function. The number of days in the specific month, is also the last day of the month. No fuss, no muss.

This is way better than you implementing a custom function, which probably involves some arcane calculation with leap and non-leap years. If you do it wrongly, you’ll end up with 28 days instead of 29 for certain February’s. The DateTime.DaysInMonth() function is impervious to these mistakes.

Duration calculation with TimeSpan

Next, we have calculations of duration. This is where TimeSpan comes in. An example illustrates its use better.

DateTime dtStart, dtEnd;
dtStart = new DateTime(2007, 10, 27);
dtEnd = new DateTime(2007, 11, 2, 13, 15, 45, 853);

TimeSpan ts = dtEnd - dtStart;
Console.WriteLine(ts.Days);
Console.WriteLine(ts.Hours);
Console.WriteLine(ts.Milliseconds);
Console.WriteLine(ts.Minutes);
Console.WriteLine(ts.Seconds);
Console.WriteLine(ts.Ticks);

Console.WriteLine(ts.TotalDays);
Console.WriteLine(ts.TotalHours);
Console.WriteLine(ts.TotalMilliseconds);
Console.WriteLine(ts.TotalMinutes);
Console.WriteLine(ts.TotalSeconds);

You’ll notice 2 different sets of properties. The Days property refers to the value of TimeSpan variable for the “day” part. The TotalDays property refers to the number of days in the duration. Similarly for the other “Total”-prepended properties. Just print out the values to get a feel of the differences.

Now for a more specific and practical example. How do we calculate the number of days between two dates?

DateTime dtStart, dtEnd;
TimeSpan ts;

dtStart = new DateTime(2007, 2, 26);
dtEnd = new DateTime(2007, 3, 2);
ts = dtEnd - dtStart;
Console.WriteLine(ts.TotalDays);

dtStart = new DateTime(2008, 2, 26);
dtEnd = new DateTime(2008, 3, 2);
ts = dtEnd - dtStart;
Console.WriteLine(ts.TotalDays);

The above gives 4 days (26 Feb, 27 Feb, 28 Feb and 1 Mar) and 5 days (as before, plus 29 Feb) respectively. Note that the leap years are also taken care of. The end date is not included in the calculation. Since the end date is at the very start of the day (0th hour, 0th minute, 0th second), it’s not counted, as far as duration is concerned.

But, what if you need the number of months instead? How do you do it? Well, you don’t need TimeSpan for this…

DateTime dtStart, dtEnd;
dtStart = new DateTime(2006, 11, 15);
dtEnd = new DateTime(2007, 2, 15);
int iNumberOfMonths = (dtEnd.Year * 12 + dtEnd.Month) - (dtStart.Year * 12 + dtStart.Month);
Console.WriteLine(iNumberOfMonths);

Just because it looks like something, doesn’t mean it has to be that something

Beginning C# – Formatting output

Real math formulae @iStockphoto / pinobarile Many times when we’re busy churning out code and worrying about the accuracy of our calculations, we can forget that ultimately our users are looking at the results. We have to care about how to present those precious calculations so our users can make sense of it.

And it’s appalling that there are programmers who make up algorithms and write custom functions to get those presentation formats, when the programming language often has a perfectly good in-built function for it. We have to become masters of our chosen programming language! We can start with the following code example.

int numberofapples = 7;
short numberoforanges = 16;
byte red, green, blue;
float distanceinmetres = 42769.307f;
decimal priceofcomputer = 2495.95m;

red = 107;
// the following two lines will fail compilation
// because the values are out of [0, 255] range
//green = -34;
//green = 256;
green = 214;
blue = 0x3f; // this is 63

// this pads the number with zeroes on the left until
// there are 5 digits. If the number is already 5 or
// more digits, then the number is simply printed out.
Console.WriteLine(numberofapples.ToString("d5"));
Console.WriteLine(numberoforanges.ToString("d5"));

// in this case, the number is converted into a string,
// then it's padded on the left with spaces (default) until
// there are 5 characters.
Console.WriteLine(numberofapples.ToString().PadLeft(5));
 // this time, we pad with the letter v (instead of spaces)
Console.WriteLine(numberoforanges.ToString().PadLeft(5, 'v'));

// the maximum number of placeholders is 3: {0}, {1} and {2}
// To go beyond 3, you'll have to use this syntax
//Console.WriteLine("{0}, {1}, {2}, {3}, {4}", new object[] { red, green, blue, 8, 13 });
Console.WriteLine("\\nRGB triplet is [{0}, {1}, {2}]", red, green, blue);
Console.WriteLine("In hexadecimal, they're [{0,0:x2}] [{1,0:x4}] [{2,3:x2}]\\n", red, green, blue);

Console.WriteLine("Distance is {0}", distanceinmetres.ToString("f2"));
distanceinmetres = 42769.304f;
Console.WriteLine("New distance is {0}", distanceinmetres.ToString("f2"));
Console.WriteLine("Distance in scientific notation is {0:e}", distanceinmetres);
Console.WriteLine("Scientific notation (2 dec places) is {0:e2}", distanceinmetres);

Console.WriteLine();

Console.WriteLine("Computer price:\\t{0:N}", priceofcomputer);
Console.WriteLine("Or this way:\\t{0:C}", priceofcomputer);
Console.WriteLine();

DateTime timenow = DateTime.Now;
Console.WriteLine(timenow.ToString("dd/MM/yyyy HH:mm:ss"));
Console.WriteLine(timenow.ToString("yyyy-MM-ddTHH:mm:sszzz"));
Console.WriteLine(timenow.ToString("dddd, MM/dd/yyyy"));
Console.WriteLine(timenow.ToString("d MMM yyyy"));
 // for more formats, search MSDN for "Custom DateTime Format Strings"

Console.WriteLine("End of program");
Console.ReadLine();

There’s quite a bit of commenting, so you can follow better while reading the code. It saves a lot of explanation in the post too. There are only a few things I want to point out.

Hexadecimal values
Any integer variable type can be assigned a hexadecimal value instead of plain vanilla numbers. Perhaps the value to be assigned is the result of a binary OR. In this case, hexadecimal values give a clearer idea of how the result is obtained, such as 0xf0 | 0x33 becomes 0xf3.

Hexadecimal values are also used in colour representations, as illustrated in our example code.

Special characters
In the example code, two new characters are introduced: \n and \t. The former is a new line character, and the latter is a tab character. They are also known as escape characters. The backslash \ escapes the normal meaning of the following character. So the “n” is not really printed out.

Automatic rounding
When the float value 42769.307f is printed to 2 decimal places, it’s rounded up to 42769.31. When 42769.304f is printed to 2 decimal places, it’s truncated to 42769.30.

Despite the convenience, I strongly suggest you do not let the computer determine your result. If you want it rounded up, ensure the result yourself.

Thousands separator
Sometimes, when the user deals with numbers in the millions, it’s hard to determine the value of a number such as 43526176849. The user has to count the digit positions to know it’s about 43 billion. This is where using “N” in the format string helps. Commas are used to separate every thousandth digit. Commas are the commonly used separator. If you live in Germany, it’s a period “.”. If you live in Sweden, it’s a space.

The “C” format string is for currency.

Date/Time formats
The combinations for formatting dates and times are the most formidable ones I’ve ever encountered. I am inclined (and due to work requirement) to use the date format used in England, where the day is before the month, like so dd/MM/yyyy.

If you’re working in ASP.NET, I suggest you don’t rely on the culture of the globalisation tag in web.config. There are short cuts for date formatting, such as DateTime.Now.ToString("d") which gives the short date version. I personally find this lacking in descriptive detail. I prefer to manually set the format string. This way, I know what’s gonna come out.

Output
Formatting output screenshot

Homework
Go search MSDN for all the possible format strings, particular those for dates and times. Then play around with the source code. Have fun!

Download source code.