Changing the file creation date on macOS

If you modify a file in-place using sed with the -i option, you will get a file that has a new file creation date. On macOS 13.3.1, this is absolutely 100% true, although you will read claims otherwise. I ran into this problem while implementing a Hazel rule that updates YAML automatically in my Obsidian notes.

Background

I have use YAML frontmatter in my Obsidian notes. It looks like:

---
uid:     20221120152124
aliases: [20221120152124, AllAboutShell]
cdate:   2022-11-20 15:21
mdate:   2023-05-18 05:14
type:    zettel
---

My goal is to update the mdate field whenever the file changes. Hazel is the perfect tool for this, so I set about writing a rule that covers this case. The heart of the rule is a shell script action that writes the modification date:

FILE="$1"
# the date the file was modified
MODDATE=$(stat -f "%Sm" -t "%Y-%m-%d %H:%M" "$FILE")

sed -i '' -E "s/(modification date:).*/\1   $MODDATE/g" "$FILE"
sed -i '' -E "s/(mdate:).*/\1   $MODDATE/g" "$FILE"

It works well; but there’s one catch: sed allegedly is modifying the file in-place, but it does not, as evidenced by the fact that the file has a new creation date.

Why is the file creation date so important to maintain anyway?

If we’re working with notes in an Obsidian vault, this is what Dataview queries use to find notes created on a certain date. If I’m constantly re-creating notes with new creation dates, then I never have an accurate record of notes created on a particular date.

Solution

The solution, on macOS only, is to grab the file creation date, make our sed substitution, then re-apply the original file creation date. Fortunately in the Xcode command line developer tools (which you need to have installed) there are two utilities that can help here:

  • SetFile (also known as setfile)
  • GetFileInfo (getfileinfo)

GetFileInfo

Running GetFileInfo on an arbitrary file, we see:

➜  ~ GetFileInfo -d “archive.txt”
05/21/2023 07:09:21

SetFile

SetFile works in a similar fashion. We can update the creation date in this way:

➜  ~ SetFile -d "02/14/2023 06:00" “archive.txt”
➜  ~ GetFileInfo -d “archive.txt”
02/14/2023 06:00:00

Assembling a solution

Since we can now read and write the file creation date, we just need to bracket our YAML modification with GetFileInfo and SetFile calls. That way Obsidian will never see the change as a new file. The original shell script action in the Hazel rule now looks like:

# update the modified field, but in so doing, try to
# preserve the original creation date.

FILE="$1"
# the date the file was modified
MODDATE=$(stat -f "%Sm" -t "%Y-%m-%d %H:%M" "$FILE")

# get the file system creation date
CDATE=$(GetFileInfo -d "$FILE")

sed -i '' -E "s/(modification date:).*/\1   $MODDATE/g" "$FILE"
sed -i '' -E "s/(mdate:).*/\1   $MODDATE/g" "$FILE"

SetFile -d "$CDATE" "$FILE"

Now we can use sed inside a Hazel rule, modying the file whose content has changed without altering the file creation date. Obsidian then is happy; and I’m happy.

References

Flatten airports in X-Plane

Some airports in X-Plane have terrain issues that can be quite entertaining.

This Delta 737-800 got lost in the maze of cargo ramps at PANC and was trying to taxi back to the terminal when it encountered a steep icy taxiway. It required 65% N1 just to get up the slope.

Clearly a fix is required. It turns out to be quite simple. In the global airports file apt.dat, find the offending airport. In this case, it’s PANC where its entry looks like:

1    149 0 0 PANC Ted Stevens Anchorage Intl
1302 city Anchorage
1302 country USA United States
1302 datum_lat 61.174155556
1302 datum_lon -149.998188889
1302 faa_code ANC
1302 gui_label 3D
1302 iata_code ANC
1302 icao_code PANC
1302 region_code PA
1302 state Alaska
1302 transition_alt 18000
...

To flatten the airport terrain, add the line 1302 flatten 1 after the airport header, so that the block now looks like:

1    149 0 0 PANC Ted Stevens Anchorage Intl
1302 flatten 1
1302 city Anchorage
1302 country USA United States
1302 datum_lat 61.174155556
1302 datum_lon -149.998188889
1302 faa_code ANC
1302 gui_label 3D
1302 iata_code ANC
1302 icao_code PANC
1302 region_code PA
1302 state Alaska
1302 transition_alt 18000
...

But what about when X-Plane is updated; the global airport apt.dat file gets overwritten. My workaround is to apply the fix programmatically after update:

#!/bin/bash

APTDAT="/Users/$(whoami)/X-Plane 12/Global Scenery/\
Global Airports/Earth nav data/apt.dat"

perl -pi -e '$_ .= qq(1302 flatten 1\n) if /PANC\sTed.+/' "$APTDAT"

I suppose, I could create a custom airport directory for the airports that I flatten, but then I’d lose out on any future modificationsto those airports that Laminar Research publishes in its updates. I’ll have to think about that one.