Skip to content

Instantly share code, notes, and snippets.

@robjwells
Last active December 21, 2015 05:19
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save robjwells/6256540 to your computer and use it in GitHub Desktop.
Save robjwells/6256540 to your computer and use it in GitHub Desktop.
days - print the number of days between now and a given date
#!/usr/local/bin/python3
"""\
Days
Calculate the number of days between a date and today or another date
Usage:
days <date> [<second_date> --format=<fmt>]
Options:
-f <fmt>, --format=<fmt> A strptime format string
[default: %Y-%m-%d]
"""
import sys
from docopt import docopt
from datetime import datetime
def parse_date(date_string, date_format):
"""datetime.strptime wrapper which exits on error
Attempt to parse date_string with date_format, both of which are
given by the user. On error, print the reason to stderror and exit
"""
try:
date = datetime.strptime(date_string, date_format)
except ValueError as error:
sys.exit(error)
else:
return date
args = docopt(__doc__)
if args['<second_date>']:
start_date = parse_date(args['<second_date>'], args['--format'])
else:
start_date = datetime.today()
target_date = parse_date(args['<date>'], args['--format'])
difference = (target_date.date() - start_date.date()).days
# .date() used to zero the time, otherwise you get odd results
if difference == 0 and not args['<second_date>']:
print("That's today!")
else:
days = 'day' if difference in [1, -1] else 'days'
message = '{} {} '.format(abs(difference), days)
if args['<second_date>']:
message += 'apart'
else:
message += ('ago' if difference < 0 else 'ahead')
print(message)
#!/bin/bash
# Assign first argument or stdin to $DATE
if [ -t 0 ]; then
DATE=$1
else
read DATE
fi
if [ "$DATE" == "" ]; then
# Print usage message if no date is supplied
echo "Usage: $(basename $0) YYYY-MM-DD"
echo " prints number of days until or since the given date"
exit 64
fi
# Exit if $DATE is not in ISO format
echo $DATE | egrep "[0-9]{4}-[0-9]{2}-[0-9]{2}" > /dev/null
if [ $? -ne 0 ]; then
echo "Abort: your date is not in YYYY-MM-DD format"
exit 65
fi
THEN=$(date -jf %Y-%m-%d $DATE +%s)
NOW=$(date -j +%s)
DAYS=$(bc <<< "($THEN - $NOW) / 86400")
if [ $DAYS -eq 0 ]; then
echo "That's today!"
exit 0
fi
MSG="$DAYS day"
if [ $DAYS -ne 1 ] && [ $DAYS -ne -1 ]; then
# Plural if number of days is not 1 and not -1
MSG="${MSG}s"
fi
if [ $DAYS -lt 0 ]; then
# Trim sign if a negative number
echo $(cut -c 2- <<< $MSG) ago
else
echo $MSG ahead
fi
exit 0
@crmdgn
Copy link

crmdgn commented May 30, 2014

This is handy. Is there a way to format the output in larger units, if it's a date in the far future? So, maybe, X years, Y months, Z days ahead?

@robjwells
Copy link
Author

@crmdgn (Sorry, only just seen your comment — no notifications for gists.)

Days are the largest unit stored by Python's datetime.timedelta objects (see line 40), and once you start dealing in months and years you've got to consider that they vary in length. It is possible, of course, but would require some thought to provide an answer that uses units larger than a week. At that point you're in the realm of true calendrical calculations as opposed to just subtracting one integer from another.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment