# DDATE.AWK - Gregorian to Discordian date contributed by Dan Nielsen
# syntax: GAWK -f DDATE.AWK [YYYYMMDD | YYYY-MM-DD | MM-DD-YYYY | DDMMMYYYY | YYYY] ...
# examples:
#   GAWK -f DDATE.AWK                    today
#   GAWK -f DDATE.AWK 20110722           one date
#   GAWK -f DDATE.AWK 20110722 20120229  two dates
#   GAWK -f DDATE.AWK 2012               yearly calendar
BEGIN {
    split("Chaos,Discord,Confusion,Bureaucracy,The Aftermath",season_arr,",")
    split("Sweetmorn,Boomtime,Pungenday,Prickle-Prickle,Setting Orange",weekday_arr,",")
    split("Mungday,Mojoday,Syaday,Zaraday,Maladay",apostle_holyday_arr,",")
    split("Chaoflux,Discoflux,Confuflux,Bureflux,Afflux",season_holyday_arr,",")
    split("31,28,31,30,31,30,31,31,30,31,30,31",days_in_month,",") # days per month in non leap year
    split("0   31  59  90  120 151 181 212 243 273 304 334",rdt," ") # relative day table
    mmm = "JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC"
#          1   2   3   4   5   6   7   8   9   10  11  12
    if (ARGV[1] == "") { # use current date
      ARGV[ARGC++] = strftime("%Y%m%d") # GAWK only
    # ARGV[ARGC++] = dos_date() # any AWK
    # timetab(arr); ARGV[ARGC++] = sprintf("%04d%02d%02d",arr["YEAR"],arr["MONTH"],arr["DAY"]) # TAWK only
    }
    for (argno=1; argno<=ARGC-1; argno++) { # validate command line arguments
      print("")
      x = toupper(ARGV[argno])
      if (x ~ /^[0-9][0-9][0-9][0-9][01][0-9][0-3][0-9]$/) { # YYYYMMDD
        main(x)
      }
      else if (x ~ /^[0-9][0-9][0-9][0-9]-[01][0-9]-[0-3][0-9]$/) { # YYYY-MM-DD
        gsub(/-/,"",x)
        main(x)
      }
      else if (x ~ /^[01][0-9]-[0-3][0-9]-[0-9][0-9][0-9][0-9]$/) { # MM-DD-YYYY
        main(substr(x,7,4) substr(x,1,2) substr(x,4,2))
      }
      else if (x ~ /^[0-3][0-9](JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)[0-9][0-9][0-9][0-9]$/) { # DDMMMYYYY
        main(sprintf("%04d%02d%02d",substr(x,6,4),int((match(mmm,substr(x,3,3))/4)+1),substr(x,1,2)))
      }
      else if (x ~ /^[0-9][0-9][0-9][0-9]$/) { # YYYY
        yearly_calendar(x)
      }
      else {
        error("begin")
      }
    }
    if (errors == 0) { exit(0) } else { exit(1) }
}
function main(x,  d,dyear,m,season_day,season_nbr,text,weekday_nbr,y,year_day) {
    y = substr(x,1,4) + 0
    m = substr(x,5,2) + 0
    d = substr(x,7,2) + 0
    days_in_month[2] = (leap_year(y) == 1) ? 29 : 28
    if (m < 1 || m > 12 || d < 1 || d > days_in_month[m]+0) {
      error("main")
      return
    }
    year_day = rdt[m] + d
    dyear = y + 1166 # Discordian year
    season_nbr = int((year_day - 1 ) / 73) + 1
    season_day = ((year_day - 1) % 73) + 1
    weekday_nbr = ((year_day - 1 ) % 5) + 1
    if (season_day == 5) {
      text = ", " apostle_holyday_arr[season_nbr]
    }
    else if (season_day == 50) {
      text = ", " season_holyday_arr[season_nbr]
    }
    if (leap_year(y) && m == 2 && d == 29) {
      printf("%04d-%02d-%02d is St. Tib's day, Year of Our Lady of Discord %s\n",y,m,d,dyear)
    }
    else {
      printf("%04d-%02d-%02d is %s, %s %s, Year of Our Lady of Discord %s%s\n",
      y,m,d,weekday_arr[weekday_nbr],season_arr[season_nbr],season_day,dyear,text)
    }
}
function leap_year(y) { # leap year: 0=no, 1=yes
    return (y % 400 == 0 || (y % 4 == 0 && y % 100)) ? 1 : 0
}
function yearly_calendar(y,  d,m) {
    days_in_month[2] = (leap_year(y) == 1) ? 29 : 28
    for (m=1; m<=12; m++) {
      for (d=1; d<=days_in_month[m]; d++) {
        main(sprintf("%04d%02d%02d",y,m,d))
      }
    }
}
function dos_date(  arr,cmd,d,x) { # under Microsoft Windows
# XP - The current date is: MM/DD/YYYY
# 8  - The current date is: DOW MM/DD/YYYY
    cmd = "DATE <NUL"
    cmd | getline x
    close(cmd) # close pipe
    d = arr[split(x,arr," ")]
    return sprintf("%04d%02d%02d",substr(d,7,4),substr(d,1,2),substr(d,4,2))
}
function error(x) {
    printf("error: argument %d is invalid, %s, in %s\n",argno,ARGV[argno],x)
    errors++
}


/* REXX */
Numeric Digits 30
Call test '9 4 6 6'
Call test '5 10 6 7'
Exit
test:
Parse Arg w1 s1 w2 s2
plist1=pp(w1,s1)
p1.=0
Do x=w1 To w1*s1
  Parse Var plist1 p1.x plist1
  End
plist2=pp(w2,s2)
p2.=0
Do x=w2 To w2*s2
  Parse Var plist2 p2.x plist2
  End
p2low.=0
Do x=w1 To w1*s1
  Do y=0 To x-1
    p2low.x=p2low.x+p2.y
    End
  End
pwin1=0
Do x=w1 To w1*s1
  pwin1=pwin1+p1.x*p2low.x
  End
Say 'Player 1 has' w1 'dice with' s1 'sides each'
Say 'Player 2 has' w2 'dice with' s2 'sides each'
Say 'Probability for player 1 to win:' pwin1
Say ''
Return

pp: Procedure
/*---------------------------------------------------------------------
* Compute and return the probabilities to get a sum x
* when throwing w dice each having s sides (marked from 1 to s)
*--------------------------------------------------------------------*/
Parse Arg w,s
str=''
cnt.=0
Do wi=1 To w
  str=str||'Do v'wi'=1 To' s';'
  End
str=str||'sum='
Do wi=1 To w-1
  str=str||'v'wi'+'
  End
str=str||'v'w';'
str=str||'cnt.'sum'=cnt.'sum'+1;'
Do wi=1 To w
  str=str||'End;'
  End
Interpret str
psum=0
Do x=0 To w*s
  p.x=cnt.x/(s**w)
  psum=psum+p.x
  End
res=''
Do x=w To s*w
  res=res p.x
  End
Return res


def roll_dice(n_dice, n_faces)
  return [[0,1]] if n_dice.zero?
  one  = [1] * n_faces
  zero = [0] * (n_faces-1)
  (1...n_dice).inject(one){|ary,_|
    (zero + ary + zero).each_cons(n_faces).map{|a| a.inject(:+)}
  }.map.with_index(n_dice){|n,sum| [sum,n]}  # sum: total of the faces
end

def game(dice1, faces1, dice2, faces2)
  p1 = roll_dice(dice1, faces1)
  p2 = roll_dice(dice2, faces2)
  p1.product(p2).each_with_object([0,0,0]) do |((sum1, n1), (sum2, n2)), win|
    win[sum1 <=> sum2] += n1 * n2        # [0]:draw, [1]:win, [-1]:lose
  end
end

[[9, 4, 6, 6], [5, 10, 6, 7]].each do |d1, f1, d2, f2|
  puts "player 1 has #{d1} dice with #{f1} faces each"
  puts "player 2 has #{d2} dice with #{f2} faces each"
  win = game(d1, f1, d2, f2)
  sum = win.inject(:+)
  puts "Probability for player 1 to win: #{win[1]} / #{sum}",
       "                              -> #{win[1].fdiv(sum)}", ""
end


#INCLUDE "datetime.bi"

DECLARE FUNCTION julian(AS DOUBLE) AS INTEGER

SeasonNames:
DATA "Chaos", "Discord", "Confusion", "Bureaucracy", "The Aftermath"
Weekdays:
DATA "Setting Orange", "Sweetmorn", "Boomtime", "Pungenday", "Prickle-Prickle"
DaysPreceding1stOfMonth:
'   jan feb mar apr may  jun  jul  aug  sep  oct  nov  dec
DATA 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334

DIM dyear AS INTEGER, dseason AS STRING, dday AS INTEGER, dweekday AS STRING
DIM tmpdate AS DOUBLE, jday AS INTEGER, result AS STRING
DIM L0 AS INTEGER

IF LEN(COMMAND$) THEN
    tmpdate = DATEVALUE(COMMAND$)
ELSE
    tmpdate = FIX(NOW())
END IF
dyear = YEAR(tmpdate) + 1166
IF (2 = MONTH(tmpdate)) AND (29 = DAY(tmpdate)) THEN
    result = "Saint Tib's Day, " & STR$(dyear) & " YOLD"
ELSE
    jday = julian(tmpdate)
    RESTORE SeasonNames
    FOR L0 = 1 TO ((jday - 1) \ 73) + 1
    	READ dseason
    NEXT
    dday = (jday MOD 73)
    IF 0 = dday THEN dday = 73
    RESTORE Weekdays
    FOR L0 = 1 TO (jday MOD 5) + 1
        READ dweekday
    NEXT
    result = dweekday & ", " & dseason & " " & TRIM$(STR$(dday)) & ", " & TRIM$(STR$(dyear)) & " YOLD"
END IF

? result
END

FUNCTION julian(d AS DOUBLE) AS INTEGER
    'doesn't account for leap years (not needed for ddate)
    DIM tmp AS INTEGER, L1 AS INTEGER
    RESTORE DaysPreceding1stOfMonth
    FOR L1 = 1 TO MONTH(d)
        READ tmp
    NEXT
    FUNCTION = tmp + DAY(d)
END FUNCTION


// Returns a vector containing the number of ways each possible sum of face
// values can occur.
fn get_totals(dice: usize, faces: usize) -> Vec<f64> {
    let mut result = vec![1.0; faces + 1];
    for d in 2..=dice {
        let mut tmp = vec![0.0; d * faces + 1];
        for i in d - 1..result.len() {
            for j in 1..=faces {
                tmp[i + j] += result[i];
            }
        }
        result = tmp;
    }
    result
}

fn probability(dice1: usize, faces1: usize, dice2: usize, faces2: usize) -> f64 {
    let totals1 = get_totals(dice1, faces1);
    let totals2 = get_totals(dice2, faces2);
    let mut wins = 0.0;
    let mut total = 0.0;
    for i in dice1..totals1.len() {
        for j in dice2..totals2.len() {
            let n = totals1[i] * totals2[j];
            total += n;
            if j < i {
                wins += n;
            }
        }
    }
    wins / total
}

fn main() {
    println!("{}", probability(9, 4, 6, 6));
    println!("{}", probability(5, 10, 6, 7));
}


func combos(sides, n) {
    n || return [1]
    var ret = ([0] * (n*sides.max + 1))
    combos(sides, n-1).each_kv { |i,v|
        v && for s in sides { ret[i + s] += v }
    }
    return ret
}

func winning(sides1, n1, sides2, n2) {
    var (p1, p2) = (combos(sides1, n1), combos(sides2, n2))
    var (win,loss,tie) = (0,0,0)
    p1.each_kv { |i, x|
        win  += x*p2.ft(0,i-1).sum
        tie  += x*p2.ft(i, i).sum
        loss += x*p2.ft(i+1).sum
    }
    [win, tie, loss] »/» p1.sum*p2.sum
}

func display_results(String title, Array res) {
    say "=> #{title}"
    for name, prob in (%w(p₁\ win tie p₂\ win) ~Z res) {
        say "P(#{'%6s' % name}) =~ #{prob.round(-11)} (#{prob.as_frac})"
    }
    print "\n"
}

display_results('9D4 vs 6D6',  winning(range(1, 4), 9, range(1,6), 6))
display_results('5D10 vs 6D7', winning(range(1,10), 5, range(1,7), 6))


@echo off
goto Parse

 Discordian Date Converter:

 Usage:
	ddate
	ddate /v
	ddate /d isoDate
	ddate /v /d isoDate

:Parse
	shift
	if "%0"==""   goto Prologue
	if "%0"=="/v" set Verbose=1
	if "%0"=="/d" set dateToTest=%1
	if "%0"=="/d" shift
goto Parse

:Prologue
	if "%dateToTest%"=="" set dateToTest=%date%
	for %%a in (GYear GMonth GDay GMonthName GWeekday GDayThisYear) do set %%a=fnord
	for %%a in (DYear DMonth DDay DMonthName DWeekday) do set %%a=MUNG
goto Start

:Start
	for /f "tokens=1,2,3 delims=/:;-. " %%a in ('echo %dateToTest%') do (
		set GYear=%%a
		set GMonth=%%b
		set GDay=%%c
	)
goto GMonthName

:GMonthName
	if %GMonth% EQU  1	set GMonthName=January
	if %GMonth% EQU  2	set GMonthName=February
	if %GMonth% EQU  3	set GMonthName=March
	if %GMonth% EQU  4	set GMonthName=April
	if %GMonth% EQU  5	set GMonthName=May
	if %GMonth% EQU  6	set GMonthName=June
	if %GMonth% EQU  7	set GMonthName=July
	if %GMonth% EQU  8	set GMonthName=August
	if %GMonth% EQU  9	set GMonthName=September
	if %GMonth% EQU 10	set GMonthName=October
	if %GMonth% EQU 11	set GMonthName=November
	if %GMonth% EQU 12	set GMonthName=December
goto GLeap

:GLeap
	set /a CommonYear=GYear %% 4
	set /a CommonCent=GYear %% 100
	set /a CommonQuad=GYear %% 400
	set GLeap=0
	if %CommonYear% EQU 0 set GLeap=1
	if %CommonCent% EQU 0 set GLeap=0
	if %CommonQuad% EQU 0 set GLeap=1
goto GDayThisYear

:GDayThisYear
	set GDayThisYear=%GDay%
	if %GMonth% GTR 11                  set /a GDayThisYear=%GDayThisYear%+30
	if %GMonth% GTR 10                  set /a GDayThisYear=%GDayThisYear%+31
	if %GMonth% GTR  9                  set /a GDayThisYear=%GDayThisYear%+30
	if %GMonth% GTR  8                  set /a GDayThisYear=%GDayThisYear%+31
	if %GMonth% GTR  7                  set /a GDayThisYear=%GDayThisYear%+31
	if %GMonth% GTR  6                  set /a GDayThisYear=%GDayThisYear%+30
	if %GMonth% GTR  5                  set /a GDayThisYear=%GDayThisYear%+31
	if %GMonth% GTR  4                  set /a GDayThisYear=%GDayThisYear%+30
	if %GMonth% GTR  3                  set /a GDayThisYear=%GDayThisYear%+31
	if %GMonth% GTR  2 if %GLeap% EQU 1 set /a GDayThisYear=%GDayThisYear%+29
	if %GMonth% GTR  2 if %GLeap% EQU 0 set /a GDayThisYear=%GDayThisYear%+28
	if %GMonth% GTR  1                  set /a GDayThisYear=%GDayThisYear%+31
goto DYear

:DYear
	set /a DYear=GYear+1166
goto DMonth

:DMonth
	set DMonth=1
	set DDay=%GDayThisYear%
	if %DDay% GTR 73 (
		set /a DDay=%DDay%-73
		set /a DMonth=%DMonth%+1
	)
	if %DDay% GTR 73 (
		set /a DDay=%DDay%-73
		set /a DMonth=%DMonth%+1
	)
	if %DDay% GTR 73 (
		set /a DDay=%DDay%-73
		set /a DMonth=%DMonth%+1
	)
	if %DDay% GTR 73 (
		set /a DDay=%DDay%-73
		set /a DMonth=%DMonth%+1
	)
goto DDay

:DDay
	if %GLeap% EQU 1 (
		if %GDayThisYear% GEQ 61 (
			set /a DDay=%DDay%-1
		)
	)
	if %DDay% EQU 0 (
		set /a DDay=73
		set /a DMonth=%DMonth%-1
	)
goto DMonthName

:DMonthName
	if %DMonth% EQU 1 set DMonthName=Chaos
	if %DMonth% EQU 2 set DMonthName=Discord
	if %DMonth% EQU 3 set DMonthName=Confusion
	if %DMonth% EQU 4 set DMonthName=Bureaucracy
	if %DMonth% EQU 5 set DMonthName=Aftermath
goto DTib

:DTib
	set DTib=0
	if %GDayThisYear% EQU 60 if %GLeap% EQU 1 set DTib=1
	if %GLeap% EQU 1 if %GDayThisYear% GTR 60 set /a GDayThisYear=%GDayThisYear%-1
	set DWeekday=%GDayThisYear%
goto DWeekday

:DWeekday
	if %DWeekday% LEQ 5 goto _DWeekday
	set /a DWeekday=%DWeekday%-5
	goto DWeekday
:_DWeekday
	if %DWeekday% EQU 1 set DWeekday=Sweetmorn
	if %DWeekday% EQU 2 set DWeekday=Boomtime
	if %DWeekday% EQU 3 set DWeekday=Pungenday
	if %DWeekday% EQU 4 set DWeekday=Prickle-Prickle
	if %DWeekday% EQU 5 set DWeekday=Setting Orange
goto GWeekday

:GWeekday
goto GEnding

:GEnding
	set GEnding=th
	for %%a in (1 21 31) do	if %GDay% EQU %%a set GEnding=st
	for %%a in (2 22) do 	if %GDay% EQU %%a set GEnding=nd
	for %%a in (3 23) do	if %GDay% EQU %%a set GEnding=rd
goto DEnding

:DEnding
	set DEnding=th
	for %%a in (1 21 31 41 51 61 71) do if %Dday% EQU %%a set DEnding=st
	for %%a in (2 22 32 42 52 62 72) do if %Dday% EQU %%a set DEnding=nd
	for %%a in (3 23 33 43 53 63 73) do if %Dday% EQU %%a set DEnding=rd
goto Display

:Display
	if "%Verbose%"=="1" goto Display2
	echo.
	if %DTib% EQU 1 (
		echo St. Tib's Day, %DYear%
	) else echo %DWeekday%, %DMonthName% %DDay%%DEnding%, %DYear%
goto Epilogue

:Display2
	echo.
	echo  Gregorian: %GMonthName% %GDay%%GEnding%, %GYear%
	if %DTib% EQU 1 (
		echo Discordian: St. Tib's Day, %DYear%
	) else echo Discordian: %DWeekday%, %DMonthName% %DDay%%DEnding%, %DYear%
goto Epilogue

:Epilogue
set Verbose=
set dateToTest=
echo.

:End


import math

fn min_of(x int, y int) int {
    if x < y {
        return x
    }
    return y
}

fn throw_die(n_sides int, n_dice int, s int, mut counts []int) {
    if n_dice == 0 {
        counts[s]++
        return
    }
    for i := int(1); i <= n_sides; i++ {
        throw_die(n_sides, n_dice - 1, s + i, mut counts)
    }
}

fn beating_probability(n_sides1 int, n_dice1 int, n_sides2 int, n_dice2 int) f64 {
    len1 := (n_sides1 + 1) * n_dice1
    mut c1 := []int{len: len1}  // all elements zero by default
    throw_die(n_sides1, n_dice1, 0, mut c1)

    len2 := (n_sides2 + 1) * n_dice2
    mut c2 := []int{len: len2}
    throw_die(n_sides2, n_dice2, 0, mut c2)
    p12 := math.pow(f64(n_sides1), f64(n_dice1)) *
           math.pow(f64(n_sides2), f64(n_dice2))

    mut tot := 0.0
    for i := int(0); i < len1; i++ {
        for j := int(0); j < min_of(i, len2); j++ {
            tot += f64(c1[i] * c2[j]) / p12
        }
    }
    return tot
}

fn main() {
    println(beating_probability(4, 9, 6, 6))
    println(beating_probability(10, 5, 7, 6))
}


var throwDie // recursive
throwDie = Fn.new { |nSides, nDice, s, counts|
    if (nDice == 0) {
        counts[s] = counts[s] + 1
        return
    }
    for (i in 1..nSides) throwDie.call(nSides, nDice-1, s + i, counts)
}

var beatingProbability = Fn.new { |nSides1, nDice1, nSides2, nDice2|
    var len1 = (nSides1 + 1) * nDice1
    var c1 = List.filled(len1, 0)
    throwDie.call(nSides1, nDice1, 0, c1)

    var len2 = (nSides2 + 1) * nDice2
    var c2 = List.filled(len2, 0)
    throwDie.call(nSides2, nDice2, 0, c2)

    var p12 = nSides1.pow(nDice1) * nSides2.pow(nDice2)
    var tot = 0
    for (i in 0...len1) {
        for (j in 0...i.min(len2)) {
            tot = tot + c1[i] * c2[j] / p12
        }
    }
    return tot
}

System.print(beatingProbability.call(4, 9, 6, 6))
System.print(beatingProbability.call(10, 5, 7, 6))


      INSTALL @lib$+"DATELIB"

      PRINT "01/01/2011 -> " FNdiscordian("01/01/2011")
      PRINT "05/01/2011 -> " FNdiscordian("05/01/2011")
      PRINT "28/02/2011 -> " FNdiscordian("28/02/2011")
      PRINT "01/03/2011 -> " FNdiscordian("01/03/2011")
      PRINT "22/07/2011 -> " FNdiscordian("22/07/2011")
      PRINT "31/12/2011 -> " FNdiscordian("31/12/2011")

      PRINT "01/01/2012 -> " FNdiscordian("01/01/2012")
      PRINT "05/01/2012 -> " FNdiscordian("05/01/2012")
      PRINT "28/02/2012 -> " FNdiscordian("28/02/2012")
      PRINT "29/02/2012 -> " FNdiscordian("29/02/2012")
      PRINT "01/03/2012 -> " FNdiscordian("01/03/2012")
      PRINT "22/07/2012 -> " FNdiscordian("22/07/2012")
      PRINT "31/12/2012 -> " FNdiscordian("31/12/2012")
      END

      DEF FNdiscordian(date$)
      LOCAL Season$(), Weekday$(), mjd%, year%, day%
      DIM Season$(4), Weekday$(4)
      Season$() =  "Chaos", "Discord", "Confusion", "Bureaucracy", "The Aftermath"
      Weekday$() = "Sweetmorn", "Boomtime", "Pungenday", "Prickle-Prickle", "Setting Orange"

      mjd% = FN_readdate(date$, "dmy", 2000)
      year% = FN_year(mjd%)

      IF FN_month(mjd%)=2 AND FN_day(mjd%)=29 THEN
        = "St. Tib's Day, YOLD " + STR$(year% + 1166)
      ENDIF

      IF FN_month(mjd%) < 3 THEN
        day% = mjd% - FN_mjd(1, 1, year%)
      ELSE
        day% = mjd% - FN_mjd(1, 3, year%) + 59
      ENDIF
      = Weekday$(day% MOD 5) + ", " + STR$(day% MOD 73 + 1) + " " + \
      \ Season$(day% DIV 73) + ", YOLD " + STR$(year% + 1166)


fcn combos(sides, n){
   if(not n) return(T(1));
   ret:=((0).max(sides)*n + 1).pump(List(),0);
   foreach i,v in (combos(sides, n - 1).enumerate()){
      if(not v) continue;
      foreach s in (sides){ ret[i + s] += v }
   }
   ret
}

fcn winning(sides1,n1, sides2,n2){
   p1, p2 := combos(sides1, n1), combos(sides2, n2);
   win,loss,tie := 0,0,0; # 'win' is 1 beating 2
   foreach i,x1 in (p1.enumerate()){
      # using accumulated sum on p2 could save some time
      win += x1*p2[0,i].sum(0);
      tie += x1*p2[i,1].sum(0);  // i>p2.len() but p2[bigi,?]-->[]
      loss+= x1*p2[i+1,*].sum(0);
   }
   s := p1.sum(0)*p2.sum(0);
   return(win.toFloat()/s, tie.toFloat()/s, loss.toFloat()/s);
}


#include <stdlib.h>
#include <stdio.h>
#include <time.h>

#define day_of_week( x ) ((x) == 1 ? "Sweetmorn" :\
                          (x) == 2 ? "Boomtime" :\
                          (x) == 3 ? "Pungenday" :\
                          (x) == 4 ? "Prickle-Prickle" :\
                          "Setting Orange")

#define season( x ) ((x) == 0 ? "Chaos" :\
                    (x) == 1 ? "Discord" :\
                    (x) == 2 ? "Confusion" :\
                    (x) == 3 ? "Bureaucracy" :\
                    "The Aftermath")

#define date( x ) ((x)%73 == 0 ? 73 : (x)%73)

#define leap_year( x ) ((x) % 400 == 0 || (((x) % 4) == 0 && (x) % 100))

char * ddate( int y, int d ){
  int dyear = 1166 + y;
  char * result = malloc( 100 * sizeof( char ) );

  if( leap_year( y ) ){
    if( d == 60 ){
      sprintf( result, "St. Tib's Day, YOLD %d", dyear );
      return result;
    } else if( d >= 60 ){
      -- d;
    }
  }

  sprintf( result, "%s, %s %d, YOLD %d",
           day_of_week(d%5), season(((d%73)==0?d-1:d)/73 ), date( d ), dyear );

  return result;
}

int day_of_year( int y, int m, int d ){
  int month_lengths[ 12 ] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

  for( ; m > 1; m -- ){
    d += month_lengths[ m - 2 ];
    if( m == 3 && leap_year( y ) ){
      ++ d;
    }
  }
  return d;
}

int main( int argc, char * argv[] ){
  time_t now;
  struct tm * now_time;
  int year, doy;

  if( argc == 1 ){
    now = time( NULL );
    now_time = localtime( &now );
    year = now_time->tm_year + 1900; doy = now_time->tm_yday + 1;
  } else if( argc == 4 ){
    year = atoi( argv[ 1 ] ); doy = day_of_year( atoi( argv[ 1 ] ), atoi( argv[ 2 ] ), atoi( argv[ 3 ] ) );
  }

  char * result = ddate( year, doy );
  puts( result );
  free( result );

  return 0;
}


using System;

public static class DiscordianDate
{
    static readonly string[] seasons = { "Chaos", "Discord", "Confusion", "Bureaucracy", "The Aftermath" };
    static readonly string[] weekdays = { "Sweetmorn", "Boomtime", "Pungenday", "Prickle-Prickle", "Setting Orange" };
    static readonly string[] apostles = { "Mungday", "Mojoday", "Syaday", "Zaraday", "Maladay" };
    static readonly string[] holidays = { "Chaoflux", "Discoflux", "Confuflux", "Bureflux", "Afflux" };

    public static string Discordian(this DateTime date) {
        string yold = $" in the YOLD {date.Year + 1166}.";
        int dayOfYear = date.DayOfYear;

        if (DateTime.IsLeapYear(date.Year)) {
            if (dayOfYear == 60) return "St. Tib's day" + yold;
            else if (dayOfYear > 60) dayOfYear--;
        }
        dayOfYear--;

        int seasonDay = dayOfYear % 73 + 1;
        int seasonNr = dayOfYear / 73;
        int weekdayNr = dayOfYear % 5;
        string holyday = "";

        if (seasonDay == 5)       holyday = $" Celebrate {apostles[seasonNr]}!";
        else if (seasonDay == 50) holyday = $" Celebrate {holidays[seasonNr]}!";
        return $"{weekdays[weekdayNr]}, day {seasonDay} of {seasons[seasonNr]}{yold}{holyday}";
    }

    public static void Main() {
        foreach (var (day, month, year) in new [] {
            (1, 1, 2010),
            (5, 1, 2010),
            (19, 2, 2011),
            (28, 2, 2012),
            (29, 2, 2012),
            (1, 3, 2012),
            (19, 3, 2013),
            (3, 5, 2014),
            (31, 5, 2015),
            (22, 6, 2016),
            (15, 7, 2016),
            (12, 8, 2017),
            (19, 9, 2018),
            (26, 9, 2018),
            (24, 10, 2019),
            (8, 12, 2020),
            (31, 12, 2020)
        })
        {
            Console.WriteLine($"{day:00}-{month:00}-{year:00} = {new DateTime(year, month, day).Discordian()}");
        }
    }

}


#include <iostream>
#include <algorithm>
#include <vector>
#include <sstream>
#include <iterator>
using namespace std;
class myTuple
{
public:
    void set( int a, int b, string c ) { t.first.first = a; t.first.second = b; t.second = c; }
    bool operator == ( pair<int, int> p ) { return p.first == t.first.first && p.second == t.first.second; }
    string second() { return t.second; }
private:
    pair<pair<int, int>, string> t;
};
class discordian
{
public:
    discordian() {
        myTuple t;
        t.set( 5, 1, "Mungday" ); holyday.push_back( t ); t.set( 19, 2, "Chaoflux" ); holyday.push_back( t );
        t.set( 29, 2, "St. Tib's Day" ); holyday.push_back( t ); t.set( 19, 3, "Mojoday" ); holyday.push_back( t );
        t.set( 3, 5, "Discoflux" ); holyday.push_back( t ); t.set( 31, 5, "Syaday" ); holyday.push_back( t );
        t.set( 15, 7, "Confuflux" ); holyday.push_back( t ); t.set( 12, 8, "Zaraday" ); holyday.push_back( t );
        t.set( 26, 9, "Bureflux" ); holyday.push_back( t ); t.set( 24, 10, "Maladay" ); holyday.push_back( t );
        t.set( 8, 12, "Afflux" ); holyday.push_back( t );
        seasons.push_back( "Chaos" ); seasons.push_back( "Discord" ); seasons.push_back( "Confusion" );
        seasons.push_back( "Bureaucracy" ); seasons.push_back( "The Aftermath" );
        wdays.push_back( "Setting Orange" ); wdays.push_back( "Sweetmorn" ); wdays.push_back( "Boomtime" );
        wdays.push_back( "Pungenday" ); wdays.push_back( "Prickle-Prickle" );
    }
    void convert( int d, int m, int y ) {
        if( d == 0 || m == 0 || m > 12 || d > getMaxDay( m, y ) ) {
            cout << "\nThis is not a date!";
            return;
        }
        vector<myTuple>::iterator f = find( holyday.begin(), holyday.end(), make_pair( d, m ) );
        int dd = d, day, wday, sea,  yr = y + 1166;
        for( int x = 1; x < m; x++ )
            dd += getMaxDay( x, 1 );
        day = dd % 73; if( !day ) day = 73;
        wday = dd % 5;
        sea  = ( dd - 1 ) / 73;
        if( d == 29 && m == 2 && isLeap( y ) ) {
            cout << ( *f ).second() << " " << seasons[sea] << ", Year of Our Lady of Discord " << yr;
            return;
        }
        cout << wdays[wday] << " " << seasons[sea] << " " << day;
        if( day > 10 && day < 14 ) cout << "th";
        else switch( day % 10) {
            case 1: cout << "st"; break;
            case 2: cout << "nd"; break;
            case 3: cout << "rd"; break;
            default: cout << "th";
        }
        cout << ", Year of Our Lady of Discord " << yr;
        if( f != holyday.end() ) cout << " - " << ( *f ).second();
    }
private:
    int getMaxDay( int m, int y ) {
        int dd[] = { 0, 31, isLeap( y ) ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; return dd[m];
    }
    bool isLeap( int y ) {
        bool l = false;
        if( !( y % 4 ) ) {
            if( y % 100 ) l = true;
            else if( !( y % 400 ) ) l = true;
        }
        return l;
    }
    vector<myTuple> holyday; vector<string> seasons, wdays;
};
int main( int argc, char* argv[] ) {
    string date; discordian disc;
    while( true ) {
        cout << "Enter a date (dd mm yyyy) or 0 to quit: "; getline( cin, date ); if( date == "0" ) break;
        if( date.length() == 10 ) {
            istringstream iss( date );
            vector<string> vc;
            copy( istream_iterator<string>( iss ), istream_iterator<string>(), back_inserter<vector<string> >( vc ) );
            disc.convert( atoi( vc[0].c_str() ), atoi( vc[1].c_str() ), atoi( vc[2].c_str() ) );
            cout << "\n\n\n";
        } else cout << "\nIs this a date?!\n\n";
    }
    return 0;
}


(require '[clj-time.core :as tc])

(def seasons ["Chaos" "Discord" "Confusion" "Bureaucracy" "The Aftermath"])
(def weekdays ["Sweetmorn" "Boomtime" "Pungenday" "Prickle-Prickle" "Setting Orange"])
(def year-offset 1166)

(defn leap-year? [year]
  (= 29 (tc/number-of-days-in-the-month year 2)))

(defn discordian-day [day leap]
  (let [offset (if (and leap (>= day 59)) 1 0)
        day-off (- day offset)
        day-num (inc (rem day-off 73))
        season (seasons (quot day-off 73))
        weekday (weekdays (mod day-off 5))]
    (if (and (= day 59) (= offset 1))
      "St. Tib's Day"
      (str weekday ", " season " " day-num))))

(defn discordian-date [year month day]
  (let [day-of-year (dec (.getDayOfYear (tc/date-time year month day)))
        dday (discordian-day day-of-year (leap-year? year))]
    (format "%s, YOLD %s" dday (+ year year-offset))))


% This program needs to be merged with PCLU's "useful.lib",
% so it can use get_argv to read the command line.
%
% pclu -merge $CLUHOME/lib/useful.lib -compile cmdline.clu

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Represent a day in the Discordian calendar %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
eris_date = cluster is from_greg_y_m_d,
                       from_date,
                       get_year,
                       get_day,
                       get_season,
                       get_weekday,
                       get_day_name,
                       get_holyday,
                       get_format
    greyface = 1166

    % A Discordian day is either St. Tib's day
    % or a day in a season
    season_day = struct[season, day: int]
    eris_day = oneof[st_tibs: null, season_day: season_day]

    % A Discordian date is a day in a year
    rep = struct[year: int, day: eris_day]

    % Offset of each Gregorian month in a non-leap year
    own month_offset: sequence[int] := sequence[int]$[
        0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
    ]

    % Length of each Gregorian month in a non-leap year
    own month_length: sequence[int] := sequence[int]$[
        31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
    ]

    own week_days: sequence[string] := sequence[string]$[
        "Sweetmorn", "Boomtime", "Pungenday", "Prickle-Prickle",
        "Setting Orange"
    ]

    own seasons: sequence[string] := sequence[string]$[
        "Chaos", "Discord", "Confusion", "Bureacuracy",
        "The Aftermath"
    ]

    own apostle_holydays: sequence[string] := sequence[string]$[
        "Mungday", "Mojoday", "Syaday", "Zaraday", "Maladay"
    ]

    own season_holydays: sequence[string] := sequence[string]$[
        "Chaoflux", "Discoflux", "Confuflux", "Bureflux", "Afflux"
    ]

    % Check if a Gregorian year is a leap year
    is_leap = proc (year: int) returns (bool)
        if year // 4 ~= 0 then return(false)
        elseif year // 100 ~= 0 then return(true)
        elseif year // 400 ~= 0 then return(false)
        else return(true)
        end
    end is_leap

    % Convert a Gregorian date to a Discordian date
    from_greg_y_m_d = proc (year, month, day: int)
                      returns (cvt) signals (invalid)
        % Make sure the month is valid
        if month<1 cor month>12 then signal invalid end

        % Saint Tib's Day?
        if month=2 cand day=29 then
            % Only valid in leap years
            if ~is_leap(year) then signal invalid end
            return(rep${year: year+greyface, day: eris_day$make_st_tibs(nil)})
        end

        % If not, make sure the day of the month is valid
        if day<1 cor day>month_length[month] then signal invalid end

        % The Discordian calendar doesn't consider Saint Tib's Day
        % part of a season, so we can use the day number for a non-leap
        % year even in a leap year
        year_day: int := (day + month_offset[month]) - 1
        sd: season_day := season_day${
            season: year_day / 73 + 1,
            day: year_day // 73 + 1
        }
        return(rep${year: year+greyface,
                    day: eris_day$make_season_day(sd)})
    end from_greg_y_m_d

    % Convert a CLU 'date' object to a Discordian date (ignoring the time)
    from_date = proc (d: date) returns (cvt) signals (invalid)
        return(down(from_greg_y_m_d(d.year, d.month, d.day)))
        resignal invalid
    end from_date

    % Retrieve year, season, day, weekday number
    get_year = proc (d: cvt) returns (int) return(d.year) end get_year

    get_day = proc (d: cvt) returns (int) signals (st_tibs)
        tagcase d.day
            tag st_tibs: signal st_tibs
            tag season_day (s: season_day): return(s.day)
        end
    end get_day

    get_season = proc (d: cvt) returns (int) signals (st_tibs)
        tagcase d.day
            tag st_tibs: signal st_tibs
            tag season_day (s: season_day): return(s.season)
        end
    end get_season

    get_weekday = proc (d: cvt) returns (int) signals (st_tibs)
        day: int := up(d).day resignal st_tibs
        season: int := up(d).season resignal st_tibs
        weekday: int := ( (season-1)*73 + (day-1) ) // 5 + 1
        return( weekday )
    end get_weekday

    % Retrieve formatted day in year
    get_day_name = proc (d: cvt) returns (string)
        begin
            fmt: stream := stream$create_output()
            stream$puts(fmt, week_days[up(d).weekday])
            stream$puts(fmt, ", day " || int$unparse(up(d).day))
            stream$puts(fmt, " of " || seasons[up(d).season])
            return(stream$get_contents(fmt))
        end except when st_tibs:
            return("St. Tib's Day")
        end
    end get_day_name

    % Retrieve holyday name if there is one
    get_holyday = proc (d: cvt) returns (string) signals (no_holyday)
        begin
            if up(d).day = 5 then return(apostle_holydays[up(d).season])
            elseif up(d).day = 50 then return(season_holydays[up(d).season])
            else signal no_holyday
            end
        end except when st_tibs:
            signal no_holyday  % St. Tib's Day is not a normal holyday
        end
    end get_holyday

    % Retrieve long format
    get_format = proc (d: cvt) returns (string)
        fmt: stream := stream$create_output()
        stream$puts(fmt, up(d).day_name)
        stream$puts(fmt, " in the YOLD ")
        stream$puts(fmt, int$unparse(up(d).year))
        stream$puts(fmt, ": celebrate " || up(d).holyday) except when no_holyday: end
        return(stream$get_contents(fmt))
    end get_format
end eris_date

% Parse a date string (MM/DD/YYYY) and return a date object
parse_date = proc (s: string) returns (date) signals (bad_format)
    begin
        parts: array[int] := array[int]$[]
        while true do
            slash: int := string$indexc('/', s)
            if slash=0 then
                array[int]$addh(parts, int$parse(s))
                break
            else
                array[int]$addh(parts, int$parse(string$substr(s, 1, slash-1)))
                s := string$rest(s, slash+1)
            end
        end
        if array[int]$size(parts) ~= 3 then signal bad_format end
        return(date$create(parts[2], parts[1], parts[3], 0, 0, 0))
    end resignal bad_format
end parse_date

% Read date(s) from the command line, or use the current date,
% and convert to the Discordian date
start_up = proc ()
    po: stream := stream$primary_output()
    args: sequence[string] := get_argv()
    dates: array[date] := array[date]$[]

    if sequence[string]$empty(args) then
        % No argument - use today's date
        stream$puts(po, "Today is ")
        array[date]$addh(dates, now())
    else
        % There are argument(s) - parse each of them
        for arg: string in sequence[string]$elements(args) do
            array[date]$addh(dates, parse_date(arg))
        end
    end

    % Convert all dates
    for d: date in array[date]$elements(dates) do
        stream$putl(po, eris_date$from_date(d).format)
    end
end start_up


import std.stdio, std.datetime, std.conv, std.string;

immutable seasons = ["Chaos", "Discord", "Confusion",
                     "Bureaucracy", "The Aftermath"],
          weekday = ["Sweetmorn", "Boomtime", "Pungenday",
                     "Prickle-Prickle", "Setting Orange"],
          apostle = ["Mungday", "Mojoday", "Syaday",
                     "Zaraday", "Maladay"],
          holiday = ["Chaoflux", "Discoflux", "Confuflux",
                     "Bureflux", "Afflux"];

string discordianDate(in Date date) pure {
    immutable dYear = text(date.year + 1166);

    immutable isLeapYear = date.isLeapYear;
    if (isLeapYear && date.month == 2 && date.day == 29)
        return "St. Tib's Day, in the YOLD " ~ dYear;

    immutable doy = (isLeapYear && date.dayOfYear >= 60) ?
                    date.dayOfYear - 1 :
                    date.dayOfYear;

    immutable dsDay = (doy % 73)==0? 73:(doy % 73); // Season day.
    if (dsDay == 5)
        return apostle[doy / 73] ~ ", in the YOLD " ~ dYear;
    if (dsDay == 50)
        return holiday[doy / 73] ~ ", in the YOLD " ~ dYear;

    immutable dSeas = seasons[(((doy%73)==0)?doy-1:doy) / 73];
    immutable dWday = weekday[(doy - 1) % 5];

    return format("%s, day %s of %s in the YOLD %s",
                  dWday, dsDay, dSeas, dYear);
}

unittest {
    assert(Date(2010, 7, 22).discordianDate ==
           "Pungenday, day 57 of Confusion in the YOLD 3176");
    assert(Date(2012, 2, 28).discordianDate ==
           "Prickle-Prickle, day 59 of Chaos in the YOLD 3178");
    assert(Date(2012, 2, 29).discordianDate ==
           "St. Tib's Day, in the YOLD 3178");
    assert(Date(2012, 3, 1).discordianDate ==
           "Setting Orange, day 60 of Chaos in the YOLD 3178");
    assert(Date(2010, 1, 5).discordianDate ==
           "Mungday, in the YOLD 3176");
    assert(Date(2011, 5, 3).discordianDate ==
           "Discoflux, in the YOLD 3177");
}

void main(string args[]) {
    int yyyymmdd, day, mon, year, sign;
    if (args.length == 1) {
       (cast(Date)Clock.currTime).discordianDate.writeln;
       return;
    }
    foreach (i, arg; args) {
        if (i > 0) {
            //writef("%d: %s: ", i, arg);
            yyyymmdd = to!int(arg);
            if (yyyymmdd < 0) {
                sign = -1;
                yyyymmdd = -yyyymmdd;
            }
            else {
                sign = 1;
            }
            day = yyyymmdd % 100;
            if (day == 0) {
               day = 1;
            }
            mon = ((yyyymmdd - day) / 100) % 100;
            if (mon == 0) {
               mon = 1;
            }
            year = sign * ((yyyymmdd - day - 100*mon) / 10000);
            writefln("%s", Date(year, mon, day).discordianDate);
        }
    }
}


program Discordian_date;

{$APPTYPE CONSOLE}

uses
  System.SysUtils,
  System.DateUtils;

type
  TDateTimeHelper = record helper for TDateTime
    const
      seasons: array of string = ['Chaos', 'Discord', 'Confusion', 'Bureaucracy',
        'The Aftermath'];
      weekday: array of string = ['Sweetmorn', 'Boomtime', 'Pungenday',
        'Prickle-Prickle', 'Setting Orange'];
      apostle: array of string = ['Mungday', 'Mojoday', 'Syaday', 'Zaraday', 'Maladay'];
      holiday: array of string = ['Chaoflux', 'Discoflux', 'Confuflux',
        'Bureflux', 'Afflux'];
    function DiscordianDate(): string;
  end;

{ TDateTimeHelper }

function TDateTimeHelper.DiscordianDate: string;
var
  isLeapYear: boolean;
begin
  isLeapYear := IsInLeapYear(self);
  var dYear := (YearOf(self) + 1166).ToString;
  if isLeapYear and (MonthOf(self) = 2) and (dayof(self) = 29) then
    exit('St. Tib''s Day, in the YOLD ' + dYear);

  var doy := DayOfTheYear(self);
  if isLeapYear and (doy >= 60) then
    doy := doy - 1;

  var dsDay := doy mod 73;

  if dsDay = 0 then
    dsDay := 73;

  if dsDay = 5 then
    exit(apostle[doy div 73] + ', in the YOLD ' + dYear);

  if dsDay = 50 then
    exit(holiday[doy div 73] + ', in the YOLD ' + dYear);

  var dSeas: string;

  if (doy mod 73) = 0 then
    dSeas := seasons[(doy - 1) div 73]
  else
    dSeas := seasons[doy div 73];

  var dWday := weekday[(doy - 1) mod 5];

  Result := format('%s, day %d of %s in the YOLD %s', [dWday, dsDay, dSeas, dYear]);

end;

procedure Test();
begin
  Assert(EncodeDate(2010, 7, 22).DiscordianDate =
    'Pungenday, day 57 of Confusion in the YOLD 3176');
  Assert(EncodeDate(2012, 2, 28).DiscordianDate =
    'Prickle-Prickle, day 59 of Chaos in the YOLD 3178');
  Assert(EncodeDate(2012, 2, 29).DiscordianDate = 'St. Tib''s Day, in the YOLD 3178');
  Assert(EncodeDate(2012, 3, 1).DiscordianDate =
    'Setting Orange, day 60 of Chaos in the YOLD 3178');
  Assert(EncodeDate(2010, 1, 5).DiscordianDate = 'Mungday, in the YOLD 3176');
  Assert(EncodeDate(2011, 5, 3).DiscordianDate = 'Discoflux, in the YOLD 3177');
  writeln('OK');
end;

var
  dt: TDateTime;
  i: Integer;

begin
  if ParamCount = 0 then
  begin
    writeln(now.DiscordianDate);
    readln;
    halt;
  end;

  for i := 1 to ParamCount do
  begin
    if not TryStrToDate(ParamStr(i), dt) then
      Continue;
    writeln(dt.DiscordianDate);
  end;

  readln;
end.


function isLeapYear(integer year)
    return remainder(year,4)=0 and remainder(year,100)!=0 or remainder(year,400)=0
end function

constant YEAR = 1, MONTH = 2, DAY = 3, DAY_OF_YEAR = 8

constant month_lengths = {31,28,31,30,31,30,31,31,30,31,30,31}
function dayOfYear(sequence Date)
    integer d
    if length(Date) = DAY_OF_YEAR then
        d = Date[DAY_OF_YEAR]
    else
        d = Date[DAY]
        for i = Date[MONTH]-1 to 1 by -1 do
            d += month_lengths[i]
            if i = 2 and isLeapYear(Date[YEAR]) then
                d += 1
            end if
        end for
    end if
    return d
end function

constant seasons = {"Chaos", "Discord", "Confusion", "Bureaucracy", "The Aftermath"}
constant weekday = {"Sweetmorn", "Boomtime", "Pungenday", "Prickle-Prickle", "Setting Orange"}
constant apostle = {"Mungday", "Mojoday", "Syaday", "Zaraday", "Maladay"}
constant holiday = {"Chaoflux", "Discoflux", "Confuflux", "Bureflux", "Afflux"}

function discordianDate(sequence Date)
    sequence dyear, dseas, dwday
    integer  leap, doy, dsday
    dyear = sprintf("%d",Date[YEAR]+1166)
    leap = isLeapYear(Date[YEAR])
    if leap and Date[MONTH] = 2 and Date[DAY] = 29 then
        return "St. Tib's Day, in the YOLD " & dyear
    end if

    doy = dayOfYear(Date)
    if leap and doy >= 60 then
        doy -= 1
    end if

    dsday = remainder(doy,73)
    if dsday = 5 then
        return apostle[doy/73+1] & ", in the YOLD " & dyear
    elsif dsday = 50 then
        return holiday[doy/73+1] & ", in the YOLD " & dyear
    end if

    dseas = seasons[doy/73+1]
    dwday = weekday[remainder(doy-1,5)+1]

    return sprintf("%s, day %d of %s in the YOLD %s", {dwday, dsday, dseas, dyear})
end function

sequence today
today = date()
today[YEAR] += 1900
puts(1, discordianDate(today))


open System

let seasons = [| "Chaos"; "Discord"; "Confusion"; "Bureaucracy"; "The Aftermath" |]

let ddate (date:DateTime) =
    let dyear = date.Year + 1166
    let leapYear = DateTime.IsLeapYear(date.Year)

    if leapYear && date.Month = 2 && date.Day = 29 then
        sprintf "St. Tib's Day, %i YOLD" dyear
    else
        // compensate for St. Tib's Day
        let dayOfYear = (if leapYear && date.DayOfYear >= 60 then date.DayOfYear - 1 else date.DayOfYear) - 1
        let season, dday = Math.DivRem(dayOfYear, 73)
        sprintf "%s %i, %i YOLD" seasons.[season] (dday+1) dyear

[<EntryPoint>]
let main argv =
    let p = Int32.Parse
    Seq.ofArray("2012-02-28 2012-02-29 2012-03-01 2010-07-22 2015-10-19 2015-10-20".Split())
    |> Seq.map(fun (s:string) ->
        let d = s.Split('-')
        (s, DateTime(p(d.[0]),p(d.[1]),p(d.[2]))))
    |> Seq.iter(fun (s,d) -> printfn "%s is %s" s (ddate d))
    0


program discordianDate
    implicit none
    ! Declare variables
    character(32) :: arg
    character(15) :: season,day,holyday
    character(80) :: Output,fmt1,fmt2,fmt3
    character(2) :: dayfix,f1,f2,f3,f4
    integer :: i,j,k,daysofyear,dayofweek,seasonnum,yold,dayofseason,t1,t2,t3
    integer,dimension(8) :: values
    integer, dimension(12) :: daysinmonth
    logical  :: isleapyear, isholyday, Pleapyear

    ! Get the current date
    call date_and_time(VALUES=values)
    ! Set some values up to defaults
    daysinmonth = (/ 31,28,31,30,31,30,31,31,30,31,30,31 /)
    isleapyear = .false.
    isholyday = .false.
    ! process any command line arguments
    ! using arguments dd mm yyyy
    j = iargc()
    do i = 1, iargc()
      call getarg(i, arg)  ! fetches argument as a character string
      if (j==3) then
        if (i==1) then
          read(arg,'(i2)') values(3)  ! convert to integer
        endif
        if (i==2) then
          read(arg,'(i2)') values(2)  ! convert to integer
        endif
        if (i==3) then
          read(arg,'(i4)') values(1)  ! convert to integer

        endif
      endif
      if (j==2) then  ! arguments dd mm
        if (i==1) then
          read(arg,'(i2)') values(3)  ! convert to integer
        endif
        if (i==2) then
          read(arg,'(i2)') values(2)  ! convert to integer
        endif
      endif
      if (j==1) then ! argument dd
        read(arg,'(i2)') values(3)  ! convert to integer
      endif
    end do

    !Start the number crunching here

    yold = values(1) + 1166
    daysofyear = 0
    if (values(2)>1) then
    do i=1 , values(2)-1 , 1
        daysofyear = daysofyear + daysinmonth(i)
     end do
   end if
    daysofyear = daysofyear + values(3)
    isholyday = .false.
    isleapyear = Pleapyear(yold)
    dayofweek = mod (daysofyear, 5)
    seasonnum = ((daysofyear - 1) / 73) + 1
    dayofseason = daysofyear - ((seasonnum - 1)  * 73)
    k = mod(dayofseason,10)  ! just to get the day number postfix
    select case (k)
      case (1)
         dayfix='st'
      case (2)
         dayfix='nd'
      case (3)
         dayfix='rd'
      case default
         dayfix='th'
    end select
    ! except between 10th and 20th  where we always have 'th'
    if (((dayofseason > 10) .and. (dayofseason < 20)) .eqv. .true.) then
      dayfix = 'th'
    end if
    select case (Seasonnum)
      case (1)
        season ='Choas'
        f4 = '5'
      case (2)
        season ='Discord'
        f4 = '7'
      case (3)
        season ='Confusion'
        f4 = '9'
      case (4)
        season ='Bureaucracy'
        f4 = '10'
      case (5)
        season ='The Aftermath'
        f4 = '13'
    end select
    select case (dayofweek)
       case (0)
         day='Setting Orange'
         f2 = '14'
       case (1)
         day ='Sweetmorn'
         f2 = '9'
       case (2)
         day = 'Boomtime'
         f2 = '8'
       case (3)
         day = 'Pungenday'
         f2 = '9'
       case (4)
         day = 'Prickle-Prickle'
         f2 = '15'
    end select
    ! check for holydays
    select case (dayofseason)
      case (5)
        isholyday = .true.
        select case (seasonnum)
          case (1)
             holyday ='Mungday'
             f1 = '7'
          case (2)
             holyday = 'Mojoday'
             f1 = '7'
          case (3)
            holyday = 'Syaday'
            f1 = '6'
          case (4)
             holyday = 'Zaraday'
             f1 = '7'
          case (5)
            holyday = 'Maladay'
            f1 = '7'
         end select
      Case (50)
        isholyday = .true.
        select case (seasonnum)
          case (1)
            holyday = 'Chaoflux'
            f1 = '8'
          case (2)
            holyday = 'Discoflux'
            f1 = '9'
          case (3)
            holyday = 'Confuflux'
            f1 = '9'
          case (4)
            holyday = 'Bureflux'
            f1 = '8'
          case (5)
            holyday = 'Afflux'
            f1 = '6'
         end select
    end select

    ! Check if it is St. Tibbs day
    if (isleapyear .eqv. .true.) then
      if ((values(2) == 2) .and. (values(3) == 29)) then
         isholyday = .true.
      end if
    end if
    ! Construct our format strings
    f3 = "2"
    if (dayofseason < 10) then
      f3 = "1"
    end if
    fmt1 = "(a,i4)"
    fmt2 = "(A,a" // f1 // ",A,A" // f2 // ",A,I" // f3 // ",A2,A,A" // f4 // ",A,I4)"
    fmt3 = "(A,A" // f2 // ",A,I" // f3 //",A2,A,A" // f4 // ",A,I4)"
    ! print an appropriate line
    if (isholyday .eqv. .true.) then
      if (values(3) == 29) then
         print fmt1,'Celebrate for today is St. Tibbs Day in the YOLD ',yold
       else
         print fmt2, 'Today is ',holyday, ' on ',day,' the ',dayofseason,dayfix,' day of ',season,' in the YOLD ',yold
       end if
     else   ! not a holyday
         print fmt3, 'Today is ',day,' the ',dayofseason,dayfix, ' day of ',season,' in the YOLD ',yold
     end if
    end program discordianDate

    ! Function to check to see if this is a leap year returns true or false!

    function Pleapyear(dloy) result(leaper)
    implicit none
    integer, intent(in) :: dloy
    logical :: leaper
    leaper = .false.
    if (mod((dloy-1166),4) == 0)  then
      leaper = .true.
    end if
    if (mod((dloy-1166),100) == 0) then
        leaper = .false.
        if (mod((dloy-1166),400)==0) then
           leaper = .true.
        end if
    end if
    end function Pleapyear


#Include "datetime.bi"

meses:
Data "Chaos", "Discord", "Confusion", "Bureaucracy", "The Aftermath"
dias_laborales:
Data "Setting Orange", "Sweetmorn", "Boomtime", "Pungenday", "Prickle-Prickle"
dias_previos_al_1er_mes:
'   ene feb mar abr may  jun  jul  ago  sep  oct  nov  dic
Data 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334

Function cJuliano(d As Double) As Integer
    'no tiene en cuenta los años bisiestos (no es necesario para ddate)
    Dim tmp As Integer, i As Integer
    Restore dias_previos_al_1er_mes
    For i = 1 To Month(d)
        Read tmp
    Next i
    Function = tmp + Day(d)
End Function

Sub cDiscordiano(Fecha As String)
    Dim As Integer dyear, dday, j, jday
    Dim As Double tempfecha = Datevalue(Fecha)
    Dim As String ddate, dseason, dweekday

    dyear = Year(tempfecha) + 1166
    If (2 = Month(tempfecha)) And (29 = Day(tempfecha)) Then
        ddate = "Saint Tib's Day, " & Str(dyear) & " YOLD"
    Else
        jday = cJuliano(tempfecha)
        Restore meses
        For j = 1 To ((jday - 1) \ 73) + 1
            Read dseason
        Next j
        dday = (jday Mod 73)
        If 0 = dday Then dday = 73
        Restore dias_laborales
        For j = 1 To (jday Mod 5) + 1
            Read dweekday
        Next j
        ddate = dweekday & ", " & dseason & " " & Trim(Str(dday)) & ", " & Trim(Str(dyear)) & " YOLD"
    End If
    Print Fecha & " -> " & ddate
End Sub

cDiscordiano("01/01/2020")
cDiscordiano("05/01/2020")
cDiscordiano("28/02/2020")
cDiscordiano("01/03/2020")
cDiscordiano("22/07/2020")
cDiscordiano("31/12/2020")
cDiscordiano("20/04/2022")
cDiscordiano("24/05/2020")
cDiscordiano("29/02/2020")
cDiscordiano("15/07/2019")
cDiscordiano("19/03/2025")
Sleep


package ddate

import (
    "strconv"
    "strings"
    "time"
)

// Predefined formats for DiscDate.Format
const (
    DefaultFmt = "Pungenday, Discord 5, 3131 YOLD"
    OldFmt     = `Today is Pungenday, the 5th day of Discord in the YOLD 3131
Celebrate Mojoday`
)

// Formats passed to DiscDate.Format are protypes for formated dates.
// Format replaces occurrences of prototype elements (the constant strings
// listed here) with values corresponding to the date being formatted.
// If the date is St. Tib's Day, the string from the first date element
// through the last is replaced with "St. Tib's Day".
const (
    protoLongSeason  = "Discord"
    protoShortSeason = "Dsc"
    protoLongDay     = "Pungenday"
    protoShortDay    = "PD"
    protoOrdDay      = "5"
    protoCardDay     = "5th"
    protoHolyday     = "Mojoday"
    protoYear        = "3131"
)

var (
    longDay = []string{"Sweetmorn", "Boomtime", "Pungenday",
        "Prickle-Prickle", "Setting Orange"}
    shortDay   = []string{"SM", "BT", "PD", "PP", "SO"}
    longSeason = []string{
        "Chaos", "Discord", "Confusion", "Bureaucracy", "The Aftermath"}
    shortSeason = []string{"Chs", "Dsc", "Cfn", "Bcy", "Afm"}
    holyday     = [][]string{{"Mungday", "Chaoflux"}, {"Mojoday", "Discoflux"},
        {"Syaday", "Confuflux"}, {"Zaraday", "Bureflux"}, {"Maladay", "Afflux"}}
)

type DiscDate struct {
    StTibs bool
    Dayy   int // zero based day of year, meaningless if StTibs is true
    Year   int // gregorian + 1166
}

func New(eris time.Time) DiscDate {
    t := time.Date(eris.Year(), 1, 1, eris.Hour(), eris.Minute(),
        eris.Second(), eris.Nanosecond(), eris.Location())
    bob := int(eris.Sub(t).Hours()) / 24
    raw := eris.Year()
    hastur := DiscDate{Year: raw + 1166}
    if raw%4 == 0 && (raw%100 != 0 || raw%400 == 0) {
        if bob > 59 {
            bob--
        } else if bob == 59 {
            hastur.StTibs = true
            return hastur
        }
    }
    hastur.Dayy = bob
    return hastur
}

func (dd DiscDate) Format(f string) (r string) {
    var st, snarf string
    var dateElement bool
    f6 := func(proto, wibble string) {
        if !dateElement {
            snarf = r
            dateElement = true
        }
        if st > "" {
            r = ""
        } else {
            r += wibble
        }
        f = f[len(proto):]
    }
    f4 := func(proto, wibble string) {
        if dd.StTibs {
            st = "St. Tib's Day"
        }
        f6(proto, wibble)
    }
    season, day := dd.Dayy/73, dd.Dayy%73
    for f > "" {
        switch {
        case strings.HasPrefix(f, protoLongDay):
            f4(protoLongDay, longDay[dd.Dayy%5])
        case strings.HasPrefix(f, protoShortDay):
            f4(protoShortDay, shortDay[dd.Dayy%5])
        case strings.HasPrefix(f, protoCardDay):
            funkychickens := "th"
            if day/10 != 1 {
                switch day % 10 {
                case 0:
                    funkychickens = "st"
                case 1:
                    funkychickens = "nd"
                case 2:
                    funkychickens = "rd"
                }
            }
            f4(protoCardDay, strconv.Itoa(day+1)+funkychickens)
        case strings.HasPrefix(f, protoOrdDay):
            f4(protoOrdDay, strconv.Itoa(day+1))
        case strings.HasPrefix(f, protoLongSeason):
            f6(protoLongSeason, longSeason[season])
        case strings.HasPrefix(f, protoShortSeason):
            f6(protoShortSeason, shortSeason[season])
        case strings.HasPrefix(f, protoHolyday):
            if day == 4 {
                r += holyday[season][0]
            } else if day == 49 {
                r += holyday[season][1]
            }
            f = f[len(protoHolyday):]
        case strings.HasPrefix(f, protoYear):
            r += strconv.Itoa(dd.Year)
            f = f[4:]
        default:
            r += f[:1]
            f = f[1:]
        }
    }
    if st > "" {
        r = snarf + st + r
    }
    return
}


import Data.Time (isLeapYear)
import Data.Time.Calendar.MonthDay (monthAndDayToDayOfYear)
import Text.Printf (printf)

type Year = Integer
type Day = Int
type Month = Int

data DDate = DDate Weekday Season Day Year
           | StTibsDay Year deriving (Eq, Ord)

data Season = Chaos
            | Discord
            | Confusion
            | Bureaucracy
            | TheAftermath
            deriving (Show, Enum, Eq, Ord, Bounded)

data Weekday = Sweetmorn
             | Boomtime
             | Pungenday
             | PricklePrickle
             | SettingOrange
             deriving (Show, Enum, Eq, Ord, Bounded)

instance Show DDate where
  show (StTibsDay y) = printf "St. Tib's Day, %d YOLD" y
  show (DDate w s d y) = printf "%s, %s %d, %d YOLD" (show w) (show s) d y

fromYMD :: (Year, Month, Day) -> DDate
fromYMD (y, m, d)
  | leap && dayOfYear == 59 = StTibsDay yold
  | leap && dayOfYear >= 60 = mkDDate $ dayOfYear - 1
  | otherwise               = mkDDate dayOfYear
  where
    yold = y + 1166
    dayOfYear = monthAndDayToDayOfYear leap m d - 1
    leap = isLeapYear y

    mkDDate dayOfYear = DDate weekday season dayOfSeason yold
      where
        weekday = toEnum $ dayOfYear `mod` 5
        season = toEnum $ dayOfYear `div` 73
        dayOfSeason = 1 + dayOfYear `mod` 73


link printf

procedure main()
   Demo(2010,1,1)
   Demo(2010,7,22)
   Demo(2012,2,28)
   Demo(2012,2,29)
   Demo(2012,3,1)
   Demo(2010,1,5)
   Demo(2011,5,3)
   Demo(2012,2,28)
   Demo(2012,2,29)
   Demo(2012,3,1)
   Demo(2010,7,22)
   Demo(2012,12,22)
end

procedure Demo(y,m,d)      #: demo display
   printf("%i-%i-%i = %s\n",y,m,d,DiscordianDateString(DiscordianDate(y,m,d)))
end

record DiscordianDateRecord(year,yday,season,sday,holiday)

procedure DiscordianDate(year,month,day)  #: Convert normal date to Discordian
static  cal
initial cal := [31,28,31,30,31,30,31,31,30,31,30,31]

   ddate := DiscordianDateRecord(year+1166)
   every (ddate.yday := day - 1) +:= cal[1 to month-1]   # zero origin
   ddate.sday := ddate.yday

   if ddate.year % 4 = 2 &  month = 2 & day = 29 then
      ddate.holiday := 1   # Note: st tibs is outside of weekdays
   else {
      ddate.season  := (ddate.yday / 73) + 1
      ddate.sday := (ddate.yday % 73) + 1
      ddate.holiday := 1 + ddate.season * case ddate.sday of { 5 : 1; 50 : 2}
   }
   return ddate
end

procedure DiscordianDateString(ddate)   #: format a Discordian Date String
static days,seasons,holidays
initial {
   days := ["Sweetmorn","Boomtime","Pungenday","Prickle-Prickle","Setting Orange"]
   seasons := ["Chaos","Discord","Confusion","Bureaucracy","The Aftermath"]
   holidays := ["St. Tib's Day","Mungday","Chaoflux","Mojoday","Discoflux",
                "Syaday","Confuflux","Zaraday","Bureflux","Maladay","Afflux"]
   }

   return (( holidays[\ddate.holiday] || "," ) |
           ( days[1+ddate.yday%5] || ", day " ||
             ddate.sday || " of " || seasons[ddate.season])) ||
          " in the YOLD " || ddate.year
end


import java.util.Calendar;
import java.util.GregorianCalendar;

public class DiscordianDate {
    final static String[] seasons = {"Chaos", "Discord", "Confusion",
        "Bureaucracy", "The Aftermath"};

    final static String[] weekday = {"Sweetmorn", "Boomtime", "Pungenday",
        "Prickle-Prickle", "Setting Orange"};

    final static String[] apostle = {"Mungday", "Mojoday", "Syaday",
        "Zaraday", "Maladay"};

    final static String[] holiday = {"Chaoflux", "Discoflux", "Confuflux",
        "Bureflux", "Afflux"};

    public static String discordianDate(final GregorianCalendar date) {
        int y = date.get(Calendar.YEAR);
        int yold = y + 1166;
        int dayOfYear = date.get(Calendar.DAY_OF_YEAR);

        if (date.isLeapYear(y)) {
            if (dayOfYear == 60)
                return "St. Tib's Day, in the YOLD " + yold;
            else if (dayOfYear > 60)
                dayOfYear--;
        }

        dayOfYear--;

        int seasonDay = dayOfYear % 73 + 1;
        if (seasonDay == 5)
            return apostle[dayOfYear / 73] + ", in the YOLD " + yold;
        if (seasonDay == 50)
            return holiday[dayOfYear / 73] + ", in the YOLD " + yold;

        String season = seasons[dayOfYear / 73];
        String dayOfWeek = weekday[dayOfYear % 5];

        return String.format("%s, day %s of %s in the YOLD %s",
                dayOfWeek, seasonDay, season, yold);
    }

    public static void main(String[] args) {

        System.out.println(discordianDate(new GregorianCalendar()));

        test(2010, 6, 22, "Pungenday, day 57 of Confusion in the YOLD 3176");
        test(2012, 1, 28, "Prickle-Prickle, day 59 of Chaos in the YOLD 3178");
        test(2012, 1, 29, "St. Tib's Day, in the YOLD 3178");
        test(2012, 2, 1, "Setting Orange, day 60 of Chaos in the YOLD 3178");
        test(2010, 0, 5, "Mungday, in the YOLD 3176");
        test(2011, 4, 3, "Discoflux, in the YOLD 3177");
        test(2015, 9, 19, "Boomtime, day 73 of Bureaucracy in the YOLD 3181");
    }

    private static void test(int y, int m, int d, final String result) {
        assert (discordianDate(new GregorianCalendar(y, m, d)).equals(result));
    }
}


/**
 * All Hail Discordia! - this script prints Discordian date using system date.
 *
 * lang: JavaScript
 * author: jklu
 * contributors: JamesMcGuigan
 *
 * changelog:
 * - Modified to return same output syntax as unix ddate + module.exports - James McGuigan, 2/Chaos/3183
 *
 * source: https://rosettacode.org/wiki/Discordian_date#JavaScript
 */
var seasons = [
  "Chaos", "Discord", "Confusion",
  "Bureaucracy", "The Aftermath"
];
var weekday = [
  "Sweetmorn", "Boomtime", "Pungenday",
  "Prickle-Prickle", "Setting Orange"
];

var apostle = [
  "Mungday", "Mojoday", "Syaday",
  "Zaraday", "Maladay"
];

var holiday = [
  "Chaoflux", "Discoflux", "Confuflux",
  "Bureflux", "Afflux"
];

Date.prototype.isLeapYear = function() {
  var year = this.getFullYear();
  if( (year & 3) !== 0 ) { return false; }
  return ((year % 100) !== 0 || (year % 400) === 0);
};

// Get Day of Year
Date.prototype.getDOY = function() {
  var dayCount  = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];
  var mn        = this.getMonth();
  var dn        = this.getDate();
  var dayOfYear = dayCount[mn] + dn;
  if( mn > 1 && this.isLeapYear() ) { dayOfYear++; }
  return dayOfYear;
};

Date.prototype.isToday = function() {
  var today = new Date();
  return this.getDate()     === today.getDate()
      && this.getMonth()    === today.getMonth()
      && this.getFullYear() === today.getFullYear()
  ;
};

function discordianDate(date) {
  if( !date ) { date = new Date(); }

  var y                = date.getFullYear();
  var yold             = y + 1166;
  var dayOfYear        = date.getDOY();
  var celebrateHoliday = null;

  if( date.isLeapYear() ) {
    if( dayOfYear == 60 ) {
      celebrateHoliday = "St. Tib's Day";
    }
    else if( dayOfYear > 60 ) {
      dayOfYear--;
    }
  }
  dayOfYear--;

  var divDay = Math.floor(dayOfYear / 73);

  var seasonDay = (dayOfYear % 73) + 1;
  if( seasonDay == 5 ) {
    celebrateHoliday = apostle[divDay];
  }
  if( seasonDay == 50 ) {
    celebrateHoliday = holiday[divDay];
  }

  var season    = seasons[divDay];
  var dayOfWeek = weekday[dayOfYear % 5];

  var nth = (seasonDay % 10 == 1) ? 'st'
          : (seasonDay % 10 == 2) ? 'nd'
          : (seasonDay % 10 == 3) ? 'rd'
                                  : 'th';

  return "" //(date.isToday() ? "Today is " : '')
         + dayOfWeek
         + ", the " + seasonDay + nth
         + " day of " + season
         + " in the YOLD " + yold
         + (celebrateHoliday ? ". Celebrate " + celebrateHoliday + "!" : '')
    ;
}

function test(y, m, d, result) {
  console.assert((discordianDate(new Date(y, m, d)) == result), [y, m, d, discordianDate(new Date(y, m, d)), result]);
}

// Only run test code if node calls this file directly
if( require.main === module ) {
  console.log(discordianDate(new Date(Date.now())));
  test(2010, 6, 22, "Pungenday, the 57th day of Confusion in the YOLD 3176");
  test(2012, 1, 28, "Prickle-Prickle, the 59th day of Chaos in the YOLD 3178");
  test(2012, 1, 29, "Setting Orange, the 60th day of Chaos in the YOLD 3178. Celebrate St. Tib's Day!");
  test(2012, 2,  1, "Setting Orange, the 60th day of Chaos in the YOLD 3178");
  test(2010, 0,  5, "Setting Orange, the 5th day of Chaos in the YOLD 3176. Celebrate Mungday!");
  test(2011, 4,  3, "Pungenday, the 50th day of Discord in the YOLD 3177. Celebrate Discoflux!");
  test(2015, 9, 19, "Boomtime, the 73rd day of Bureaucracy in the YOLD 3181");
}

module.exports = discordianDate;


using Dates

function discordiandate(year::Integer, month::Integer, day::Integer)
    discordianseasons = ["Chaos", "Discord", "Confusion", "Bureaucracy", "The Aftermath"]
    holidays = Dict(
        "Chaos 5" => "Mungday",
        "Chaos 50" => "Chaoflux",
        "Discord 5" => "Mojoday",
        "Discord 50" => "Discoflux",
        "Confusion 5" => "Syaday",
        "Confusion 50" => "Confuflux",
        "Bureaucracy 5" => "Zaraday",
        "Bureaucracy 50" => "Bureflux",
        "The Aftermath 5" => "Maladay",
        "The Aftermath 50" => "Afflux")
    today = Date(year, month, day)
    isleap = isleapyear(year)
    if isleap && month == 2 && day == 29
        rst = "St. Tib's Day, YOLD " * string(year + 1166)
    else
        dy = dayofyear(today)
        if isleap && dy >= 60
            dy -= 1
        end
        rst = string(discordianseasons[div(dy, 73) + 1], " ", rem(dy, 73)) # day
        if haskey(holidays, rst)
            rst *= " ($(holidays[rst]))" # if holiday
        end
        rst *= ", YOLD $(year + 1166)" # year
    end
    return rst
end

@show discordiandate(2017, 08, 15)
@show discordiandate(1996, 02, 29)
@show discordiandate(1996, 02, 19)


import java.util.Calendar
import java.util.GregorianCalendar

enum class Season {
    Chaos, Discord, Confusion, Bureaucracy, Aftermath;
    companion object { fun from(i: Int) = values()[i / 73] }
}
enum class Weekday {
    Sweetmorn, Boomtime, Pungenday, Prickle_Prickle, Setting_Orange;
    companion object { fun from(i: Int) = values()[i % 5] }
}
enum class Apostle {
    Mungday, Mojoday, Syaday, Zaraday, Maladay;
    companion object { fun from(i: Int) = values()[i / 73] }
}
enum class Holiday {
    Chaoflux, Discoflux, Confuflux, Bureflux, Afflux;
    companion object { fun from(i: Int) = values()[i / 73] }
}

fun GregorianCalendar.discordianDate(): String {
    val y = get(Calendar.YEAR)
    val yold = y + 1166

    var dayOfYear = get(Calendar.DAY_OF_YEAR)
    if (isLeapYear(y)) {
        if (dayOfYear == 60)
            return "St. Tib's Day, in the YOLD " + yold
        else if (dayOfYear > 60)
            dayOfYear--
    }

    val seasonDay = --dayOfYear % 73 + 1
    return when (seasonDay) {
        5 -> "" + Apostle.from(dayOfYear) + ", in the YOLD " + yold
        50 -> "" + Holiday.from(dayOfYear) + ", in the YOLD " + yold
        else -> "" + Weekday.from(dayOfYear) + ", day " + seasonDay + " of " + Season.from(dayOfYear) + " in the YOLD " + yold
    }
}

internal fun test(y: Int, m: Int, d: Int, result: String) {
    assert(GregorianCalendar(y, m, d).discordianDate() == result)
}

fun main(args: Array<String>) {
    println(GregorianCalendar().discordianDate())

    test(2010, 6, 22, "Pungenday, day 57 of Confusion in the YOLD 3176")
    test(2012, 1, 28, "Prickle-Prickle, day 59 of Chaos in the YOLD 3178")
    test(2012, 1, 29, "St. Tib's Day, in the YOLD 3178")
    test(2012, 2, 1, "Setting Orange, day 60 of Chaos in the YOLD 3178")
    test(2010, 0, 5, "Mungday, in the YOLD 3176")
    test(2011, 4, 3, "Discoflux, in the YOLD 3177")
    test(2015, 9, 19, "Boomtime, day 73 of Bureaucracy in the YOLD 3181")
}


          R   DISCORDIAN DATE CALCULATION
          R
          R   PUNCH CARD SHOULD CONTAIN -
          R   MM/DD/YYYY
          R   IN GREGORIAN CALENDAR

            NORMAL MODE IS INTEGER

            VECTOR VALUES MLENGT =
          0   0,0,31,59,90,120,151,181,212,243,273,304,334
            VECTOR VALUES TIBS =
          0   $31HSAINT TIBS DAY IN THE Y.O.L.D. ,I4*$
            VECTOR VALUES DISDAT =
          0   $C,6H, DAY ,I2,S1,3HOF ,C,S1,16HIN THE Y.O.L.D. ,I4*$

            VECTOR VALUES DISDAY = $SWEETMORN$, $BOOMTIME$,
          0   $PUNGENDAY$, $PRICKLE-PRICKLE$, $SETTING ORANGE$
            VECTOR VALUES DISSSN = $CHAOS$, $DISCORD$,
          0   $CONFUSION$, $BUREAUCRACY$, $THE AFTERMATH$

            VECTOR VALUES CLDAY  = $10HCELEBRATE ,C,3HDAY*$
            VECTOR VALUES CLFLUX = $10HCELEBRATE ,C,4HFLUX*$
            VECTOR VALUES HOLY5  = $MUNG$,$MOJO$,$SYA$,$ZARA$,$MALA$
            VECTOR VALUES HOLY50 = $CHAO$,$DISCO$,$CONFU$,$BURE$,$AF$

            READ FORMAT GREG,GMONTH,GDAY,GYEAR
            VECTOR VALUES GREG = $2(I2,1H/),I4*$

            WHENEVER GMONTH.E.2 .AND. GDAY.E.29
                PRINT FORMAT TIBS, GYEAR + 1166
            OTHERWISE
                YRDAY = MLENGT(GMONTH)+GDAY
                SEASON = YRDAY/73
                DAY = YRDAY-SEASON*73
                WKDAY = (YRDAY-1)-(YRDAY-1)/5*5
                PRINT FORMAT DISDAT, DISDAY(WKDAY), DAY,
          0         DISSSN(SEASON), GYEAR + 1166
                WHENEVER DAY.E.5
                    PRINT FORMAT CLDAY, HOLY5(SEASON)
                OR WHENEVER DAY.E.50
                    PRINT FORMAT CLFLUX, HOLY50(SEASON)
                END OF CONDITIONAL
            END OF CONDITIONAL
            END OF PROGRAM


convertDiscordian := proc(year, month, day)
	local days31, days30, daysThisYear, i, dYear, dMonth, dDay, seasons, week, dayOfWeek;
	days31 := [1, 3, 5, 7, 8, 10, 12];
	days30 := [4, 6, 9, 11];
	if month < 1 or month >12 then
		error "Invalid month: %1", month;
	end if;
	if (member(month, days31) and day > 31) or (member(month, days30) and day > 30) or (month = 2 and day > 29) or day < 1 then
		error "Invalid date: %1", day;
	end if;
	dYear := year + 1166;
	if month = 2 and day = 29 then
		printf("The date is St. Tib's Day, YOLD %a.\n", dYear);
	else
		seasons := ["Chaos", "Discord", "Confusion", "Bureaucracy", "The Aftermath"];
		week := ["Sweetmorn", "Boomtime", "Pungenday", "Prickle-Prickle", "Setting Orange"];
		daysThisYear := 0;
		for i to month-1 do
			if member(i, days31) then
				daysThisYear := daysThisYear + 31;
			elif member(i, days30) then
				daysThisYear := daysThisYear + 30;
			else
				daysThisYear := daysThisYear + 28;
			end if;
		end do;
		daysThisYear := daysThisYear + day -1;
		dMonth := seasons[trunc((daysThisYear) / 73)+1];
		dDay := daysThisYear mod 73 +1;
		dayOfWeek := week[daysThisYear mod 5 +1];
		printf("The date is %s %s %s, YOLD %a.\n", dayOfWeek, dMonth, convert(dDay, ordinal), dYear);
	end if;
end proc:

convertDiscordian (2016, 1, 1);
convertDiscordian (2016, 2, 29);
convertDiscordian (2016, 12, 31);


DiscordianDate[y_, m_, d_] := Module[{year = ToString[y + 1166], month = m, day = d},

  DMONTHS = {"Chaos", "Discord", "Confusion", "Bureaucracy", "The Aftermath"};
  DDAYS = {"Sweetmorn", "Boomtime", "Pungenday", "Prickle-Prickle", "Setting Orange"};
  DayOfYear = DateDifference[{y} ,{y, m, d}] + 1;
  LeapYearQ = (Mod[#, 4]== 0 && (Mod[#, 100] != 0 || Mod[#, 400] == 0))&@ y;

  If [ LeapYearQ && month == 2 && day == 29,
    Print["Today is St. Tib's Day, YOLD ", ]
  ,
    If [ LeapYearQ && DayOfYear >= 60, DayOfYear -= 1 ];
     {season, dday} = {Quotient[DayOfYear, 73], Mod[DayOfYear, 73]};
     Print["Today is ", DDAYS[[Mod[dday,4] + 1]],", ",DMONTHS[[season+1]]," ",dday,", YOLD ",year]
    ];
]


import times, strformat

const

  DiscordianOrigin = -1166
  SaintTibsDay = "St. Tib’s Day"

type

  Season = enum
    sCha = (1, "Chaos"), sDis = "Discord", sCon = "Confusion",
    sBur = "Bureaucracy", sAft = "The Aftermath"

  ErisianWeekDay = enum
    eSwe = "Sweetmorn", eBoo = "Boomtime", ePun = "Pungenday",
    ePri = "Prickle-Prickle", eSet = "Setting Orange"

  SeasonDayRange = range[1..73]

  # Description of a discordian date.
  DiscordianDate = object
    year: int
    yearday: YeardayRange
    case isSaintTibsDay: bool
    of false:
      seasondayZero: int
      seasonZero: int
      weekday: ErisianWeekDay
    else:
      nil

#---------------------------------------------------------------------------------------------------

proc toDiscordianDate(gdate: DateTime): DiscordianDate =
  ## Convert a DateTime to a discordian date.
  ## All the time fields are ignored.

  # Create the object.
  result = DiscordianDate(isSaintTibsDay: gdate.isLeapDay)

  # The yearday field is unchanged.
  result.yearday = gdate.yearday

  # The year is simply translated.
  result.year = gdate.year - DiscordianOrigin

  # For remaining fields, we must take in account leap years.
  if not result.isSaintTibsDay:
    var yearday = result.yearday
    if gdate.year.isLeapYear and result.yearday > 59:
      dec yearday
    # Now, we have simply to use division and modulo using the corrected yearday.
    result.seasonZero = yearday div SeasonDayRange.high
    result.seasondayZero = yearday mod SeasonDayRange.high
    result.weekday = ErisianWeekDay(yearday mod 5)

#---------------------------------------------------------------------------------------------------

proc `$`(date: DiscordianDate): string =
  ## Convert a discordian date to a string.
  if date.isSaintTibsDay:
    result = SaintTibsDay
  else:
    result = fmt"{date.weekday}, {Season(date.seasonZero + 1)} {date.seasondayZero + 1}"
  result &= fmt", {date.year} YOLD"

#---------------------------------------------------------------------------------------------------

proc showDiscordianDate(year, month, day: Natural) =
  ## Show the discordian date corresponding to a gregorian date.
  let gdate = initDateTime(year = year, month = Month(month), monthday = day,
                           hour = 0, minute = 0, second = 0)
  echo gdate.format("YYYY-MM-dd"), ": ", $gdate.toDiscordianDate()

#———————————————————————————————————————————————————————————————————————————————————————————————————

showDiscordianDate(2100, 12, 31)
showDiscordianDate(2012, 02, 28)
showDiscordianDate(2012, 02, 29)
showDiscordianDate(2012, 03, 01)
showDiscordianDate(2010, 07, 22)
showDiscordianDate(2012, 09, 02)
showDiscordianDate(2012, 12, 31)


program ddate;
{
This program is free software, it's done it's time
and paid for it's crime. You can copy, edit and use this
software under the terms of the GNU GPL v3 or later.

Copyright Pope Englebert Finklestien,
On this day Boomtime, the 71st day of Confusion in the YOLD 3183

This program will print out the current date in Erisian format as specified in

                  P R I N C I P I A   D I S C O R D I A

If you run it with a date it the command line in european format (dd mm yy) it
will print the equvolent Discordian date. If you omit the year and  month the
current Anerisiean month and year as assumed.

POPE Englebert Finklestien.
}
uses Sysutils;

var
  YY,MM,DD : word;
  YOLD : Boolean;
  Hedgehog: integer;
  Eris: string;
  snub: string;
  chaotica: string;
  midget: string;
  bob: string;

  Anerisiandaysinmonth: array[1..12] of integer = (31,28,31,30,31,30,31,31,30,31,30,31);

procedure anerisiandate;
{ tHIS JUST GETS THE DATE INTO THE  DD,MM,YY VARIABLES }
begin
    DeCodeDate(date,yy,mm,dd);
end;

procedure BORIS;
{ This just tests to see if we are in a leap year }
var
  snafu : boolean;
begin
  snafu := False;
  if (yy mod 4 = 0) then snafu := True;
  if ((yy mod 100 = 0) and (yy mod 400 <> 0)) then snafu := False;
  if ((snafu) and (mm = 2 ) and (dd=29)) then YOLD := True;
end;

function hodgepodge: integer;
{ This returns the total number of days since the year began.
It doesn't bother with leap years at all.
I get a wierd optical illusion looking at the until in this }
var
  fnord : integer;
begin
   Hedgehog := 1;
   hodgepodge := 0;
   fnord :=0;
   if (mm > 1) then repeat
             fnord := fnord + Anerisiandaysinmonth[Hedgehog];
             Hedgehog := Hedgehog +1;
       until Hedgehog = mm;
   fnord := fnord + dd;
   hodgepodge := fnord;
 end;

function treesaregreen(): string;
{Returns the YOLD as a string}

begin
    treesaregreen := IntTOStr(yy+1166);
end;

procedure GRAYFACE;
{This calculates everything, but does not bother much about leap years}
var
   wrestle: integer;
   Thwack: string;
begin
    Hedgehog := hodgepodge;
    wrestle := 0;
    Thwack := 'th';
    {set bob to the name of the holyday or St. Tibs day }
    bob := 'St. Tibs Day';
    if (Hedgehog = 5 )  then bob := 'Mungday';
    if (Hedgehog = 50 ) then bob := 'Chaoflux';
    if (Hedgehog = 78 ) then bob := 'Mojoday';
    if (Hedgehog = 123) then bob := 'Discoflux';
    if (Hedgehog = 151) then bob := 'Syaday';
    if (Hedgehog = 196) then bob := 'Confuflux';
    if (Hedgehog = 224) then bob := 'Zaraday';
    if (Hedgehog = 269) then bob := 'Bureflux';
    if (Hedgehog = 297) then bob := 'Maladay';
    if (Hedgehog = 342) then bob := 'Afflux';
    {Not doing things the usual way
    Lets find the week day and count the number of
	5 day weeks all at the same time}
    while (Hedgehog > 5) do begin
      Hedgehog := Hedgehog -5;
      wrestle := Wrestle + 1;
    end;
    if (Hedgehog = 1) then snub := 'Sweetmorn'  ;
    if (Hedgehog = 2) then snub := 'BoomTime';
    if (Hedgehog = 3) then snub := 'Pungenday';
    if (Hedgehog = 4) then snub := 'Prickle-Prickle';
    if (Hedgehog = 5) then snub := 'Setting Orange';
	{Now to set the Season name}
    chaotica:='The Aftermath';
    if (wrestle <=57) then chaotica := 'Bureaucracy';
    if ((wrestle = 58) and (Hedgehog < 3)) then chaotica := 'Bureaucracy';
    if (wrestle <= 42) then chaotica := 'Confusion';
    if ((wrestle = 43) and (Hedgehog < 5)) then chaotica := 'Confusion';
    if (wrestle <=28) then chaotica := 'Discord';
    if ((wrestle = 29) and (Hedgehog < 2)) then chaotica := 'Discord';
    if (wrestle <=13) then chaotica := 'Chaos';
    if ((wrestle = 14) and (Hedgehog < 4)) then chaotica := 'Chaos';

	{Now all we need the day of the season}
    wrestle := (wrestle*5)+Hedgehog;
    while (wrestle >73) do wrestle := wrestle -73;
	{pick the appropriate day postfix, allready set to th}
    if (wrestle in [1,21,31,41,51,61,71]) then Thwack:='st';
    if (wrestle in [2,22,32,42,52,62,72]) then Thwack:='nd';
    if (wrestle in [3,23,33,43,53,63,73]) then Thwack:='rd';
	{Check to see if it is a holy day, if so bob will have
	the right holyday name already, including St Tibs Day}
    if (wrestle in [5,50]) then YOLD := True;
	{I love this line of code}
    midget := IntToStr(wrestle) + Thwack;
end;

{The main program starts here}
begin
    anerisiandate;
    if (ParamCount >=1) then dd := StrTOInt(ParamStr(1));
    if (ParamCount >=2) then mm := StrToInt(ParamStr(2));
    if (ParamCount =3) then yy := StrToInt(ParamStr(3));
    BORIS;
    GRAYFACE;
	{ The only thing to bother about is holy days and St Tibs day }
    Eris := 'Today is: ' + snub +' the ' + midget +' day of the season of ' + chaotica;
    if (YOLD) then begin
	    Eris := 'Celebrate for today, ' + snub + ' the ' + midget + ' day of ' +chaotica + ' is the holy day of ' + bob;
    end;
	{The only place we deal with St. Tibs Day}
    if ((YOLD) and ((mm=2) and (dd=29))) then Eris := 'Celebrate ' + bob + ' Chaos';
	{This next line applies to all possibilities}
    Eris := Eris + ' YOLD ' + treesaregreen;
    WriteLn(Eris);
end.


use 5.010;
use strict;
use warnings;
use Time::Piece ();

my @seasons = (qw< Chaos Discord Confusion Bureaucracy >, 'The Aftermath');
my @week_days = (qw< Sweetmorn Boomtime Pungenday Prickle-Prickle >, 'Setting Orange');

sub ordinal {
	my ($n) = @_;
	return $n . "th" if int($n/10) == 1;
	return $n . ((qw< th st nd rd th th th th th th>)[$n % 10]);
}

sub ddate {
	my $d = Time::Piece->strptime( $_[0], '%Y-%m-%d' );
	my $yold = 'in the YOLD ' . ($d->year + 1166);

	my $day_of_year0 = $d->day_of_year;

	if( $d->is_leap_year ) {
		return "St. Tib's Day, $yold" if $d->mon == 2 and $d->mday == 29;
		$day_of_year0-- if $day_of_year0 >= 60; # Compensate for St. Tib's Day
	}

	my $weekday = $week_days[ $day_of_year0 % @week_days ];
	my $season = $seasons[ $day_of_year0 / 73 ];
	my $season_day = ordinal( $day_of_year0 % 73 + 1 );

	return "$weekday, the $season_day day of $season $yold";
}

say "$_ is " . ddate($_) for qw< 2010-07-22 2012-02-28 2012-02-29 2012-03-01 >;


with javascript_semantics
constant seasons = {"Chaos", "Discord", "Confusion", "Bureaucracy", "The Aftermath"},
         weekday = {"Sweetmorn", "Boomtime", "Pungenday", "Prickle-Prickle", "Setting Orange"},
         apostle = {"Mungday", "Mojoday", "Syaday", "Zaraday", "Maladay"},
         holiday = {"Chaoflux", "Discoflux", "Confuflux", "Bureflux", "Afflux"}

function discordianDate(sequence dt)
    integer {y,m,d} = dt,
            leap = is_leap_year(y),
            doy = day_of_year(y,m,d)-1
    string dyear = sprintf("%d",y+1166)
    if leap and m=2 and d=29 then
        return "St. Tib's Day, in the YOLD " & dyear
    end if
    if leap and doy>=60 then
        doy -= 1
    end if
    integer dsday = remainder(doy,73)+1,
            dseason = floor(doy/73+1)
    if dsday=5 then
        return apostle[dseason] & ", in the YOLD " & dyear
    elsif dsday=50 then
        return holiday[dseason] & ", in the YOLD " & dyear
    end if
    string dseas = seasons[dseason],
           dwday = weekday[remainder(doy,5)+1]
    return sprintf("%s, day %d of %s in the YOLD %s", {dwday, dsday, dseas, dyear})
end function

-- test code

sequence dt = {2015,1,1,0,0,0,0,0}
include timedate.e
atom oneday = timedelta(days:=1)
set_timedate_formats({"DD/MM/YYYY"})
for i=1 to 5 do
    printf(1,"%s: %s\n",{format_timedate(dt),discordianDate(dt)})
    dt = adjust_timedate(dt,oneday*72)
    printf(1,"%s: %s\n",{format_timedate(dt),discordianDate(dt)})
    dt = adjust_timedate(dt,oneday)
end for


<?php
    $Anerisia = array(31,28,31,30,31,30,31,31,30,31,30,31);
    $MONTHS = array("Choas","Discord","Confusion","Bureacracy","The Aftermath");
    $DAYS = array("Setting Orange","Sweetmorn","BoomTime","Pungenday","Prickle-Prickle");
    $Dsuff = array('th','st','nd','rd','th','th','th','th','th','th');
    $Holy5 = array("Mungday","MojoDay","Syaday","Zaraday","Maladay");
    $Holy50 = array("Chaoflux","Discoflux","Confuflux","Bureflux","Afflux");

    // Get the current system date and assign to some variables
	$edate = explode(" ",date('Y m j L'));
	$usery = $edate[0];
	$userm = $edate[1];
	$userd = $edate[2];
	$IsLeap = $edate[3];

  // If the user supplied us with a date overide the one we got from the system.
  // If you could get the date from users browser via javascript and then call
  // this script with the users date. ddate.php?y=year&m=month&d=day mostly it
  // won't matter but if the server is in a different time zone to the user
  // There will be occasional incorrect results from the users POV.

    if (isset($_GET['y']) && isset($_GET['m']) && isset($_GET['d'])) {
        $usery = $_GET['y'];
        $userm = $_GET['m'];
        $userd = $_GET['d'];
        $IsLeap = 0;
        if (($usery%4 == 0) && ($usery%100 >0)) $IsLeap =1;
        if ($usery%400 == 0) $IsLeap = 1;
    }

 // We need to know the total number of days in the year so far

    $userdays = 0;
    $i = 0;
    while ($i < ($userm-1)) {

        $userdays = $userdays + $Anerisia[$i];
        $i = $i +1;
    }
    $userdays = $userdays + $userd;

    // We can now work out the full discordian date for most dates
    // PHP does not do integer division, so we use 73.2 as a divisor
    // the value 73.2 works, larger values cause an off-by-one on season
    // changes for the later seasons .
    // This is not needed with the mod operator.

    $IsHolyday = 0;
    $dyear = $usery + 1166;
    $dmonth = $MONTHS[$userdays/73.2];
    $dday = $userdays%73;
	if (0 == $dday) $dday = 73;
    $Dname = $DAYS[$userdays%5];
    $Holyday = "St. Tibs Day";
    if ($dday == 5) {
        $Holyday = $Holy5[$userdays/73.2];
        $IsHolyday =1;
    }
    if ($dday == 50) {
        $Holyday = $Holy50[$userdays/73.2];
        $IsHolyday =1;
    }

  if (($IsLeap ==1) && ($userd ==29) and ($userm ==2)) $IsHolyday = 2;

   // work out the suffix to the day number
   $suff = $Dsuff[$dday%10] ;
   if ((11 <= $dday) && (19 >= $dday)) $suff='th';

    // code to display the date ...

 if ($IsHolyday ==2)
      echo "
Celeberate ",$Holyday," ",$dmonth," YOLD ",$dyear;
    if ($IsHolyday ==1)
      echo "
Celeberate for today ", $Dname , " The ", $dday,"",$suff,"", " day of ", $dmonth , " YOLD " , $dyear , " is the holy day of " , $Holyday;
    if ($IsHolyday == 0)
       echo "
Today is " , $Dname , " the " , $dday ,"",$suff, " day of " , $dmonth , " YOLD " , $dyear;

 ?>


(de disdate (Year Month Day)
   (let? Date (date Year Month Day)
      (let (Leap (date Year 2 29)  D (- Date (date Year 1 1)))
         (if (and Leap (= 2 Month) (= 29 Day))
            (pack "St. Tib's Day, YOLD " (+ Year 1166))
            (and Leap (>= D 60) (dec 'D))
            (pack
               (get
                  '("Chaos" "Discord" "Confusion" "Bureaucracy" "The Aftermath")
                  (inc (/ D 73)) )
               " "
               (inc (% D 73))
               ", YOLD "
               (+ Year 1166) ) ) ) ) )


> Calendar.Discordian.now()->format_ext_ymd();
 Result: "Pungenday, 59 Bureaucracy 3177"
> Calendar.Discordian.Day(Calendar.Day(2011,11,11))->format_ext_ymd();
 Result: "Setting Orange, 23 The Aftermath 3177"
> Calendar.Discordian.Day(Calendar.Badi.Day(168,13,9))->format_ext_ymd();
 Result: "Setting Orange, 23 The Aftermath 3177"
> Calendar.Day((Calendar.Discordian.Month()+1)->day(1));
 Result: Day(Thu 20 Oct 2011)


#COMPILE EXE
#DIM ALL

'change this for systems that use a different character
$DATESEP = "-"

FUNCTION day(date AS STRING) AS LONG
    'date is same format as date$
    DIM tmpdash1 AS LONG, tmpdash2 AS LONG
    tmpdash1 = INSTR(date, $DATESEP)
    tmpdash2 = INSTR(-1, date, $DATESEP)
    FUNCTION = VAL(MID$(date, tmpdash1 + 1, tmpdash2 - tmpdash1 - 1))
END FUNCTION

FUNCTION month(date AS STRING) AS LONG
    'date is same format as date$
    DIM tmpdash AS LONG
    tmpdash = INSTR(date, $DATESEP)
    FUNCTION = VAL(LEFT$(date, tmpdash - 1))
END FUNCTION

FUNCTION year(date AS STRING) AS LONG
    'date is same format as date$
    DIM tmpdash AS LONG
    tmpdash = INSTR(-1, date, $DATESEP)
    FUNCTION = VAL(MID$(date, tmpdash + 1))
END FUNCTION

FUNCTION julian(date AS STRING) AS LONG
    'date is same format as date$
    'doesn't account for leap years (not needed for ddate)
    'days preceding 1st of month
    '   jan feb mar apr may  jun  jul  aug  sep  oct  nov  dec
    DATA 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
    FUNCTION = VAL(READ$(month(date))) + day(date)
END FUNCTION

FUNCTION PBMAIN () AS LONG
    'Season names
    DATA "Chaos", "Discord", "Confusion", "Bureaucracy", "The Aftermath"
    'Weekdays
    DATA "Setting Orange", "Sweetmorn", "Boomtime", "Pungenday", "Prickle-Prickle"

    DIM dyear AS LONG, dseason AS STRING, dday AS LONG, dweekday AS STRING
    DIM tmpdate AS STRING, jday AS LONG, result AS STRING

    IF LEN(COMMAND$) THEN
        tmpdate = COMMAND$
    ELSE
        tmpdate = DATE$
    END IF
    dyear = year(tmpdate) + 1166
    IF (2 = month(tmpdate)) AND (29 = day(tmpdate)) THEN
        result = "Saint Tib's Day, " & STR$(dyear) & " YOLD"
    ELSE
        jday = julian(tmpdate)
        dseason = READ$((jday \ 73) + 1)
        dday = (jday MOD 73)
        IF 0 = dday THEN dday = 73
        dweekday = READ$((jday MOD 5) + 6)
        result = dweekday & ", " & dseason & " " & TRIM$(STR$(dday)) & ", " & TRIM$(STR$(dyear)) & " YOLD"
    END IF

    ? result
END FUNCTION


function ConvertTo-Discordian ( [datetime]$GregorianDate )
{
$DayOfYear = $GregorianDate.DayOfYear
$Year = $GregorianDate.Year + 1166
If ( [datetime]::IsLeapYear( $GregorianDate.Year ) -and $DayOfYear -eq 60 )
    { $Day = "St. Tib's Day" }
Else
    {
    If ( [datetime]::IsLeapYear( $GregorianDate.Year ) -and $DayOfYear -gt 60 )
        { $DayOfYear-- }
    $Weekday = @( 'Sweetmorn', 'Boomtime', 'Pungenday', 'Prickle-Prickle', 'Setting Orange' )[(($DayOfYear - 1 ) % 5 )]
    $Season  = @( 'Chaos', 'Discord', 'Confusion', 'Bureaucracy', 'The Aftermath' )[( [math]::Truncate( ( $DayOfYear - 1 ) / 73 ) )]
    $DayOfSeason = ( $DayOfYear - 1 ) % 73 + 1
    $Day = "$Weekday, $Season $DayOfSeason"
    }
$DiscordianDate = "$Day, $Year YOLD"
return $DiscordianDate
}

ConvertTo-Discordian ([datetime]'1/5/2016')
ConvertTo-Discordian ([datetime]'2/29/2016')
ConvertTo-Discordian ([datetime]'12/8/2016')


% See https://en.wikipedia.org/wiki/Discordian_calendar

main:-
    test(2022, 4, 20),
    test(2020, 5, 24),
    test(2020, 2, 29),
    test(2019, 7, 15),
    test(2025, 3, 19),
    test(2017, 12, 8).

test(Gregorian_year, Gregorian_month, Gregorian_day):-
    ddate(Gregorian_year, Gregorian_month, Gregorian_day,
          Discordian_date),
    format('~|~`0t~d~2+-~|~`0t~d~2+-~|~`0t~d~2+: ~w~n', [Gregorian_year,
           Gregorian_month, Gregorian_day, Discordian_date]).

ddate(Gregorian_year, 2, 29, Discordian_date):-
    convert_year(Gregorian_year, Discordian_year),
    swritef(Discordian_date, 'St. Tib\'s Day in the YOLD %w',
            [Discordian_year]),
    !.
ddate(Gregorian_year, Gregorian_month, Gregorian_day,
      Discordian_date):-
    convert_year(Gregorian_year, Discordian_year),
    day_of_year(Gregorian_month, Gregorian_day, Daynum),
    Season is Daynum//73,
    Weekday is Daynum mod 5,
    Day_of_season is 1 + Daynum mod 73,
    season(Season, Season_name),
    week_day(Weekday, Day_name),
    (holy_day(Season, Day_of_season, Holy_day) ->
        swritef(Discordian_date, '%w, day %w of %w in the YOLD %w. Celebrate %w!',
                [Day_name, Day_of_season, Season_name, Discordian_year, Holy_day])
        ;
        swritef(Discordian_date, '%w, day %w of %w in the YOLD %w',
                [Day_name, Day_of_season, Season_name, Discordian_year])
    ).

convert_year(Gregorian_year, Discordian_year):-
    Discordian_year is Gregorian_year + 1166.

day_of_year(M, D, N):-
    month_days(M, Days),
    N is Days + D - 1.

month_days(1, 0).
month_days(2, 31).
month_days(3, 59).
month_days(4, 90).
month_days(5, 120).
month_days(6, 151).
month_days(7, 181).
month_days(8, 212).
month_days(9, 243).
month_days(10, 273).
month_days(11, 304).
month_days(12, 334).

season(0, 'Chaos').
season(1, 'Discord').
season(2, 'Confusion').
season(3, 'Bureacracy').
season(4, 'The Aftermath').

week_day(0, 'Sweetmorn').
week_day(1, 'Boomtime').
week_day(2, 'Pungenday').
week_day(3, 'Prickle-Prickle').
week_day(4, 'Setting Orange').

holy_day(0, 5, 'Mungday').
holy_day(0, 50, 'Chaoflux').
holy_day(1, 5, 'Mojoday').
holy_day(1, 50, 'Discoflux').
holy_day(2, 5, 'Syaday').
holy_day(2, 50, 'Confuflux').
holy_day(3, 5, 'Zaraday').
holy_day(3, 50, 'Bureflux').
holy_day(4, 5, 'Maladay').
holy_day(4, 50, 'Afflux').


Procedure.s Discordian_Date(Y, M, D)
  Protected DoY=DayOfYear(Date(Y,M,D,0,0,0)), Yold$=Str(Y+1166)
  Dim S.s(4)
  S(0)="Chaos": S(1)="Discord": S(2)="Confusion": S(3)="Bureaucracy"
  S(4)="The Aftermath"
  If (Y%4=0 And Y%100) Or Y%400=0
    If M=2 And D=29
      ProcedureReturn "St. Tib's Day, YOLD " + Yold$
    ElseIf DoY>=2*30
      DoY-1
    EndIf
  EndIf
  ProcedureReturn S(DoY/73)+" "+Str(DoY%73)+", Yold "+Yold$
EndProcedure


import datetime, calendar

DISCORDIAN_SEASONS = ["Chaos", "Discord", "Confusion", "Bureaucracy", "The Aftermath"]

def ddate(year, month, day):
    today = datetime.date(year, month, day)
    is_leap_year = calendar.isleap(year)
    if is_leap_year and month == 2 and day == 29:
        return "St. Tib's Day, YOLD " + (year + 1166)

    day_of_year = today.timetuple().tm_yday - 1

    if is_leap_year and day_of_year >= 60:
        day_of_year -= 1 # Compensate for St. Tib's Day

    season, dday = divmod(day_of_year, 73)
    return "%s %d, YOLD %d" % (DISCORDIAN_SEASONS[season], dday + 1, year + 1166)


#lang racket/base
;;;; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
;;;; Derived from 'D' Implementation
;;;; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
(require racket/date racket/match)

(define seasons '(Chaos     Discord   Confusion Bureaucracy     |The Aftermath|))
(define weekday '(Sweetmorn Boomtime  Pungenday Prickle-Prickle |Setting Orange|))
(define apostle '(Mungday   Mojoday   Syaday    Zaraday         Maladay))
(define holiday '(Chaoflux  Discoflux Confuflux Bureflux        Afflux))

(define (ymd->date y m d) (seconds->date (find-seconds 0 0 0 d m y)))
(define (leap-year? y) (with-handlers ((exn? (λ (x) #f))) (= 29 (date-day (ymd->date y 2 29)))))

(define (discordian-date d)
  (define leap? (leap-year? (date-year d)))
  (define year-day (match* (leap? (date-year-day d))
                     [(#t (? (λ (D) (>= D 59)) d0)) d0]
                     [(_ d0) (add1 d0)]))

  (define season-day (modulo year-day 73)) ; season day
  (define (list-ref-season l)
    (define season-index (quotient year-day 73))
    (symbol->string (list-ref l season-index)))

  (string-append
   (match* (season-day leap? (date-month d) (date-day d))
     [( _ #t 2 29) "St. Tib's Day,"]
     [((app (match-lambda
              (5 apostle) (50 holiday) (_ #f))
            (and (not #f) special)) _ _  _)
      (string-append (list-ref-season special) ",")]
     [( _  _ _  _)
      (define week-day-name (list-ref weekday (modulo (sub1 year-day) 5)))
      (format "~a, day ~a of ~a" week-day-name season-day (list-ref-season seasons))])
   " in the YOLD " (number->string (+ (date-year d) 1166))))

(displayln (discordian-date (current-date)))

;; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
;; passing these tests makes me consistent with D implementation
(module+ test
  (require rackunit)
  (define discordian/ymd (compose discordian-date ymd->date))
  (check-equal? (discordian/ymd 2010 7 22) "Pungenday, day 57 of Confusion in the YOLD 3176")
  (check-equal? (discordian/ymd 2012 2 28) "Prickle-Prickle, day 59 of Chaos in the YOLD 3178")
  (check-equal? (discordian/ymd 2012 2 29) "St. Tib's Day, in the YOLD 3178");
  (check-equal? (discordian/ymd 2012 3  1) "Setting Orange, day 60 of Chaos in the YOLD 3178")
  (check-equal? (discordian/ymd 2010 1  5) "Mungday, in the YOLD 3176")
  (check-equal? (discordian/ymd 2011 5  3) "Discoflux, in the YOLD 3177"))
;; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~FIN


my @seasons = << Chaos Discord Confusion Bureaucracy 'The Aftermath' >>;
my @days = << Sweetmorn Boomtime Pungenday Prickle-Prickle 'Setting Orange' >>;
sub ordinal ( Int $n ) { $n ~ ( $n % 100 == 11|12|13
    ?? 'th' !! < th st nd rd th th th th th th >[$n % 10] ) }

sub ddate ( Str $ymd ) {
    my $d = DateTime.new: "{$ymd}T00:00:00Z" or die;

    my $yold = 'in the YOLD ' ~ $d.year + 1166;

    my $day_of_year0 = $d.day-of-year - 1;

    if $d.is-leap-year {
        return "St. Tib's Day, $yold" if $d.month == 2 and $d.day == 29;
        $day_of_year0-- if $day_of_year0 >= 60; # Compensate for St. Tib's Day
    }

    my $weekday    = @days[    $day_of_year0 mod  5     ];
    my $season     = @seasons[ $day_of_year0 div 73     ];
    my $season_day = ordinal(  $day_of_year0 mod 73 + 1 );

    return "$weekday, the $season_day day of $season $yold";
}

say "$_ is {.&ddate}" for < 2010-07-22 2012-02-28 2012-02-29 2012-03-01 >;


/*REXX program  converts  a   mm/dd/yyyy    Gregorian date   ───►   a  Discordian date. */
 @day.1= 'Sweetness'                             /*define the 1st day─of─Discordian─week*/
 @day.2= 'Boomtime'                              /*   "    "  2nd  "   "      "       " */
 @day.3= 'Pungenday'                             /*   "    "  3rd  "   "      "       " */
 @day.4= 'Prickle-Prickle'                       /*   "    "  4th  "   "      "       " */
 @day.5= 'Setting Orange'                        /*   "    "  5th  "   "      "       " */

@seas.0= "St. Tib's day,"                        /*define the leap─day of Discordian yr.*/
@seas.1= 'Chaos'                                 /*   "   1st season─of─Discordian─year.*/
@seas.2= 'Discord'                               /*   "   2nd    "    "      "       "  */
@seas.3= 'Confusion'                             /*   "   3rd    "    "      "       "  */
@seas.4= 'Bureaucracy'                           /*   "   4th    "    "      "       "  */
@seas.5= 'The Aftermath'                         /*   "   5th    "    "      "       "  */

parse arg  gM  '/'  gD  "/"  gY .                /*obtain the specified Gregorian date. */
if gM=='' | gM=="," | gM=='*'  then parse value  date("U")   with   gM  '/'  gD  "/"  gY .

gY=left( right( date(), 4), 4 - length(Gy) )gY   /*adjust for two─digit year or no year.*/

                                                 /* [↓]  day─of─year,  leapyear adjust. */
doy= date('d', gY || right(gM, 2, 0)right(gD ,2, 0),  "s")   -   (leapyear(gY)   &   gM>2)

dW= doy//5;                if dW==0  then dW=  5 /*compute the Discordian weekday.      */
dS= (doy-1) % 73  +  1                           /*   "     "       "     season.       */
dD= doy//73;               if dD==0  then dD= 73 /*   "     "       "     day─of─month. */
if leapyear(gY)  &  gM==2  &  gD==29 then ds=  0 /*is this St. Tib's day  (leapday) ?   */
if ds==0  then dD=                               /*adjust for the Discordian leap day.  */

say space(@day.dW','  @seas.dS dD","  gY + 1166) /*display Discordian date to terminal. */
exit                                             /*stick a fork in it,  we're all done. */
/*──────────────────────────────────────────────────────────────────────────────────────*/
leapyear: procedure;   parse arg y               /*obtain a four─digit Gregorian year.  */
          if y//4  \==  0  then return 0         /*Not  ÷  by 4?   Then not a leapyear. */
          return y//100 \== 0  |  y//400 == 0    /*apply the  100  and  400  year rules.*/


require 'date'

class DiscordianDate
  SEASON_NAMES = ["Chaos","Discord","Confusion","Bureaucracy","The Aftermath"]
  DAY_NAMES = ["Sweetmorn","Boomtime","Pungenday","Prickle-Prickle","Setting Orange"]
  YEAR_OFFSET = 1166
  DAYS_PER_SEASON = 73
  DAYS_PER_WEEK = 5
  ST_TIBS_DAY_OF_YEAR = 60

  def initialize(year, month, day)
    gregorian_date = Date.new(year, month, day)
    @day_of_year = gregorian_date.yday

    @st_tibs = false
    if gregorian_date.leap?
      if @day_of_year == ST_TIBS_DAY_OF_YEAR
        @st_tibs = true
      elsif @day_of_year > ST_TIBS_DAY_OF_YEAR
        @day_of_year -= 1
      end
    end

    @season, @day = (@day_of_year-1).divmod(DAYS_PER_SEASON)
    @day += 1     #←              ↑ fixes of-by-one error (only visible at season changes)
    @year = gregorian_date.year + YEAR_OFFSET
  end
  attr_reader :year, :day

  def season
    SEASON_NAMES[@season]
  end

  def weekday
    if @st_tibs
      "St. Tib's Day"
    else
      DAY_NAMES[(@day_of_year - 1) % DAYS_PER_WEEK]
    end
  end

  def to_s
    %Q{#{@st_tibs ? "St. Tib's Day" : "%s, %s %d" % [weekday, season, day]}, #{year} YOLD}
  end
end


extern crate chrono;

use chrono::NaiveDate;
use std::str::FromStr;

fn main() {
    let date = std::env::args().nth(1).expect("Please provide a YYYY-MM-DD date.");
    println!("{} is {}", date, NaiveDate::from_str(&date).unwrap().to_poee());
}

// The necessary constants for the seasons, weekdays, and holydays.
const APOSTLES: [&str; 5] = ["Mungday", "Mojoday", "Syaday", "Zaraday", "Maladay"];
const HOLYDAYS: [&str; 5] = ["Chaoflux", "Discoflux", "Confuflux", "Bureflux", "Afflux"];
const SEASONS: [&str; 5] = ["Chaos", "Discord", "Confusion", "Bureaucracy", "The Aftermath"];
const WEEKDAYS: [&str; 5] = ["Sweetmorn", "Boomtime", "Pungenday", "Prickle-Prickle", "Setting Orange"];

// The necessary constants for the conversion.
const APOSTLE_HOLYDAY: usize = 5;
const CURSE_OF_GREYFACE: i32 = 1166;
const SEASON_DAYS: usize = 73;
const SEASON_HOLYDAY: usize = 50;
const ST_TIBS_DAY: usize = 59;
const WEEK_DAYS: usize = 5;

// This extends the `Datelike` trait of Rust's Chrono crate with a method that
// prints any Datelike type as a String.
impl<T: Datelike> DiscordianDate for T {}
pub trait DiscordianDate: Datelike {
    fn to_poee(&self) -> String {
        let day = self.ordinal0() as usize;
        let leap = self.year() % 4 == 0 && self.year() % 100 != 0 || self.year() % 400 == 0;
        let year = self.year() + CURSE_OF_GREYFACE;

        if leap && day == ST_TIBS_DAY { return format!("St. Tib's Day, in the YOLD {}", year); }

        let day_offset = if leap && day > ST_TIBS_DAY { day - 1 } else { day };

        let day_of_season = day_offset % SEASON_DAYS + 1;

        let season = SEASONS[day_offset / SEASON_DAYS];
        let weekday = WEEKDAYS[day_offset % WEEK_DAYS];

        let holiday = if day_of_season == APOSTLE_HOLYDAY {
            format!("\nCelebrate {}", APOSTLES[day_offset / SEASON_DAYS])
        } else if day_of_season == SEASON_HOLYDAY {
            format!("\nCelebrate {}", HOLYDAYS[day_offset / SEASON_DAYS])
        } else {
            String::with_capacity(0)
        };

        format!("{}, the {} day of {} in the YOLD {}{}",
            weekday, ordinalize(day_of_season), season, year, holiday)
    }
}

/// A helper function to ordinalize a numeral.
fn ordinalize(num: usize) -> String {
    let s = format!("{}", num);

    let suffix = if s.ends_with('1') && !s.ends_with("11") {
        "st"
    } else if s.ends_with('2') && !s.ends_with("12") {
        "nd"
    } else if s.ends_with('3') && !s.ends_with("13") {
        "rd"
    } else {
        "th"
    };

    format!("{}{}", s, suffix)
}


package rosetta

import java.util.GregorianCalendar
import java.util.Calendar

object DDate extends App {
  private val DISCORDIAN_SEASONS = Array("Chaos", "Discord", "Confusion", "Bureaucracy", "The Aftermath")

  // month from 1-12; day from 1-31
  def ddate(year: Int, month: Int, day: Int): String = {
    val date = new GregorianCalendar(year, month - 1, day)
    val dyear = year + 1166

    val isLeapYear = date.isLeapYear(year)
    if (isLeapYear && month == 2 && day == 29) // 2 means February
      "St. Tib's Day " + dyear + " YOLD"
    else {
      var dayOfYear = date.get(Calendar.DAY_OF_YEAR) - 1
      if (isLeapYear && dayOfYear >= 60)
        dayOfYear -= 1 // compensate for St. Tib's Day

      val dday = dayOfYear % 73
      val season = dayOfYear / 73
      "%s %d, %d YOLD".format(DISCORDIAN_SEASONS(season), dday + 1, dyear)
    }
  }
  if (args.length == 3)
    println(ddate(args(2).toInt, args(1).toInt, args(0).toInt))
  else if (args.length == 0) {
    val today = Calendar.getInstance
    println(ddate(today.get(Calendar.YEAR), today.get(Calendar.MONTH)+1, today.get(Calendar.DAY_OF_MONTH)))
  } else
    println("usage: DDate [day month year]")
}