1

We have an application which uses the current date (using Datetime.Now) to calculate specific values.

We need to be able to run these calculations on the server based on a different date as well. Unfortunately the two obvious choices are not viable -

a) although we have the code for the app, due to politics and the fact that other environments also use the service, will not be able to change it and inject a specific date and

b) due to other applications and processes running on the server we cannot change the system date.

What I was wondering was:

would it be possible to intercept calls made to the framework (Datetime.Now in this case) and return a specific value? This can be from either the same application or not. Unfortunately I'm not sure if you can actually intercept these calls in this way, and even then be able to identify the calling process. I realise you could potentially do this with some testing frameworks, but they will require you to change your codebase in any case.

thanks

Francois
  • 92
  • 9
  • 1
    Can't you use a flag to build specifically for said environment distinctly from others? – Rafalon May 25 '18 at 12:26
  • It is technically possible, requires an IL rewriter. I *think* PostSharp can do this, not completely sure. Refactoring the code so only a single method returns DateTime.Now is the much more logical choice of course. – Hans Passant May 25 '18 at 12:43

3 Answers3

3

If you absolutely have no other way than to patch DateTime.Now at runtime - you can do this with Harmony library. I obviously won't recommend to do such things, but you can decide for yourself.

First download binary here (no nuget package yet). Reference it and then:

class Program {
    static void Main(string[] args) {
        var now = DateTime.Now; // normal now
        var harmony = HarmonyInstance.Create("test");
        // patch
        harmony.PatchAll(Assembly.GetExecutingAssembly());
        // now + 100 years
        var newNow = DateTime.Now;
    }

    // we are rewriting method get_Now, which is a getter of property Now
    // of type DateTime
    [HarmonyPatch(typeof(DateTime), "get_Now")]
    class Patch {
        // this method runs after original one
        // __result stores value produced by original
        static DateTime Postfix(DateTime __result) {
            // add 100 years to it
            return __result.AddYears(100);
        }
    }
}

That will change DateTime.Now property getter at runtime so that it returns its original value + 100 years. You can obviously change that to return any value you need.

This patch does not affect any other processes, DateTime.Now will work as usual for them.

Evk
  • 98,527
  • 8
  • 141
  • 191
2

Some googling led me to the software RunAsDate by NirSoft. I cannot endorse this software but I can see that it claims to do precisely what you are asking for.

Please let us know if this works for .NET applications!

TheSoftwareJedi
  • 34,421
  • 21
  • 109
  • 151
0

Really thought it would be easier to do in .net, but in the end I decided to make a code change. While this change is not ideal, it should not introduce any issues I could think of.

High level - Added a datetimeprovider function to the app with a function taking a string and returning datetime, which gets called instead of datetime.now.

In there, it will check for the existence of a config file containing one or more environmentname/connection string pairs. using the parameters already received it matches the incoming request with an environment, and if matched, connects to the db in question and get the date to use (QA environments have their dates saved in a table as they can jump to any date)

If a match is not found, it will merely return datetime.now

Anyway, as mentioned this is not ideal, but is easier imo in this case to do than integrate other solution mentioned. I also appreciate what I've mentioned might not make sense outside the scope of our solution, so apologies for that.

But thanks for the advice regardless, it did give me some ideas for other problems I'm looking into.

Francois
  • 92
  • 9