Date and Time is one of those frustrating things in building an application that can be difficult to get right.
- Ensuring that you can capture your domain accurately across timezones.
- Forgotting that your API can be hosted in UTC, i.e. different to your local machine.
- Side Note: This is why it's important to always test in a deployed environment before handing over for testing or the Product Owner for sign-off.
- Allowing your code to be testable, i.e. date sensitive business rules
I have previously tried to solve the last point using a
ITimeProvider interfaces. A few years ago, I rolled up my experiences from several projects into a library DateTimeProvider.
In my (humble) opinion, DateTimeProvider has a few great features:
staticinjection that you can access
DateTimeProvider.Nowfrom anywhere in your code similar to
DateTime.Now. I prefer this approach so that you aren't forced to dependency inject an instance of
IDateTimeProviderdown through layers.
It allows you to Pin DateTime so can be manipulated in a fixed scope. An example below:
I think this is a really useful feature as it allows to you to write and test time dependent code.
Finally, it has a Rosyln Analyser that detects usages of
DateTime.Nowand in Visual Studio suggests replacing usages :lightbulb: with
One caveat of using a
static implementation is that you need to
theadsafe or otherwise this will happen.
This is relatively easier to using
AsyncLocal<T> as I'll show you in a bit.
This abstraction works well if your domain only needs
DateTime and rough approximations of
TimeZone, i.e. most simple applications.
In the application that I'm tech leading, we have non-functional requirements that need precise handling of date and/or time across multiple time-zones. After a bit of research, If you're in a similar situation, I recommend using NodaTime by Jon Skeet - it gives you the much needed confidence that you are capturing date/time correctly.
Learn how to use NodaTime's
IClockinterface which pretty much does what this is trying to do.
I agreed! :+1: Except... that NodaTime prefers the dependency injection method, which as discussed above, I think for a core cross-cutting concern you want to make the usage as easy as possible.
So, let's wrap the Noda's
IClock interface in a static provider that is also