Visualizing daylight saving time
Earlier this week, we launched the day graph for Cushion’s time-tracking beta. Previously, Cushion only had a week graph, which would let you add time throughout the week or start a timer, but there wasn’t much of a visual for when the entry occurred. Now, with the day graph, people can focus on a single day with labels for each hour.
This new day graph is our first foray into visualizing time. We’ve been lucky so far—only dealing with dates, which don’t need to worry too much about timezones or other time-related traps. Now that we’ve taken the red pill, however, we get to reunite with my old friend—daylight saving time.
If your country, or state (hi Arizona!), doesn’t participate in daylight saving time, you have it so good—you have no idea. For New York, daylight saving time started earlier this month, so we all “lost” an hour—meaning on March 12th, we skipped 2am. If you were watching a digital clock, you’d see time “spring forward” from 1:59am to 3am. Since most of the clocks in our lives are digital, everything happens automatically, but back in my day we had to move the hour hand forward ourself. And sometimes we would forget and our world would be in complete disarray!
With the day graph, daylight saving time caused several issues. The first involved the hour labels on the graph. Instead of creating 25 labels (one for each hour, and an additional for tomorrow’s midnight), we needed to create only 24—for days where daylight saving time started, because we lose an hour.
To account for this, I made use of the date.getTimezoneOffset() method, which returns the difference in minutes between the local timezone and UTC (universal time). After selecting the date to visualize, Cushion will check the timezone offset for the beginning of the day and the end of the day. If they match, great—there are a full 24 hours in the day. If they don’t, we’ve either started or ended daylight saving time. For now, we’re only going to talk about starting daylight saving time.
In my logic for calculating the number of hour labels to draw, I simply needed to apply the difference between the timezone offsets—no conditions needed:
numHours = 24 + 1 + (endOfDayOffset - startOfDayOffset) / 60
This takes care of the number of labels, but what about the hours themselves. Since we’re skipping 2am, we’ll have to skip that label as well (i.e., the graph should show “12am 1am 3am 4am”). At first, I took the start date and simply added an hour for each label. This should have worked, but unfortunately, date-fns (the date/time library I used) had a bug.
date.getTime()), then create a new date using those milliseconds combined with the duration in milliseconds (
new Date(date.getTime() + duration)). After several hours of pulling my hair out, I discovered that the library I used was adding time by setting time. Instead of creating a new date with the milliseconds added, it was getting the current hour, then using
date.setHour to increase the time.
This wasn’t obvious to me at first, so the hour labels kept reading “12am 1am 3am 3am 4am”. This happened because when setting the time (instead of adding it), 3 hours from midnight uses
date.setHours(0 + 3), which results in 3am. Since we’re starting daylight saving time, however, when adding only 2 hours from midnight,
date.setHours(0 + 2) will also return 3am because we’re skipping 2am. When skipping 2am, 3 hours from midnight should return 4am. We could easily avoid this problem by adding milliseconds.
I submitted a Github issue and the fine folks maintaining date-fns fixed it same day.
Since daylight saving time is handled automatically now, a graph showing “12am 1am 3am” might catch people off guard and cause a bit of confusion. Because of this, I decided to add a marker for when daylight saving time starts. Using the same offset check between the start of the day and the end of the day, I’m able to detect when a day is experiencing a daylight saving time shift. Then, I simply need to narrow down which hour starts the shift and display the marker at that point. I even went an extra step and added a directional arrow indicating whether daylight saving time starts or ends.
For the small percentage of devs who made it through this entire post, I definitely plan to write the second half of the story where we deal with daylight saving time ending. Skipping an hour is easy, but how do we handle falling back? I’m seriously asking because we still need to figure that out. Until next time!