Kick the DateTime.Now addiction

How many times do we naively rely on DateTime.Now for business dates. Yikes, I just reviewed a system that I wrote a while back and I have chained myself to this concept in a very painful way. Now, I am not responsible for this system any longer but I wanted to try and think of a solution to this problem so that I don’t do it again.

Captain Obvious says: “So with time we seem to have two major units that we care about ‘Date’ and ‘Time’.”

Dates are all about a calendar so lets have an ICalendar and times are all about clocks so lets have an IClock too. Now we can have some concrete SystemCalender and SystemClock that basically just wrap the DateTime.Now type functions. The more interesting behavior for your business would be / could be wrapped in a concrete implementation called, wait for it, Business Calendar and BusinessClock.

public interface IClock
{
	DateTime Now {get;}
}

public interface ICalendar
{
	DateTime Today {get;}
}

Some things that I like about this. I like that this can be database backed, or by a filesystem, or a configuration file. It really becomes less of a concern if I need to change something. If I care more about calendar than clocks I can always use the system clock but then use the business calendar. Really at this point its all about making the choice an explicit choice. Its another one of these small things that can make your system much easier to rerun should a run fail, or just more tolerant of changes in time or what holidays you recognize, etc.

public class SystemClock : IClock
{
	public DateTime Now { get { return DateTime.Now; } }
}

public class SystemCalendar : ICalendar
{
	public DateTime Today { get{ return DateTime.Today; } }
}

not to abuse extension methods (oh what the hell why not, its fun)

public static class CalendarExtensions
{
	public static bool IsHoliday(this ICalendar calendar)
	{
		return _holidayRepository.Find(calendar.Today).IsNotNull();
	}
}

Ok, the code above is way contrived and totally made up but I like how the base calendars don’t have to have every function you may want and can instead focus on the problem of representing dates and times the correct way and that you can bolt on stuff for your app later. Of course you can get yourself into trouble this way as well but sharp tools are nice!

As always leave improvements in the comments!

-d

About Dru Sellers

Sr. Software Engineer at Dovetail Software.
This entry was posted in Uncategorized. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • Anonymous

     Absolutely. but the big thing for me is, like the .Net File System API – we need to break off of this static method syndrome and use a real API if our systems are going to depend on it for critical things. When I wrote this post, I worked at a bank and dates were VERY important yet we used the base library for time. :(

  • Scott Brickey

     Of course, an extension method can just as easily target the native DateTime object.

    I get your point though; if ever we needed a different calendar system (for alpha centuri 5), an interface/provider system can handle it when most can’t… none the less.So far the simplest reason seems to be for unit testing. Overriding the native DateTime.Now seems to be a pain, especially if your tools are limited (it seems TypeMock Isolator handles it well enough).

  • Discofunk

    In a recent project we littered our code with DateTime.Now which caused a lot of headaches in the end. We found it became important to make the distinction between a day and a time. A clock object with the ability to freeze time, warp-into-the-future and blast-into-the-past is also helpful for unit testing timed events.

  • Karsten

    What do you think about TypeMock?
    I guess you will say that’s a bad idea. But why?
    It’s a honest question. I’m very new to unit testing and still thinking about starting with TDD. But to me Typemock would seem as a good solution for this problem??

  • Andrew

    @Robz

    I probably should have worded it a bit differently. I have quite a bit of experience working in the banking industry, and there is some truly fugly code out there based around the fact that most banks do nightly processing. Unfortuately for them, banking is now pretty much 24/7 (Credit Card processors generally have downtime over the weekends though), so I’ve dealt with all the same pains in the past.

    Granted, I know next to nothing about the system in question otther than that processing can extend past midnight (a very common issue in the finance industry), I just can’t see how messing around with DateTime.Now can solve the problem since the code that drives the Date Time should never have been there in the first place.

    There should be a basic thought process that takes effect when tackling a problem: 1) Why does something need to be done? 2) What do we need to do? and 3) How do we do it? The problem is, a lot of developers think in reverse. We come up with nifty, cool solutions to problems that don’t really need fixing. We’re worried about “the How” when we should be worried about “the What”. In this case, something as simple as DateTime.Now is now being turned into two interfaces, two classes and some extension methods. Backing up a step further, someone needs to ask “Why” this problem needs to be fixed, and the answer is “because sometimes processing goes past midnight and DateTime.Now is no longer ‘correct’”. So basically, we have “bad” data, so instead of overcomplicating the bad data to insure you get the right data, wouldn’t it be easier for everyone to pass in the correct data in the first place (from a database, from a file, from a user control where they chose a Date, etc/)?

    DateTime.Now is great for timestamping a Forum Post, setting the value of a “Last Updated”, etc. but it shouldn’t be used to drive business logic if there is a possiblity that it might prevent it from working.

    Also, if this is indeed just a temporary fix becase the project is so bad, why not JFHCI with a fix, throw a “HACK” comment above it and move on since that logic has a very, very good chance of being ripped out of that particular method anyways?

    Just playing Devil’s advocate more than anything, I have a few years of banking experience and I’m just shocked at how horrible the code is especially when it comes to their ETLs/Nightly Processing. Makes me want to hide money under my mattress. ;)

  • http://ferventcoder.com Robz

    @Andrew: In Dru’s defense, he didn’t write the system that does this crazy stuff.

    We are keeping our fingers crossed that that system can be rewritten. It’s a very bad egg.

  • Andrew

    >>Where I work we run processes 24 hours a day, sometimes things have to run after midnight for stuff that has technically happened the day before. Before this abstraction we had to, HAD TO, make sure we had the processes complete before midnight. If we didn’t, it would screw up the calculation of someone’s loan (if memory serves we basically dropped a day’s interest).

    There’s your problem, it was just bad design. In this situation you never should have been using DateTime.Now in the first place. You always should have been passing in the date (or dates if necessary).

    I’m with Chris on this one, you’ve overcomplicated a system which (more the likely) could have been solved with one or two DateTime parameters.

  • drusellers

    @Roman: As a big IoC guy I would probably request either the calendar or the clock via constructor injection.

    @Avner: see below. I wouldn’t say that DateTime.Now is a total no-no, but rather you should make an active and aware decision to use it, and not just let it be the default.

    @Chris: point taken.

    @Igor: See DateTimeOffset, even better

    @Jimmy: Ohhh, that’s nice too.

    Where I work we run processes 24 hours a day, sometimes things have to run after midnight for stuff that has technically happened the day before. Before this abstraction we had to, HAD TO, make sure we had the processes complete before midnight. If we didn’t, it would screw up the calculation of someone’s loan (if memory serves we basically dropped a day’s interest). We could always solve it with workarounds but it was painful and messy. Time is an important aspect in our domain as well, which is another reason why the abstraction has come up.

    Wow! You guys rock and I can’t wait for more. Thanks for all the comments, I hope my response has cleared some of this up.

    -d

  • http://jimmybogard.lostechies.com Jimmy Bogard

    @Chris

    We’re not testing DateTime.Now, but rather, code that _uses_ DateTime.Now. Any time we have code that looks at DateTime.Now for decisions, it will be a brittle test UNLESS we’re able to specify that indirect input for that test.

    All it really takes to shift to this kind of abstraction is one test/build failure because of an environmental concern. When we had tests fail because of what time zone our build server was on, we moved to an abstraction. Going forward, our tests were easier to discern when both the observed “today” date as well as calculated dates were specified.

  • http://www.centrifugalbumblepuppy.com/ Andrew

    I have implemented the abstracted clock for a new project and it is proving very useful. We use the Static Gateway Pattern from this website, although Ayende’s way also looks like a neat alternative.

    Our code regularly has to deal with time zones and handling them right is very important for the business but far from straightforward. Having the code properly unit tested around edge cases is a life saver, and certainly helped to speed up the development.

  • Paul

    @Chris,
    If I had this conversation at work, I’m sure 90%+ of the guys I deal with would say all the things you’ve said. It’s very hard for it not to sound like overkill until it’s caused you some pain. For the record I don’t implement this solution in every project I do, but I know it’s there in the toolbox should I need it.
    The thing is because it’s such a trivial thing to implement you can see why people might just use it from the off if they think they might need it. The YAGNI discussion is for another day though.

  • http://panteravb.com Chris

    @Paul,

    I was kinda generalizing(slash mocking) with the DateTime.Now remarks.

    It’s hard to not see wrapping DateTime.Now behind an interface as overkill. I’m a huge TDD fan, this particular thing though, I just don’t get it. I’m sure next week(month) I’ll hit my first time sensitive coding issue and the only way of testing it is with an ISomethingToDoWithDateAndTime interface.

  • Paul

    @Chris,

    Also I don’t think anyone is suggesting that the interface is to help test if DateTime.Now works. It is useful for helping to test if your user code responds appropriately when it has to use the current time as part of its logic, in a deterministic way.

  • http://panteravb.com Chris

    @Jimmy,

    I would 100 percent agree with the your last statement so long as it was prefaced with one word, “[Useful] Tests/specifications are the only repeatable, reliable way to know if we built the software right.”

    Hacking up an interface to test whether DateTime.Now works is no different than testing getters and setters, waste of time.

  • Paul

    @Chris,

    Ha :-) It’s ok you can hang it back up for another day.
    I know what you are saying in that his production code was working but the question is how would you know that other than by checking it manually or via an automated test.
    I’ve no vested interest in trying to persuade you. I’m by no means an expert (or zealot) in tdd. I’ve just been using more and more over the last few years. I think tdd is a very personal experience and it’s only when you’ve experienced some real value from an extra level of decoupling than you may normally have chose on a few of your projects that it becomes easier to buy into more and more of these kind of pre-emptive decisions.
    And please don’t take any of that as implying that you don’t get tdd or anything, pragmatism rules the day and if you say you don’t need it this particular abstraction for your projects then I believe you. Who better to make that decision.

  • http://jimmybogard.lostechies.com Jimmy Bogard

    @Chris

    His non-test code was working fine? How did he prove that? Gut feeling? Manual testing when we feel like it?

    Tests/specifications are the only repeatable, reliable way to know if we built the software right.

  • http://panteravb.com Chris

    @Paul,

    I just can’t buy into the whole abstracting DateTime.Now.

    There are two ends of the spectrum of testing. The ones who just flat out don’t do it at all, and the ones who spend WAY too much time overthinking things like how to test DateTime.Now.

    Ayende’s post actually indicates that there wasn’t a problem with his non-test code, just his test code, so time was spent(wasted) trying to make his test work, while all that time his non-test code was working fine.

    IClock, IDateTime, and ISystemTime, et al, can all very easily be replaced with another interface: ILikeToWasteMyClientsMoney

    I’m wearing my flame retardant tshirt today :)

  • Paul

    @Chris

    Here is a real life example from a project Ayende was having problems testing.

    http://ayende.com/Blog/archive/2008/07/07/Dealing-with-time-in-tests.aspx

  • http://jimmybogard.lostechies.com Jimmy Bogard

    We used to use the IClock quite a bit in our projects BUT….it got quite annoying having the IClock dependency littered everywhere.

    We’re now leaning towards the Ayende ™ way of things:

    http://www.lostechies.com/blogs/jimmy_bogard/archive/2008/11/09/systemtime-versus-isystemclock-dependencies-revisited.aspx

    Yeah, I’ll be That Guy who links to his own blog. What _is_ nice about wrapping the dependencies in an interface is the extension methods.

    However, extension methods around primitives are often a sign that you have a case of primitive obsession. Why not separate creation (SystemTime.Now or whatever) and behavior? You could have a pretty rich SystemTime or SystemCalendar if you put all the behavior pertaining to those items actually in that class.

  • http://panteravb.com Chris

    Right, so back to my first comment, i just need an example so I can wrap my head around this.

    To date(no pun intended), this would solve exactly zero problems I’ve had, so rather than a theoretical example, I’m curious of an original problem that this solves. And not one that was made up this morning to help support this post, just one from a real project. Actually, the one that inspired Dru’s post would be perfect.

  • Paul

    @Chris, yes but it isn’t DateTime.Now. If the method under test uses DateTime.Now as part of it’s internal logic, then how are you going to control that when you come to test it. You’re not going to change the source code of your method are you?

  • http://panteravb.com Chris

    @Paul,

    isn’t this deterministic:

    new DateTime(2008, 12, 25)

    ??

  • http://igorbrejc.net Igor Brejc

    I agree with abstracting Date/Time, but I think your interfaces should return DateTimeOffset instead of DateTime.

    see http://www.danrigsby.com/blog/index.php/2008/08/23/datetime-vs-datetimeoffset-in-net/

  • Paul

    @Chris, it could aid in testing as now you can create a mock IClock or ICalendar to return a deterministic date for testing purposes

  • http://panteravb.com Chris

    It would be easier to see why you would want to clutter a codebase with these interfaces if you could provide real examples of the problem with DateTime.Now, followed by examples of how hiding behind the an interface is helpful and more testable.

  • http://weblogs.asp.bet/avnerk Avner Kashtan

    Your implementation is pretty simple and straightforward, but I’m just missing one thing – what’s so horrible about using DateTime.Now? I can understand scenarios where the current server’s system clock isn’t the standardized time you want to rely on, but what is the rationale for marking DateTime.Now as a total no-no?

  • Paul

    @Damien, it’s not immediately obvious to me why this makes using time zones more difficult than calling DateTime.Now directly? Can you expand and suggest any improvements you would make?

  • http://blog.jendrusz.pl Roman Jendrusz

    As Jesper said it is a really great way for writing a more testable code. I think that many projects have some fancy helper methods for dealing with time and dates. As you said these metods can be provided by this interfaces or extension methods.

    What is your prefered way to gain access to objects that implements these interfaces? Are you using some service locator?

  • http://damieng.com Damien Guard

    This makes dealing with time zones more difficult.

    [)amien

  • http://blog.kamstrup-linnet.dk Jesper Kamstrup Linnet

    I once worked on a project where a lot of the business logic was very dependent on dates. Using an interface like the ICalendar was a great help, especially when writing unit tests exercising the date related logic.