The other day, after running my blog on Jekyll for 13 years, I switched to Hugo. Moving from one static site generator to another involves a lot of things to take care of. Among them was this directory with hundreds of microblog posts written in Markdown. The posts published dates encoded as Unix timestamps in the filenames: 1665322487.md etc.

Micro.blog hosts my new Hugo blog, and their import tools do not support Unix timestamps in file names. Instead, publish dates should be put in the YAML front matter, as follows:

1
2
3
4
---
date: 2022-10-09 13:34
---
Had a great day in the forest with friends. Grilling sausages, roasting marshmallows, and taking a short hike. Hair smells like a campfire now. 🥰

So, I basically had two options. I could manually fix this by snatching the timestamp from the filename, converting it to the right date-time format, and pasting that into the file’s YAML front matter. Rinse and repeat. Hundreds of times.

Or I could just write a script that automated the process for me. As a programmer, this is what I usually do. Even when it won’t actually save me any time in the end. 😅

But, instead, I tried a third option and asked ChatGPT to do it for me. I’ve played a lot with the chatbot and had it generate code for me in the past. But just toy examples, not, you know, anything useful in the real world. This task, though, seemed like a perfect fit. Easy to define and limited in scope. So, without further ado, here’s me and ChatGPT programming away. Complete with lazy typing and spelling mistakes (mostly mine).

My pair programming session with ChatGPT (GPT-4)

Sven: Write a Ruby script that loops thorough all files in the directory provided and adds the filename to yaml frontmatter without extension.

For example the file named 1676537577.md should end up like this in the frontmatter:

-–
date: 1676537577
-–

ChatGPT: Here is a Ruby script that loops through all files in a specified directory and adds the filename without the extension as the date field in the YAML frontmatter.

ruby

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
require 'yaml'
require 'fileutils'

def process_file(file)
  frontmatter = { 'date' => File.basename(file, '.*') }
  content = File.read(file)
  content.sub!(/---\s*\n.*?---\s*\n/m, '') # Remove existing frontmatter if present
  File.open(file, 'w') do |f|
    f.write(YAML.dump(frontmatter) + "---\n" + content)
  end
end

dir = ARGV[0] || '.'

raise "Directory #{dir} not found" unless Dir.exist?(dir)

Dir.glob(File.join(dir, '*.md')) do |file|
  process_file(file)
  puts "Processed: #{file}"
end

puts 'All files have been processed.'

Save this script as add_date_to_frontmatter.rb and run it with the desired directory path as an argument:

bash

1
ruby add_date_to_frontmatter.rb /path/to/directory

If no argument is provided, the script will default to the current working directory. This script assumes that your files have the .md extension.

Sven: Great! Now make is so that the file name (without extension) is processed as a unix timestamp and converted to a iso-8601 formatted date and time instead.

ChatGPT: To modify the script to process the filename as a UNIX timestamp and convert it to an ISO-8601 formatted date and time, you can use Ruby’s built-in Time class.

Here’s the updated script:

ruby

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
require 'yaml'
require 'fileutils'

def process_file(file)
  filename_without_ext = File.basename(file, '.*')
  timestamp = filename_without_ext.to_i
  iso_date = Time.at(timestamp).utc.iso8601

  frontmatter = { 'date' => iso_date }
  content = File.read(file)
  content.sub!(/---\s*\n.*?---\s*\n/m, '') # Remove existing frontmatter if present
  File.open(file, 'w') do |f|
    f.write(YAML.dump(frontmatter) + "---\n" + content)
  end
end

dir = ARGV[0] || '.'

raise "Directory #{dir} not found" unless Dir.exist?(dir)

Dir.glob(File.join(dir, '*.md')) do |file|
  process_file(file)
  puts "Processed: #{file}"
end

puts 'All files have been processed.'

Save this script as add_iso_date_to_frontmatter.rb and run it with the desired directory path as an argument:

bash

1
ruby add_iso_date_to_frontmatter.rb /path/to/directory

If no argument is provided, the script will default to the current working directory. This script assumes that your files have the .md extension.

Sven: Okay, I changed my mind, instead of ISO-8601 make it on the format yyyy-mm-dd hh:mm. For example 2020-03-28 22:10.

ChatGPT: Sure, you can modify the script to format the date and time as “yyyy-mm-dd hh:mm” by using Ruby’s strftime method. Here’s the updated script:

ruby

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
require 'yaml'
require 'fileutils'

def process_file(file)
  filename_without_ext = File.basename(file, '.*')
  timestamp = filename_without_ext.to_i
  formatted_date = Time.at(timestamp).utc.strftime('%Y-%m-%d %H:%M')

  frontmatter = { 'date' => formatted_date }
  content = File.read(file)
  content.sub!(/---\s*\n.*?---\s*\n/m, '') # Remove existing frontmatter if present
  File.open(file, 'w') do |f|
    f.write(YAML.dump(frontmatter) + "---\n" + content)
  end
end

dir = ARGV[0] || '.'

raise "Directory #{dir} not found" unless Dir.exist?(dir)

Dir.glob(File.join(dir, '*.md')) do |file|
  process_file(file)
  puts "Processed: #{file}"
end

puts 'All files have been processed.'

Save this script as add_formatted_date_to_frontmatter.rb and run it with the desired directory path as an argument:

bash

1
ruby add_formatted_date_to_frontmatter.rb /path/to/directory

If no argument is provided, the script will default to the current working directory. This scrit assumes that your files have the .md extension.

Closing thoughts

The final output from ChatGPT just worked and successfully completed the task at hand. That’s pretty cool! Is the implementation perfect? No, but it gets the job done. For a one-off script, I can’t complain.

The resulting program is short, and I could’ve come up with a solution and typed it out myself. But it’s fascinating to program in plain English. And I doubt I would have come up with the solution as fast as ChatGPT. I didn’t time our session, but estimate it took ChatGPT about a minute to generate each answer. So, from my first prompt to a job well done, it took no more than five minutes.

By the way, the first draft of the program ran fine as well. In the second draft, ChatGPT didn’t require 'time' at the top, but after human intervention, that version also ran correctly.

So, March 22, 2023 was the first time I paired programmed with a large language model to successfully solve a real world problem. And I’m certain we’ll team up again in the future to solve even bigger and more complicated problems.