Skip to content

Instantly share code, notes, and snippets.

@jmsb
Last active September 19, 2024 14:34
Show Gist options
  • Save jmsb/5716c2f9a2e8c7e163ac952b529b6386 to your computer and use it in GitHub Desktop.
Save jmsb/5716c2f9a2e8c7e163ac952b529b6386 to your computer and use it in GitHub Desktop.
C#.NET Time Machine service, for modifying how a service perceives time.
// Interface for a Date Time Service
public interface IDateTimeService
{
DateTime Now();
DateTime UtcNow();
DateTime Today();
DateTime UtcToday();
}
// Default Impl of IDateTimeService
public class DateTimeService : IDateTimeService
{
public DateTime Now()
{
return DateTime.Now;
}
public DateTime UtcNow()
{
return DateTime.UtcNow;
}
public DateTime Today()
{
return DateTime.Today;
}
public DateTime UtcToday()
{
return DateTime.UtcNow.Date;
}
}
// Impl of a Time Machine
/// <summary>
/// A replacement for the static DateTime methods (Now, UtcNow, Today) for getting the current
/// date or time. Any entity utilizing this service can have its perception of time adjusted
/// from the outside, allowing tests to simulate the passage of time.
/// </summary>
public class TimeMachine : IDateTimeService
{
private DateTime? FrozenDateTime { get; set; }
private TimeSpan Offset { get; set; }
public TimeMachine()
{
RevertAllTimeTravel();
}
/// <summary>
/// Retrieve the current date and time.
/// </summary>
public DateTime Now()
{
return FrozenDateTime != null
? FrozenDateTime.Value
: DateTime.Now.Add(Offset);
}
/// <summary>
/// Retrieve the current date and time in UTC.
/// </summary>
public DateTime UtcNow()
{
return FrozenDateTime != null
? FrozenDateTime.Value.ToUniversalTime()
: DateTime.UtcNow.Add(Offset);
}
/// <summary>
/// Retrieve the current date.
/// </summary>
public DateTime Today()
{
return FrozenDateTime != null
? FrozenDateTime.Value.Date
: DateTime.Today.Add(Offset).Date;
}
/// <summary>
/// Retrieve the current date in UTC.
/// </summary>
public DateTime UtcToday()
{
return FrozenDateTime != null
? FrozenDateTime.Value.ToUniversalTime().Date
: DateTime.UtcNow.Add(Offset).Date;
}
/// <summary>
/// Move forward or backward in time by the specified amount of time.
/// </summary>
/// <param name="adjustment">The amount of time, forward or backward, to shift by.</param>
public void TimeTravel(TimeSpan adjustment)
{
Offset += adjustment;
}
/// <summary>
/// Move to the specific point in time provided.
/// </summary>
/// <param name="newDateTime">The point in time to move to.</param>
public void TimeTravelTo(DateTime newDateTime)
{
Offset = newDateTime.Subtract(DateTime.Now);
}
/// <summary>
/// Halts the progress of time.
/// </summary>
public void FreezeTime()
{
if (FrozenDateTime == null)
FrozenDateTime = Now();
}
/// <summary>
/// Resumes the progress of time.
/// </summary>
public void UnfreezeTime()
{
if (FrozenDateTime != null)
{
TimeTravelTo(FrozenDateTime.Value);
FrozenDateTime = null;
}
}
/// <summary>
/// Undoes all adjustments to the flow of time.
/// </summary>
public void RevertAllTimeTravel()
{
UnfreezeTime();
Offset = TimeSpan.Zero;
}
/// <summary>
/// Are we currently time traveling or not?
/// </summary>
/// <returns></returns>
public bool IsCurrentlyTimeTraveling()
{
return FrozenDateTime != null || !Offset.Equals(TimeSpan.Zero);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment