DEV Community

Cover image for Why you should never use DateTime.Now directly - Dealing with Volatile Dependencies in C#
Rogelio Gámez
Rogelio Gámez

Posted on

Why you should never use DateTime.Now directly - Dealing with Volatile Dependencies in C#

Why you should never use DateTime.Now directly

Dealing with Volatile Dependencies in C#

Problem Statement

You are happily working in your application and use the DateTime.Now property for something inoffensive like:

public int GetNumberOfDaysInCurrentMonth()
{
    return DateTime.DaysInMonth(DateTime.Now.Year, DateTime.Now.Month);
}
Enter fullscreen mode Exit fullscreen mode

Some years passed by, and now you receive a bug on February 29. You start digging through your code the morning of the next day but everything is working correctly. You can probably guess what happened by now.

So now you fixed your application by adding support to leap years and you want to make sure everything will work correctly in the future by adding a unit test. How can you unit test the method above if every month will have different results? I hope you don't think of checking the current month in your unit test and assert based on that...

Volatile Dependencies

DateTime.Now is an example of volatile behavior.

A dependency can be considered volatile if any of the following criteria are true:

  • The dependency introduces a requirement to set up and configure a runtime environment. Example: databases, message queues, web services, file system.
  • The dependency doesn't yet exist, or is still in development.
  • The dependency isn't installed on all machines in the development organization. Example: expensive third-party libraries.
  • The dependency contains non-deterministic behavior. Example: random number generators, algorithms that depend on the current date or time.

– Deursen, S. V., & Seemann, M. (2019). Dependency injection: Principles, practices, and patterns. Manning Publications.

In this case DateTime.Now contains non-deterministic behavior because every time it is called, it will return a different value.

Dealing with Volatile Dependencies

Instead of using volatile dependencies directly in your code, you should introduce a new layer of abstraction, for example, an Adapter or Facade structural patterns.

In this case of the standard DateTime library, we can use a simple interface:

public interface IDateTimeProvider
{
    DateTime GetCurrentDateTime();
}

public class DefaultDateTimeProvider : IDateTimeProvider
{
    public DateTime GetCurrentDateTime()
    {
        return DateTime.Now;
    }
}
Enter fullscreen mode Exit fullscreen mode

Then we can inject this provider into the class that we are using.

public class DateTimeUtility
{
    IDateTimeProvider _dateTimeProvider;

    public DateTimeUtility(IDateTimeProvider dateTimeProvider)
    {
        _dateTimeProvider = dateTimeProvider;
    }

    public int GetNumberOfDaysInCurrentMonth()
    {
        return DateTime.DaysInMonth(
            _dateTimeProvider.GetCurrentDateTime().Year, 
            _dateTimeProvider.GetCurrentDateTime().Month);
    }
}
Enter fullscreen mode Exit fullscreen mode

Now we are not using DateTime.Now directly in our code, but by an abstraction. We can use another IDateTimeProvider in our code without changing the current implementation.

Unit Testing DateTime.Now through an Interface

Since we are now using an interface, we can use any mocking framework to test our behavior:

Example using Moq (https://github.com/moq/moq4):

[TestMethod]
public void GivenIsLeapYear_WhenGettingNumberOfDaysInFeb_ThenWeGet29()
{
    // Arrange
    Mock<IDateTimeProvider> dateTimeProviderMock = new Mock<IDateTimeProvider>();

    dateTimeProviderMock
        .Setup(mock => mock.GetCurrentDateTime())
        .Returns(new DateTime(2024, 02, 01)); /// Return feb of leap year

    IDateTimeProvider dateTimeProvider = dateTimeProviderMock.Object;

    DateTimeUtility dateTimeUtility = new DateTimeUtility(dateTimeProvider);

    // Act
    int daysInMonth = dateTimeUtility.GetNumberOfDaysInCurrentMonth();

    // Assert
    Assert.AreEqual(29, daysInMonth);
}
Enter fullscreen mode Exit fullscreen mode

Final Thoughts

This was a very simple example of a volatile dependency. Date and random number generators are both examples of non-deterministic methods, but volatile dependencies can also include third-party libraries or out-of-process dependencies such as databases. When in doubt always remember: depend on abstractions, not concretions.

References

  • Deursen, S. V., & Seemann, M. (2019). Dependency injection: Principles, practices, and patterns. Manning Publications.

Top comments (1)

Collapse
 
ant_f_dev profile image
Anthony Fung

It's a shame that we have to inject a 'time-service' into our code to do this, but yes - I like this pattern.

Another thing I generally do is declare the time as a variable in methods where I have to use it:

var now = MyTimeService.Now;
Enter fullscreen mode Exit fullscreen mode

This means that if I'm debugging, the time will be the (same) time that it was when the method was invoked, regardless of when I mouse-over the variable to see its value.