Best Practices Using DateTime in .NET Framework
Many programmers encounter assignments that require them to accurately store and process data that contain date and time information. On first glance, the common language runtime (CLR) DateTime data type appears to be perfect for these tasks. It isn't uncommon, however, for programmers, but more likely testers, to encounter cases where a program simply loses track of correct time values. This article focuses on issues associated with logic involving DateTime, and in doing so, uncovers best practices for writing and testing programs that capture, store, retrieve, and transmit DateTime information.
"The CLR System.DateTime value type represents dates and times ranging from 12:00:00 midnight, January 1, 0001 AD to 11:59:59 PM, December 31 9999 AD." Reading further, we learn, unsurprisingly, that a DateTime value represents an instant at a point in time, and that a common practice is to record point-in-time values in Coordinated Universal Time (UCT)—more commonly known as Greenwich Mean Time (GMT).
At first glance, then, a programmer discovers that a DateTime type is pretty good at storing time values that are likely to be encountered in current-day programming problems, such as in business applications. With this confidence, many an unsuspecting programmer begins coding, confident that they can learn as much as they need to about time as they go forward. This "learn-as-you-go" approach can lead you into a few problems, so let's start identifying them. They range from issues in the documentation to behaviors that need to be factored into your program designs.
The V1.0 and 1.1 documentation for System.DateTime makes a few generalizations that can throw the unsuspecting programmer off track. For instance, the documentation currently still says that the methods and properties found on the DateTime class always use the assumption that the value represents the local machine local time zone when making calculations or comparisons. This generalization turns out to be untrue because there are certain types of Date and Time calculations that assume GMT, and others that assume a local time zone view.
The Rules
- Calculations and comparisons of DateTime instances are only meaningful when the instances being compared or used are representations of points in time from the same time-zone perspective.
- A developer is responsible for keeping track of time-zone information associated with a DateTime value via some external mechanism. Typically this is accomplished by defining another field or variable that you use to record time-zone information when you store a DateTime value type. This approach (storing the time-zone sense alongside the DateTime value) is the most accurate and allows different developers at different points in a program's lifecycle to always have a clear understanding of the meaning of a DateTime value. Another common approach is to make it a "rule" in your design that all time values are stored in a specific time-zone context. This approach does not require additional storage to save a user's view of the time-zone context, but introduces the risk that a time value will be misinterpreted or stored incorrectly down the road by a developer that isn't aware of the rule.
- Performing date and time calculations on values that represent machine local time may not always yield the correct result. When performing calculations on time values in time-zone contexts that practice daylight savings time, you should convert values to universal time representations before performing date arithmetic calculations. For a specific list of operations and proper time-zone contexts, see the table in the Sorting out DateTime Methods section.
- A calculation on an instance of a DateTime value does not modify the value of the instance, thus a call to MyDateTime.ToLocalTime() does not modify the value of the instance of the DateTime. The methods associated with the Date (in Visual Basic®) and DateTime (in the .NET CLR) classes return new instances that represent the result of a calculation or operation.
- When using the .NET Framework version 1.0 and 1.1, DO NOT send a DateTime value that represents UCT time thru System.XML.Serialization. This goes for Date, Time and DateTime values. For Web services and other forms of serialization to XML involving System.DateTime, always make sure that the value in the DateTime value represents current machine local time. The serializer will properly decode an XML Schema-defined DateTime value that is encoded in GMT (offset value = 0), but it will decode it to the local machine time viewpoint.
- In general, if you are dealing with absolute elapsed time, such as measuring a timeout, performing arithmetic, or doing comparisons of different DateTime values, you should try and use a Universal time value if possible so that you get the best possible accuracy without effects of time zone and/or daylight savings having an impact.
- When dealing with high-level, user-facing concepts such as scheduling, and you can safely assume that each day has 24 hours from a user's perspective, it may be okay to counter Rule #6 by performing arithmetic, et cetera, on local times.
Best Practice #1
When coding, store the time-zone information associated with a DateTime type in an adjunct variable.
Best Practice #2
When testing, check to see that stored values represent the point-in-time value you intend in the time zone you intend.
Best Practice #3
When coding, be careful if you need to perform DateTime calculations (add/subtract) on values representing time zones that practice daylight savings time. Unexpected calculation errors can result. Instead, convert the local time value to universal time, perform the calculation, and convert back to achieve maximum accuracy.
Best Practice #4
When testing, calculate the value you expect to see in the XML string that is serialized using a machine local time view of the point in time being tested. If the XML in the serialization stream differs, log a bug!
Best Practice #5
When writing code to serialize classes that have DateTime member variables, the values must represent local time. If they do not contain local time, adjust them prior to any serialization step, including passing or returning types that contain DateTime values in Web services.
Best Practice #6
When coding, make DateTime member variables private and provide two properties for manipulating your DateTime members in either local or universal time. Bias the storage in the private member as UCT time by controlling the logic in your getters and setters. Add the XML serialization attributes to the local time property declaration to make sure that the local time value is what is serialized.
Best Practice #7
When testing, if your programs accept user input specifying date and time values, be sure to test for data loss on "spring-ahead", "fall-back" 23- and 25-hour days. Also make sure to test for dates gathered on a machine in one time zone and stored on a machine in another time zone.
Best Practice #8
When you are coding and desire to store current time represented as universal time, avoid calling DateTime.Now() followed by a conversion to universal time. Instead, call the DateTime.UtcNow function directly.
Caveat: If you are going to serialize a class that contains a DateTime value, be sure that the value being serialized does not represent Universal time. XML serialization will not support UCT serialization until the Whidbey release of Visual Studio.
Tags: .NET, .NET Framework, DateTime, Best Practices, CLR, System.DateTime, programming, time zones, universal time
Can't find what you're looking for? Try Google Search!
Comments on "Best Practices Using DateTime in .NET Framework"