Discordian date

From Rosetta Code
Revision as of 20:01, 6 October 2011 by rosettacode>Glennj (→‎{{header|Ruby}}: different output method that does not require the explicit to_s call)
Task
Discordian date
You are encouraged to solve this task according to the task description, using any language you may know.
Convert a given date from the Gregorian calendar to the Discordian calendar.

See Also

Ada

discordian.adb: <lang Ada>with Ada.Calendar.Arithmetic; with Ada.Text_IO; procedure Discordian is

  use Ada.Calendar;
  subtype Year_Number is Integer range 3067 .. 3565;
  type Seasons is (Chaos, Discord, Confusion, Bureaucracy, The_Aftermath);
  subtype Day_Number is Integer range 1 .. 73;
  type Discordian_Date is record
     Year        : Year_Number;
     Season      : Seasons;
     Day         : Day_Number;
     Is_Tibs_Day : Boolean := False;
  end record;
  procedure Convert (From : Time; To : out Discordian_Date) is
     use Ada.Calendar.Arithmetic;
     First_Day   : Time;
     Number_Days : Day_Count;
  begin
     First_Day   := Time_Of (Year => Year (From), Month => 1, Day => 1);
     Number_Days := From - First_Day;
     To.Year        := Year (Date => From) + 1166;
     To.Is_Tibs_Day := False;
     if (To.Year - 2) mod 4 = 0 then
        if Number_Days > 59 then
           Number_Days := Number_Days - 1;
        elsif Number_Days = 59 then
           To.Is_Tibs_Day := True;
        end if;
     end if;
     To.Day := Day_Number (Number_Days mod 73 + 1);
     case Number_Days / 73 is
        when 0 => To.Season := Chaos;
        when 1 => To.Season := Discord;
        when 2 => To.Season := Confusion;
        when 3 => To.Season := Bureaucracy;
        when 4 => To.Season := The_Aftermath;
        when others => raise Constraint_Error;
     end case;
  end Convert;
  procedure Put (Item : Discordian_Date) is
  begin
     Ada.Text_IO.Put ("YOLD" & Integer'Image (Item.Year));
     if Item.Is_Tibs_Day then
        Ada.Text_IO.Put (", St. Tib's Day");
     else
        Ada.Text_IO.Put (", " & Seasons'Image (Item.Season));
        Ada.Text_IO.Put (" " & Integer'Image (Item.Day));
     end if;
     Ada.Text_IO.New_Line;
  end Put;
  Test_Day  : Time;
  Test_DDay : Discordian_Date;

begin

  Test_Day := Clock;
  Convert (From => Test_Day, To => Test_DDay);
  Put (Test_DDay);
  Test_Day := Time_Of (Year => 2012, Month => 2, Day => 28);
  Convert (From => Test_Day, To => Test_DDay);
  Put (Test_DDay);
  Test_Day := Time_Of (Year => 2012, Month => 2, Day => 29);
  Convert (From => Test_Day, To => Test_DDay);
  Put (Test_DDay);
  Test_Day := Time_Of (Year => 2012, Month => 3, Day => 1);
  Convert (From => Test_Day, To => Test_DDay);
  Put (Test_DDay);
  Test_Day := Time_Of (Year => 2010, Month => 7, Day => 22);
  Convert (From => Test_Day, To => Test_DDay);
  Put (Test_DDay);
  Test_Day := Time_Of (Year => 2012, Month => 9, Day => 2);
  Convert (From => Test_Day, To => Test_DDay);
  Put (Test_DDay);
  Test_Day := Time_Of (Year => 2012, Month => 12, Day => 31);
  Convert (From => Test_Day, To => Test_DDay);
  Put (Test_DDay);

end Discordian;</lang>

Output:

YOLD 3177, CHAOS  21
YOLD 3178, CHAOS  59
YOLD 3178, St. Tib's Day
YOLD 3178, CHAOS  60
YOLD 3176, CONFUSION  56
YOLD 3178, BUREAUCRACY  25
YOLD 3178, THE_AFTERMATH  73

AWK

<lang AWK>

  1. DDATE.AWK - Gregorian to Discordian date contributed by Dan Nielsen
  2. syntax: GAWK -f DDATE.AWK [YYYYMMDD | YYYY-MM-DD | MM-DD-YYYY | DDMMMYYYY | YYYY] ...
  3. examples:
  4. GAWK -f DDATE.AWK today
  5. GAWK -f DDATE.AWK 20110722 one date
  6. GAWK -f DDATE.AWK 20110722 20120229 two dates
  7. 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. 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( cmd,x) { # under Microsoft Windows XP

   cmd = "DATE <NUL"
   cmd | getline x # The current date is: MM/DD/YYYY
   close(cmd) # close pipe
   return sprintf("%04d%02d%02d",substr(x,28,4),substr(x,22,2),substr(x,25,2))

} function error(x) {

   printf("error: argument %d is invalid, %s, in %s\n",argno,ARGV[argno],x)
   errors++

} </lang>

output:

GAWK -f DDATE.AWK
2011-08-22 is Prickle-Prickle, Bureaucracy 15, Year of Our Lady of Discord 3177

GAWK -f DDATE.AWK 20110722 20120229
2011-07-22 is Pungenday, Confusion 57, Year of Our Lady of Discord 3177
2012-02-29 is St. Tib's day, Year of Our Lady of Discord 3178

BASIC

Works with: FreeBASIC
Translation of: PowerBASIC

<lang qbasic>#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 \ 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</lang>

C

For the source code of ddate in util-linux package, see [[1]].

<lang C>#include <string.h>

  1. include <malloc.h>
  2. include <stdlib.h>
  3. include <stdio.h>
  4. include <time.h>
  1. define season( x ) ((x) == 0 ? "Chaos" :\
                   (x) == 1 ? "Discord" :\
                   (x) == 2 ? "Confusion" :\
                   (x) == 3 ? "Bureaucracy" :\
                   "The Aftermath")
  1. define date( x ) ((x)%73 == 0 ? 73 : (x)%73)
  1. 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 %d, YOLD %d", season( 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 ] ) );
 }
 
 printf( "%s\n", ddate( year, doy ) );
 return 0;

}</lang>

Demonstration:

$ ./ddate #today is Jan 7th, 2011
Chaos 7, YOLD 3177
$ ./ddate 2011 1 7
Chaos 7, YOLD 3177
$ ./ddate 2012 2 28
Chaos 59, YOLD 3178
$ ./ddate 2012 2 29
St. Tib's Day, YOLD 3178
$ ./ddate 2012 3 1
Chaos 60, YOLD 3178
$ ./ddate 2010 7 22
Confusion 57, YOLD 3176

D

<lang d>import std.stdio, std.datetime, std.conv, std.string;

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

string discordianDate(in Date date) {

   auto dyear = to!string(date.year + 1166);
   auto isLeapYear = date.isLeapYear;
   if (isLeapYear && date.month == 2 && date.day == 29)
       return "St. Tib's Day, in the YOLD " ~ dyear;
   auto doy = date.dayOfYear;
   if (isLeapYear && doy >= 60)
       doy--;
   auto dsday = 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;
   auto dseas = seasons[doy / 73];
   auto dwday = weekday[(doy-1) % 5];
   return format("%s, day %s of %s in the YOLD %s", dwday, dsday, dseas, dyear);

}

void main() {

   auto today = cast(Date)Clock.currTime();
   auto ddate = discordianDate(today);
   writeln(ddate);

} </lang>

<lang d>unittest {

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

}</lang>

Euphoria

Translation of: D

<lang euphoria>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))</lang>

F#

<lang F#>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
       let season, dday = Math.DivRem(dayOfYear, 73)
       sprintf "%s %i, %i YOLD" seasons.[season] dday dyear</lang>

Go

A package modeled after the time package in the Go standard library <lang go>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 := *eris
   t.Month = 1
   t.Day = 1
   bob := int(eris.Seconds()-t.Seconds()) / (24 * 60 * 60)
   raw := int(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

}</lang> Example program using above package <lang go>package main

import (

   "ddate"
   "fmt"
   "os"
   "strings"
   "time"

)

func main() {

   pi := 1
   fnord := ddate.DefaultFmt
   if len(os.Args) > 1 {
       switch os.Args[1][0] {
       case '+':
           pi++
           fnord = os.Args[1][1:]
       case '-':
           usage()
       }
   }
   var eris *time.Time
   switch len(os.Args) - pi {
   case 0:
       eris = time.LocalTime()
   case 3:
       var err os.Error
       eris, err = time.Parse("2 1 2006", strings.Join(os.Args[pi:pi+3], " "))
       if err != nil {
           fmt.Println(err)
           usage()
       }
   default:
       usage()
   }
   fmt.Println(ddate.New(eris).Format(fnord))

}

func usage() {

   fmt.Fprintf(os.Stderr, "usage: %s [+format] [day month year]\n", os.Args[0])
   os.Exit(1)

}</lang> Example output:

> ddate
Pungenday, Confusion 62, 3177 YOLD

> ddate 29 2 2012
St. Tib's Day, 3178 YOLD

Haskell

<lang haskell>import Data.List import Data.Time import Data.Time.Calendar.MonthDay

seasons = words "Chaos Discord Confusion Bureaucracy The_Aftermath"

discordianDate (y,m,d) = do

 let doy = monthAndDayToDayOfYear (isLeapYear y) m d
     (season, dday) = divMod doy 73
     dos = dday - fromEnum (isLeapYear y && m >2)
     dDate

| isLeapYear y && m==2 && d==29 = "St. Tib's Day, " ++ show (y+1166) ++ " YOLD" | otherwise = seasons!!season ++ " " ++ show dos ++ ", " ++ show (y+1166) ++ " YOLD"

 putStrLn dDate</lang>

Examples:

*Main> mapM_ discordianDate [(2012,2,28),(2012,2,29),(2012,3,1),(2010,9,2),(2010,12,6)]
Chaos 59, 3178 YOLD
St. Tib's Day, 3178 YOLD
Chaos 60, 3178 YOLD
Bureaucracy 26, 3176 YOLD
The_Aftermath 48, 3176 YOLD

In GHCi we can also execute shell commands.

  • Using Linux utility ddate
*Main> :! ddate
Today is Setting Orange, the 26th day of Bureaucracy in the YOLD 3176

*Main> :! ddate 29 2 2012
Sint Tibs

*Main> :! ddate 2 9 2010
Setting Orange, Bureaucracy 26, 3176 YOLD

Icon and Unicon

This version is loosely based on a modified translation of the original ddate.c and like the original the leap year functionality is Julian not Gregorian. <lang Icon>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</lang>

Output:
2010-1-1 = Sweetmorn, day 1 of Chaos in the YOLD 3176
2010-7-22 = Pungenday, day 57 of Confusion in the YOLD 3176
2012-2-28 = Prickle-Prickle, day 59 of Chaos in the YOLD 3178
2012-2-29 = St. Tib's Day, in the YOLD 3178
2012-3-1 = Setting Orange, day 60 of Chaos in the YOLD 3178
2010-1-5 = Mungday, in the YOLD 3176
2011-5-3 = Discoflux, in the YOLD 3177
2012-2-28 = Prickle-Prickle, day 59 of Chaos in the YOLD 3178
2012-2-29 = St. Tib's Day, in the YOLD 3178
2012-3-1 = Setting Orange, day 60 of Chaos in the YOLD 3178
2010-7-22 = Pungenday, day 57 of Confusion in the YOLD 3176
2012-12-22 = Sweetmorn, day 64 of The Aftermath in the YOLD 3178

J

<lang j>require'dates' leap=: _1j1 * 0 -/@:= 4 100 400 |/ {.@] bs=: ((#:{.) + 0 j. *@[ * {:@]) +. disc=: ((1+0 73 bs[ +^:(58<]) -/@todayno@(,: 1 1,~{.)@]) ,~1166+{.@])~ leap</lang>

Example use:

<lang> disc 2012 2 28 3178 1 59

  disc 2012 2 29

3178 1 59j1

  disc 2012 3 1

3178 1 60j1

  disc 2012 12 31

3178 5 73j1

  disc 2013 1 1

3179 1 1</lang>

see talk page. But, in essence, this version uses season ordinals with a single imaginary day after the 59th of the first season, on leap years. This is implemented so that that imaginary day has been passed for all remaining days of a leap year.

JavaScript

<lang javascript>/**

* All Hail Discordia! - this script prints Discordian date using system date.
* author: s1w_, lang: JavaScript
*/

function print_ddate(mod) {

 var p;
 switch(mod || 0) {// <--choose display pattern or pass option by parameter
  default:
   case 0:/* Sweetmorn, Day 57 of the Season of Confusion, Anno Mung 3177 */ p="{0}, [Day {1} of the Season of {2}], Anno Mung {3}"; break;
   case 1:/* Sweetmorn, The 57th Day of Confusion, 3177 YOLD              */ p="{0}, [The {1}th Day of {2}], {3} YOLD";              break;
   case 2:/* Sweetmorn, the 57th day of Confusion, AM 3177                */ p="{0}, [the {1}th day of {2}], AM {3}";                break;
   case 3:/* Sweetmorn / Confusion 57th / AM 3177                         */ p="{0} / [{2} {1}th] / AM {3}";                         break;
 }
 var ddateStr, curr, sum, extra, today, day, month, year, dSeason, dDay, season, ddate, dyear;
 format = function(s, $1, $2, $3, $4) {
   if ($2 != undefined) {
     var postfix;
     switch(parseInt($2.charAt($2.length-1))) {
       case 1: postfix = '}st'; break;
       case 2: postfix = '}nd'; break;
       case 3: postfix = '}rd'; break;
       default:postfix = '}th';
     }
     return p.replace(/\}th/, postfix).replace(/(\[|\])/g, ).format($1, $2, $3, $4);
   }
   else return p.replace(/\[.*?\]/,"{2}").format($1, $2, $3, $4);
 }
 
 String.prototype.format = function() {
   var pattern = /\{\d+\}/g;
   var args = arguments;
   return this.replace(pattern, function(capture){ return args[capture.match(/\d+/)]; });
 }
 dDay = new Array("Sweetmorn", "Boomtime", "Pungenday", "Prickle-Prickle", "Setting Orange");
 dSeason = new Array("Chaos", "Discord", "Confusion", "Bureaucracy", "Aftermath");
 curr = new Date(); extra = new Array(0,3,0,3,2,3,2,3,3,2,3,2);
 today = curr.getDate(); month = curr.getMonth(); year = curr.getFullYear();
 sum = month * 28;
 for(var i=0; i<=month; i++)
   sum += extra[i];
 sum += today;
 day = (sum - 1) % 5; ddate = sum % 73;
 season = (month==1)&&(today==29) ? "St. Tib\'s Day" : dSeason[Math.floor(sum/73)];
 dyear = year+1166;
 ddateStr = ""+dDay[day]+ddate+season+dyear;
 document.write(ddateStr.replace(/(\D+)(?:(\d+)(?!St)|(?:\d+))(\D+)(\d+)/i, format));

}</lang>

Example use:

<lang javascript>print_ddate() "Sweetmorn, Day 57 of the Season of Confusion, Anno Mung 3177" print_ddate(1) "Sweetmorn, The 57th Day of Confusion, 3177 YOLD" print_ddate(2) "Sweetmorn, the 57th day of Confusion, AM 3177" print_ddate(3) "Sweetmorn / Confusion 57th / AM 3177" </lang>

look at calendar; lern about holydays

Perl 6

<lang perl6>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 >; </lang>

Output:
2010-07-22 is Pungenday, the 57th day of Confusion in the YOLD 3176
2012-02-28 is Prickle-Prickle, the 59th day of Chaos in the YOLD 3178
2012-02-29 is St. Tib's Day, in the YOLD 3178
2012-03-01 is Setting Orange, the 60th day of Chaos in the YOLD 3178

PicoLisp

Translation of: Python

<lang PicoLisp>(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) ) ) ) ) )</lang>

Pike

Pike includes a Discordian calendar. dates can be converted from any calendar to any other. <lang Pike> > 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)

</lang>

PowerBASIC

<lang powerbasic>#COMPILE EXE

  1. 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</lang>

PureBasic

<lang PureBasic>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</lang>

Python

<lang python>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>

REXX

<lang rexx> /*REXX program converts mm/dd/yyyy Gregorian date ───> Discordian date. */ /*Gregorian date may be m/d/yy ──or── m/d format. */

day.1='Sweetness' /*define 1st day-of-Discordian-week.*/ day.2='Boomtime' /*define 2nd day-of-Discordian-week.*/ day.3='Pungenday' /*define 3rd day-of-Discordian-week.*/ day.4='Prickle-Prickle' /*define 4th day-of-Discordian-week.*/ day.5='Setting Orange' /*define 5th day-of-Discordian-week.*/

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

parse arg gM '/' gD "/" gY . /*get the date specified. */ gY=left(right(date(),4),4-length(Gy))gY /*adjust for a 2-dig yr or none*/

                                      /*below:get day-of-year,adj LeapY*/

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 /*compute the Discordian season. */ dD=doy//73;if dD==0 then dD=73; dD=dD',' /*Discordian day-of-month.*/ if leapyear(gY) & gM==02 & gD==29 then do; dD=; ds=0; end /*St. Tib's?*/ say space(day.dW',' seas.dS dD gY+1166) /*show and tell Discordian date*/ exit

/*─────────────────────────────────────LEAPYEAR subroutine──────────────*/ leapyear: procedure; arg y if y//4\==0 then return 0 /* not ≈ by 4? Not a leapyear.*/ return y//100\==0 | y//400==0 /*apply 100 and 400 year rule. */ </lang> Output when using the inputs of:

2/28/2012
2/29/2012
3/1/2012
7/22/2010
9/2/2012
12/31/2011

Prickle-Prickle, Chaos 59, 3178
Setting Orange, St. Tib's day, 3178
Setting Orange, Chaos 60, 3178
Pungenday, Confusion 57, 3176
Setting Orange, Bureaucracy 26, 3178
Setting Orange, The Aftermath 73, 3177

Ruby

<lang ruby>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.divmod(DAYS_PER_SEASON)
   @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</lang>

Testing: <lang ruby>[[2012, 2, 28], [2012, 2, 29], [2012, 3, 1], [2011, 10, 5]].each do |date|

 dd = DiscordianDate.new(*date)
 puts "#{"%4d-%02d-%02d" % date} => #{dd}"

end</lang> Outputs:

2012-02-28 => Prickle-Prickle, Chaos 59, 3178 YOLD
2012-02-29 => St. Tib's Day, 3178 YOLD
2012-03-01 => Setting Orange, Chaos 60, 3178 YOLD
2011-10-05 => Pungenday, Bureaucracy 59, 3177 YOLD

Scala

Translation of: Python

<lang scala>import java.util.{GregorianCalendar, Calendar}

val DISCORDIAN_SEASONS=Array("Chaos", "Discord", "Confusion", "Bureaucracy", "The Aftermath") 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)
     return "St. Tib's Day "+dyear+" YOLD"
  var dayOfYear=date.get(Calendar.DAY_OF_YEAR)
  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, dyear)

}</lang>

Tcl

<lang tcl>package require Tcl 8.5 proc disdate {year month day} {

   # Get the day of the year
   set now [clock scan [format %02d-%02d-%04d $day $month $year] -format %d-%m-%Y]
   scan [clock format $now -format %j] %d doy
   # Handle leap years
   if {!($year%4) && (($year%100) || !($year%400))} {

if {$doy == 60} { return "St. Tib's Day, [expr {$year + 1166}] YOLD" } elseif {$doy > 60} { incr doy -1 }

   }
   # Main conversion to discordian format now that special cases are handled
   incr doy -1; # Allow div/mod to work right
   set season [lindex {Chaos Discord Confusion Bureaucracy {The Aftermath}} \

[expr {$doy / 73}]]

   set dos [expr {$doy % 73 + 1}]
   incr year 1166
   return "$season $dos, $year YOLD"

}</lang> Demonstrating: <lang tcl>puts [disdate 2010 7 22]; # Today puts [disdate 2012 2 28] puts [disdate 2012 2 29] puts [disdate 2012 3 1]</lang> Output:

Confusion 57, 3176 YOLD
Chaos 59, 3178 YOLD
St. Tib's Day, 3178 YOLD
Chaos 60, 3178 YOLD