Discordian date

From Rosetta Code
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

C

<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(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 ddoy = date.dayOfYear;
   if (isLeapYear && ddoy >= 60)
       ddoy--;
   auto indx = ddoy / 73;
   auto seas = seasons[indx];
   auto wday = weekday[(ddoy-1) % 5];
   auto sday = ddoy % 73; // season day
   if (sday == 5)  return apostle[indx] ~", in the YOLD " ~ dyear;
   if (sday == 50) return holiday[indx] ~", in the YOLD " ~ dyear;
   return format("%s, day %s of %s in the YOLD %s", wday, sday, seas, 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>

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>

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

J

<lang j>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 29 3178 1 59j1</lang>

see talk page.

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>

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>

Ruby

Using *NIX's ddate. <lang ruby>puts `ddate`.to_s</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

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