DEV Community

loading...
Cover image for Make the global Date object iterable

Make the global Date object iterable

aminnairi profile image Amin ・3 min read

DISCLAIMER: this article is a demonstration, not a you-should-do-it. JavaScript is a versatile and fun language. Also don't put babies in the microwave.

Let's say you want a list of all dates, between two dates (like one week later and today). Separated by one day each.

// 2020-04-19T00:00:00.000Z
// 2020-04-18T00:00:00.000Z
// 2020-04-17T00:00:00.000Z
// 2020-04-16T00:00:00.000Z
// 2020-04-15T00:00:00.000Z
// 2020-04-14T00:00:00.000Z
// 2020-04-13T00:00:00.000Z

We probably will end up with this code.

const date  = new Date("2020-04-19"); // one week later
const today = new Date();
const dates = [];

while (date > today) {
    dates.push(date);

    date.setDate(date.getDate() - 1);
}

dates.forEach(date => console.log(date));

It's worth noting that I'm using a hard coded date. If you test this code in a near future, chances are it won't work because you read this article too late... Shame on you!

Eventually, we would like to have something reusable to repeat this operation. Like a function.

function untilToday(date) {
    const TODAY         = new Date();
    const currentDate   = new Date(date);
    const dates         = [];

    while (currentDate > TODAY) {
        dates.push(new Date(currentDate));

        currentDate.setDate(currentDate.getDate() - 1);
    }

    return dates;
}

untilToday(new Date("2020-04-19")).forEach(date => console.log(date));

This function will behave exactly like we did earlier, except this time it won't mutate the date we provide (it will prevent some unexpected side-effects).

But we want to have some fun! Isn't there an alternative syntax we could use to do that? Like for instance this syntax?

[...new Date("2020-04-19")].forEach(date => console.log(date));

Well, this is possible, using the iterator symbol and a generator function.

Date.prototype[Symbol.iterator] = function* () {
    const TODAY = new Date();
    const date = new Date(this);

    while (date > TODAY) {
        yield new Date(date);

        date.setDate(date.getDate() - 1);
    }
};

That's it! In fact, we didn't had to change our function too much. Except now it is part of the global Date object, and uses the yield keyword to return each dates in an iterator object.

We can now use this new syntax and have some fun back with Dates! Feels like we added a new feature to the JavaScript engine, doesn't it?

This is what the iterator symbol allows us to do: it enables any object to be iterable. Like the global Date object for instance in this article. But you could do it with your own objects or classes.

By using a generator function, we also enable our new syntax to be used in functions that can receive an iterator as its parameter. Like for instance, console.log or Array.from.

And I showed you one way of dealing with this operator, but you can implement it the way YOU want. If you want this operator to always output the seven days after the iterated date, be my guest! It's up to you.

That will be the challenge for today: implement this operator on the global Date object so that it will output the seven next days from the iterated one.

console.log([...new Date("2020-04-19")]);
// The next seven days...

Show me what you came up with!

woman hitting her clock saying time is ticking

Discussion (2)

pic
Editor guide
Collapse
waju profile image
Abolarin Olanrewaju Olabode • Edited

Nice.
How about creating your own class that extends Date and then adding the Symbol.iterator to it. That way you don't change the global Date object.
If ECMA adds some kind of interation to Date in the future it won't affect your code and it makes your code more explicit.
new CustomDate()

Nice job once again

Collapse
aminnairi profile image
Amin Author

You are right. As said in the article, you can also make any classes or objects iterable. This also includes your custom date object.