Two little date commands

This morning, I was composing an email to a client about an unpaid invoice when I realized I didn’t know how old the invoice was in days. I had the invoice in front of me, so I knew the date on which it was issued, but I didn’t trust myself to do the date math in my head. Our billing system will tell me how old an invoice is, but I need to go down the hall to the accounting office to do that. Which is what I did, because I wanted to get the email out and get back to work, but I made a note to write a script tonight that would do the calendar math for me in the future.

Python’s datetime module has all the functions needed to do the calculations. In fact, if you’re in an interactive Python session, it takes only a couple of lines to learn that August 29 was 95 days ago:

>>> from datetime import date
>>> date.today() - date(2014, 8, 29)
datetime.timedelta(95)

Of course, I seldom have an interactive Python session running, so this isn’t a practical solution. I wanted a shell script that required as little typing as possible. What I came up with is this script, called ago:

python:
 1:  #!/usr/bin/python
 2:  
 3:  from datetime import date
 4:  import sys
 5:  
 6:  now = date.today()
 7:  month = int(sys.argv[1])
 8:  day = int(sys.argv[2])
 9:  try:
10:    year = int(sys.argv[3])
11:    if 50 <= year < 100:    # assume 20th century
12:      year += 1900
13:    if 0 <= year < 50:      # assume 21st century
14:      year += 2000
15:  except IndexError:        # assume this year
16:    year = now.year
17:  
18:  then = date(year, month, day)
19:  ago = now - then
20:  print ago.days

It takes two or three arguments, the month, the day, and (optionally) the year, and returns the number of days between then and today. If the year isn’t given, it assumes you mean this year. If you give a two-digit year, it assumes you mean 20nn if nn is less than 50 and 19nn otherwise. Thus

ago 8 29

returns 95, and

ago 8 23 60

returns 19824, which means I’ll be 20,000 days old in a little less than six months.

If you’re European, you’ll want to switch the assignment of the month and day in Lines 7 and 8.

I think ago is pretty Unixy, especially its short, pronouncable name. It is by no means a professional quality utility, because it has no error handling whatsoever. But I wrote it for me, and I know better than to pass it bad arguments. And even if I do screw up and try to run

ago Aug 29

I’ll get an error message from Line 7 that I’ll understand. The world won’t come to an end.

Although it’s meant to be used with dates in the past, it can handle future dates, too. Running

ago 5 27 15

returns -176, which tells me May 27 of next year is 176 days away. That’s when I’ll be 20,000 days old. Mark your calendar.

At first, I thought that instead of creating a special-purpose script like ago, I’d repurpose a one-liner I wrote years ago called doy.

bash:
1:  #!/bin/sh
2:  
3:  date +"%j"

This is so short, it really should be a shell function instead of a standalone script, but I don’t see any reason to change it now. It returns the day of the year of the current date, and I feel certain I wrote it when I first learned about the %j directive for strftime.

As I said, my first thought was to change doy to accept arguments so it would return the day of the year for whatever date I passed to it. But I liked the simplicity of doy too much to change it. It, too, has a nice, short Unixy name.