What happens you want to set the Last-Modified HTTP header and all you have is a UTC date? Most likely you’ll get an exception.
The culprit here is the sloppily designed interface of HttpCachePolicy. It so happens that you should pass to the SetLastModified a non-UTC date because here’s what happens inside the method:
public void SetLastModified (DateTime date)
{
DateTime utcDate = DateTimeUtil.ConvertToUniversalTime(date);
this.UtcSetLastModified(utcDate);
}
What if I only have a UTC date? As an aside, handling time zone
differences in .NET is very problematic. It’s a big grey area. I’ve seen
several products which used local dates for convenience—a practice which eventually came back to bite them in the rear. Having been burned before, we now store all dates in UTC and then recalculate them to each user’s time zone preferences as needed.
It’s odd that MSDN doesn’t mention the fact that
SetLastModified expects a local date. Converting a UTC date to a local date only to have it converted back is idiotic. Worse still, it throws an exception saying the utcDate parameter is wrong. Talk about confusion.
It also turns out there’s a UtcSetLastModified, but someone, in his/her infinite wisdom, made it protected. Why? With a little reflection you can still get to it:
MethodInfo m = Response.Cache.GetType ().GetMethod (
"UtcSetLastModified",
BindingFlags.NonPublic | BindingFlags.Instance);
m.Invoke (
Response.Cache,
BindingFlags.NonPublic | BindingFlags.Instance,
null, new object[] { someUtcDate }, null);
My beef with the interface itself
In a shameless appeal to authority, I’ll quote Steve McConnell’s seminal book Code Complete where he talks about “semantic violations of encapsulation” and gives a few examples very similar to the problem at hand.
“The problem [with the said examples] is that they make the client code dependent not on the class’s public interface, but on its private implementation. Anytime you find yourself looking at a class’s implementation to figure out how to use the class, you’re not programming to the interface; you’re programming through the interface to the implementation. If you’re programming through the interface, encapsulation is broken, and once encapsulation starts to break down, abstraction won’t be far
behind.”
And along the same lines:
“If a class offers an incomplete set of services, other routines might find they need to read or write its internal data directly. That opens up the class, making it a glass box instead of a black box, and it virtually eliminates the class’s encapsulation.”
HttpCachePolicy is a good
example of a poorly designed interface. It has more leaks than the navy.
- It raises an exception disclosing an implementation detail.
- It doesn’t offer a complete set of services.
- It forces you to hand it a local date (bad assumption!).
- It’s not self-documenting.
Looking back, I realize I had to dive into the .NET Framework source code on too many occasions. For example, you can’t understand how .NET collections work without a bottle and Reflector (read Collection Madness part
1 and part 2).
My faith in and patience with the .NET framework designers fades by the day.