Geoff Ruddock

How to batch modify dates of daily journal files

Goal

I’ve been using Obsidian as the primary hub for personal notes for the past year. My daily notes act as a sort of captain’s log, and have superceded my use of a dedicated journaling app. So I exported all my journal entries to markdown, and added them to my Obsidian vault as daily notes.

In the process of migrating journal entries between apps over the years1, I must have messed up some metadata at some point, because I just realized today that all my entries before a certain point in time were wrong by one day. So I wrote a small script (below) to batch correct these files.

Setup

To help write and debug the script, I put a handful of dummy files in a staging directory, each following the YYYY-MM-DD.md naming convention.

We’ll use pathlib.Path(…).glob('*.md') to get a list of markdown files in this directory.

import datetime as dt
from pathlib import Path
p = Path('staging')
all_md_files = sorted(list(p.glob('*.md')))

# Print the contents of each file
for f in all_md_files:
    print('\n' + '=' * len(f.name))
    print(f.name)
    print('=' * len(f.name) + '\n')
    with open(f, 'r') as open_file:
        print(open_file.read())
=============
2013-12-31.md
=============

# 2013-12-31

This file should become 2014-01-01.

=============
2014-01-01.md
=============

# 2014-01-01

This file should become 2014-01-02.

=============
2014-01-03.md
=============

# 2014-01-03

This file should remain as 2014-01-03.

Note that these files contain the date in their names, but also as an h1 heading within the file itself, so we’ll need to change both.

The script

My original issue only affected files up to a certain date, so let’s filter the list of markdown files.

Because we are incrementing the dates of files, we’ll want to work through the list in reverse order. Before making yesterday today, we must make today tomorrow—else there will be a conflict.

selected_files = sorted([f for f in all_md_files if f.stem <= '2014-01-02'], reverse=True)
for f in selected_files:
    print(f.name)
2014-01-01.md
2013-12-31.md

The actual work to be done here is relatively simple:

  1. Convert string to datetime, increment by 1d, convert back to string.
  2. Replace references to the previous date string with the new one, inside file contents.
  3. Rename the file itself.
def replace_in_file(fp: str, old: str, new:str) -> None:
    """ Replace 'old' strings with 'new' strings in a given file (fp) """
    with open(fp, 'r') as open_file:
        old_contents = open_file.read()
        new_contents = old_contents.replace(old, new)
    
    with open(fp, 'w') as open_file:
        open_file.write(new_contents)


for f in selected_files:
    
    old_dt = dt.datetime.strptime(f.stem, '%Y-%m-%d')
    new_dt = (old_dt + dt.timedelta(days=1))
    new_ds = new_dt.strftime('%Y-%m-%d')
    
    replace_in_file(f, f.stem, new_ds)
    f.rename(f.parent / (new_ds + '.md'))

Check results

Visually inspecting the files in the staging directory, we can see the final result matches what we hoped to achieve.

for f in sorted(list(p.glob('*.md'))):
    print('\n' + '=' * len(f.name))
    print(f.name)
    print('=' * len(f.name) + '\n')
    with open(f, 'r') as open_file:
        print(open_file.read())
=============
2014-01-01.md
=============

# 2014-01-01

This file should become 2014-01-01.

=============
2014-01-02.md
=============

# 2014-01-02

This file should become 2014-01-02.

=============
2014-01-03.md
=============

# 2014-01-03

This file should remain as 2014-01-03.

If you’re running a script that modifies your files in-place like this, be sure to have recent, working backups before you start, in case something goes wrong!


  1. I started journaling with OhLife, before it was shut down in 2014, replaced it with Dabble.Me, then most recently ported everything over to Day One in 2019. ↩︎


comments powered by Disqus