Exploring the pains of date and time manipulation.
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:
const 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:
const tomorrow = date.getTime() + (86400 * 1000); => 1510882049427 ms const yesterday = date.getTime() - (86400 * 1000); => 1510709249427 ms
Converting milliseconds into dates
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.
const 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.
const 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:
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: