Obsidian file creation date debacle and a solution

Obsidian is pretty reckless with file creation dates. If you modify a note in Obsidian, it updates the file creation date. This renders Dataview queries that rely on it useless. For an introduction to this issue, see this lengthy thread on the Obsidian forums.

Workarounds

There are a several solutions to this problem.

1. YAML-based dates

One can include a cdate (or similar) field in the note’s front matter and just direct the Dataview query against that, e.g. LIST FROM "" WHERE startswith(cdate,"2023-05-29") SORT file.ctime asc. This works, but of course it requires you to always place that field ahead of your note content. Some people like that; others not so much.

2. Timestamp in the note title

This is what a lot of Zettelkasten folks do. But, queries against the title are going to need a lot of fancy string manipulation to make that work.

Fixing the file system date

This leaves open the question of what to do about the file system dates that Obsidian keeps changing.

1. If you include the creation date in the front matter…

All of my notes have a creation date cdate filed in the YAML, so I can use that to update the file system creation date. What follows is a macOS-specific solution (sorry, Linux and Windows users.) If you’re not on macOS, you can at least see the approach that I take and use that as a basis for a more suitable solution on your platform. It does require SetFile which is a tool included in the macOS Developer command line tools. You’ll have to install those first.

#!/bin/bash

OBSDIR="/path/to/your/obsidian/vault"
find "$OBSDIR" -type f -newerBt "$(date -v-15m)" |
while read -r line ; do
   # get the actual date created from cdate in the file
   ACTUAL_DATE=$(cat "$line" | grep -E 'cdate:[[:space:]]+[0-9]{4}')
   if [[ "${#ACTUAL_DATE}" -gt 0 ]]; then
      ACTUAL_DATE=$(echo "$ACTUAL_DATE" | 
         sed -E 's/cdate:[[:space:]]+([0-9]{4})-([0-9]{2})-([0-9]{2})/\2\/\3\/\1/g')
      
      # get the file system creation date
      FS_DATE=$(getfileinfo -d "$line" | 
         sed -E 's/(.*[[:space:]]+[[:digit:]]+:[[:digit:]]+).*/\1/g')
      
      # check for mismatched dates
      if [ ! "$FS_DATE" = "$ACTUAL_DATE" ]; then
         # mismatched dates, need to fix filesystem
         setfile -d "$ACTUAL_DATE" "$line"
         
         # inform user
         FN=$(basename -- "$line")
         printf "Fixed creation date for %s\n" "$FN"
      fi
   fi
done

If you set this up to run as a global daemon every 5 minutes, it will continue fixing files “created” into last few minutes.

2. If you encode the creation date in the title…

If you name your notes with a timestamp like the Zettelkasten folks do (like, 20230530211642 This is my note title) then you can go through the same process as above, but parse the date a little differently.

#!/bin/bash

OBSDIR="/path/to/your/obsidian/vault"
FILES=$(find "$OBSDIR" -type f -newerBt "$(date -v-15M)" | grep -E '[0-9]{14}')

if [ "${#FILES[@]}" -gt 1 ]; then
   echo "Obsidian has ${#FILES[@]} ZK files with new creation dates."
fi

IFS=$'\n'
for FPATH in $FILES; do
   FN=$(basename -- "$FPATH")
   echo "$FN"
   ACTUAL=$(echo "$FN" |
      sed -E 's/([0-9]{4})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2}).*/\2\/\3\/\1 \4:\5/g')
   setfile -d "$ACTUAL" "$FPATH"
done

Again, you can set this up as a global daemon to run periodically and it will reset the creation date, similar to the previous code.