Yes, you can fake the timezone in which your unit tests are running.
Here's a simple class that changes the local timezone to the timeZoneInfo
provided in the constructor and resets the original local timezone when disposed.
using System;
using ReflectionMagic;
namespace TestProject
{
public class FakeLocalTimeZone : IDisposable
{
private readonly TimeZoneInfo _actualLocalTimeZoneInfo;
private static void SetLocalTimeZone(TimeZoneInfo timeZoneInfo)
{
typeof(TimeZoneInfo).AsDynamicType().s_cachedData._localTimeZone = timeZoneInfo;
}
public FakeLocalTimeZone(TimeZoneInfo timeZoneInfo)
{
_actualLocalTimeZoneInfo = TimeZoneInfo.Local;
SetLocalTimeZone(timeZoneInfo);
}
public void Dispose()
{
SetLocalTimeZone(_actualLocalTimeZoneInfo);
}
}
}
The FakeLocalTimeZone
class is using ReflectionMagic to access private fields (which are protected by a lock), so don't use this in production code, only in your unit tests!
Here is how you can use it:
using System;
using Xunit;
namespace TestProject
{
public class UnitTest
{
[Fact]
public void TestFakeLocalTimeZone()
{
using (new FakeLocalTimeZone(TimeZoneInfo.FindSystemTimeZoneById("US/Eastern")))
{
// In this scope, the local time zone is US/Eastern
// Here, DateTime.Now returns 2020-09-02T02:58:46
Assert.Equal("US/Eastern", TimeZoneInfo.Local.Id);
Assert.Equal(TimeSpan.FromHours(-5), TimeZoneInfo.Local.BaseUtcOffset);
}
// In this scope (i.e. after the FakeLocalTimeZone is disposed) the local time zone is the one of the computer.
// It is not safe to assume anything about which is the local time zone here.
// Here, DateTime.Now returns 2020-09-02T08:58:46 (my computer is in the Europe/Zurich time zone)
}
}
}
This answers how to fake the fact my unit test is running in a different time zone.
Now, as user3292642 suggested in the comments, a better design would be to use an interface and not call DateTime.Now
directly in your code so that you can provide a fake now in your unit tests.
And an even better choice would be to use Noda Time instead of the DateTime
type. Noda Time has all the abstractions and types to properly work with date and time. Even if you don't plan to use it, you should read its user guide, you will learn a lot.