Skip to content
Permalink
master
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time
executable file 184 lines (152 sloc) 6.62 KB
#!/usr/bin/env python3
# Copyright (C) 2010 Richard Mortier <mort@cantab.net>. All Rights Reserved.
#
# Licensed under the GPL v3; see LICENSE.md in the root of this distribution or
# the full text at https://opensource.org/licenses/GPL-3.0
# Replacement for standard UNIX cal utility, supporting date ranges, and
# defaulting to Monday as first day-of-week. Deprecated in favour of my OCaml
# version, <https://github.com/mor1/ocal>.
import sys, calendar, getopt, datetime
def die_with_usage(err="Usage: ", code=0):
print("""ERROR: %s
%s: <options> <dates...> where available <options> are:
-h/--help : print this message
-n/--nohilight : turn off highlighting
-t/--today <today> : set <today=YYYY-MM-DD> for highlighting
-y/--year : interpret arguments as years
-c/--columns <n> : format calendar across <n> columns
-s/--separator <s> : format calendar using <s> as month separator
-f/--firstday <d> : format calendar with <d> as first day-of-week
and <dates> are formatted as:
<n> : month <n> if 1 <= n <= 12, else year <n>
<m>/<y> : month <m> in year <y>
<m1>-<m2> : months from <m1> to <m2>, inclusive
<y1>-<y2> : years from <y1> to <y2>, inclusive
<m1>-<m2>/<y> : months from <m1> to <m2> in year <y>, inclusive
<m1>/<y1>-<m2>/<y2> : months from <m1> in year <y1> to <m2> in <y2>
for day <d> either 1-7, Monday-Sunday, or abbreviation thereof;
month <m> either 1-12, January-December, or abbreviation thereof; and
year <y> is fully qualified, ie., 20 is year 20, not 2020.
Defaults to printing current month, with Monday as first day-of-week.
""" % (err, sys.argv[0]))
sys.exit(code)
Year = Month = Date = None
Months = [ '',
'january', 'february', 'march', 'april', 'may', 'june',
'july', 'august', 'september', 'october', 'november', 'december' ]
Days = [ '',
'monday', 'tuesday', 'wednesday', 'thursday', 'friday',
'saturday', 'sunday' ]
def _format(f, s):
if nohilight or len(s.strip()) == 0: return s
else:
return '\x1b[0%s%s\x1b[0m' % (f,s)
def bold(s): return _format(';1m', s)
def standout(s): return _format(';1;7m', s)
def underline(s): return _format(';4m', s)
def lookup(l, i):
if i.isdigit(): return int(i)
for j in range(max(map(len,l)),0,-1):
ls = list(map(lambda s:s[:j], l))
if i in ls: return ls.index(i)
raise Exception("unknown month: %s" % (i,))
def incr_month(y,m):
m += 1
if m > 12: y += 1; m = 1
return y,m
def range_months(sy,sm, ey,em):
y,m = sy,sm
while True:
yield calendar.month(y,m)
y,m = incr_month(y,m)
if (y,m) > (ey,em): break
def format_months(ms, ncols=3, sep=' '):
empty_month = [((" "*20)+"\n")*8]
ms = list(ms)
ms += empty_month*ncols
year = month = None
while len(ms) > ncols:
mss = [ m.split("\n") for m in ms[:ncols] ]
ms = ms[ncols:]
for i in range(ncols):
mss[i] += ['']*(len(mss[i]) % 8)
if len(mss[i][0].strip().split()) > 0:
month, year = mss[i][0].strip().split()
mss[i][0] = bold(mss[i][0].strip().center(20))
mss[i][1] = underline(mss[i][1].ljust(20))
for j in range(1,8):
m = mss[i][j].split()
if (year == Year
and Date in m
and Months.index(month.lower()) == int(Month)
):
## only pad front for first line
if j == 2: newm = [' '] * (7-len(m))
else: newm = [ ]
for d in m:
if d == Date: d = standout("%02s" % d)
else: d = "%02s" % d
newm.append(d)
## OSX10.6 upgrade change? ljust() started treating escape
## chars as real chars (ie., can't do 20 for both higlighted
## and unhighlighted).
mss[i][j] = ' '.join(newm).ljust(32)
else:
mss[i][j] = mss[i][j].ljust(20)
for i in range(8):
for m in mss: yield "%s%s" % (m[i], sep)
yield "\n"
if __name__ == '__main__':
## option parsing
pairs = [ "h/help", "y/year", "n/nohilight",
"c:/columns=", "s:/separator=", "f:/firstday=", "t:/today=", ]
shortopts = "".join([ pair.split("/")[0] for pair in pairs ])
longopts = [ pair.split("/")[1] for pair in pairs ]
try: opts, args = getopt.getopt(sys.argv[1:], shortopts, longopts)
except getopt.GetoptError as err: die_with_usage(err, 2)
ncols = 3
sep = ' '*4
fullyear = False
today = datetime.date.today()
nohilight = False
try:
for o, a in opts:
if o in ("-h", "--help"): die_with_usage()
elif o in ("-y", "--year"): fullyear = True
elif o in ("-c", "--columns"): ncols = int(a)
elif o in ("-s", "--separator"): sep = a
elif o in ("-n", "--nohilight"): nohilight = True
elif o in ("-t", "--today"):
y,m,d = map(int, a.split("-"))
today = datetime.date(y,m,d)
elif o in ("-f", "--firstday"):
d = int(lookup(Days, a.lower()))
if not (1 <= d <= 7):
raise Exception("bad first day-of-week %s; "
"must be 1 (Monday) to 7 (Sunday)" % d)
calendar.setfirstweekday(d-1)
else: raise Exception("unhandled option")
except Exception as err: die_with_usage(err, 3)
Year, Month, Date = datetime.date.isoformat(today).split("-")[0:3]
Date = Date.lstrip('0')
## compute the months to print
months = []
if len(args) == 0: args = [ Month ]
for a in args:
if fullyear: sy,sm, ey,em = int(a),1, int(a),12
else:
s,e = a,a
if "-" in a: s,e = a.split("-")
em,ey = e,int(Year)
if "/" in e: em,ey = e.split("/")
try: em = lookup(Months, em)
except Exception as err: die_with_usage(err, 4)
sm,sy = s,ey ## start year is end year if only latter given
if "/" in s: sm,sy = s.split("/")
try: sm = lookup(Months, sm)
except Exception as err: die_with_usage(err, 5)
## fix up if no month is given, only year or year range
if sm > 12 or em > 12: sy,ey = sm,em ; sm,em = 1,12
months.extend(list(range_months(int(sy),int(sm), int(ey),int(em))))
## format and print computed months
for line in format_months(months, ncols, sep): print(line, end="")