Roman-style date formatting Today (March 15) is the Ides of March, best known for being the date that Julius Caesar was assassinated.

What's “Ides”?  Well, the ancient Romans didn't simply number the days of month from 1 to 31 like we do.  Instead, they had a more complex system of counting backwards to the Kalends, Nones, or Ides.

Challenge

Write, in as few bytes as possible, a program that will display the current date (on the Gregorian calendar, in the local timezone) using the abbreviated Roman-style convention described below.  Your program should work until at least the end of 2037.

Roman dates

Months of the year

These should look familiar to you: Ianuarius, Februarius, Martius, Aprilis, Maius, Iunius, Iulius (Quintilis), Augustus (Sextilis), September, October, November, December.

Your program is to abbreviate all months to three letters, using the classical Latin alphabet (all caps, with J=I and U=V).  That is, IAN, FEB, MAR, APR, MAI, IVN, IVL, AVG, SEP, OCT, NOV, DEC.

Special dates of the month


Kalendis, or the Kalends (abbr. KAL) is the first day of each month.
Idibus, or the Ides (abbr. ID) is the 15th day of March, May, July, or October; but the 13th day of the other eight months.
Nonas, or the Nones (abbr. NON) is 8 days before the Ides (5th or 7th of the month)


Other days of the month

Other days of the month are numbered by counting backwards and inclusively to the next reference date (Kalends, Ides, or Nones).  Notation is AD (abbreviation for ante diem, no relation to Jesus of Nazareth) followed by the appropriate Roman numeral.

Exception: Write PR (for pridie) instead of AD II for the day before Kalends, Nones, or Ides.

The leap day

In leap years, two days (February 24 and 25 in modern reckoning) are given the date AD VI KAL MAR.  (You do not need to distinguish the two days in your program.)  February 14-23 are AD XVI KAL MAR through AD VII KAL MAR as in non-leap years.  Feb 26 = AD V KAL MAR, Feb 27 = AD IV KAL MAR, Feb 28 = AD III KAL MAR, and Feb 29 = PR KAL MAR, offset by one from the corresponding dates in non-leap years.

Example

In March, the output of your program should be:


On March 1: KAL MAR
On March 2: AD VI NON MAR
On March 3: AD V NON MAR
On March 4: AD IV NON MAR
On March 5: AD III NON MAR
On March 6: PR NON MAR
On March 7: NON MAR
On March 8: AD VIII ID MAR
...
On March 13: AD III ID MAR
On March 14: PR ID MAR
On March 15: ID MAR
On March 16: AD XVII KAL APR
...
On March 30: AD III KAL APR
On March 31: PR KAL APR
Tcl/Tk, 675 bytes

Well, I suppose I could crunch it more, but this is it for now...

set a {y m d}
rename proc p
p r {n {s ""}} {foreach {v d} {10 X 5 V 1 I} {while {$n&gt;=$v} {set s $s$d;incr n -$v}};string map {VIIII IX IIII IV} $s}
p y y {expr !($y%100?$y%4:$y%400)}
p i $a {expr {($m in {3 5 7 10})?15:13}}
p n $a {expr [i $y $m $d]-8}
p k $a {expr $m==2?29+($d&gt;24&amp;&amp;[y $y]):$m&lt;8?31+$m%2:32-$m%2}
p N {} {clock f [clock se] -f {%Y %N %e}}
p M m {lindex {IAN FEB MAR APR MAI IVN IVL AVG SEP OCT NOV DEC IAN} $m-1}
p d $a {foreach f {n i k} n {NON ID KAL} mo {0 0 1} {if {$d&lt;=[$f $y $m $d]} break}
string map {{AD I } {} {AD II } {PR }} "AD [r [expr [$f $y $m $d]+1-$d]] $n [M [expr $m+$mo]]"}
p p $a {if {$d==1} {puts "KAL [M $m]"} {puts [d $y $m $d]}}
p {*}[N]


How it works

The first thing is to collect today's date as a list: year, month, day. Everything works over that. The first of the month is a simple special case; we handle that separately.

All remaining cases work like this:


Find the future reference day's date in (5,7,13,15,days-in-month)
Get the abbreviation corresponding to that reference (NON, ID, KAL)
For Kalends we increment the month by one
Assemble the date string as:
"AD &lt;days-in-roman-numerals&gt; &lt;abbreviated-reference-day&gt; &lt;abbreviated-month-name&gt;"
Apply a text replace to correct AD I and AD II.


The first step is done with a function lookup table for each of the Nones, Ides, and Kalends. Nones and Ides are straight-forward subtraction and lookup, respectively. Computing the Kalends is only a little more complicated (notice the trick with leap-February's day number.):


February → 28 days + (it's a leap year AND day > 24)
January..July → 30 days + (it's January, March, May or July)
August..December → 31 days - (it's September or November)


The days is a simple difference: (reference_day - todays_day + 1)

Computing the leap year is done with a somewhat unusual arrangement of the standard formula so that it could be crunched better (removing braces and parentheses from the expr command).

Converting decimal to Roman numerals is a simple table-driven approach, stripped to use only ones, fives, and tens since we don't need to print the year in Roman numerals (domain is [1,39]).

Almost everything else is application of lookup tables.

A note on Tcl and reducing bytes

The first two lines of the script eat bytes by simply being shorter than spelling out proc f {y m d} for all instances.

Single-letter proc names and variables, of course:

    r := decimal→roman(n)    i := ides(y,m,d)        M := month-abbreviation(month)
    y := is-leap(year)       n := nones(y,m,d)       d := date→roman(y,m,d) [d ≠ 1]
    N := today → (y,m,d)     k := kalends(y,m,d)     p := date→roman(y,m,d) [∀d]


Testing for dates other than today

Change the very last line from p {*}[N] to 

p {*}$argv


and run the program with the date desired:

% tclsh roman.tcl 2016 2 25
AD VI KAL MAR


Enjoy!