TextExpander date snippets via bash

You have, presumably seen this post by David Sparks, in which he provides a bunch of TextExpander snippets for showing dates and times in different formats, some of which involve calendrical calculations. That’s a combination of topics I simply cannot resist commenting on.

First, if you download his snippet collection and double-click on the file, it’ll be imported into its own folder in TextExpander. If you already have some date snippets—and I suspect most of you do—you don’t have to worry that David’s will overwrite or get mixed in with the ones you already have.

MacSparky date snippets

Second, you’ll notice that most of David’s abbreviations start with x. He uses that the same way I use the semicolon: as a signal character that makes it easy to come up with meaningful abbreviations that don’t conflict with real words. By using a letter instead of a punctuation character, David can sync his snippets between his Mac and his iOS devices and not have to worry about his abbreviations requiring a trip to a secondary iOS keyboard. I’d use this except for the fact that I often use variable names that start with x,1 and my fear of conflicts is greater than my desire for snippet harmony.

Finally, and most interestingly, David has a few snippets for inserting dates that are for some date in the future. I’ve made similar snippets, but mine have always been for a fixed number of days from today. David’s are more subtle. As you can see from the screenshot, he has snippets for things like “next Monday” and “next Friday.” TextExpander’s built-in date calculation commands can’t handle things like that, so David’s snippets use AppleScript (written by Ben Waldie).

I wrote a post about these kinds of calculations last fall, discussing both AppleScript and Python solutions. In the comments to that post, David Cross (@roguemonk) pointed out a clever use of the date command that blew me away. It manages to do these same calculations in way that’s both more compact and easier to understand at a glance. The technique relies on the use of date’s -v option.

Let’s say you want to replicate the “Next Monday” snippet shown above. The date invocation that’ll do it is

date -v +1d -v +mon +"%Y-%m-%d"

Like the AppleScript in the screenshot, this gives you the next Monday (-v +mon) after (-v +1d) today.2 3 The formatting of the date is done through the familiar strftime library used by lots of Unix utilities and programming languages.

To make this command work in TextExpander, you need to add the shebang line for bash and you probably want to use echo -n to suppress the linefeed that date adds to the end. Your final snippet would look like this:

echo -n `date -v +1d -v +mon +"%Y-%m-%d"`

Next Friday would look like this:

echo -n `date -v +1d -v +fri +"%Y-%m-%d"`

And so on. The great advantage of this approach is that the strftime format is much more flexible than what you can get out of AppleScript. Just look at the contortions poor Ben had to go through to get a simple year-month-date format. If you wanted a format like “8 May 2013,” the date specifier would be easy to change:

echo -n `date -v +1d -v +mon +"%-d %b %Y"`

I realize that writing this post so close on the heels last week’s gentle poke at Merlin Mann makes it look like I’m taking on the role of TextExpander policeman. But if I could control my obsessions they wouldn’t be obsessions, would they?

  1. Contrary to popular opinion, variable names that start with x aren’t necessarily a recipe for obscure code. I write scripts that do coordinate calculations, and the variables that represent the x-, y-, and z-coordinates of points often start with those letters. 

  2. The order of the -v options is important, as they’re applied sequentially. In this case, we start by moving forward a day and then look for the “next” Monday. If the order of the -v’s were reversed, we’d go to the next Monday and then step forward a day, landing on a Tuesday. 

  3. If tomorrow is a Monday, that’s the date that’ll be returned. The + sign for day-of-week searches works more like than >