At the time of this post, the DocSpring blog was powered by Hugo, which is an awesome static site generator.
Update from April 2020: We migrated our blog to Ghost! Hugo is great, but we wanted to have a proper CMS where we could edit and publish blog posts.
Hugo is written with Go, so it’s incredibly fast. Generating the static pages for our blog only takes 139 ms.
When you create a new blog post, the convention is to use <slug>.md:

This means that your posts will be sorted by alphabetical order in your text editor (I use VS Code). I wanted to sort my posts by date so that it would be easier to find recent posts.
To do this, I’ve written a new_post script that adds a timestamp to the beginning of the filename. (It also opens the URL in my browser, opens the file in my text editor, and starts the hugo server if it’s not already running.)
#!/bin/bash
set -e
cd blog
POST_SLUG="$1"
if [ -z "$POST_SLUG" ]; then
read -p "Post Name (e.g. your-new-post): " POST_SLUG
fi
TIMESTAMP=`date +%Y%m%d%H%M%S`
POST_FILENAME="${TIMESTAMP}-${POST_SLUG}.md"
hugo new "posts/${POST_FILENAME}"
(
sleep 0.2 &&
echo "Opening localhost:1313/blog/posts/${POST_SLUG}/" &&
open "http://localhost:1313/blog/posts/${POST_SLUG}/"
) &
(
sleep 0.5 &&
echo "Opening blog/content/posts/${POST_FILENAME}" &&
open "content/posts/${POST_FILENAME}" &
) &
pgrep -x hugo > /dev/null && echo "Hugo is already running!" || hugo server -D
sleep 1This helper uses macOS’s open command to launch the browser and editor. Replace those calls with the appropriate commands for another operating system.
Running ./new_post test-post will generate a file at: ./blog/content/posts/20180924162015-test-post.md.
This gets us most of the way, but the default post title will be “20180924162015 Test Post”, and the URL will be: /blog/posts/20180924162015-test-post. I just want the timestamp in the filename, and I don’t want to include it in the title or the URL.
I updated my posts archetype (a.k.a. template) to strip the timestamp using the replace regex function (replaceRE.) Here’s my new blog/archetypes/posts.md:
---
title: "{{ .TranslationBaseName | replaceRE "^[0-9]{14}-" "" | replaceRE "-" " " | title }}"
slug: {{ .TranslationBaseName | replaceRE "^[0-9]{14}-" "" }}
date: {{ .Date }}
draft: false
---Hugo will generate a new post with the following contents:
---
title: "Test Post"
slug: test-post
date: 2018-09-24T16:20:15+07:00
draft: false
---
Setting the slug means that the post URL will be: /blog/posts/test-post
I also used a script to rename all of my existing posts and set the original slug:
require 'yaml'
require 'date'
require 'time'
require 'json'
# Run from the directory containing the posts. Review the resulting git diff.
Dir.glob('*.md').sort.each do |path|
next if File.basename(path).match?(/\A\d{14}-/)
content = File.read(path)
match = /\A---\r?\n(.*?)\r?\n---(?:\r?\n|\z)/m.match(content)
raise "Missing YAML front matter: #{path}" unless match
data = YAML.safe_load(match[1], permitted_classes: [Time, Date, DateTime])
date = data.fetch('date')
date = Time.iso8601(date) if date.is_a?(String)
target = "#{date.strftime('%Y%m%d%H%M%S')}-#{path}"
raise "File already exists: #{target}" if File.exist?(target)
unless data.key?('slug')
# JSON strings are also valid YAML strings, including quotes and spaces.
slug = JSON.generate(File.basename(path, '.md'))
content = content.sub(/\A---\r?\n/) { |opening| "#{opening}slug: #{slug}\n" }
File.write(path, content)
end
File.rename(path, target)
end
Now my post filenames have timestamps, so they’re sorted by date and I can find the most recent post at the bottom:

Another little advantage of setting the slug is that it’s easier to copy/paste it. This helps when I’m creating links in different posts.
This post was written for Hugo v0.48 in 2018. Check the current replaceRE documentation when adapting the archetype.