Cloning Dates in Moment.js

Avoiding unexpected date mutations by cloning.

I’ve been using Moment.js quite a bit for a scripting project and I’ve been blown away by how everything I need is there, as well as how fuss-free the API is.

The only head-scratcher I’ve run into is the concept of cloning. Or rather, the concept of cloning was clear once I knew it existed in Moment.js; before that, I was tracking down some bizarre behavior with dates changing unexpectedly.

Mutability in Moment.js

Let’s say you want to track today’s date and the date, say, 30 days ago.

Here’s an example of what I did initially:

const now = moment().local();

const date30DaysAgo = now.subtract(30, "days");

So if now is July 31st, my expectation was that date30DaysAgo would be July 1st.

But it turns out that Moment.js objects are mutable, and calling methods like subtract on them changes the original object.

So, in this case, both of these variables end up with July 1st as their values. How do you get around this?

Cloning in Moment.js

Moment.js’s answer to this need is cloning. From the docs:

All moments are mutable. If you want a clone of a moment, you can do so implicitly or explicitly.

I’ll put my vote in that this should always be done explicitly. The explicit method makes it exceptionally clear what’s going on, whereas the implicit way is magic and you will forget (or not notice) what’s going on when you come back to your code after a break.

Here’s the previous buggy example re-written with the explicit clone method:

const now = moment().local();

const date30DaysAgo = now.clone().subtract(30, "days");

Done! Now I have both dates captured in variables as expected.

Luxon

While I was writing this, I went to grab some links from the Moment.js site, and I noticed a third option in the navbar: Luxon. That sounded mysterious so I clicked. Turns out it’s a modern take on Moment.js by one of the maintainers.

There’s a great document titled “Why does Luxon exist?” which is worth a read. From the doc:

Luxon started because I had a bunch of ideas on how to improve Moment but kept finding Moment wasn't a good codebase to explore them with.

And:

Luxon is built around a few core ideas:
- Keep the basic chainable date wrapper idea from Moment.
- Make all the types immutable.
- Make the API explicit; different methods do different things and have well-defined options.
- (continues...)

Sounds like a new take on the issue I described above. Compare Luxon’s approach to mutability to that of Moment.js here.

I’m not going to rush out to switch over to Luxon right this, uh, moment; Moment.js has been a pleasure to work with, quirks with mutability aside. But I hope to have a reason to try out Luxon some day.