Time Travel in Vanilla Javascript

Gabi Cepeda-Procell
headwayio
Published in
4 min readAug 27, 2018

In one of our internal projects for learning tools and techniques, the team was using Jest and Enzyme to test drive the creation of a React app. We wanted to provide an interface to move forward or backward by one day and decided to tackle it using Vanilla JavaScript. However, there is a lot of pomp and circumstance that comes along with changing a date by one day in JS!

The app displays tasks for the current day by default, and when a task is added the task’s date is set to new Date() which returns a date/time object like so: 2017–11–16T01:27:29.427Z. This was definitely the simplest part of the date-handling.

Finding tomorrow and yesterday, the painful way.

Finding the next and previous dates required a bit of math. In order to add time to the date, one must first convert the date object into milliseconds (Essentially, the Date instance returns a value that is equal to the number of milliseconds since January 1, 1970 UTC). The getTime() method will return the value of a given date in milliseconds:

1 2 3 4 5const date = new Date(); => 2017-11-16T01:27:29.427Z // Our date-time object const dateInMS = date.getTime(); => 10795649427 // A lot of milliseconds'

Next, we do some math to find how many milliseconds there are in a day.

  • There are 86,400 seconds in a day.
  • 1 millisecond = 0.0001 seconds
  • ms/day = 86,400 sec * 0.001 sec

Therefore, calculating yesterday or tomorrow would involve adding or subtracting the total milliseconds in a day to or from the number of milliseconds from a given date. For example:

1 2 3 4 5const tomorrow = date.getTime() + (86400 * 1000); => 1510882049427 ms const yesterday = date.getTime() - (86400 * 1000); => 1510709249427 ms

After getting the time of the date we are looking for, we then need to create a new instance of Date and use the setTime() method to set our Date object to the time that we calculated in milliseconds since January 1, 1970.

1 2 3 4 5const yesterday = new Date(date.setTime(date.getTime() - (86400 * 1000))) => 2017-11-15T01:27:29.427Z const tomorrow = new Date(date.setTime(date.getTime() + (86400 * 1000))) => 2017-11-17T01:27:29.427Z

After finding today’s, tomorrow’s, and yesterday’s dates, we also needed to be able to display the date and any tasks that were created on that date. For display purposes, dates were converted to strings.

1 2 3 4const tasks = this.state.tasks.filter(item => item.date.toDateString() === date.toDateString(); ); => Returns item with date formatted as such : Tue Nov 14 2017

More issues arose with testing that our dates and tasks would return as expected when a user clicked the Tomorrow or Yesterday buttons. A roadblock when testing this appeared in the form of a type error that calling the toDateString() method was not a function. What wasn’t it a function of? That turned out to be an empty array. If our task list was free of tasks, our filter method wanted nothing to do with that. We would then have to account for this edge case in our code, along with multiple transformations of our data in order to perform a seemingly mundane task.

Less painful ways of handling time.

In comparison, many members of our team have handled date and time with Rails’ Active Support Extensions via the DateTime class. According to the Rails documentation, DateTime comes with all of the methods that pair with the Date class, except they will always return dateTimes. Some of these methods include:

  • yesterday
  • tomorrow
  • advance
  • weeks_ago
  • years_since

Those are just a few of the many helpful methods that perform the same functions that we are trying to do with our buttons in Vanilla JavaScript but without the headache of calculating dates and times.

While it is nice to maintain a slim figure with your application and not relying heavily on importing libraries and tools, there is something to be said for considering if the effort of a task (such as finding the value of the next or previous day) is worth more of your time in the end. In this case, we ended up using Moment.js as it was more useful and intuitive in the long run. Testing became easier when we didn’t have to transform our date object multiple times. Quite frankly, it also reads nicely in the end, and other devs who touch your code may even thank you for it later.

For more information, check out the following links:

MDN Web Docs on JavaScript’s Date Instance

Ruby on Rails Guide to Active Support Extension to Date

Moment.js, our Lord and Savior

--

--

Gabi Cepeda-Procell
headwayio

Developer at Echobind. Noob at quick bios. Loves: comfort food, table-top gaming, hugs, sloths, and horror movies.