A recent post on c# corner† provides a straightforward introduction to the TimeSpan object and shows a couple innocuous examples. Further down, Ms. Choksi disclaims any further nuances such as time zone differences or daylight saving.
The problem is her code is more harmful than helpful. The TimeSpan object is very tricky and everyone needs to take Daylight Saving Time into consideration. Ignoring the topic is setting folks up for trouble down the road.
Time Zones Are Irrelevant
Time zones are irrelevant because now is now wherever you are. It appears differently on your console screen because you may be in London or Sydney. But now is now everywhere simultaneously. If I were to serialize a timestamp, it would include my timezone offset. If I were to enter a timestamp now in Austin, and then you entered a timestamp in Sydney an hour later, guess what? The timespan is one hour. You can print it out the times in whatever timezone you want, but the DateTime objects themselves encapsulate timezone data.
DateTime dtSomeTime = DateTime.Now;
dtSomeTime = dtSomeTime.ToUniversalTime();
The first line of code sets the instant in time on the object, encapsulating timezone and Daylight Saving Time information. The second line of code does not change the value of the object. No Effect. You can discard it.
Daylight Saving Time
What's wrong with this code?
// Oct 28 Noon
DateTime dtDayOne = new DateTime(2006,10,28,12,0,0);
// Oct 29 Noon
DateTime dtDayTwo = new DateTime(2006,10,29,12,0,0);
TimeSpan ts = dtDayTwo.Subtract(dtDayOne);
Console.Write("Days Difference: " + ts.TotalDays);
Console.Write("Hours Difference: " + ts.TotalHours);
// Displays
// > 1
// > 24
The problem is, well, the results displayed are wrong.
If you were providing a calculation for drive time for a long trip, you would be giving out the wrong answer. There are twenty-five hours between Noon Oct 28 and Noon Oct 29 (In US Central Standard Time). Daylight Saving Time. Fall Back and all that stuff. You can't ignore it. You can't disclaim it away. There's no hiding from it. You must deal with it.
So what is the right way to calculate time differences? The trick is to compare the time differences after each DateTime is converted to Universal Time. Like this:
// 2006 Oct 28 Noon
DateTime dtDayOne = new DateTime(2006,10,28,12,0,0);
// 2006 Oct 29 Noon
DateTime dtDayTwo = new DateTime(2006,10,29,12,0,0);
TimeSpan ts = dtDayTwo.ToUniversalTime().Subtract(dtDayOne.ToUniversalTime());
Console.Write("Days Difference: " + ts.TotalDays);
Console.Write("Hours Difference: " + ts.TotalHours);
// Displays
// > 1.04166666666667
// > 25
This provides the correct result because Universal Time keeps marching forward two hours between 2am and 3am Central Time on October 29th.
DateTime Best Practices
There are other nuances of DateTime manipulation that every .NET programmer should be aware of and by far the best source of information is Dan Roger's Coding Best Practices Using DateTime in the .NET Framework.
The article is pretty lengthy, but it covers the full gamut of gotchas -- the stuff you get called out for at 4am on October 29th to fix. Dan's article is especially important if you are serializing DateTimes into XML.
Although it was written in 2004, the information is valid and applicable to every project done today. You could say it's....timeless.
† Ms. Choksi, it seems, was alerted to my critique of her article and she has, to her credit, updated the article to address the issue of time zones.
Comments
Thanks for the information. I'm starting to learn C#.NET, and I was wondering how I could handle 1. date/time values 2. different character sets. At least that's one mystery out of the way! :) Useful link as well. Thanks again.
Glad this article was a help. The MSDN article was a great help for me while developing Vine Type since it uses XML to store blog posts and keeps track of how many more days until comments expire.
The lack of decent support of time zones in .NET drives me nuts.
I remember how some devs at my previous job struggled with time and date calculations across various time zones. It was one dev's responsibility to maintain a table of time zones and updating offsets by hand when daylight savings kicked in! Somehow it didn't occur to them to store everything in GMT. Truly a WTF episode. :)
Things should be easier now I'm guessing. I know that when Vine Type serializes timestamps to XML the timezone offset is sometimes -05:00 and sometimes -06:00 depending on the time of year. I just let the server keep track of Daylight Saving Time for me.
The article addresses the "save everything as GMT" scenario -- which is good but .NET Framework 1.1 doesn't allow native XML Serialization into GMT.
Thanks for this post Carl. I was actually unaware of this issue.
It just occurred to me. Windows itself maintains information about time zones, daylight savings, etc. We use it at work successfully.
It's great in the sense that you don't need to know or track offsets at any time of the year. It does involve a calling Win32 API, but it's an acceptable price to pay for the flexibility.
Yes, I believe that Windows, when it creates the DateTime object, embeds the GMT offset into the object. For me here in Austin, sometimes it's -05:00 and sometimes it's -06:00. And I agree I would rather call an API to keep track of these things.