The Problem with Time That .NET 8 Finally Fixed
Vložit
- čas přidán 29. 03. 2023
- Check out my courses: dometrain.com
Become a Patreon and get source code access: / nickchapsas
Hello everybody I'm Nick, and in this video, I will introduce you to the brand new .NET 8 type, called TimeProvider, which aims to solve a problem that has been in .NET since its inception.
Workshops: bit.ly/nickworkshops
Don't forget to comment, like and subscribe :)
Social Media:
Follow me on GitHub: bit.ly/ChapsasGitHub
Follow me on Twitter: bit.ly/ChapsasTwitter
Connect on LinkedIn: bit.ly/ChapsasLinkedIn
Keep coding merch: keepcoding.shop
#csharp #dotnet
Ignore the fact that in this example this code is running on the server. It's irrelevant to the point of this video. It could be a CLI tool or a Blazor Server app that runs locally as a Dektop app with Electron. Focus on the Time point.
did you post the link you mention at 7:09?
Why not simply pass DateTime as method parameter or mock static property DateTime.Now in test? Both cases work as desired and would produce less overhead in production.
DateTimeOffset vs DateTime has always confused me. Some teams have contracts with offset others with stock.
DateTimeOffset is an unambiguous point in time where DateTime combines with the Kind property. So if you are trying to do the right thing and always use UtcNow() but you store it in a Datetime and don't set the Kind to UTC, some date/time conversions (like serialization) might not give you the results you expect.
I think DateTimeOffset is more precise than Datetime as well.
You can feed dates with a time zone offset into a DateTime just fine, It'll localise it to the same instant in the local timezone with the proper DateTimeKind set.
Whether the respects the Kind is a different matter alltogether (usually the answer is no)
When it comes to data structures, when you care for the TimeZone (e.g. for localisation), you will generally use DateTimeOffset over DateTime. Unfortunately it's not always the case.
This. Please make a video on this.
Same
There are actually very good reasons for both.
One is an absolute date with time, and the other is a relative date/time, as in "offset".
Can you please make a video about the datetimeoffset and why it's better over datetime?
@Dionte Smith in simple scenarios like you've just described it doesn't matter much.
Working in a company that employs employees in several timezones, having the timezone instead of using UTC built in could provide extra context. Is it created in the morning of creator's time? Is it edited in the afternoon of the editor's time?
Without that, you have to lookup each users's timezone. Not very ideal if your employees crossing timezones frequently.
And yes, it's not for everyone. If you are happy with UTC datetime, then use it.
To me, it's funny seeing the "20 years" in your cover image, because I stared with .NET the day it came out in 2002.
I've had to endure these pains for over 20 years.
Thanks for shedding light on it!
I'd created a IDateTimeProvider very similarly to the ISystemClock as you've referenced. I really like how they've put some of the Stopwatch functionality in this new TimeProvider class; it should make asserting time differences easier in unit tests (E.g. when you log how long a service took to run)
It's good that they provide some framework-level time dependency like this. However, due to the issues that you just showed with setting up a mock, I'd rather stick to my custom IDateTimeProvider. What the team behind it should do is to provide an interface for the TimeProvider.
Since .NET 8 is far from finalized, the team should be made aware that this design is highly suspect.
I went even further beyond and created a TimeProvider interface which provides an overridable definition of Delay() (to replace Task.Delay()) so I can do deterministic unit testing of time-dependent code - for example I can make a unit test for "does a token expire from the token cache after 24 hours" just by fast-forwarding simulated time
This is a great application for this
For my curiosity, how would it be any different than providing an instance of your interface where 24 hours would already have passed?
Can you throw a quick example of your test and that TimeProvider? Github gist would be great
I treated DateTime variables as external dependencies (since they are when they are based on system time) and as consequence I would want to receive DateTime as a parameter. in the video example the endpoint would have a date parameter that then would be passed to the method
Sure but then you are just passing the problem up the chain. Now that method that passes in the DateTime is not testable.
@@maxbradley9534 I am passing the responsibility to provide the date to the end user. That doesnt make the method non testable, on the contrary it makes the method that exists testable, because any unit test that uses DateTime.Now or other methods that rely on external resources should be considered failed tests . I can change the machine time and make it output whatever i want, thus the test is not consistent and is a bad test.
@@madsxcva Eh? So when you're unit testing, you prefer to sit there for 24 hours? Because "consistent" time runs at a rate of 1 second per second.
Looking forward for the DateTime vs DateTimeOffset video 🎉
Me too
The best channel for .NET enthusiasts, keep on going mate 🦾
Its better to have an excuse why i cant do a unit test
That's funny because in the field it's so true. :)
Love the vid. I'm really bad with dates, timestamps, etc. Glad to have content on this and hoping for more!
This is pretty similar to what I do already. Although I take a much simpler approach and have one source class for all of my time related events, and I have multiple classes that listen to those events and then notify other classes that they need to perform specific functions. No interfaces necessary.
I've implemented this before as ambient context with a good default, such as the one you're using there, given it's a cross cutting, basic concern. Still solves it but no need to inject it everywhere.
It's like you're reading my mind. I'm currently working with datetime logic and had to write a time wrapper so I can unit test it. Mind is also an abstract class..
They have also added a class called FakeTimeProvider to Microsoft.Extensions.Time.Testing which makes mocking a lot easier.
Re: DateTimeOffset vs DateTime I would appreciate to hear your perspective on the reason for which is better
This should totally have been an interface, that mocking setup is complicated.
On the GitHub PR there's a link to a 1.5hr CZcams video that - somewhere in it - contains an argument as to why it's not.
If you can find it, please link it to me :p.. I saw the PR yesterday during break and I forgot about it
Agree 100%, this is ugly.
Absolutely! I dont think the setup would be that much different tough…
It's not that bad imo, pretty much anything you'd mock ends up being messy. Unless it's super simple unit tests.
They will very likely provide a fake class so you won't need to mock it anyway and just use their implementation.
There's so much they can do with the built in DateTime object too with respect to its Kind, they could genuinely make it intuitive, but I don't expect them to because of legacy concerns.
DateTime uses its Kind wrong in almost every single context; it cares about it when you don't, it disregards it when you care about it, and it makes ridiculous assumptions when you don't have one.
That’s cool. But passing the frequency in, doesn’t that make it system dependent as well? Or did I did not understand that right? Awesome content. As always.
Please make a video about datetimeoffset!
I was using an interface with basically the same name of .NET 8, it was ITimeProvider, in general I think interfaces can be better but an abstract class is still valid
Great vid! Where’s the link to the article you mentioned ?
Why DateTimeOffset? Love the videos, thanks for the great content!
I generally prefer using minus operator for getting elapsed time so I’d like to see that with the timers but since c# requires operators be defined statically you wouldn’t be able to mock it ☹️
I would have preferred a minimalistic interface, but I followed the discussion as it was streamed on youtube, and I get why it is the way it is
Do expand a bit please
Do you have a link to this discussion please?
@@ProfessorCodemunkie czcams.com/video/KVDnUSH90qI/video.html
@@ProfessorCodemunkie czcams.com/video/KVDnUSH90qI/video.htmlh34m27s
I believe AftercastGames is referring to this link? czcams.com/video/WW65sGkDINQ/video.html
Edit: Seems this is a link to when the discussion was a bit older, but still relevant imo.
What we need now is a abstraction layer for file system, it would make possible some very interesting things like some tests for file system operations
There already is System.IO.Abstractions
@@bomret isnt this 3rd party lib?
@@adrian_franczak yes. But it’s been around for more than a decade and is very widely used.
using an abstract class allows them to add more and more methods to the TimeProvider without breaking all existing software unlinke an interface.
its the same with DbConnection vs IDbConnection.
But that's why they added default implementations in interfaces in C# 8
@@nickchapsas default interface implementations allow for "multiple inheritance" and are limited to only function if the caller explicitly invokes the methods on the interface, you can't use them on the concrete types that implement the interface
@@nickchapsas I think those don't work because they are planning to ship it down level.
I've seen some ways to solve it by using MS test shims. Which is of course are working in enterprise environment. I usually use my own interface for DateTime... But the need to test such scenario with DateTime is happening not very often
I agree that it should be an Interface!
It feels that this provider lacks the interface segregation principle. It contains several different features that may be split into multiple interfaces. From the usage perspective it will be much more obvious, as now it looks similar to a service locator (anti-?)pattern.
I had rather that they created their own nodatime variation. Even with a type to store time zone and version and hook it up with EF core to make some geolocation based time calculations. It’s quite a lot of work to maintain and a consistent code base with different time zones and versions.
Yeah. And most probably that's exactly why they did not.
Please make a video on why to use DateTimeOffset ❤
Why should we use date time offset? Can you please make a video?
its important if your app is dealing with different timezones and storing everything as UTC will be confusing for user (summer/winter time)
I’d love to see the stopwatch type take one of these in as a constructor so you could test your code with a stopwatch
They probably used an abstract rather than an interface with default implementations for backwards compatibility with netstandard2.0, which doesn't support default implementations for interfaces.
Please consider your (maybe our) wish for an ITimeProvider interface.
If they do it, this will obsolete all our nice homebrew stuff.
The timeprovider is nice, but I'll stay with my custom interface for now.
At least we can note that they addressed the issue.
Please, make a video about why we should use DateTimeOffset
Much ado about about the obvious. Over 30 years ago I was writing business programs in Fortran. It was clear then that the date and time printed on business reports sometimes need be independent of the current computer clock. Viola write a layer to provide times. I'm sure there is some "gang of four" jargon for this approach but it is something any decent programmer should know intuitively.
It also took them 20 years to create a file-scoped namespaces. This tiny little code formatting feature should have been done like 10 years ago
Can I have written version of this video? I have a hard time with DateTime handling.
When it comes to DateTime, every language/Framework out there has it's own ups & downs. I usually depend on My won Custom Class/Interfaces/Services but it a bit confusing/Tidy/messy sometimes and When I opened this video I was honestly expecting an Interface(ITimeProvider) instead of an abstract class but hey it's a good progress still I guess and the team should be made aware of this.
This has never been a problem in any big consulting firm:
Just test it 3 times a day! 🤣
jokes aside: thanks for the update, great video as usual 🤗
The main bug with time - it's how the time was implemented in real world. I mean timezones and local times. All of us have common time and we need to use one time for all areas of the world, then million of time issues will be solved. Morning, Day, Evening, Night will have own time range for each area, but time should be equal in any point of the Earth.
Can we use TimeProvider as scoped service instead of singleton?
Why is generate greeting not taking the date time as a parameter?
I don't use DateTimeOffset because I convert all dates to UTC in my systems before storage and all processing and computation is done in UTC. Conversion to local time is only done during presentation in the UI. DateTimeOffset is only needed when you want to store time with an offset that is not zero. UTC's offset is zero.
So you don’t care about the user’s offset from utc? For example when you wanna email all users during their morning hours to improve email opening rates, how do you know what is "morning" for them when there is no UI or client? Storing everything is UTC out of context is naive
@@nickchapsas I store the user's actual desired timezone in a database field that they can always change later. That's what I use to accept datetime from them and convert to UTC using TimeZoneInfo.ConvertTimeToUtc()
before saving. Also when displaying, or doing something like the email you talked about, I use their stored timezone to convert from UTC to their desired timezone.
One major advantage of converting to UTC before saving in database is that you are never affected by daylight savings changes. Also computation or filtering data based on time is easier because they are all stored in UTC.
Please make a video explaining why you think they made an abstract class, and why they should be using interfaces for that!
So, it looks like this would not stop someone on your project calling DateTime.Now. Are you aware of any analyzer, or similar, which could make sure everyone uses the new abstraction? I was thinking the other day about making an analyzer which can do that. Though its only the DateTime.Now and things like the methods on File which seem like clear targets for something like that.
Would it be better to inject a delegate to this method which could provide a datetime?
Why is the stopwatch functionality put into the timeprovider?
When would you mock the stopwatch?
If you’re using it to measure something and that measurement is part of something like a log parameter and you need to verify it
For e.g a REST API... can time provider be the client time (zone)?
Depends on how you use it but yeah you can set a LocalTimeZone as long as you have the user’s offset from UTC
I agree that it should be an interface. If the system doesn't provide it, for my own newly development projects, I would use a custom time provider interface.
They should make *ITimeProvider* a d have *TimeProvider* implement it. That way it would be easy to make a mocked implementation of the interface. This will be moot when roles and extensions/shapes get added though, since then you can just define the shape/interface you need after the fact.
I *really* hope it's coming un 8.0, but I'm not holding my breath..
TBH this should be a standard thing to provide interface for a class. The hell, make it part of the language, like if I have class Foo, then I have e.g. Foo.interface. I can't even tell how much time I *wasted* (yes, it is a waste) to write those wrapper classes just to be able to abstract from framework or some referenced library logic.
Hello TimeProvider is not available in visual studio 17.6 preview 2, What can I do to bring in my project?
Download preview 4
@@nickchapsasI found Preview 1 and Preview 2 for Visual Studio 17.6, but I'm looking for a download link for Preview 4. Could you please provide a link or direct me to a trusted source where I can download it? Thank you.
@@nickchapsas I was able to find Preview 1,2 and Preview 3 for Visual Studio 17.6, but I'm looking for a download link for Preview 4. Could you please provide a link or direct me to a trusted source where I can download it? Thank you
I'm a total und utter noob in C# (and programming) and just starting to learn, but wouldn't the "dateTimeNow" object be 'tightly coupled' with GenerateGreetText, something one should avoid, and I should inject a DateTime object with the time of the greeting I want? So I can mock it in the test? Like I said, I just learn C# via CZcams and I have no idea what I'm doing.
Your instinct is correct. Logic that works on time should be separated from code that gets the time.
In a more complex scenario, however, that does multiple steps with delays and timeouts, having a mockable time interface that speeds up time is helpful.
Yes it should be an interface with default implementations on the interface.
I kind of hate the clock abstraction they made. So bad I'm tempted to keep using my own or NodaTime.
make a video on datetime and datetimeoffset pls
Did you ask this question to chatgpt!! As far I remember you did!! And your reaction was out of the world!
Yeap, that was part of the test that ChatGPT solved!
ITimeProvider interface. Cool. Finally a standard one for basic uses. But I'm absolutely shocked that they introduced TimeProvider class (ok, let's have a handy default impl) with STATIC getters wired to system time AGAIN. Holy cow, what for? Those who currently use hard-wired system time, once they hear "the new TimeProvider is better, please do use it instead", they're going to use the static getters as they are accustomed to, gaining literally nothing. It's a goddamn FifthElement's BigRedDontPressMeButton. You just don't add old broken features under bunch of new names if you want to fix your library and want your users to learn to do better. D'oh!
Pretty shocked they went an abstract class route. Hopefully they do go w/ an interface approach
Please tell me about DateTimeOffset
But yeah I’ve convinced every company I’ve worked in the past 10 years to used an IDateTimeProvider instead of DateTime static directly. Easy sell, there’s no other way to reliably unit test time dependent logic
Finally, hopefully over time this will improve now its finally here.
If you decide to do a DateTime video please consider looking at something like NodaTime in that same video. Would be interesting to see if nodatime is really required.
Not gonna lie. I've never seen a need to unit test time of day.
Very interesting solution to a problem I never really worried about. :D
let them fix/simplify that working with client(saved peer user/taken from request) timezone, instead relying on assumption that client cares if it's afternoon on server or not..
???
Ugh. TBH this doesn't really change much for me, since I have a design rule to only ever mock interfaces and never concrete or abstract types. I know that sounds a bit dogmatic, but I'd rather have a slight bit of mess in a trivial wrapper than risk having weird test code.
It's little confusing that they added the stopwatch features to it. Why not using them directly over the Stopwatch class?
That was my thought too, seems like unnecessary duplication of the framework surface area.
I've been coding dotnet professionally since 1.1. In every one of my projects I've added an ITime with a default implementation returning utcnow. I've never understood why Microsoft refuse to provide this common mockable interface. It's simple...
Good grief I'll stick with my own IDateTimeProvider that takes 1 line to mock for the default common utcnow use case
Yayy new nick upload!
Definitely should be an interface. Who knows, maybe they change it in the future
I don't understand why one couldn't just create a DateTime object instead of using system time and pass it to the test cases.
Seems like interfaces are default and the most "expected" way to abstract things, espesially for external consumers.
So I really dont uderstand why they "expose" abstract class, with this silly ticks parameter)
so why utcnow instead of now?
hmm probably you mean why DTOffset?
Why could this all of not been done in an interface?
I think I'll still use Nodatime
Trivial problem, and trivial unit test....!!!
@Nick, do you have plans just in case the AI takeover happens within the next couple of years? Like do you have an alternative career path or a lucrative non-tech side project in case this happens in the short term?
I’m finally gonna start my dream beekeeping business
@@nickchapsasStart now to corner the AI bee market!
You could use the swarm intelligence of the bees as your AI 😄
@@nickchapsas Cool! Maybe you have another CZcams channel about it?
In complex scenarios the reason why Dapper is faster is because you are using SQL, a native language for your database engine - the example in video is where compiling a query takes more time than fetching data, in contrary, for complex queries the opposite happens: compilation of the query is fast, but the query itself is complex, so database engine takes time to process it, then other factors comes to place, mostly that you are using LINQ, so some things might be "lost in translation" when EF is compiling to SQL.
I made this error only once, I spend enormous time to optimise LINQ for complicated query with some minor success and then I just downloaded Dapper, wrote it in SQL and it just worked few times faster (BTW, we were still using EF everywhere else, it's too convenient, Dapper was only for report-like complicated queries). My mistake was that I was using wrong tool to solve the problem - SQL is a language that was specifically designed to deal with querying data, there is no reason to make your life difficult for purity sake.
I was so hoping you would say Dotnet was removing null. :)
I wish!
It looks to me like test induced design that offers no benefit for production code. Maybe someone can reply with an example of how this helps a real world scenario to change my mind.
Finally I can remove all my IDateTimeProvider interface usage and substitute it with .NET native interf... oh, wait... an abstract class?
I think I'll stick with Noda and IClock.
Comment to remind you DateTimeOffSet Video. Cheers.
And solution looks like addon of stopwarch...still raw solution, so we have to expect different implementaion after sometime ...looks like
Sorry, someone is going to have to explain to me the difference between this video and just, in the tests, putting DateTime(2023, 01, 01, 8, 0, 0). I can’t see the difference because the video is not explaining in detail where it’s getting it’s time from.
Im just wondering why u use nsubstitute instead of moq. Thanks for this great video :)
I always dislike the fact there are no interfaces. I mean its 2022 decoupling is mandatory i never inject an intstance always an interface and the exception to the rule is third party instances only when i can mock them easily in unit test. If not they will get abstracted away behind a concrete implementation of own with an exclude from codecoverage attribute. Very bad but at least i control my own interaction with the 3rd party code.
sometime interfaces are useless - "abstraction hell" and errors in runtime, in most cases you have only one implementation and interface is only used for testing but it can be solved differently
@@adrian_franczak i get your point but it doesnt mean interfaces are a way to abstraction hell and not are useless when you only have one concrete class implementing the interface. If you want to swap out the old implementation for the new one you get rekt with your hard dependencies and your "ugly" way of testing your own implementation interacting with the 3rd party library
they shouldve named it timeservice
Lol you just answered your own question for why it’s an abstract class and not an interface, the high frequency stuff
Well done, Microsoft.
This is the perfect example of something to never unit test at all. Very simple code that doesn't need testing. If you even did, you could have just passed in date times with specific times to test and never used a time provider.
My IClock implementation is much better. Based on an interface, default service calls DateTime.* and if I want, I can TimeTravel in my unit tests in the TestableClock instance. Microsoft did it again, wrong.
What's wrong with having DateTime as parameter and using DateTime new() or .Parse in test to run it? There you get consistent results. Omg what is this video even about...
Oh, inject this, inject that... In a blink of an your constructor becomes total mess
You can always just resolve it using the IServiceProvider when you need it.
Lmao
Use a source generator if this is a great problem for you, or better, split your code better and not create so powerful classes
If your constructor is taking a lot of dependencies, your class is probably doing too much. If your constructor is a mess, that also suggests that the constructor itself is doing too much. Ideally, a constructor should just get the object in a valid state so it can start taking method calls; often that just means assigning the dependencies to fields.
ahh I don't like it, IDateTime interface would be much nicer
The more everything become more abstract/interfaces and dependency-injected, the more over-complicated and over-engineered the code seem to me.