Visually Validate Date Ranges

Two coincident things happened at work today. Both things coincidentally were solved by me using emacs to provide a visual depiction of date ranges.

First, as part of a larger project, I wrote a bit of code to break up a date range into multiple intervals. A key issue of debate is whether it would be better to split up the full date range into a bunch of smaller intervals with gaps in between or to have gapless intervals. To bring this to the stakeholders, I thought it would be a better idea to present a visual idea.

Second, the data engineering team asked me to check if my requested data was loaded in full. The data is partitioned by date ranges, so a quick query to check for the following was more than enough:

  • That all the days have the same number of rows in it.
  • That all the dates in the daterange were complete.

The former was easy - a quick SQL query and that yielded all true. The latter on the other hand, was tedious to manually check. A visual check on a calendar would be very much quicker job.

Having two coincident needs to visualize date ranges I decided to look into doing it.

Visualizing Date Ranges

I find visualizing date ranges on a calendar to be a most helpful thinking aid. Simply laying out the dates in a calendar and marking the dates allows me to quickly spot gaps and other things.

In Emacs

I use emacs as my operating system text editor. And emacs is really really full featured. One of the things that is available in emacs is M-x calendar. If you are a Org-mode user you may have encountered it. I don’t use it, but was vaguely aware of it because there are good calendar rendering algorithms in there that I had referenced once upon a time. M-x calendar renders a calendar in a buffer.

In The Terminal

If you don’t have emacs, you can also render a calendar in your terminal with the command cal -3, which will render three months’ worth of calendar. Adding a -H YYYY-MM-DD argument will highlight a particular day in the calendar.

Highlighting Ranges

The issue with cal is that it’s not able to highlight a range of dates. So I proceeded with Emacs. Afterall, this was for a quick and dirty visualization to check things.

M-x calendar is linked to a diary file (typically at ~/.emacs.d/diary). By default Emacs marks dates on the calendar when there are items on the diary file.

I don’t use the Emacs diary. So what I have is a good way to mark date ranges. Emacs will trawl through the diary file and if the first columns are a date, then it will mark the date on the calendar. Thus the solution is simple: write a list of dates into the diary file!

Here’s an example that allowed me to quickly see that a date range is skipped for the loading of data:

A screenshot of Emacs showing a calendar of three months - from July to August 2012. Some dates are highlighted in yellow while others are not.

After taking the screenshots, I then emptied the diary file and unmarked the calendar by M-x calendar-unmark.

Advanced Highlighting

The diary file is very useful. Each line is an entry into a date. Useful data could be placed on the same line. Here’s a quick snippet that I threw up on my scratch buffer to handle additional highlighting rules based on data (the visualization with advanced highlighting ended up not being used)

(defface my-clashing '((t :background "red")) "")

(defun parse-diary-file ()
  (with-temp-buffer
    (insert-file-contents "~/.emacs.d/diary")
    (let (parsed-data)
      (while (re-search-forward "\\([0-9]+\\)/\\([0-9]+\\)/\\([0-9]+\\) *\\([0-9]+\\)" nil t)
        (let ((mm (string-to-number (match-string 1)))
              (dd (string-to-number (match-string 2)))
              (yyyy (string-to-number (match-string 3)))
              (value (string-to-number (match-string 4))))
          (push (list (list mm dd yyyy) value) parsed-data)))
      (reverse parsed-data))))

(dolist (x (parse-diary-file))
  (let ((dt (car x))
	(v (car (nthcdr 1 x))))
    (cond ((> v 365399941) (calendar-mark-visible-date dt 'my-clashing))
	  (t (calendar-mark-visible-date dt 'diary-entry-marker)))))

Using this I could pass in a space separated into the diary file and it would automatically highlight the dates that do not have the correct row count.

This part of the dolist loop can be easily switched out (for example to mark interval ranges with different colours):

(cond ((> v 365399941) (calendar-mark-visible-date dt 'my-clashing))
	  (t (calendar-mark-visible-date dt 'diary-entry-marker)))

In this way it is quite trivial to mark up a calendar in any way I want. I could of course wrap that in a defun and pass in the body as a symbol but why do that when you have access to the entire lisp machine at any given time?

Some Interesting Things I Learned.

Along the way I learned some interesting things. Here they are:

American Date Formats Are Default

One of the more annoying things for me when I did this was that the default accepted date formats of the Emacs calendar-and-diary system are American date formats (ugh Americentrism strikes again). So my first attempt highlighted the wrong dates. I ended up re-formatting all my dates as American dates (MM/DD/YYYY) when dumping it into the diary file.

Later as I was writing this section of this blog post I discovered M-x calendar-set-date-style which sets the date formatting. Of all places it popped up in a Corfu autocomplete suggestion. I have yet to test that to see what it would look like.

Go Date Formats

By accident I had written code that looks like dt.Format("01/02/2016") to get my code to print out American dates (seriously, I’ve only ever needed to use time.DateOnly as a date formatter in my 14 years of writing Go code). Note that it’s 2016, not 2006. The output was most definitely interesting - the year printed was 10116 instead. Not realizing the typo I made, I went to hunt for the bug and ended up learning a lot about how time format layouts are parsed. Frankly I think the parsing bit is quite cool.

Miscelleny

The impetus behind this blog post is captured nicely in the conversation in this screenshot.

A screenshot of my slack coversation with my colleague

It took me about 5 minutes to hack up the visualization, and that includes quickly perusing the documentation for the Emacs calendar-and-diary system. Though I must note that I did spend an additional 5-10 minutes hacking up the parse-diary-file function, after which I realize I do not have the stylistic ability to properly colour my calendar before my meeting so I ended up using the single-coloured one.

I guess the point of this post is two fold:

  1. One should be aware of the capabilities in one’s environment in order to leverage it.
  2. An environment like emacs - a live lisp-machine like thing, where the innards are malleable by the user, allows for flexibility and efficiency.

Of course if this weren’t a throwaway thing I’d put more effort into it, but even so, the environment itself may be leveraged. But I think that is a story for another day.

Personally for me, I wrote this post partly because I am actively trying silence my inner critic, which has been the root cause of why this blog has been so bereft of posts in the past few years.

emacs 
comments powered by Disqus