Using Timestamped Posts

The more blog posts I write the harder its getting to find the one I am looking for at a glance. Since the filenames are sorted in lexicographical order and I name files solely by topic with no consideration of their resulting order, adding a new filename to me is tantamount to tossing a needle in a haystack. I create the post and when I save the file I think: "Where did it go?". A post I created today and one I created five minutes ago might be miles apart. Conversely, a post created five years ago and one created five minutes ago might be right next to each other. The experience is disorienting and costly as I end up spending more time searching for posts. Of course, I can always use consult-line from consult but I do not want to do this every time. Ultimately, I want posts for my blog to be in the order that they where created. To that end, I decided to prefix a timestamp at the beginning of the filename of each post.

deciding on a timestamp format

I did not know how timestamps were normally written. Should there be some separator between certain time components? If so what is it and where should it go? I suspect the answer largely depends on use-case and preference.

In a question similar to my own the following timestamp format was generated using format-time-string.

            
(format-time-string "%Y-%m-%d--%H-%M-%S")
;; => "2024-04-24--14-31-16"
            
        

In contrast the timestamp used for org IDs which is much more conservative about separators. It only uses "T" to separate the year and a "." to separate anything smaller than a second.

            
(require 'org-id)
(org-id-new)
;; => "20240424T141308.453178"
            
        

Not wanting excessively long filenames, I was inclined to avoid using separators. It was when I actually tried using timestamps with no separators that I realized how difficult it is to distinguish time components without them. Compare with this. I think the year is easy to see but suppose I wanted the hour and minute instead. I for one would have to sit there a few seconds trying to figure it out.

            
(format-time-string "%Y%m%d%H%M%S%N")
;; => "20240504145659269408809"
            
        

It is apparent that the question of whether to have separators or not is a trade off between readability and brevity. Then again the question. After all, if I never need to read the timestamp myself and can manipulate them programmatically then.

Ultimately, I opted for readability by separating individual time components with either a "-" or "–". Although the minor seconds are not pretty I still leave them so I can be sure that filenames are different even if they are created in a short time span from each other.

            
(format-time-string "%Y-%m-%d--%H-%M-%S--%N")
;; =>; "2024-05-01--05-04-45--204039545"
            
        

automating the creation of new blog posts

Typing out the timestamp is too error prone and tedious. Instead, I wrote the following function that prompts me for a blog post title and then creates a blog post from the given name, opening the file in Emacs1 and inserting the title and author information.

            
(defun oo-blog-new-post (title)
  "Create a new blog post."
  (interactive "sBlog post title: ")
  (find-file (oo--blog-post-filename title))
  (insert (format "#+TITLE: %s\n" (capitalize title)))
  (insert (format "#+AUTHOR: Luis Henriquez-Perez\n" (capitalize title))))
            
        

generating the path of a blog post from a title

For testing it is often much easier to segregate side-effects (the creation of the file) from computations (the generation of the path). That is why even though the body of this function is small enough to fit in oo-blog-new-post without making it excessively long, I place it into its own function.

            
(defun oo--blog-post-filename (title)
  "Return a valid blog post name from TITLE.
TITLE is a string describing the topic of a post."
  (let ((timestamp (format-time-string "%Y-%m-%d--%H-%M-%S--%N"))
        (name (downcase (string-replace "\s" "-" title)))
        (post-dir "~/Documents/blog/org/posts/"))
    (expand-file-name (format "%s--%s.org" timestamp name) post-dir)))
            
        

This way I can test whether the correct filename is generated without worrying about whether inadvertently creating a file in the wrong directory in case of I get the code wrong.

            
(abbreviate-file-name (oo--blog-post-filename "writing a post while upside down"))
;; => "~/Documents/blog/org/posts/2024-05-02--12-11-11--127335202--writing-a-post-while-upside-down.org"

(abbreviate-file-name (oo--blog-post-filename "writing a post underwater"))
;; => "~/Documents/blog/org/posts/2024-05-02--12-11-23--036941798--writing-a-post-underwater.org"
            
        

I could generalize this function maybe for just generating a timestamp filename. I could definitely see myself wanting more such files in the future. But since I have no concrete need as of yet I will leave it as is.

conclusion

After adding timestamps I have achieved what I wanted. Now blog posts are displayed in a predictable order when viewed via ls or any file viewer.

timestaped-blog-posts.png

Figure 1: The dired buffer of the directory containing my blog posts.

Footnotes:

1

To be more precise, it creates a buffer that visits the post file using find-file.