Why you should never use DateTime.Now directly - Dealing with Volatile Dependencies in C#
Tags: csharp, designpatterns, tutorial, beginners
Published: 2023-03-02 00:00:16

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:

```csharp
public int GetNumberOfDaysInCurrentMonth()
{
	return DateTime.DaysInMonth(DateTime.Now.Year, DateTime.Now.Month);
}
```

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:

```csharp
public interface IDateTimeProvider
{
	DateTime GetCurrentDateTime();
}

public class DefaultDateTimeProvider : IDateTimeProvider
{
	public DateTime GetCurrentDateTime()
	{
		return DateTime.Now;
	}
}
```

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

```csharp
public class DateTimeUtility
{
	IDateTimeProvider _dateTimeProvider;
	
	public DateTimeUtility(IDateTimeProvider dateTimeProvider)
	{
		_dateTimeProvider = dateTimeProvider;
	}

	public int GetNumberOfDaysInCurrentMonth()
	{
		return DateTime.DaysInMonth(
			_dateTimeProvider.GetCurrentDateTime().Year, 
			_dateTimeProvider.GetCurrentDateTime().Month);
	}
}
```

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):
```csharp
[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);
}
```

## 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.