DEV Community

loading...
Cover image for Don't Use The Date Object
Thinkster

Don't Use The Date Object

Joe Eames
Mormon, Christian, Father, Educator, CEO of Thinkster.io, Organizer of @ngconf, @frameworksummit & React Conf. Front end developer, and Software Craftsmanship Evangelist.
・3 min read

JavaScript has a nice Date Object. It can do all kinds of cool things like tell you the current date and time, etc. Lots of languages have a similar facility.

And you shouldn't use it.

By that I mean, you shouldn't use it directly.

Let me clarify a little bit. I don't mean don't use date objects, those are a fundamental data type in JavaScript (or your preferred language). But what I mean is that you shouldn't directly use the functionality that tells you the current date and time, or some calculation based on current date/time, such as getting tomorrow's date. Why?

Because the date and time is an interaction with an exterior system. Because of how languages incorporate them into basic functionality, we have a hard time seeing that, but you should view it the same way you view the HTTP call that charges a credit card. Would you access the XMLHTTPRequest object directly from your code and hardcode in the URL to charge a credit card? No. You abstract that into its own functionality.

There are two reasons we do this. The first is that the code is complex and we want to extract it to a simpler interface. That really doesn't apply with getting a date & time. But the second reason does apply. And that reason is that extracting the functionality to somewhere else allows us to test and build our system rapidly by creating fake situations.

Let's examine a simple application that sends you an email on Wednesday evening at 8 pm to remind you to take the garbage out. How can you test this functionality to assert that it's working? Wait a whole week until Wednesday evening at 8 pm? Definitely not. Would you modify the system date/time on your development machine? Please for the love of all that is good and holy and bug-free don't do that. Maybe you can make the trigger date/time configurable, and when testing, keep setting it to 1 minute from now. Even that isn't the best idea. To test you'll have to keep changing settings in your development data store.

Instead, imagine you coded your system something like this (I'll use a bit of pseudocode):

image

Ignore my abstracted pseudocode to make this more readable (JavaScript dates are terribly unreadable). The key here is the "myDateClass" object. I wouldn't actually name it that but you get the point. This isn't the Date() function. This is something else. This class wraps the Date() object/function. The same way that you'd wrap the native XHR object, or any other functionality that accesses an external system. That way you can easily test your system by providing an alternative implementation that makes "now" exactly what you want it to be.

There are other ways to make this work. You can treat the IsTimeToSendEmail as the abstraction that you fake. You don't need to wrap all the functionality of your built-in Date object. But whatever you do, use an abstraction.

This is the proper way to use the current Date/Time functionality in a system.

Most developers don't automatically see the current date/time as an external system, but it is. So abstract accessing it away in its own function/class/service/whatever makes your code easier to write, read, and maintain.

Happy Coding!

Signup for my newsletter here.

Visit Us: thinkster.io | Facebook: @gothinkster | Twitter: @gothinkster

Discussion (10)

Collapse
webbureaucrat profile image
webbureaucrat

I'm not crazy about this solution. You wrote a whole wrapper class and a fake for that wrapper class, all of which you have to maintain, when you could just

// pseudocode
function isTimeToSendEmail(now: Date): bool
{
    return now.ToFormattedString() === "Wednesday 8:00";
}
Enter fullscreen mode Exit fullscreen mode

This solution takes on less technical debt and is also functionally pure and referentially transparent. You know that every time the function is called for a given date object, it will always return the same result, which helps A LOT with debugging.

(Not that there aren't any valid reasons for writing a date wrapper--I've written them before for different reasons.)

Collapse
josepheames profile image
Joe Eames Author

Very good. Great alternative solution. Ultimately my solution and your solution are very similar. Two ways to accomplish the same goal.

On a different point, "this solution takes on technical debt". I see no evidence of either solution containing any "technical debt". Cutting a corner is technical debt. Debt is something that usually needs to be paid back. These techniques (like what you showed here) are about improving the quality of code, which if it has any effect on technical debt, is a reduction.

Collapse
webbureaucrat profile image
webbureaucrat

Good point. I was really unclear in my earlier comment. Perhaps "technical debt" isn't exactly the right phrase. When I said "technical debt", I was referring to the additional maintenance cost of the wrapper and the fake(s) and the cost of the additional developer time it takes to understand the codebase fully. Maybe it's not exactly "technical debt" like a hacky workaround that you know you'll have to eventually "pay back" by fully undoing. More like a technical utility bill or a technical property tax--a long term cost that is a lot smaller than a loan but that you don't ever really get rid of.

Collapse
sebbdk profile image
Sebastian Vargr

What is wrong with creating a date object with a specific date/time for your unit test?

Yes, let's wrap this API in an API that our API can use.

Alt text of image

Collapse
sebbdk profile image
Sebastian Vargr

Also, I do not use XMLHTTPRequest because no sane person would want to bootstrap all that every time they needed to do a request, it's a crap interface. :)

hence why we used to go out of our way to abstract it away.

Sorry, if I seem aggressive, maybe I need to get some sleep.
But this just seems so wrong to me in JS context. :)

Collapse
igoradamenko profile image
Igor Adamenko

Huh. I partially agree on the topic, I guess. Partially, because I believe that it's alright to use semantic names for the functions. It's kinda “clean code” and Uncle Bob must be happy about this way.

But I mostly agree w/ this topic because Date object is broken. It's easy to understand the point when you see how many people use libraries such as Moment, Luxon, Date FNS, etc. Most of the time you're not satisfied of the built in functional of Date object.

Actually, in my team we've even built our own tiny wrapper for Date API:

GitHub logo funbox / chronos

One library to rule the time

Chronos avatar: white-on-black gloomy antique half-human half-clock face

Chronos is a tiny, immutable, typed date manipulation library which does not bloat your JS bundle,
but does everything you need

Features

Chronos picture: gloomy antique half-human half-clock carved in stone face

  • Immutable & pure function per file. Every function does not have side effects, nor mutates the params If you use a function, only this function is added to your bundle, not the whole lib.
  • ESM & CommonJS. Works in Node.js and browser. Setup the target browsers for transpiling ES6 by your own in your bundler.
  • TypeScript. Every function is typed and the typings are bundled with the package.
  • Native API. Uses Date and Intl under the hood.
  • Russian locale only. And it has docs in Russian.

Rationale

When we started to develop our projects, we picked the most popular date library that existed back then We had used only two methods it provided, but got all the bundled ones, with all the possible locales.

OK, we set…

Collapse
josepheames profile image
Joe Eames Author

But is it testable?

Collapse
igoradamenko profile image
Igor Adamenko

What do you mean? The repo has tests, if this what you're talking about.

Also there's no problem with E2E or Unit tests in our projects.

Thread Thread
josepheames profile image
Joe Eames Author

sorry, it sounded aggressive. Didn't mean it to be.

I meant exactly that. do unit tests work well?

And very cool job. Well done with putting that together.

Collapse
alemagio profile image
Alessandro Magionami

Definitely agree on that.
I made something similar in the past. I used the JS Date object as a dependency, wrapped in a service which was the only point able to handle directly the Date object.

Also consider that there are a lot of libraries to handle JS and this approach lets you change to one of those pretty easily if you want to.

Forem Open with the Forem app