In .NET (and some other languages) DateTime.Now, DateTime.UtcNow and DateTime.Today (and their language specif equivalents) are not stub-able. Therefor, usage should be discouraged. Detecting the usage is not the challenge obviously, writing down what to do instead in such a way that it helps developers might be.
I already gave it a try for (.NET)[Rule for using a testable (date) time provider by Corniel · Pull Request #4207 · SonarSource/sonar-dotnet · GitHub]:
Adaptable static reference
A static reference that can be adjusted, ideally supports an IDisposable method, that not only adjusts the time behaviour for the current thread only, but also for scope of the using.
public static class Clock
{
public static DateTime UtcNow();
public static IDisposable SetTimeForCurrentThread(Func<DateTime> time);
}
Interface
An interface that relies on the (UTC) time provided:
public interface IClock
{
DateTime UtcNow();
}
Compliant code with static reference
public class Cookie
{
public DateTime? ExpirationDate { get; set; }
public bool HasExpired() => ExpirationDate .HasValue && ExpirationDate .Value < Clock.UtcNow();
}
public class CookieTest
{
[Test]
public void Cookie_has_expired_when_expiration_date_in_past()
{
using (Clock.SetTimeForCurrentThread(() => new DateTime(2017, 06, 11)))
{
Assert.IsTrue(new Cookie { ExpirationDate = new DateTime(2017, 06, 10) }.HasExpired());
}
}
}
Compliant code with interface
public class Cookie
{
public DateTime? ExpirationDate { get; set; }
public bool HasExpired(IClock clock) => ExpirationDate .HasValue && ExpirationDate .Value < clock.UtcNow();
}
public class CookieTest
{
[Test]
public void Cookie_has_expired_when_expiration_date_in_past()
{
var clock = new TestClock(new DateTime(2017, 06, 11));
Assert.IsTrue(new Cookie { ExpirationDate = new DateTime(2017, 06, 10) }.HasExpired(clock));
}
}
). I was just trying to get the right folks’ attention on it & that’s more likely to happen with the right tags. Some teams only monitor “their” tags.