December 2, 2014 at 9:59 PM by Dr. Drang
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.
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
python: 1: #!/usr/bin/python 2: 3: from datetime import date 4: import sys 5: 6: now = date.today() 7: month = int(sys.argv) 8: day = int(sys.argv) 9: try: 10: year = int(sys.argv) 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
day in Lines 7 and 8.
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
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
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.