Calendar: Difference between revisions

From Rosetta Code
Content added Content deleted
(First entry for Mathematica code for Calendar. (It's also my first entry ever.))
Line 975: Line 975:
26 27 28 29 30 31 │ 23 24 25 26 27 28 29│ 28 29 30 31
26 27 28 29 30 31 │ 23 24 25 26 27 28 29│ 28 29 30 31
│ 30 │ </lang>
│ 30 │ </lang>


=={{header|Mathematica}}==


Calendar is set of routines for handling calendars; It is built into Mathematica.
We're only going to use it for a single function, namely, '''DaysBetween'''.

<lang Mathematica>
Needs["Calendar`"];
</lang>

Monthly calendar takes a year and a month and returns a simply formatted calendar for that month.
It knows about leap years.

<lang Mathematica>
monthlyCalendar[y_, m_] :=
Module[{days = {Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday},
months = {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"},
d1, shortDays, offset, daysInMonth},
d1 = DayOfWeek[{y, m, 1}];
daysInMonth[year_, month_] := DaysBetween[{year, month, 1}, {If[month == 12, year + 1, year], If[month == 12, 1, month + 1], 1}];
shortDays = (StringTake[ToString[#], 3] & /@ days);
offset = d1 /. Thread[days -> Range[0, 6]];
Grid[
Prepend[
Prepend[
Partition[
PadRight[PadLeft[Range[daysInMonth[y, m]], daysInMonth[y, m] + offset, ""],
36, "" ],
7],
shortDays],
{months[[m]], SpanFromLeft}]]]

</lang>

'''yearlyCalendar''' prints out calendars for each of the 12 months in the year.

<lang Mathematica>
yearlyCalendar[y_] := Grid[Partition[Table[f[y, k], {k, 12}], 3], Spacings -> {4, 2}];

</lang>

--[[User:Davidcarraher|Davidcarraher]] 00:57, 16 November 2012 (UTC)


=={{header|Perl}}==
=={{header|Perl}}==

Revision as of 00:57, 16 November 2012

Task
Calendar
You are encouraged to solve this task according to the task description, using any language you may know.

Create a routine that will generate a text calendar for any year. Test the calendar by generating a calendar for the year 1969, on a device of the time. Choose one of the following devices:

  • A line printer with a width of 132 characters.
  • An IBM 3278 model 4 terminal (80×43 display with accented characters). Target formatting the months of the year to fit nicely across the 80 character width screen. Restrict number of lines in test output to 43.

(Ideally, the program will generate well-formatted calendars for any page width from 20 characters up.)

Kudos (κῦδος) for routines that also correctly transition from Julian to Gregorian calendar in September 1752.

This task is inspired by Real Programmers Don't Use PASCAL by Ed Post, Datamation, volume 29 number 7, July 1983.

THE REAL PROGRAMMER'S NATURAL HABITAT
"Taped to the wall is a line-printer Snoopy calender for the year 1969."

For further Kudos see task CALENDAR, where all code is to be in UPPERCASE.

For economy of size, do not actually include Snoopy generation in either the code or the output, instead just output a place-holder.

Ada

Specification of a "Printable_Calendar" package (printable_calendar.ads):

<lang Ada>with Ada.Calendar.Formatting;

package Printable_Calendar is

  subtype String20 is String(1 .. 20);
  type Month_Rep_Type is array (Ada.Calendar.Month_Number) of String20;
  type Description is record
     Weekday_Rep: String20;
     Month_Rep: Month_Rep_Type;
  end record;
  -- for internationalization, you only need to define a new description
  Default_Description: constant Description :=
    (Weekday_Rep =>
      "Mo Tu We Th Fr Sa So",
     Month_Rep   =>
     ("      January       ", "      February      ", "       March        ",
      "       April        ", "        May         ", "        June        ",
      "        July        ", "       August       ", "      September     ",
      "       October      ", "      November      ", "      December      "));
  type Calendar (<>) is tagged private;
  -- Initialize a calendar for devices with 80- or 132-characters per row
  function Init_80(Des: Description := Default_Description) return Calendar;
  function Init_132(Des: Description := Default_Description) return Calendar;
  -- the following procedures output to standard IO; override if neccessary
  procedure New_Line(Cal: Calendar);
  procedure Put_String(Cal: Calendar; S: String);
  -- the following procedures do the real stuff
  procedure Print_Line_Centered(Cal: Calendar'Class; Line: String);
  procedure Print(Cal: Calendar'Class;
                  Year:  Ada.Calendar.Year_Number;
                  Year_String: String); -- this is the main Thing

private

     type Calendar is tagged record
     Columns, Rows, Space_Between_Columns: Positive;
     Left_Space: Natural;
     Weekday_Rep: String20;
     Month_Rep: Month_Rep_Type;
  end record;

end Printable_Calendar;</lang>

Next the implementation (printable_calendar.ads):

<lang Ada>with Ada.Text_IO;

package body Printable_Calendar is

  use Ada.Calendar;
  package F renames Ada.Calendar.Formatting;
  function Days_Per_Month(Year: Year_Number; Month: Month_Number)
                         return Day_Number is
  begin
     case Month is
        when 1 | 3 | 5 | 7 | 8 | 10 | 12 => return 31;
        when 4 | 6 | 9 | 11              => return 30;
        when 2 =>
           if Year mod 4 /= 0 then
              return 28;
           elsif Year mod 100 /= 0 then
              return 29;
           elsif Year mod 400 /= 0 then
              return 28;
           else
              return 29;
           end if;
     end case;
  end Days_Per_Month;
  type Full_Month_Rep is array (1 .. 6) of String20;
  function Generate_Printable_Month (Y: Ada.Calendar.Year_Number;
                                     M: Ada.Calendar.Month_Number)
                                    return Full_Month_Rep is
     X: Full_Month_Rep := (others => "                    ");
     -- If X=Generate_Printable_Month(2011, 01), the result could be
     --  "       January      ", -- Month_Rep(01)
     --  "Mo Tu We Th Fr Sa Su"  -- Weekday_Rep
     --  "                1  2"  -- X(1)
     --  " 3  4  5  6  7  8  9"  -- X(2)
     --  "10 11 12 13 14 15 16"  -- X(3)
     --  "17 18 19 20 21 22 23"  -- X(4)
     --  "24 25 26 27 28 29 30"  -- X(5)
     --  "31                  "  -- X(6)
     Row:       Integer range 1 .. 6  := 1;
     Day_Index: constant array(F.Day_Name) of Positive
       := (1, 4, 7, 10, 13, 16, 19);
  begin
     for I in 1 .. Days_Per_Month(Y, M) loop
        declare
          Weekday: constant F.Day_Name    := F.Day_Of_Week(F.Time_Of(Y, M, I));
          Pos: constant Positive          := Day_Index(Weekday);
          Cleartext_Name: constant String := Day_Number'Image(I);
          L: constant Positive            := Cleartext_Name'Last;
        begin
           X(Row)(Pos .. Pos+1) := Cleartext_Name(L-1 .. L);
           if F."="(Weekday, F.Sunday) then
              Row := Row + 1;
           end if;
        end;
     end loop;
     return X;
  end Generate_Printable_Month;


  procedure Print(Cal: Calendar'class;
                  Year:  Ada.Calendar.Year_Number;
                  Year_String: String) is
     The_Month: Month_Number := Month_Number'First;
     procedure Write_Space(Length: Natural) is
     begin
        for I in 1 .. Length loop
           Cal.Put_String(" ");
        end loop;
     end Write_Space;
     Year_Rep: array(Month_Number) of Full_Month_Rep;
  begin
     -- print the year
     Cal.Print_Line_Centered(Year_String);
     -- generate a printable form for all the months
     for Month in Month_Number loop
        Year_Rep(Month) := Generate_Printable_Month(Year, Month);
     end loop;
     begin
        while True loop
           -- new line
           Cal.New_Line;
           -- write month names
           Write_Space(Cal.Left_Space);
           for Month in The_Month .. The_Month+Cal.Columns-2 loop
              Cal.Put_String(Cal.Month_Rep(Month));
              Write_Space(Cal.Space_Between_Columns);
           end loop;
           Cal.Put_String(Cal.Month_Rep(The_Month+Cal.Columns-1));
           Cal.New_Line;
           -- write "Mo Tu .. So" - or whatever is defined by Weekday_Rep
           Write_Space(Cal.Left_Space);
           for Month in The_Month .. The_Month+Cal.Columns-2 loop
              Cal.Put_String(Cal.Weekday_Rep);
              Write_Space(Cal.Space_Between_Columns);
           end loop;
           Cal.Put_String(Cal.Weekday_Rep);
           Cal.New_Line;
           -- write the dates
           for I in 1 .. 6 loop
              Write_Space(Cal.Left_Space);
              for Month in The_Month .. The_Month+Cal.Columns-2 loop
                 Cal.Put_String(Year_Rep(Month)(I));
                 Write_Space(Cal.Space_Between_Columns);
              end loop;
           Cal.Put_String(Year_Rep(The_Month+Cal.Columns-1)(I));
           Cal.New_Line;
           end loop;
           The_Month := The_Month + Cal.Columns;
           -- this will eventually raise Constraint_Error to terminate the loop
        end loop;
     exception
        when Constraint_Error => null;
     end;
  end Print;
  procedure New_Line(Cal: Calendar) is
  begin
     Ada.Text_IO.New_Line;
  end New_Line;
  procedure Put_String(Cal: Calendar; S: String) is
  begin
     Ada.Text_IO.Put(S);
  end Put_String;
  procedure Print_Line_Centered(Cal: Calendar'Class; Line: String) is
     Width : constant Positive := Cal.Columns*20
       + (Cal.Columns-1)*Cal.Space_Between_Columns
       + Cal.Left_Space;
  begin
     if Line'Length    >= Width-1 then
        Cal.Put_String(Line);
        Cal.New_Line;
     else
        Print_Line_Centered(Cal, " " & Line & " ");
     end if;
  end Print_Line_Centered;
  function Init_80(Des: Description := Default_Description) return Calendar is
     X: Calendar:=
       (Columns => 3, Rows => 4, Space_Between_Columns => 4,
        Left_Space => 1,
        Weekday_Rep =>  Des.Weekday_Rep,
        Month_Rep   =>  Des.Month_Rep
       );
  begin
     return X;
  end Init_80;
  function Init_132(Des: Description := Default_Description) return Calendar is
     X: Calendar:=
       (Columns => 6, Rows => 2, Space_Between_Columns => 2,
        Left_Space => 1,
        Weekday_Rep =>  Des.Weekday_Rep,
        Month_Rep   =>  Des.Month_Rep
       );
  begin
     return X;
  end Init_132;

end Printable_Calendar;</lang>

Now, the main program is really simple:

<lang Ada>with Printable_Calendar;

procedure Cal is

  C: Printable_Calendar.Calendar := Printable_Calendar.Init_80;

begin

  C.Print_Line_Centered("[reserved for Snoopy]");
  C.New_Line;
  C.Print(1969, "Nineteen-Sixty-Nine");

end Cal;</lang>

Here is the output:

                        [reserved for Snoopy]                        

                         Nineteen-Sixty-Nine                         

       January                 February                 March        
 Mo Tu We Th Fr Sa So    Mo Tu We Th Fr Sa So    Mo Tu We Th Fr Sa So
        1  2  3  4  5                    1  2                    1  2
  6  7  8  9 10 11 12     3  4  5  6  7  8  9     3  4  5  6  7  8  9
 13 14 15 16 17 18 19    10 11 12 13 14 15 16    10 11 12 13 14 15 16
 20 21 22 23 24 25 26    17 18 19 20 21 22 23    17 18 19 20 21 22 23
 27 28 29 30 31          24 25 26 27 28          24 25 26 27 28 29 30
                                                 31                  

        April                    May                     June        
 Mo Tu We Th Fr Sa So    Mo Tu We Th Fr Sa So    Mo Tu We Th Fr Sa So
     1  2  3  4  5  6              1  2  3  4                       1
  7  8  9 10 11 12 13     5  6  7  8  9 10 11     2  3  4  5  6  7  8
 14 15 16 17 18 19 20    12 13 14 15 16 17 18     9 10 11 12 13 14 15
 21 22 23 24 25 26 27    19 20 21 22 23 24 25    16 17 18 19 20 21 22
 28 29 30                26 27 28 29 30 31       23 24 25 26 27 28 29
                                                 30                  

         July                   August                 September     
 Mo Tu We Th Fr Sa So    Mo Tu We Th Fr Sa So    Mo Tu We Th Fr Sa So
     1  2  3  4  5  6                 1  2  3     1  2  3  4  5  6  7
  7  8  9 10 11 12 13     4  5  6  7  8  9 10     8  9 10 11 12 13 14
 14 15 16 17 18 19 20    11 12 13 14 15 16 17    15 16 17 18 19 20 21
 21 22 23 24 25 26 27    18 19 20 21 22 23 24    22 23 24 25 26 27 28
 28 29 30 31             25 26 27 28 29 30 31    29 30               
                                                                     

        October                November                December      
 Mo Tu We Th Fr Sa So    Mo Tu We Th Fr Sa So    Mo Tu We Th Fr Sa So
        1  2  3  4  5                    1  2     1  2  3  4  5  6  7
  6  7  8  9 10 11 12     3  4  5  6  7  8  9     8  9 10 11 12 13 14
 13 14 15 16 17 18 19    10 11 12 13 14 15 16    15 16 17 18 19 20 21
 20 21 22 23 24 25 26    17 18 19 20 21 22 23    22 23 24 25 26 27 28
 27 28 29 30 31          24 25 26 27 28 29 30    29 30 31  


To get a 132-character-wide output, you just have to replace "Init_80" by "Init_132" in the main program.

ALGOL 68

Works with: ALGOL 68 version Revision 1 - no extensions to language used
Works with: ALGOL 68G version Any - tested with release 1.18.0-9h.tiny.

<lang algol68>#!/usr/local/bin/a68g --script #

PROC print calendar = (INT year, page width)VOID: (

 []STRING month names = (
     "January","February","March","April","May","June",
     "July","August","September","October","November","December"),
   weekday names = ("Su","Mo","Tu","We","Th","Fr","Sa");
 FORMAT weekday fmt = $g,n(UPB weekday names - LWB weekday names)(" "g)$;
 # Juggle the calendar format to fit the printer/screen width #
 INT day width = UPB weekday names[1], day gap=1;
 INT month width = (day width+day gap) * UPB weekday names-1;
 INT month heading lines = 2;
 INT month lines = (31 OVER UPB weekday names+month heading lines+2); # +2 for head/tail weeks #
 INT year cols = (page width+1) OVER (month width+1);
 INT year rows = (UPB month names-1)OVER year cols + 1;
 INT month gap = (page width - year cols*month width + 1)OVER year cols;
 INT year width = year cols*(month width+month gap)-month gap;
 INT year lines = year rows*month lines;
 MODE MONTHBOX = [month lines, month width]CHAR;
 MODE YEARBOX  = [year  lines, year  width]CHAR;
 INT week start = 1; # Sunday #
 PROC days in month = (INT year, month)INT:
   CASE month IN 31,
     IF year MOD 4 EQ 0 AND year MOD 100 NE 0 OR year MOD 400 EQ 0 THEN 29 ELSE 28 FI,
     31, 30, 31, 30, 31, 31, 30, 31, 30, 31
   ESAC;
 PROC day of week = (INT year, month, day)INT: (
   # Day of the week by Zeller’s Congruence algorithm from 1887 #
   INT y := year, m := month, d := day, c;
   IF m <= 2 THEN m +:= 12; y -:= 1 FI;
   c := y OVER 100;
   y %*:= 100;
   (d - 1 + ((m + 1) * 26) OVER 10 + y + y OVER 4 + c OVER 4 - 2 * c) MOD 7
 );
 MODE SIMPLEOUT = UNION(STRING, []STRING, INT);
 PROC cputf = (REF[]CHAR out, FORMAT fmt, SIMPLEOUT argv)VOID:(
   FILE f; STRING s; associate(f,s);
   putf(f, (fmt, argv));
   out[:UPB s]:=s;
   close(f)
 );
 PROC month repr = (INT year, month)MONTHBOX:(
   MONTHBOX month box; FOR line TO UPB month box DO month box[line,]:=" "* 2 UPB month box OD;
   STRING month name = month names[month];
 # center the title #
   cputf(month box[1,(month width - UPB month name ) OVER 2+1:], $g$, month name);
   cputf(month box[2,], weekday fmt, weekday names);
   INT first day := day of week(year, month, 1);
   FOR day TO days in month(year, month) DO
     INT line = (day+first day-week start) OVER UPB weekday names + month heading lines + 1;
     INT char =((day+first day-week start) MOD UPB weekday names)*(day width+day gap) + 1;
     cputf(month box[line,char:char+day width-1],$g(-day width)$, day)
   OD;
   month box
 );
 PROC year repr = (INT year)YEARBOX:(
   YEARBOX year box;
   FOR line TO UPB year box DO year box[line,]:=" "* 2 UPB year box OD;
   FOR month row FROM 0 TO year rows-1 DO
     FOR month col FROM 0 TO year cols-1 DO
       INT month = month row * year cols + month col + 1;
       IF month > UPB month names THEN
         done
       ELSE
         INT month col width = month width+month gap;
         year box[
           month row*month lines+1 : (month row+1)*month lines,
           month col*month col width+1 : (month col+1)*month col width-month gap
         ] := month repr(year, month)
       FI
     OD
   OD;
   done: year box
 );
 INT center = (year cols*(month width+month gap) - month gap - 1) OVER 2;
 INT indent = (page width - year width) OVER 2;
 printf((
   $n(indent + center - 9)k  g l$, "[Insert Snoopy here]",
   $n(indent + center - 1)k 4d l$, year, $l$,
   $n(indent)k n(year width)(g)  l$, year repr(year)
 ))

);

main: ( CO inspired by http://www.ee.ryerson.ca/~elf/hack/realmen.html

          Real Programmers Don't Use PASCAL - Ed Post
           Datamation, volume 29 number 7, July 1983
             THE REAL PROGRAMMER'S NATURAL HABITAT

"Taped to the wall is a line-printer Snoopy calender for the year 1969." CO

 INT mankind stepped on the moon = 1969,
     line printer width = 80; # as at 1969! #
 print calendar(mankind stepped on the moon, line printer width)

)</lang> Output:

                             [Insert Snoopy here]
                                     1969

        January                    February                    March        
  Su Mo Tu We Th Fr Sa       Su Mo Tu We Th Fr Sa       Su Mo Tu We Th Fr Sa
            1  2  3  4                          1                          1
   5  6  7  8  9 10 11        2  3  4  5  6  7  8        2  3  4  5  6  7  8
  12 13 14 15 16 17 18        9 10 11 12 13 14 15        9 10 11 12 13 14 15
  19 20 21 22 23 24 25       16 17 18 19 20 21 22       16 17 18 19 20 21 22
  26 27 28 29 30 31          23 24 25 26 27 28          23 24 25 26 27 28 29
                                                        30 31               
         April                       May                        June        
  Su Mo Tu We Th Fr Sa       Su Mo Tu We Th Fr Sa       Su Mo Tu We Th Fr Sa
         1  2  3  4  5                    1  2  3        1  2  3  4  5  6  7
   6  7  8  9 10 11 12        4  5  6  7  8  9 10        8  9 10 11 12 13 14
  13 14 15 16 17 18 19       11 12 13 14 15 16 17       15 16 17 18 19 20 21
  20 21 22 23 24 25 26       18 19 20 21 22 23 24       22 23 24 25 26 27 28
  27 28 29 30                25 26 27 28 29 30 31       29 30               
                                                                            
          July                      August                   September      
  Su Mo Tu We Th Fr Sa       Su Mo Tu We Th Fr Sa       Su Mo Tu We Th Fr Sa
         1  2  3  4  5                       1  2           1  2  3  4  5  6
   6  7  8  9 10 11 12        3  4  5  6  7  8  9        7  8  9 10 11 12 13
  13 14 15 16 17 18 19       10 11 12 13 14 15 16       14 15 16 17 18 19 20
  20 21 22 23 24 25 26       17 18 19 20 21 22 23       21 22 23 24 25 26 27
  27 28 29 30 31             24 25 26 27 28 29 30       28 29 30            
                             31                                             
        October                    November                   December      
  Su Mo Tu We Th Fr Sa       Su Mo Tu We Th Fr Sa       Su Mo Tu We Th Fr Sa
            1  2  3  4                          1           1  2  3  4  5  6
   5  6  7  8  9 10 11        2  3  4  5  6  7  8        7  8  9 10 11 12 13
  12 13 14 15 16 17 18        9 10 11 12 13 14 15       14 15 16 17 18 19 20
  19 20 21 22 23 24 25       16 17 18 19 20 21 22       21 22 23 24 25 26 27
  26 27 28 29 30 31          23 24 25 26 27 28 29       28 29 30 31         
                             30                                             

BBC BASIC

The day and month names are in the language for which the PC is configured. <lang bbcbasic> INSTALL @lib$+"DATELIB"

     VDU 23,22,640;570;8,15,16,128
     
     year% = 1969
     PRINT TAB(38); year%
     DIM dom%(2), mjd%(2), dim%(2)
     
     FOR day% = 1 TO 7
       days$ += LEFT$(FN_date$(FN_mjd(day%, 1, 1905), "ddd"), 2) + " "
     NEXT
     
     FOR month% = 1 TO 10 STEP 3
       PRINT
       FOR col% = 0 TO 2
         mjd%(col%) = FN_mjd(1, month% + col%, year%)
         month$ = FN_date$(mjd%(col%), "MMMM")
         PRINT TAB(col%*24 + 16 - LEN(month$)/2) month$;
       NEXT
       FOR col% = 0 TO 2
         PRINT TAB(col%*24 + 6) days$;
         dim%(col%) = FN_dim(month% + col%, year%)
       NEXT
       dom%() = 1
       col% = 0
       REPEAT
         dow% = FN_dow(mjd%(col%))
         IF dom%(col%)<=dim%(col%) THEN
           PRINT TAB(col%*24 + dow%*3 + 6); dom%(col%);
           dom%(col%) += 1
           mjd%(col%) += 1
         ENDIF
         IF dow%=6 OR dom%(col%)>dim%(col%) col% = (col% + 1) MOD 3
       UNTIL dom%(0)>dim%(0) AND dom%(1)>dim%(1) AND dom%(2)>dim%(2)
       PRINT
     NEXT

</lang> Output:

                                      1969

            January                 February                 March
      Su Mo Tu We Th Fr Sa    Su Mo Tu We Th Fr Sa    Su Mo Tu We Th Fr Sa
               1  2  3  4                       1                       1
      5  6  7  8  9  10 11    2  3  4  5  6  7  8     2  3  4  5  6  7  8
      12 13 14 15 16 17 18    9  10 11 12 13 14 15    9  10 11 12 13 14 15
      19 20 21 22 23 24 25    16 17 18 19 20 21 22    16 17 18 19 20 21 22
      26 27 28 29 30 31       23 24 25 26 27 28       23 24 25 26 27 28 29
                                                      30 31

             April                    May                     June
      Su Mo Tu We Th Fr Sa    Su Mo Tu We Th Fr Sa    Su Mo Tu We Th Fr Sa
            1  2  3  4  5                 1  2  3     1  2  3  4  5  6  7
      6  7  8  9  10 11 12    4  5  6  7  8  9  10    8  9  10 11 12 13 14
      13 14 15 16 17 18 19    11 12 13 14 15 16 17    15 16 17 18 19 20 21
      20 21 22 23 24 25 26    18 19 20 21 22 23 24    22 23 24 25 26 27 28
      27 28 29 30             25 26 27 28 29 30 31    29 30

              July                   August                September
      Su Mo Tu We Th Fr Sa    Su Mo Tu We Th Fr Sa    Su Mo Tu We Th Fr Sa
            1  2  3  4  5                    1  2        1  2  3  4  5  6
      6  7  8  9  10 11 12    3  4  5  6  7  8  9     7  8  9  10 11 12 13
      13 14 15 16 17 18 19    10 11 12 13 14 15 16    14 15 16 17 18 19 20
      20 21 22 23 24 25 26    17 18 19 20 21 22 23    21 22 23 24 25 26 27
      27 28 29 30 31          24 25 26 27 28 29 30    28 29 30
                              31

            October                 November                December
      Su Mo Tu We Th Fr Sa    Su Mo Tu We Th Fr Sa    Su Mo Tu We Th Fr Sa
               1  2  3  4                       1        1  2  3  4  5  6
      5  6  7  8  9  10 11    2  3  4  5  6  7  8     7  8  9  10 11 12 13
      12 13 14 15 16 17 18    9  10 11 12 13 14 15    14 15 16 17 18 19 20
      19 20 21 22 23 24 25    16 17 18 19 20 21 22    21 22 23 24 25 26 27
      26 27 28 29 30 31       23 24 25 26 27 28 29    28 29 30 31
                              30

C

With arbitrary display width (>= 20 though) and auto spacing. <lang C>#include <stdio.h>

  1. include <stdlib.h>
  2. include <string.h>

int width = 80, year = 1969; int cols, lead, gap;

char *wdays[] = { "Su", "Mo", "Tu", "We", "Th", "Fr", "Sa" }; struct months { char *name; int days, start_wday, at; } months[12] = { { "January", 31, 0, 0 }, { "Februray", 28, 0, 0 }, { "March", 31, 0, 0 }, { "April", 30, 0, 0 }, { "May", 31, 0, 0 }, { "June", 30, 0, 0 }, { "July", 31, 0, 0 }, { "August", 31, 0, 0 }, { "September", 30, 0, 0 }, { "October", 31, 0, 0 }, { "November", 30, 0, 0 }, { "December", 31, 0, 0 } };

void space(int n) { while (n-- > 0) putchar(' '); }

void init_months() { int i;

if ((!(year % 4) && (year % 100)) || !(year % 400)) months[1].days = 29;

year--; months[0].start_wday = (year * 365 + year/4 - year/100 + year/400 + 1) % 7;

for (i = 1; i < 12; i++) months[i].start_wday = (months[i-1].start_wday + months[i-1].days) % 7;

cols = (width + 2) / 22; while (12 % cols) cols--; gap = cols - 1 ? (width - 20 * cols) / (cols - 1) : 0; if (gap > 4) gap = 4; lead = (width - (20 + gap) * cols + gap + 1) / 2;

       year++;

}

void print_row(int row) { int c, i, from = row * cols, to = from + cols; space(lead); for (c = from; c < to; c++) { i = strlen(months[c].name); space((20 - i)/2); printf("%s", months[c].name); space(20 - i - (20 - i)/2 + ((c == to - 1) ? 0 : gap)); } putchar('\n');

space(lead); for (c = from; c < to; c++) { for (i = 0; i < 7; i++) printf("%s%s", wdays[i], i == 6 ? "" : " "); if (c < to - 1) space(gap); else putchar('\n'); }

while (1) { for (c = from; c < to; c++) if (months[c].at < months[c].days) break; if (c == to) break;

space(lead); for (c = from; c < to; c++) { for (i = 0; i < months[c].start_wday; i++) space(3); while(i++ < 7 && months[c].at < months[c].days) { printf("%2d", ++months[c].at); if (i < 7 || c < to - 1) putchar(' '); } while (i++ <= 7 && c < to - 1) space(3); if (c < to - 1) space(gap - 1); months[c].start_wday = 0; } putchar('\n'); } putchar('\n'); }

void print_year() { int row; char buf[32]; sprintf(buf, "%d", year); space((width - strlen(buf)) / 2); printf("%s\n\n", buf); for (row = 0; row * cols < 12; row++) print_row(row); }

int main(int c, char **v) { int i, year_set = 0; for (i = 1; i < c; i++) { if (!strcmp(v[i], "-w")) { if (++i == c || (width = atoi(v[i])) < 20) goto bail; } else if (!year_set) { if (!sscanf(v[i], "%d", &year) || year <= 0) year = 1969; year_set = 1; } else goto bail; }

init_months(); print_year(); return 0;

bail: fprintf(stderr, "bad args\nUsage: %s year [-w width (>= 20)]\n", v[0]); exit(1); }</lang>

C#

An attempt to abuse the DateTime class for all static information. In the event that the number of days and months changes, so long as the DateTime class is updated accordingly, this should still print properly. It also abuses iterators to allow for a concise month printing method, but with the ability to still print x months per line.

<lang csharp>

using System; using System.Collections.Generic; using System.Linq; using System.Text;

namespace CalendarStuff {

   class Program
   {
       static void Main(string[] args)
       {
           Console.WindowHeight = 46;
           Console.Write(buildMonths(new DateTime(1969, 1, 1)));
           Console.Read(); 
       }
       private static string buildMonths(DateTime date)
       {
           StringBuilder sb = new StringBuilder();
           sb.AppendLine(center("[Snoop]", 24 * 3));
           sb.AppendLine();
           sb.AppendLine(center(date.Year.ToString(), 24 * 3));
           List<DateTime> dts = new List<DateTime>();
           while (true)
           {
               dts.Add(date);
               if (date.Year != ((date = date.AddMonths(1)).Year))
               {
                   break;
               }
           }
           var jd = dts.Select(a => buildMonth(a).GetEnumerator()).ToArray();
           
           int sCur=0;
           while (sCur<dts.Count)
           {
               sb.AppendLine();
               int curMonth=0; 
               var j = jd.Where(a => curMonth++ >= sCur && curMonth - 1 < sCur + 3).ToArray(); //grab the next 3
               sCur += j.Length; 
               bool breakOut = false;
               while (!breakOut)
               {
                   int inj = 1;
                   foreach (var cd in j)
                   {
                       if (cd.MoveNext())
                       {
                           sb.Append((cd.Current.Length == 21 ? cd.Current : cd.Current.PadRight(21, ' ')) + "     ");
                       }
                       else
                       {
                           sb.Append("".PadRight(21, ' ') + "     ");
                           breakOut = true;
                       }
                       if (inj++ % 3 == 0) sb.AppendLine();
                   }
               }
           }
           return sb.ToString();
       }


       private static IEnumerable<string> buildMonth(DateTime date)
       {
           yield return center(date.ToString("MMMM"),7*3);
           var j = DateTime.DaysInMonth(date.Year, date.Month);
           yield return Enum.GetNames(typeof(DayOfWeek)).Aggregate("", (current, result) => current + (result.Substring(0, 2).ToUpper() + " "));
           string cur = "";
           int total = 0;
           foreach (var day in Enumerable.Range(-((int)date.DayOfWeek),j + (int)date.DayOfWeek))
           {
               cur += (day < 0 ? "  " : ((day < 9 ? " " : "") + (day + 1))) +" ";
               if (total++ > 0 && (total ) % 7 == 0)
               {
                   yield return cur;
                   cur = "";
               }
           }
           yield return cur;
       }
       private static string center(string s, int len)
       {
           return (s.PadLeft((len - s.Length) / 2 + s.Length, ' ').PadRight((len), ' '));
       } 
   }

}

</lang>

D

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

void printCalendar(in int year, in int cols) {

   enforce(1 <= cols && cols <= 12);
   immutable rows = 12 / cols + (12 % cols != 0);
   auto date = Date(year, 1, 1);
   auto offs = cast(int)date.dayOfWeek();
   const monthNames = "January February March April May June "
   "July August September October November December".split(" ");
   string[8][12] mons;
   foreach (m; 0 .. 12) {
       mons[m][0] = monthNames[m].center(21);
       mons[m][1] = " Su Mo Tu We Th Fr Sa";
       immutable dim = date.daysInMonth();
       foreach (d; 1 .. 43) {
           immutable day = d > offs && d <= offs + dim;
           immutable str = day ? format(" %2s", d-offs) : "   ";
           mons[m][2 + (d - 1) / 7] ~= str;
       }
       offs = (offs + dim) % 7;
       date.add!"months"(1);
   }
   writeln("[Snoopy Picture]".center(cols * 24 + 4));
   writeln(text(year).center(cols * 24 + 4), "\n");
   foreach (r; 0 .. rows) {
       auto s = new string[8];
       foreach (c; 0 .. cols) {
           if (r * cols + c > 11) break;
           foreach (i, line; mons[r * cols + c])
               s[i] ~= format("   %s", line);
       }
       writeln(s.join("\n"), "\n");
   }

}

void main() {

   printCalendar(1969, 3);

}</lang>

                              [Snoopy Picture]
                                    1969

          January                February                  March
    Su Mo Tu We Th Fr Sa    Su Mo Tu We Th Fr Sa    Su Mo Tu We Th Fr Sa
              1  2  3  4                       1                       1
     5  6  7  8  9 10 11     2  3  4  5  6  7  8     2  3  4  5  6  7  8
    12 13 14 15 16 17 18     9 10 11 12 13 14 15     9 10 11 12 13 14 15
    19 20 21 22 23 24 25    16 17 18 19 20 21 22    16 17 18 19 20 21 22
    26 27 28 29 30 31       23 24 25 26 27 28       23 24 25 26 27 28 29
                                                    30 31

           April                    May                    June
    Su Mo Tu We Th Fr Sa    Su Mo Tu We Th Fr Sa    Su Mo Tu We Th Fr Sa
           1  2  3  4  5                 1  2  3     1  2  3  4  5  6  7
     6  7  8  9 10 11 12     4  5  6  7  8  9 10     8  9 10 11 12 13 14
    13 14 15 16 17 18 19    11 12 13 14 15 16 17    15 16 17 18 19 20 21
    20 21 22 23 24 25 26    18 19 20 21 22 23 24    22 23 24 25 26 27 28
    27 28 29 30             25 26 27 28 29 30 31    29 30


           July                   August                 September
    Su Mo Tu We Th Fr Sa    Su Mo Tu We Th Fr Sa    Su Mo Tu We Th Fr Sa
           1  2  3  4  5                    1  2        1  2  3  4  5  6
     6  7  8  9 10 11 12     3  4  5  6  7  8  9     7  8  9 10 11 12 13
    13 14 15 16 17 18 19    10 11 12 13 14 15 16    14 15 16 17 18 19 20
    20 21 22 23 24 25 26    17 18 19 20 21 22 23    21 22 23 24 25 26 27
    27 28 29 30 31          24 25 26 27 28 29 30    28 29 30
                            31

          October                November                December
    Su Mo Tu We Th Fr Sa    Su Mo Tu We Th Fr Sa    Su Mo Tu We Th Fr Sa
              1  2  3  4                       1        1  2  3  4  5  6
     5  6  7  8  9 10 11     2  3  4  5  6  7  8     7  8  9 10 11 12 13
    12 13 14 15 16 17 18     9 10 11 12 13 14 15    14 15 16 17 18 19 20
    19 20 21 22 23 24 25    16 17 18 19 20 21 22    21 22 23 24 25 26 27
    26 27 28 29 30 31       23 24 25 26 27 28 29    28 29 30 31
                            30

Icon and Unicon

The procedures printCalendar handles formatting of large components and uses co-expressions to keep the formatting of week elements in each column synchronized. The procedure CalendarFormatWeek is a generator that returns heading elements, alignment spacing, and individual days. <lang Icon>procedure main(A) printCalendar(\A[1]|1969) end

procedure printCalendar(year) #: Print a 3 column x 80 char calendar

  cols := 3                                        # fixed width 
  mons := []                                       # table of months
  "January February March April May June " || 
  "July August September October November December " ?
         while put(mons, tab(find(" "))) do move(1)    
  write(center("[Snoopy Picture]",cols * 24 + 4))  # mandatory ..
  write(center(year,cols * 24 + 4), "\n")          # ... headers
  
  M := list(cols)	                               # coexpr container               
  every  mon := 0 to 9 by cols do {                # go through months by cols
     writes("    ")
     every i := 1 to cols do {
        writes(center(mons[mon+i],24))             # header months
        M[i] := create CalendarFormatWeek(1969,mon + i)  # formatting coexpr 
        }
     write()
     every 1 to 7 do {                             # 1 to max rows                   
        every c := 1 to cols do {                  # for each column
           writes("    ")
           every 1 to 7 do writes(right(@M[c],3))  # each row day element 
           }
        write()
        }
     }

end

link datetime

procedure CalendarFormatWeek(year,m) #: Format Week for Calendar static D initial D := [31,28,31,30,31,30,31,31,30,31,30,31]

every suspend "Su"|"Mo"|"Tu"|"We"|"Th"|"Fr"|"Sa" # header every 1 to (d := (julian(m,1,year)+1)%7) do suspend "" # lead day alignment every suspend 1 to D[m] do d +:= 1 # days if m = 2 & IsLeapYear(year) then suspend (d +:= 1, 29) # LY adjustment every d to (6*7) do suspend "" # trailer alignment end</lang>

datetime.icn provides julian and IsLeapYear

Output:

                              [Snoopy Picture]
                                    1969

            January                 February                 March
     Su Mo Tu We Th Fr Sa     Su Mo Tu We Th Fr Sa     Su Mo Tu We Th Fr Sa
               1  2  3  4                        1                        1
      5  6  7  8  9 10 11      2  3  4  5  6  7  8      2  3  4  5  6  7  8
     12 13 14 15 16 17 18      9 10 11 12 13 14 15      9 10 11 12 13 14 15
     19 20 21 22 23 24 25     16 17 18 19 20 21 22     16 17 18 19 20 21 22
     26 27 28 29 30 31        23 24 25 26 27 28        23 24 25 26 27 28 29
                                                       30 31
             April                    May                     June
     Su Mo Tu We Th Fr Sa     Su Mo Tu We Th Fr Sa     Su Mo Tu We Th Fr Sa
            1  2  3  4  5                  1  2  3      1  2  3  4  5  6  7
      6  7  8  9 10 11 12      4  5  6  7  8  9 10      8  9 10 11 12 13 14
     13 14 15 16 17 18 19     11 12 13 14 15 16 17     15 16 17 18 19 20 21
     20 21 22 23 24 25 26     18 19 20 21 22 23 24     22 23 24 25 26 27 28
     27 28 29 30              25 26 27 28 29 30 31     29 30

              July                   August                September
     Su Mo Tu We Th Fr Sa     Su Mo Tu We Th Fr Sa     Su Mo Tu We Th Fr Sa
            1  2  3  4  5                     1  2         1  2  3  4  5  6
      6  7  8  9 10 11 12      3  4  5  6  7  8  9      7  8  9 10 11 12 13
     13 14 15 16 17 18 19     10 11 12 13 14 15 16     14 15 16 17 18 19 20
     20 21 22 23 24 25 26     17 18 19 20 21 22 23     21 22 23 24 25 26 27
     27 28 29 30 31           24 25 26 27 28 29 30     28 29 30
                              31
            October                 November                December
     Su Mo Tu We Th Fr Sa     Su Mo Tu We Th Fr Sa     Su Mo Tu We Th Fr Sa
               1  2  3  4                        1         1  2  3  4  5  6
      5  6  7  8  9 10 11      2  3  4  5  6  7  8      7  8  9 10 11 12 13
     12 13 14 15 16 17 18      9 10 11 12 13 14 15     14 15 16 17 18 19 20
     19 20 21 22 23 24 25     16 17 18 19 20 21 22     21 22 23 24 25 26 27
     26 27 28 29 30 31        23 24 25 26 27 28 29     28 29 30 31
                              30

J

Solution: <lang j>require 'dates format' NB. J6.x require 'dates general/misc/format' NB. J7.x calBody=: (1 1 }. _1 _1 }. ":)@(-@(<.@%&22)@[ ]\ calendar@]) calTitle=: (<: - 22&|)@[ center '[Insert Snoopy here]' , ,:~ ":@] formatCalendar=: calTitle , calBody</lang> Example use: <lang j> 80 formatCalendar 1969

                     [Insert Snoopy here]                       
                             1969                               
                                                                
        Jan         │         Feb         │         Mar         
Su Mo Tu We Th Fr Sa│ Su Mo Tu We Th Fr Sa│ Su Mo Tu We Th Fr Sa
          1  2  3  4│                    1│                    1
 5  6  7  8  9 10 11│  2  3  4  5  6  7  8│  2  3  4  5  6  7  8
12 13 14 15 16 17 18│  9 10 11 12 13 14 15│  9 10 11 12 13 14 15
19 20 21 22 23 24 25│ 16 17 18 19 20 21 22│ 16 17 18 19 20 21 22
26 27 28 29 30 31   │ 23 24 25 26 27 28   │ 23 24 25 26 27 28 29
                    │                     │ 30 31               

─────────────────────┼─────────────────────┼─────────────────────

        Apr         │         May         │         Jun         
Su Mo Tu We Th Fr Sa│ Su Mo Tu We Th Fr Sa│ Su Mo Tu We Th Fr Sa
       1  2  3  4  5│              1  2  3│  1  2  3  4  5  6  7
 6  7  8  9 10 11 12│  4  5  6  7  8  9 10│  8  9 10 11 12 13 14
13 14 15 16 17 18 19│ 11 12 13 14 15 16 17│ 15 16 17 18 19 20 21
20 21 22 23 24 25 26│ 18 19 20 21 22 23 24│ 22 23 24 25 26 27 28
27 28 29 30         │ 25 26 27 28 29 30 31│ 29 30               
                    │                     │                     

─────────────────────┼─────────────────────┼─────────────────────

        Jul         │         Aug         │         Sep         
Su Mo Tu We Th Fr Sa│ Su Mo Tu We Th Fr Sa│ Su Mo Tu We Th Fr Sa
       1  2  3  4  5│                 1  2│     1  2  3  4  5  6
 6  7  8  9 10 11 12│  3  4  5  6  7  8  9│  7  8  9 10 11 12 13
13 14 15 16 17 18 19│ 10 11 12 13 14 15 16│ 14 15 16 17 18 19 20
20 21 22 23 24 25 26│ 17 18 19 20 21 22 23│ 21 22 23 24 25 26 27
27 28 29 30 31      │ 24 25 26 27 28 29 30│ 28 29 30            
                    │ 31                  │                     

─────────────────────┼─────────────────────┼─────────────────────

        Oct         │         Nov         │         Dec         
Su Mo Tu We Th Fr Sa│ Su Mo Tu We Th Fr Sa│ Su Mo Tu We Th Fr Sa
          1  2  3  4│                    1│     1  2  3  4  5  6
 5  6  7  8  9 10 11│  2  3  4  5  6  7  8│  7  8  9 10 11 12 13
12 13 14 15 16 17 18│  9 10 11 12 13 14 15│ 14 15 16 17 18 19 20
19 20 21 22 23 24 25│ 16 17 18 19 20 21 22│ 21 22 23 24 25 26 27
26 27 28 29 30 31   │ 23 24 25 26 27 28 29│ 28 29 30 31         
                    │ 30                  │                     </lang>


Mathematica

Calendar is set of routines for handling calendars; It is built into Mathematica. We're only going to use it for a single function, namely, DaysBetween.

<lang Mathematica> Needs["Calendar`"]; </lang>

Monthly calendar takes a year and a month and returns a simply formatted calendar for that month. It knows about leap years.

<lang Mathematica> monthlyCalendar[y_, m_] :=

   Module[{days = {Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday}, 
                months = {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}, 
               d1, shortDays, offset, daysInMonth},
    d1 = DayOfWeek[{y, m, 1}];
    daysInMonth[year_, month_] := DaysBetween[{year, month, 1}, {If[month == 12, year + 1, year],  If[month == 12, 1, month + 1], 1}];
    shortDays = (StringTake[ToString[#], 3] & /@ days);
    offset = d1 /. Thread[days -> Range[0, 6]];
    Grid[
            Prepend[
                    Prepend[
                            Partition[
                                  PadRight[PadLeft[Range[daysInMonth[y, m]], daysInMonth[y, m] + offset, ""], 
                                                 36, ""  ], 
                                          7], 
                                  shortDays],
                                   {monthsm, SpanFromLeft}]]]

</lang>

yearlyCalendar prints out calendars for each of the 12 months in the year.

<lang Mathematica>

  yearlyCalendar[y_] := Grid[Partition[Table[f[y, k], {k, 12}], 3], Spacings -> {4, 2}];

</lang>

--Davidcarraher 00:57, 16 November 2012 (UTC)

Perl

This example is incorrect. Please fix the code and remove this message.

Details: You are supposed to implement it in Perl. Not use a external program.

This will call to the external program, cal.

<lang perl>$_=$ARGV[0]//1969;`cal $_ >&2`</lang>

Perl 6

<lang perl6>my $months-per-col = 3; my @week-day-names = <Mo Tu We Th Fr Sa Su>; my @month-names = <Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec>;

my Int $year = +(@*ARGS.shift || 1969);

say fmt-year($year); exit;

sub fmt-year ($year) {

       my $str = (' ' x 30) ~ $year ~ "\n";
       my @month-strs;
       @month-strs[$_] = fmt-month($year, $_).lines for 1 .. 12;
       loop ( my $month = 1; $month <= 12; $month += $months-per-col ) {
               while @month-strs[$month] {
                       for ^$months-per-col {
                               next unless @month-strs[$month+$_];
                               $str ~= @month-strs[$month+$_].shift;
                               $str ~= " " x 3;
                       }
                       $str ~= "\n";
               }
               $str ~= "\n";
       }
       return $str;

} sub fmt-month ($year, $month) {

       my $str = sprintf "%-20s\n", @month-names[$month-1];
       $str ~= @week-day-names~"\n";
       my $date = DateTime.new(year => $year, month => $month);
       my $week-day = $date.day-of-week;
       $str ~= ("  " xx $week-day-1).join(" ");
       for $date.day .. $date.days-in-month -> $day {
               $date = DateTime.new(year => $year, month => $month, day => $day);
               $str ~= " " if 1 < $week-day < 8;
               if $week-day == 8 {
                       $str ~= "\n";
                       $week-day = 1;
               }
               $str ~= sprintf "%2d", $day;
               $week-day++;
       }
       $str ~= " " if $week-day < 8;
       $str ~= ("  " xx 8-$week-day).join(" ");
       $str ~= "\n";
       return $str;

} </lang>

PicoLisp

This "calendar" is nicely formatted, and fits into 20 columns ;-) <lang PicoLisp>(de cal (Year)

  (prinl "====== " Year " ======")
  (for Dat (range (date Year 1 1) (date Year 12 31))
     (let D (date Dat)
        (tab (3 3 4 8)
           (when (= 1 (caddr D))
              (get *Mon (cadr D)) )
           (caddr D)
           (day Dat *Day)
           (when (=0 (% (inc Dat) 7))
              (pack "Week " (week Dat)) ) ) ) ) )

(cal 1969)</lang> Output:

====== 1969 ======
Jan  1 Wed        
     2 Thu        
     3 Fri        
     4 Sat        
     5 Sun        
     6 Mon  Week 2
     7 Tue        
....
    28 Sat        
    29 Sun        
    30 Mon Week 27
Jul  1 Tue        
     2 Wed        
     3 Thu        
     4 Fri        
....
    25 Thu        
    26 Fri        
    27 Sat        
    28 Sun        
    29 Mon Week 53
    30 Tue        
    31 Wed

Pike

the Calendar in Pike does not handle the transitions from the Julian to the Gregorian calendar, but it handles those separately. the default calendar is ISO, other options are Gregorian, Julian, Bahai, Coptic, Islamic and Discordian.

this script also highlights holidays by region. regions may be chosen by 2-letter country name, as well as some special 'regions': christianity, orthodox, bahai, islamic, among others.

<lang Pike>

  1. !/bin/env pike

int main(int argc, array(string) argv) {

   object cal = Calendar;
   object year;
   string region = "us";
   array date = argv[1..];
   if (sizeof(date) && objectp(Calendar[date[0]]) && Calendar[date[0]]->Day)
   {
       cal = Calendar[date[0]];
       date = Array.shift(date)[1];
   }
   if (sizeof(date) && (int)date[0])
   {
       year = cal.Year((int)date[0]);
       date = Array.shift(date)[1];
   }
   if (sizeof(date))
       region = date[0];
   if (!year)
       year = cal.Year();
   print_year(year, region);

}

array make_month(object month, int field_width, void|string region) {

   array out =({});
   mapping holidays = ([]);
   object today = Calendar.Day();
   if (region)
       holidays = Calendar.Events.find_region(region)->scan_events(month);
   array weekday_names = sprintf("%*.*s", field_width, field_width, month->week()->days()->week_day_shortname()[*]);
   out += ({ ({ month->month_name(), month->month_no(), month->year_name() }) });
   out += ({ weekday_names });
   out += showday(month->weeks()->days()[*][*], month, today, holidays, field_width);  
   out += ({ ({ " "*field_width })*sizeof(weekday_names) });
   return out;

}

string print_month(object _month, void|int field_width, void|string region) {

   if (!field_width)
       field_width = 2;
   array month = make_month(_month, field_width, region);
   string out = "";
   out += sprintf("%|*s\n", (field_width+1)*sizeof(month[1])-1, sprintf("%s", month[0][0])); 
   out += sprintf((month[1..][*]*" ")*"\n");
   return out;

}

string print_year(object year, void|string region) {

       array output = ({});
       int day_width = 2;
       int columns = Stdio.stdout.tcgetattr()->columns;
       int month_width = sizeof(make_month(year->month(), day_width)[1]) * (day_width+1) - 1;
       if (columns < month_width)
           columns = month_width;
       // try to find an optimal good looking solution to spread the months
       // across the terminal width
       // for the common calendar of 12 months this is easy but we need to
       // account for caledars that have more than 12 months
       float max_width = (float)((columns+2)/(month_width+2));
       float max_height = ceil(year->number_of_months()/max_width);
       float w = max_width;
       
       while(ceil(year->number_of_months()/(w-1)) == max_height)
           w--;
       
       foreach(print_month(year->months()[*], day_width, region)/w;; array row)
       {
           array rows = row[*]/"\n";
           int l = max(@sizeof(rows[*]));
           foreach(rows; int i;)
           {
               // the last line of each month is an empty line.
               // repeat the line as many times as needed to make the months equally long
               rows[i]+=({ rows[i][-1] })*(l-sizeof(rows[i]));
           }
           rows = Array.transpose(rows);
           output += rows[*]*"  ";
       }
       write("%*|s\n", sizeof(output[1]), year->format_nice());
       write(output * "\n");
       write("\n");

}

string showday(object day, object month, object today, mapping holidays, int field_width) {

   string dayname;
   if (day->month() == month)
   {
       dayname = (string)day->month_day();
       dayname = " "*(sizeof((string)month->number_of_days())-sizeof(dayname))+dayname;
       if (day == today)
           dayname = sprintf("%|*.*s", field_width, field_width, dayname);
       else
           dayname = sprintf("%|*.*s", field_width, field_width, dayname);
       if (holidays[day])
           dayname = sprintf("%|s", dayname);
   }
   else
       dayname = " "*field_width;
   return dayname;

} </lang> Output: (holidays lost in copy-paste)

                              1969                              
      January               February               March        
Mo Tu We Th Fr Sa Su  Mo Tu We Th Fr Sa Su  Mo Tu We Th Fr Sa Su
       1  2  3  4  5                  1  2                  1  2
 6  7  8  9 10 11 12   3  4  5  6  7  8  9   3  4  5  6  7  8  9
13 14 15 16 17 18 19  10 11 12 13 14 15 16  10 11 12 13 14 15 16
20 21 22 23 24 25 26  17 18 19 20 21 22 23  17 18 19 20 21 22 23
27 28 29 30 31        24 25 26 27 28        24 25 26 27 28 29 30
                                            31                  
                                                                
       April                  May                   June        
Mo Tu We Th Fr Sa Su  Mo Tu We Th Fr Sa Su  Mo Tu We Th Fr Sa Su
    1  2  3  4  5  6            1  2  3  4                     1
 7  8  9 10 11 12 13   5  6  7  8  9 10 11   2  3  4  5  6  7  8
14 15 16 17 18 19 20  12 13 14 15 16 17 18   9 10 11 12 13 14 15
21 22 23 24 25 26 27  19 20 21 22 23 24 25  16 17 18 19 20 21 22
28 29 30              26 27 28 29 30 31     23 24 25 26 27 28 29
                                            30                  
                                                                
        July                 August              September      
Mo Tu We Th Fr Sa Su  Mo Tu We Th Fr Sa Su  Mo Tu We Th Fr Sa Su
    1  2  3  4  5  6               1  2  3   1  2  3  4  5  6  7
 7  8  9 10 11 12 13   4  5  6  7  8  9 10   8  9 10 11 12 13 14
14 15 16 17 18 19 20  11 12 13 14 15 16 17  15 16 17 18 19 20 21
21 22 23 24 25 26 27  18 19 20 21 22 23 24  22 23 24 25 26 27 28
28 29 30 31           25 26 27 28 29 30 31  29 30               
                                                                
      October               November              December      
Mo Tu We Th Fr Sa Su  Mo Tu We Th Fr Sa Su  Mo Tu We Th Fr Sa Su
       1  2  3  4  5                  1  2   1  2  3  4  5  6  7
 6  7  8  9 10 11 12   3  4  5  6  7  8  9   8  9 10 11 12 13 14
13 14 15 16 17 18 19  10 11 12 13 14 15 16  15 16 17 18 19 20 21
20 21 22 23 24 25 26  17 18 19 20 21 22 23  22 23 24 25 26 27 28
27 28 29 30 31        24 25 26 27 28 29 30  29 30 31            
                                                        

Python

The Python calendar.pryear function prints calendars with the following formatting options: optional parameters w, l, and c are for date column width, lines per week, and number of spaces between month columns, respectively.

Although rumoured to be getting an anti-gravity module, Python as yet does not include a snoopy module ;-) <lang python>>>> import calendar >>> help(calendar.prcal) Help on method pryear in module calendar:

pryear(self, theyear, w=0, l=0, c=6, m=3) method of calendar.TextCalendar instance

   Print a years calendar.

>>> calendar.prcal(1969)

                                 1969
     January                   February                   March

Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su

      1  2  3  4  5                      1  2                      1  2
6  7  8  9 10 11 12       3  4  5  6  7  8  9       3  4  5  6  7  8  9

13 14 15 16 17 18 19 10 11 12 13 14 15 16 10 11 12 13 14 15 16 20 21 22 23 24 25 26 17 18 19 20 21 22 23 17 18 19 20 21 22 23 27 28 29 30 31 24 25 26 27 28 24 25 26 27 28 29 30

                                                   31
      April                      May                       June

Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su

   1  2  3  4  5  6                1  2  3  4                         1
7  8  9 10 11 12 13       5  6  7  8  9 10 11       2  3  4  5  6  7  8

14 15 16 17 18 19 20 12 13 14 15 16 17 18 9 10 11 12 13 14 15 21 22 23 24 25 26 27 19 20 21 22 23 24 25 16 17 18 19 20 21 22 28 29 30 26 27 28 29 30 31 23 24 25 26 27 28 29

                                                   30
       July                     August                  September

Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su

   1  2  3  4  5  6                   1  2  3       1  2  3  4  5  6  7
7  8  9 10 11 12 13       4  5  6  7  8  9 10       8  9 10 11 12 13 14

14 15 16 17 18 19 20 11 12 13 14 15 16 17 15 16 17 18 19 20 21 21 22 23 24 25 26 27 18 19 20 21 22 23 24 22 23 24 25 26 27 28 28 29 30 31 25 26 27 28 29 30 31 29 30

     October                   November                  December

Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su

      1  2  3  4  5                      1  2       1  2  3  4  5  6  7
6  7  8  9 10 11 12       3  4  5  6  7  8  9       8  9 10 11 12 13 14

13 14 15 16 17 18 19 10 11 12 13 14 15 16 15 16 17 18 19 20 21 20 21 22 23 24 25 26 17 18 19 20 21 22 23 22 23 24 25 26 27 28 27 28 29 30 31 24 25 26 27 28 29 30 29 30 31

>>> print('1234567890'*8) 12345678901234567890123456789012345678901234567890123456789012345678901234567890 >>> </lang>

REXX

Rather than build a program from scratch, I took my generalized REXX program that generated (as an
option) any calendar for any month, and by extension, any number of months (so that an entire
year could be displayed/written). To make the program much smaller, a LOT of code was removed
(but it's still a sledgehammer doing the work of a small hammer). Some of the code removed:

  • almost all error checking and the issuing of error messages.
  • the option of using "simple" characters as opposed to a true grid (of ASCII extended characters).
  • the option of highlighting of today's date.
  • the option of using colors.
  • the specifying of what language to use (over 90 languages, default is English).
  • the option of using indentation.
  • the option of using different date formats (to specify the date).
  • the option of using a time addition/subtraction for the date (* -4score-7years).
  • the option of showing the phases of the moon.
  • the option of showing the day-of-the-year (as on financial calendars and others).
  • the option of specifying a calendar fill character (those empty cells in a monthly calender).
  • the option of drawing Snoopy (three versions) and also many other animals/ASCII art.
  • the specifing of alternate outputs (a file/printer/XEDIT/stack/GLOBALVs/etc).
  • the option of displaying the year in blocked form (at the top of the calendar):
                   11          9999999       6666666       9999999
                  111         999999999     666666666     999999999
                 1111        99      99    66      66    99      99
                   11        99      99    66            99      99
                   11        99     999    66            99     999
                   11         999999999    66 666666      999999999
                   11          99999 99    6666666666      99999 99
                   11                99    666     66            99
                   11                99    66      66            99
                   11        99      99    66      66    99      99
                 111111       99999999      66666666      99999999
                 111111        999999        666666        999999


Most of the code is dealing with drawing a cell (grid) for each day-of-the-month, so the program
is a bit more complex than it has to be for this task. Also, it was designed for any year, any
month(s), and any size screen/device to show (display) it. Accomodations were made in the program
to allow the choice of what type of cells would be generated: small/smaller/smallest for height, and
narrow/narrower/narrowest for the width. The size (width/depth) of the output device/terminal can be
specified. The choice could've been performed programatically, but I never believe that a program
could best choose over what the user wants, as it depends on esthetics and looks (fashion). <lang rexx> /*REXX program to show any year's (monthly) calendar (with/without grid)*/

@abc='abcdefghijklmnopqrstuvwxyz'; @abcU=@abc; upper @abcU calfill=' '; mc=12; _='1 3 1234567890' "fb"x parse var _ grid calspaces # chk . cv_ days.1 days.2 days.3 daysn sd sw _=0; parse var _ cols 1 jd 1 lowerCase 1 maxKalPuts 1 narrow 1,

                narrower 1 narrowest 1 short 1 shorter 1 shortest 1,
                small 1 smaller 1 smallest 1 upperCase

parse arg mm '/' dd "/" yyyy _ '(' ops; uops=ops if _\== | \is#(mm) | \is#(dd) | \is#(yyyy) then call erx 86

 do while ops\==; ops=strip(ops,'L'); parse var ops _1 2 1 _ . 1 _o ops
 upper _
      select
      when  abb('CALSPaces')  then calspaces=nai()
      when  abb('DEPth')      then        sd=nai()
      when abbn('GRIDs')      then      grid=no()
      when abbn('LOWercase')  then lowerCase=no()
      when  abb('CALMONths')  then        mc=nai()
      when abbn('NARrow')     then    narrow=no()
      when abbn('NARROWER')   then  narrower=no()
      when abbn('NARROWESt')  then narrowest=no()
      when abbn('SHORt')      then     short=no()
      when abbn('SHORTER')    then   shorter=no()
      when abbn('SHORTESt')   then  shortest=no()
      when abbn('SMALl')      then     small=no()
      when abbn('SMALLER')    then   smaller=no()
      when abbn('SMALLESt')   then  smallest=no()
      when abbn('UPPercase')  then upperCase=no()
      when  abb('WIDth')      then        sw=nai()
      otherwise nop
      end    /*select*/
 end         /*do while opts\== ...*/

mc=int(mc,'monthscalender'); if mc>0 then cal=1 days='Sunday Monday Tuesday Wednesday Thursday Friday Saturday' months='January February March April May June July August September October November December' days=' 'days; months=' 'months cyyyy=right(date(),4); hyy=left(cyyyy,2); lyy=right(cyyyy,2) dy.=31; _=30; parse var _ dy.4 1 dy.6 1 dy.9 1 dy.11; dy.2=28+ly(yyyy) yy=right(yyyy,2); sd=p(sd 43); sw=p(sw 80); cw=10; cindent=1; calwidth=76 if small then do; narrow=1  ; short=1  ; end if smaller then do; narrower=1 ; shorter=1 ; end if smallest then do; narrowest=1; shortest=1; end if shortest then shorter=1 if shorter then short =1 if narrow then do; cw=9; cindent=3; calwidth=69; end if narrower then do; cw=4; cindent=1; calwidth=34; end if narrowest then do; cw=2; cindent=1; calwidth=20; end cv_=calwidth+calspaces+2 calfill=left(copies(calfill,cw),cw)

     do j=1 for 7;         _=word(days,j)
           do jw=1 for 3;  _d=strip(substr(_,cw*jw-cw+1,cw))
           if jw=1 then _d=centre(_d,cw+1)
                   else _d=left(_d,cw+1)
           days.jw=days.jw||_d
           end   /*jw*/
     __=daysn
     if narrower  then daysn=__||centre(left(_,3),5)
     if narrowest then daysn=__||center(left(_,2),3)
     end   /*j*/

_yyyy=yyyy; calPuts=0; cv=1; _mm=mm+0; month=word(months,mm) dy.2=28+ly(_yyyy); dim=dy._mm; _dd=01; dow=dow(_mm,_dd,_yyyy); $dd=dd+0

/*─────────────────────────────now: the business of the building the cal*/ call calGen

              do _j=2 to mc
              if cv_\== then do
                               cv=cv+cv_
                               if cv+cv_>=sw then do; cv=1; call calPut
                                                  call fcalPuts;call calPb
                                                  end
                                             else calPuts=0
                               end
                          else do;call calPb;call calPut;call fcalPuts;end
              _mm=_mm+1;  if _mm==13 then do;  _mm=1; _yyyy=_yyyy+1;  end
              month=word(months,_mm); dy.2=28+ly(_yyyy); dim=dy._mm
              dow=dow(_mm,_dd,_yyyy); $dd=0; call calGen
              end   /*_j*/

call fcalPuts return _

/*─────────────────────────────calGen subroutine────────────────────────*/ calGen: cellX=;cellJ=;cellM=;calCells=0;calline=0 call calPut call calPutl copies('─',calwidth),"┌┐"; call calHd call calPutl month ' ' _yyyy  ; call calHd if narrowest | narrower then call calPutl daysn

                       else do jw=1 for 3
                            if space(days.jw)\== then call calPutl days.jw
                            end

calft=1; calfb=0

 do jf=1 for dow-1; call cellDraw calFill,calFill; end
 do jy=1 for dim; call cellDraw jy; end

calfb=1

 do 7; call cellDraw calFill,calFill; end

if sd>32 & \shorter then call calPut return

/*─────────────────────────────cellDraw subroutine──────────────────────*/ cellDraw: parse arg zz,cdDOY;zz=right(zz,2);calCells=calCells+1 if calCells>7 then do

                  calLine=calLine+1
                  cellX=substr(cellX,2)
                  cellJ=substr(cellJ,2)
                  cellM=substr(cellM,2)
                  cellB=translate(cellX,,")(─-"#)
                  if calLine==1 then call cx
                  call calCsm; call calPutl cellX; call calCsj; call cx
                  cellX=; cellJ=; cellM=; calCells=1
                  end

cdDOY=right(cdDOY,cw); cellM=cellM'│'center(,cw) cellX=cellX'│'centre(zz,cw); cellJ=cellJ'│'center(,cw) return

/*═════════════════════════════general 1-line subs══════════════════════*/ abb: arg abbu; parse arg abb; return abbrev(abbu,_,abbl(abb)) abbl: return verify(arg(1)'a',@abc,'M')-1 abbn: parse arg abbn; return abb(abbn) | abb('NO'abbn) calCsj: if sd>49 & \shorter then call calPutl cellB; if sd>24 & \short then call calPutl cellJ; return calCsm: if sd>24 & \short then call calPutl cellM; if sd>49 & \shorter then call calPutl cellB; return calHd: if sd>24 & \shorter then call calPutl  ; if sd>32 & \shortest then call calPutl  ; return calPb: calPuts=calPuts+1; maxKalPuts=max(maxKalPuts,calPuts); if symbol('CT.'calPuts)\=='VAR' then ct.calPuts=; ct.calPuts=overlay(arg(1),ct.calPuts,cv); return calPutl: call calPut copies(' ',cindent)left(arg(2)"│",1)center(arg(1),calwidth)||right('│'arg(2),1);return cx:cx_='├┤';cx=copies(copies('─',cw)'┼',7);if calft then do;cx=translate(cx,'┬',"┼");calft=0;end;if calfb then do;cx=translate(cx,'┴',"┼");cx_='└┘';calfb=0;end;call calPutl cx,cx_;return dow: procedure; arg m,d,y; if m<3 then do; m=m+12; y=y-1; end; yl=left(y,2); yr=right(y,2); w=(d+(m+1)*26%10+yr+yr%4+yl%4+5*yl)//7; if w==0 then w=7; return w er :parse arg _1,_2; call '$ERR' "14"p(_1) p(word(_1,2) !fid(1)) _2;if _1<0 then return _1; exit result err: call er '-'arg(1),arg(2); return erx: call er '-'arg(1),arg(2); exit fcalPuts: do j=1 for maxKalPuts; call put ct.j; end; ct.=; maxKalPuts=0; calPuts=0; return int: int=numx(arg(1),arg(2));if \isint(int) then call erx 92,arg(1) arg(2); return int/1 is#: return verify(arg(1),#)==0 isint: return datatype(arg(1),'W') lower:return translate(arg(1),@abc,@abcU) ly: if arg(1)\== then call erx 01,arg(2);parse var ops na ops;if na== then call erx 35,_o;return na nai: return int(na(),_o) nan: return numx(na(),_o) no: if arg(1)\== then call erx 01,arg(2); return left(_,2)\=='NO' num: procedure;parse arg x .,f,q;if x== then return x;if datatype(x,'N') then return x/1;x=space(translate(x,,','),0);if datatype(x,'N') then return x/1;return numnot() numnot: if q==1 then return x;if q== then call er 53,x f;call erx 53,x f numx: return num(arg(1),arg(2),1) p: return word(arg(1),1) put: _=arg(1);_=translate(_,,'_'chk);if \grid then _=ungrid(_);if lowerCase then _=lower(_);if upperCase then upper _;if shortest&_=' ' then return;call tell _;return tell: say arg(1);return ungrid: return translate(arg(1),,"│║─═┤┐└┴┬├┼┘┌╔╗╚╝╟╢╞╡╫╪╤╧╥╨╠╣") </lang> Output when using the input of: 1/1/1969 (noGrid smallest narrowest)

                          «Snoopy "picture" here» 

     January   1969          February   1969            March   1969
  Su Mo Tu We Th Fr Sa     Su Mo Tu We Th Fr Sa     Su Mo Tu We Th Fr Sa
            1  2  3  4                        1                        1
   5  6  7  8  9 10 11      2  3  4  5  6  7  8      2  3  4  5  6  7  8
  12 13 14 15 16 17 18      9 10 11 12 13 14 15      9 10 11 12 13 14 15
  19 20 21 22 23 24 25     16 17 18 19 20 21 22     16 17 18 19 20 21 22
  26 27 28 29 30 31        23 24 25 26 27 28        23 24 25 26 27 28 29
                                                    30 31

      April   1969              May   1969              June   1969
  Su Mo Tu We Th Fr Sa     Su Mo Tu We Th Fr Sa     Su Mo Tu We Th Fr Sa
         1  2  3  4  5                  1  2  3      1  2  3  4  5  6  7
   6  7  8  9 10 11 12      4  5  6  7  8  9 10      8  9 10 11 12 13 14
  13 14 15 16 17 18 19     11 12 13 14 15 16 17     15 16 17 18 19 20 21
  20 21 22 23 24 25 26     18 19 20 21 22 23 24     22 23 24 25 26 27 28
  27 28 29 30              25 26 27 28 29 30 31     29 30

      July   1969             August   1969           September   1969
  Su Mo Tu We Th Fr Sa     Su Mo Tu We Th Fr Sa     Su Mo Tu We Th Fr Sa
         1  2  3  4  5                     1  2         1  2  3  4  5  6
   6  7  8  9 10 11 12      3  4  5  6  7  8  9      7  8  9 10 11 12 13
  13 14 15 16 17 18 19     10 11 12 13 14 15 16     14 15 16 17 18 19 20
  20 21 22 23 24 25 26     17 18 19 20 21 22 23     21 22 23 24 25 26 27
  27 28 29 30 31           24 25 26 27 28 29 30     28 29 30
                           31

     October   1969          November   1969          December   1969
  Su Mo Tu We Th Fr Sa     Su Mo Tu We Th Fr Sa     Su Mo Tu We Th Fr Sa
            1  2  3  4                        1         1  2  3  4  5  6
   5  6  7  8  9 10 11      2  3  4  5  6  7  8      7  8  9 10 11 12 13
  12 13 14 15 16 17 18      9 10 11 12 13 14 15     14 15 16 17 18 19 20
  19 20 21 22 23 24 25     16 17 18 19 20 21 22     21 22 23 24 25 26 27
  26 27 28 29 30 31        23 24 25 26 27 28 29     28 29 30 31
                           30

Output when using the input of: 1/1/1969 (smallest narrowest width 156 calSpaces 5)
A width of 156 was used to illustrate showing a grid, otherwise it would fit in 132 columns.

 ┌────────────────────┐   ┌────────────────────┐   ┌────────────────────┐   ┌────────────────────┐   ┌────────────────────┐   ┌────────────────────┐
 │   January   1969   │   │  February   1969   │   │    March   1969    │   │    April   1969    │   │     May   1969     │   │    June   1969     │
 │Su Mo Tu We Th Fr Sa│   │Su Mo Tu We Th Fr Sa│   │Su Mo Tu We Th Fr Sa│   │Su Mo Tu We Th Fr Sa│   │Su Mo Tu We Th Fr Sa│   │Su Mo Tu We Th Fr Sa│
 ├──┬──┬──┬──┬──┬──┬──┤   ├──┬──┬──┬──┬──┬──┬──┤   ├──┬──┬──┬──┬──┬──┬──┤   ├──┬──┬──┬──┬──┬──┬──┤   ├──┬──┬──┬──┬──┬──┬──┤   ├──┬──┬──┬──┬──┬──┬──┤
 │  │  │  │ 1│ 2│ 3│ 4│   │  │  │  │  │  │  │ 1│   │  │  │  │  │  │  │ 1│   │  │  │ 1│ 2│ 3│ 4│ 5│   │  │  │  │  │ 1│ 2│ 3│   │ 1│ 2│ 3│ 4│ 5│ 6│ 7│
 ├──┼──┼──┼──┼──┼──┼──┤   ├──┼──┼──┼──┼──┼──┼──┤   ├──┼──┼──┼──┼──┼──┼──┤   ├──┼──┼──┼──┼──┼──┼──┤   ├──┼──┼──┼──┼──┼──┼──┤   ├──┼──┼──┼──┼──┼──┼──┤
 │ 5│ 6│ 7│ 8│ 9│10│11│   │ 2│ 3│ 4│ 5│ 6│ 7│ 8│   │ 2│ 3│ 4│ 5│ 6│ 7│ 8│   │ 6│ 7│ 8│ 9│10│11│12│   │ 4│ 5│ 6│ 7│ 8│ 9│10│   │ 8│ 9│10│11│12│13│14│
 ├──┼──┼──┼──┼──┼──┼──┤   ├──┼──┼──┼──┼──┼──┼──┤   ├──┼──┼──┼──┼──┼──┼──┤   ├──┼──┼──┼──┼──┼──┼──┤   ├──┼──┼──┼──┼──┼──┼──┤   ├──┼──┼──┼──┼──┼──┼──┤
 │12│13│14│15│16│17│18│   │ 9│10│11│12│13│14│15│   │ 9│10│11│12│13│14│15│   │13│14│15│16│17│18│19│   │11│12│13│14│15│16│17│   │15│16│17│18│19│20│21│
 ├──┼──┼──┼──┼──┼──┼──┤   ├──┼──┼──┼──┼──┼──┼──┤   ├──┼──┼──┼──┼──┼──┼──┤   ├──┼──┼──┼──┼──┼──┼──┤   ├──┼──┼──┼──┼──┼──┼──┤   ├──┼──┼──┼──┼──┼──┼──┤
 │19│20│21│22│23│24│25│   │16│17│18│19│20│21│22│   │16│17│18│19│20│21│22│   │20│21│22│23│24│25│26│   │18│19│20│21│22│23│24│   │22│23│24│25│26│27│28│
 ├──┼──┼──┼──┼──┼──┼──┤   ├──┼──┼──┼──┼──┼──┼──┤   ├──┼──┼──┼──┼──┼──┼──┤   ├──┼──┼──┼──┼──┼──┼──┤   ├──┼──┼──┼──┼──┼──┼──┤   ├──┼──┼──┼──┼──┼──┼──┤
 │26│27│28│29│30│31│  │   │23│24│25│26│27│28│  │   │23│24│25│26│27│28│29│   │27│28│29│30│  │  │  │   │25│26│27│28│29│30│31│   │29│30│  │  │  │  │  │
 └──┴──┴──┴──┴──┴──┴──┘   └──┴──┴──┴──┴──┴──┴──┘   ├──┼──┼──┼──┼──┼──┼──┤   └──┴──┴──┴──┴──┴──┴──┘   └──┴──┴──┴──┴──┴──┴──┘   └──┴──┴──┴──┴──┴──┴──┘
                                                   │30│31│  │  │  │  │  │
                                                   └──┴──┴──┴──┴──┴──┴──┘
 ┌────────────────────┐   ┌────────────────────┐   ┌────────────────────┐   ┌────────────────────┐   ┌────────────────────┐   ┌────────────────────┐
 │    July   1969     │   │   August   1969    │   │  September   1969  │   │   October   1969   │   │  November   1969   │   │  December   1969   │
 │Su Mo Tu We Th Fr Sa│   │Su Mo Tu We Th Fr Sa│   │Su Mo Tu We Th Fr Sa│   │Su Mo Tu We Th Fr Sa│   │Su Mo Tu We Th Fr Sa│   │Su Mo Tu We Th Fr Sa│
 ├──┬──┬──┬──┬──┬──┬──┤   ├──┬──┬──┬──┬──┬──┬──┤   ├──┬──┬──┬──┬──┬──┬──┤   ├──┬──┬──┬──┬──┬──┬──┤   ├──┬──┬──┬──┬──┬──┬──┤   ├──┬──┬──┬──┬──┬──┬──┤
 │  │  │ 1│ 2│ 3│ 4│ 5│   │  │  │  │  │  │ 1│ 2│   │  │ 1│ 2│ 3│ 4│ 5│ 6│   │  │  │  │ 1│ 2│ 3│ 4│   │  │  │  │  │  │  │ 1│   │  │ 1│ 2│ 3│ 4│ 5│ 6│
 ├──┼──┼──┼──┼──┼──┼──┤   ├──┼──┼──┼──┼──┼──┼──┤   ├──┼──┼──┼──┼──┼──┼──┤   ├──┼──┼──┼──┼──┼──┼──┤   ├──┼──┼──┼──┼──┼──┼──┤   ├──┼──┼──┼──┼──┼──┼──┤
 │ 6│ 7│ 8│ 9│10│11│12│   │ 3│ 4│ 5│ 6│ 7│ 8│ 9│   │ 7│ 8│ 9│10│11│12│13│   │ 5│ 6│ 7│ 8│ 9│10│11│   │ 2│ 3│ 4│ 5│ 6│ 7│ 8│   │ 7│ 8│ 9│10│11│12│13│
 ├──┼──┼──┼──┼──┼──┼──┤   ├──┼──┼──┼──┼──┼──┼──┤   ├──┼──┼──┼──┼──┼──┼──┤   ├──┼──┼──┼──┼──┼──┼──┤   ├──┼──┼──┼──┼──┼──┼──┤   ├──┼──┼──┼──┼──┼──┼──┤
 │13│14│15│16│17│18│19│   │10│11│12│13│14│15│16│   │14│15│16│17│18│19│20│   │12│13│14│15│16│17│18│   │ 9│10│11│12│13│14│15│   │14│15│16│17│18│19│20│
 ├──┼──┼──┼──┼──┼──┼──┤   ├──┼──┼──┼──┼──┼──┼──┤   ├──┼──┼──┼──┼──┼──┼──┤   ├──┼──┼──┼──┼──┼──┼──┤   ├──┼──┼──┼──┼──┼──┼──┤   ├──┼──┼──┼──┼──┼──┼──┤
 │20│21│22│23│24│25│26│   │17│18│19│20│21│22│23│   │21│22│23│24│25│26│27│   │19│20│21│22│23│24│25│   │16│17│18│19│20│21│22│   │21│22│23│24│25│26│27│
 ├──┼──┼──┼──┼──┼──┼──┤   ├──┼──┼──┼──┼──┼──┼──┤   ├──┼──┼──┼──┼──┼──┼──┤   ├──┼──┼──┼──┼──┼──┼──┤   ├──┼──┼──┼──┼──┼──┼──┤   ├──┼──┼──┼──┼──┼──┼──┤
 │27│28│29│30│31│  │  │   │24│25│26│27│28│29│30│   │28│29│30│  │  │  │  │   │26│27│28│29│30│31│  │   │23│24│25│26│27│28│29│   │28│29│30│31│  │  │  │
 └──┴──┴──┴──┴──┴──┴──┘   ├──┼──┼──┼──┼──┼──┼──┤   └──┴──┴──┴──┴──┴──┴──┘   └──┴──┴──┴──┴──┴──┴──┘   ├──┼──┼──┼──┼──┼──┼──┤   └──┴──┴──┴──┴──┴──┴──┘
                          │31│  │  │  │  │  │  │                                                     │30│  │  │  │  │  │  │
                          └──┴──┴──┴──┴──┴──┴──┘                                                     └──┴──┴──┴──┴──┴──┴──┘
                                                                      ,-~~-.___.
                                                                     / ()=(()   \
                                                                    (   (        0
                                                                     \._\, ,----'
                                                                ##XXXxxxxxxx
                                                                       /  ---'~;
                                                                      /    /~|-
                                                                    =(   ~~  |
                                                              /~~~~~~~~~~~~~~~~~~~~~\
                                                             /_______________________\
                                                            /_________________________\
                                                           /___________________________\
                                                              |____________________|
                                                              |____________________|
                                                              |____________________|
                                                              |                    |

Ruby

Date class, from the standard library, knows how many days in a month, and which day is Sunday, for both Julian and Gregorian calendars. This program uses Date class, plus its own assumptions, to create the calendar. This program assumes that every year has 12 months and starts with January 1, and every month fits in 6 weeks starting with Sunday.

  • This program requires Ruby 1.8.7 because it calls Array#each_slice.
Works with: Ruby version 1.8.7

<lang ruby>require 'date'

  1. Creates a calendar of _year_. Returns this calendar as a multi-line
  2. string fit to _columns_.

def cal(year, columns)

 # Start at January 1.
 #
 # Date::ENGLAND marks the switch from Julian calendar to Gregorian
 # calendar at 1752 September 14. This removes September 3 to 13 from
 # year 1752. (By fortune, it keeps January 1.)
 #
 date = Date.new(year, 1, 1, Date::ENGLAND)
 # Collect calendars of all 12 months.
 months = (1..12).collect do |month|
   rows = [Date::MONTHNAMES[month].center(20), "Su Mo Tu We Th Fr Sa"]
   # Make array of 42 days, starting with Sunday.
   days = []
   date.wday.times { days.push "  " }
   while date.month == month
     days.push("%2d" % date.mday)
     date += 1
   end
   (42 - days.length).times { days.push "  " }
   days.each_slice(7) { |week| rows.push(week.join " ") }
   next rows
 end
 # Calculate months per row (mpr).
 #  1. Divide columns by 22 columns per month, rounded down. (Pretend
 #     to have 2 extra columns; last month uses only 20 columns.)
 #  2. Decrease mpr if 12 months would fit in the same months per
 #     column (mpc). For example, if we can fit 5 mpr and 3 mpc, then
 #     we use 4 mpr and 3 mpc.
 mpr = (columns + 2).div 22
 mpr = 12.div((12 + mpr - 1).div mpr)
 # Use 20 columns per month + 2 spaces between months.
 width = mpr * 22 - 2
 # Join months into calendar.
 rows = ["[Snoopy]".center(width), "#{year}".center(width)]
 months.each_slice(mpr) do |slice|
   slice[0].each_index do |i|
     rows.push(slice.map {|a| a[i]}.join "  ")
   end
 end
 return rows.join("\n")

end


ARGV.length == 1 or abort "usage: #{$0} year"

  1. Guess width of terminal.
  2. 1. Obey environment variable COLUMNS.
  3. 2. Try to require 'io/console' from Ruby 1.9.3.
  4. 3. Try to run `tput co`.
  5. 4. Assume 80 columns.

columns = begin Integer(ENV["COLUMNS"] || "")

         rescue
           begin require 'io/console'; IO.console.winsize[1]
           rescue LoadError
             begin Integer(`tput co`)
             rescue
               80; end; end; end

puts cal(Integer(ARGV[0]), columns)</lang>

Here is 1969 in 132 columns.

$ ruby cal.rb 1969                                                             
                                                             [Snoopy]                                                             
                                                               1969                                                               
      January               February               March                 April                  May                   June        
Su Mo Tu We Th Fr Sa  Su Mo Tu We Th Fr Sa  Su Mo Tu We Th Fr Sa  Su Mo Tu We Th Fr Sa  Su Mo Tu We Th Fr Sa  Su Mo Tu We Th Fr Sa
          1  2  3  4                     1                     1         1  2  3  4  5               1  2  3   1  2  3  4  5  6  7
 5  6  7  8  9 10 11   2  3  4  5  6  7  8   2  3  4  5  6  7  8   6  7  8  9 10 11 12   4  5  6  7  8  9 10   8  9 10 11 12 13 14
12 13 14 15 16 17 18   9 10 11 12 13 14 15   9 10 11 12 13 14 15  13 14 15 16 17 18 19  11 12 13 14 15 16 17  15 16 17 18 19 20 21
19 20 21 22 23 24 25  16 17 18 19 20 21 22  16 17 18 19 20 21 22  20 21 22 23 24 25 26  18 19 20 21 22 23 24  22 23 24 25 26 27 28
26 27 28 29 30 31     23 24 25 26 27 28     23 24 25 26 27 28 29  27 28 29 30           25 26 27 28 29 30 31  29 30               
                                            30 31                                                                                 
        July                 August              September              October               November              December      
Su Mo Tu We Th Fr Sa  Su Mo Tu We Th Fr Sa  Su Mo Tu We Th Fr Sa  Su Mo Tu We Th Fr Sa  Su Mo Tu We Th Fr Sa  Su Mo Tu We Th Fr Sa
       1  2  3  4  5                  1  2      1  2  3  4  5  6            1  2  3  4                     1      1  2  3  4  5  6
 6  7  8  9 10 11 12   3  4  5  6  7  8  9   7  8  9 10 11 12 13   5  6  7  8  9 10 11   2  3  4  5  6  7  8   7  8  9 10 11 12 13
13 14 15 16 17 18 19  10 11 12 13 14 15 16  14 15 16 17 18 19 20  12 13 14 15 16 17 18   9 10 11 12 13 14 15  14 15 16 17 18 19 20
20 21 22 23 24 25 26  17 18 19 20 21 22 23  21 22 23 24 25 26 27  19 20 21 22 23 24 25  16 17 18 19 20 21 22  21 22 23 24 25 26 27
27 28 29 30 31        24 25 26 27 28 29 30  28 29 30              26 27 28 29 30 31     23 24 25 26 27 28 29  28 29 30 31         
                      31                                                                30                                        

The output in 80 columns is similar, except that there are only three months per row, and the "[Snoopy]" and "1969" are centered above February.

Run BASIC

<lang runbasic>input "Gimme a year";yr cls ' clear screen mths$ = "January,February,March,April,May,June,July,August,September,October,November,December"

html "

" html ""

for mm = 1 to 12

 gosub [getMonthInfo]
 x = 0
html "" if mm mod 4 = 0 then html "" ' 4 months across next mm html "

Year:";yr;"

" html ""
 for i = 1 to mmDays + dow
   if i > dow then  x = x + 1
if x = 0 then html "" else html "" if (i mod 7) = 0 then html "" next i html "
"
 html "  ";word$(mths$,mm,",")
html "
SunMonTueWedThrFriSat
";x;"

"

wait

[getMonthInfo] ' ---------------------------------------- ' day of week when month begins. ' days in a month ' ---------------------------------------- if yr < 100 then yr = val(date$("yy")) if mm < 1 or mm > 12 then mm = val(date$("mm")) dayOne$ = mm;"-01-";yr n = date$(dayOne$) dow = 1 + (n Mod 7) ' Day of Week month begins m1 = mm n1 = n + 27 while m1 = mm n1 = n1 + 1 n$ = date$(n1) m1 = val(left$(n$,2)) wend mmDays = n1 - n ' Days in Month RETURN</lang>

Input any valid year

Year:1969

January
Sun Mon Tue Wed Thr Fri Sat
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
February
Sun Mon Tue Wed Thr Fri Sat
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28
March
Sun Mon Tue Wed Thr Fri Sat
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31
April
Sun Mon Tue Wed Thr Fri Sat
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
May
Sun Mon Tue Wed Thr Fri Sat
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
June
Sun Mon Tue Wed Thr Fri Sat
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30
July
Sun Mon Tue Wed Thr Fri Sat
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31
August
Sun Mon Tue Wed Thr Fri Sat
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
September
Sun Mon Tue Wed Thr Fri Sat
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
October
Sun Mon Tue Wed Thr Fri Sat
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
November
Sun Mon Tue Wed Thr Fri Sat
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
December
Sun Mon Tue Wed Thr Fri Sat
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31

Scala

Works with: Scala version 2.9.1

<lang Scala>import java.util.TimeZone import java.util.Locale import java.util.Calendar import java.util.GregorianCalendar

object Helper {

 def monthsMax(locale: Locale = Locale.getDefault): Int = {
   val cal = Calendar.getInstance(locale)
   cal.getMaximum(Calendar.MONTH)
 } 
 def numberOfMonths(locale: Locale = Locale.getDefault): Int = monthsMax(locale)+1
 
 def namesOfMonths(year: Int, locale: Locale = Locale.getDefault): List[String] = {
   val cal = Calendar.getInstance(locale)
   val f1: Int => String = i => {
     cal.set(year,i,1)
     cal.getDisplayName(Calendar.MONTH, Calendar.LONG, locale)
   }
   (0 to monthsMax(locale)) map f1 toList
 }
 def jgt(cal: GregorianCalendar): Int = {cal.setTime(cal.getGregorianChange); cal.get(Calendar.YEAR)}
 def isJGT(year: Int, cal: GregorianCalendar) = year==jgt(cal)
 def offsets(year: Int, locale: Locale = Locale.getDefault): List[Int] = {
   val cal = Calendar.getInstance(locale)
   val months = cal.getMaximum(Calendar.MONTH)
   val f1: Int => Int = i => {
     cal.set(year,i,1)
     cal.get(Calendar.DAY_OF_WEEK)
   }
   ((0 to months) map f1 toList) map {i=>if((i-2)<0) i+5 else i-2}
 }
 
 def headerNameOfDays(locale: Locale = Locale.getDefault) = {
   val cal = Calendar.getInstance(locale)
   val mdow = cal.getDisplayNames(Calendar.DAY_OF_WEEK, Calendar.SHORT, locale) // map days of week
   val it = mdow.keySet.iterator
   import scala.collection.mutable.ListBuffer
   val lb = new ListBuffer[String]
   while (it.hasNext) lb+=it.next
   val lpdow = lb.toList.map{k=>(mdow.get(k),k.substring(0,2))} // list pair days of week
   (lpdow map {p=>if((p._1-2)<0) (p._1+5, p._2) else (p._1-2, p._2)} sortWith(_._1<_._1) map (_._2)).foldRight("")(_+" "+_)
 }
 

}

object CalendarPrint extends App {

 import Helper._
 val tzd = TimeZone.getDefault
 val locd = Locale.getDefault
 
 def printCalendar(year: Int, printerWidth: Int, loc: Locale, tz: TimeZone) = {
 
   def getCal: List[Triple[Int, Int, String]] = {
     val cal = new GregorianCalendar(tz, loc)
   
     def getGregCal: List[Triple[Int, Int, String]] = {
       val month = 0
       val day = 1
       cal.set(year,month,day)
       val f1: Int => Triple[Int, Int, Int] = i => {
         val cal = Calendar.getInstance(tz, loc)
         cal.set(year,i,1)
         val minday = cal.getActualMinimum(Calendar.DAY_OF_MONTH)
         val maxday = cal.getActualMaximum(Calendar.DAY_OF_MONTH)
         (i, minday, maxday)
       }
       val limits = (0 to monthsMax(loc)) map f1
       val f2: (Int, Int, Int) => String = (year, month, day) => {
         val cal = Calendar.getInstance(tz, loc)
         cal.set(year,month,day)
         cal.getDisplayName(Calendar.DAY_OF_WEEK, Calendar.SHORT, loc)
       }
       val calend = for {
         i <- 0 to monthsMax(loc)
         j <- limits(i)._2 to limits(i)._3
         val dow = f2(year, i, j)
       } yield (i, j, dow)
       if (isJGT(year, new GregorianCalendar(tz, loc))) calend.filter{_._1!=9}.toList else calend.toList
     }
   
     def getJGT: List[Triple[Int, Int, String]] = {
       if (!isJGT(year, new GregorianCalendar(tz, loc))) return Nil
       val cal = new GregorianCalendar(tz, loc)
       cal.set(year,9,1)
       var ldom = 0
       def it = new Iterator[Tuple3[Int,Int, String]]{
         def next={
           val res = (cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH), cal.getDisplayName(Calendar.DAY_OF_WEEK, Calendar.SHORT, Locale.GERMAN))
           ldom = res._2
           cal.roll(Calendar.DAY_OF_MONTH, true)
           res
         }
         def hasNext = (cal.get(Calendar.DAY_OF_MONTH)>ldom)
       }
       it.toList
     }
     (getGregCal++getJGT).sortWith((s,t)=>s._1*100+s._2<t._1*100+t._2)
   }
   def printCal(calList: List[Triple[Int, Int, String]]) = {
     val pwmax = 300   // printer width maximum
     val mw = 20   // month width
     val gwf = 2   // gap width fixed
     val gwm = 2   // gap width minimum
     val fgw = true
   
     val arr = Array.ofDim[String](6,7)
     def clear = for (i <- 0 until 6) for (j <- 0 until 7) arr(i)(j) = "  "
   
     def limits(printerWidth: Int): Tuple5[Int, Int, Int, Int, Int] = {
       val pw = if (printerWidth<20) 20 else if (printerWidth>300) 300 else printerWidth
 
       def getFGW(msbs: Int,gsm: Int) = {val r=(pw-msbs*mw-gsm)/2; (r,gwf,r)}   // fixed gap width
       def getVGW(msbs: Int,gsm: Int) = pw-msbs*mw-gsm match {                  // variable gap width
                                          case a if (a<2*gwm) => (a/2,gwm,a/2)
                                          case b => {val x = (b+gsm)/(msbs+1); (x,x,x)}
                                        }
   
       // months side by side, gaps sum minimum
       val (msbs, gsm) = {
         val (x, y) = {val c = if (pw/mw>12) 12 else pw/mw; if (c*mw+(c-1)*gwm<=pw) (c, c*gwm) else (c-1, (c-1)*gwm)}
         val x1 = x match {
           case 5 => 4
           case a if (a>6 && a<12) => 6
           case other => other
         }  
         (x1, (x1-1)*gwm)
       }
       // left margin, gap width, right margin
       val (lm,gw,rm) = if (fgw) getFGW(msbs,gsm) else getVGW(msbs,gsm)
       (pw,msbs,lm,gw,rm)
     }

     val (pw,msbs,lm,gw,rm) = limits(printerWidth)
     val monthsList = (0 to monthsMax(loc)).map{m=>calList.filter{_._1==m}}.toList
     val nom = namesOfMonths(year,loc)
     val offsetList = offsets(year,loc)
     val hnod = headerNameOfDays(loc)
     
     val fsplit: List[(Int, Int, String)] => List[String] = list => {

val fap: Int => (Int, Int) = p => (p/7,p%7) clear for (i <- 0 until list.size) arr(fap(i+offsetList(list(i)._1))._1)(fap(i+offsetList(list(i)._1))._2)="%2d".format(list(i)._2) //arr.toList.map(_.toList).map(_.foldLeft("")(_+" "+_)) arr.toList.map(_.toList).map(_.foldRight("")(_+" "+_)) }

     val monthsRows = monthsList.map(fsplit)
     
     val center: (String, Int) => String = (s,l) => {
       if (s.size>=l) s.substring(0,l) else
       (" "*((l-s.size)/2)+s+" "*((l-s.size)/2)+" ").substring(0,l)
     } 
     println(center("[Snoopy Picture]",pw))
     println
     println(center(""+year,pw))
     println
     
     val ul = numberOfMonths(loc)
     val rowblocks = (1 to ul/msbs).map{i=>
       (0 to 5).map{j=>
         val lb = new scala.collection.mutable.ListBuffer[String]
         val k = (i-1)*msbs
         (k to k+msbs-1).map{l=>
           lb+=monthsRows(l)(j)
         }
         lb.toList
       }.toList
     }.toList
     
     val mheaders = (1 to ul/msbs).map{i=>(0 to msbs-1).map{j=>center(nom(j+(i-1)*msbs),20)}.toList}.toList
     val dowheaders = (1 to ul/msbs).map{i=>(0 to msbs-1).map{j=>center(hnod,20)}.toList}.toList
     (1 to 12/msbs).foreach{i=>
       println(" "*lm+mheaders(i-1).foldRight("")(_+" "*gw+_))
       println(" "*lm+dowheaders(i-1).foldRight("")(_+" "*gw+_))
       rowblocks(i-1).foreach{xs=>println(" "*lm+xs.foldRight("")(_+" "*(gw-1)+_))}
       println
     }
   }
   val calList = getCal
   printCal(calList)
   
 }  
 def printGregCal(year: Int = 1969, pw: Int = 80, JGT: Boolean = false, loc: Locale = Locale.getDefault, tz: TimeZone = TimeZone.getDefault) {
   val _year = if (JGT==false) year else jgt(new GregorianCalendar(tz, loc))  
   printCalendar(_year, pw, loc, tz)
 }
 
 printGregCal()
 printGregCal(JGT=true, loc=Locale.UK, pw=132, tz=TimeZone.getTimeZone("UK/London"))
 

}</lang>

Output:

                                [Snoopy Picture]                                

                                      1969                                      

               Januar               Februar                 März          
        Mo Di Mi Do Fr Sa So  Mo Di Mi Do Fr Sa So  Mo Di Mi Do Fr Sa So  
               1  2  3  4  5                  1  2                  1  2  
         6  7  8  9 10 11 12   3  4  5  6  7  8  9   3  4  5  6  7  8  9  
        13 14 15 16 17 18 19  10 11 12 13 14 15 16  10 11 12 13 14 15 16  
        20 21 22 23 24 25 26  17 18 19 20 21 22 23  17 18 19 20 21 22 23  
        27 28 29 30 31        24 25 26 27 28        24 25 26 27 28 29 30  
                                                    31                    

               April                  Mai                   Juni          
        Mo Di Mi Do Fr Sa So  Mo Di Mi Do Fr Sa So  Mo Di Mi Do Fr Sa So  
            1  2  3  4  5  6            1  2  3  4                     1  
         7  8  9 10 11 12 13   5  6  7  8  9 10 11   2  3  4  5  6  7  8  
        14 15 16 17 18 19 20  12 13 14 15 16 17 18   9 10 11 12 13 14 15  
        21 22 23 24 25 26 27  19 20 21 22 23 24 25  16 17 18 19 20 21 22  
        28 29 30              26 27 28 29 30 31     23 24 25 26 27 28 29  
                                                    30                    

                Juli                 August              September        
        Mo Di Mi Do Fr Sa So  Mo Di Mi Do Fr Sa So  Mo Di Mi Do Fr Sa So  
            1  2  3  4  5  6               1  2  3   1  2  3  4  5  6  7  
         7  8  9 10 11 12 13   4  5  6  7  8  9 10   8  9 10 11 12 13 14  
        14 15 16 17 18 19 20  11 12 13 14 15 16 17  15 16 17 18 19 20 21  
        21 22 23 24 25 26 27  18 19 20 21 22 23 24  22 23 24 25 26 27 28  
        28 29 30 31           25 26 27 28 29 30 31  29 30                 
                                                                          

              Oktober               November              Dezember        
        Mo Di Mi Do Fr Sa So  Mo Di Mi Do Fr Sa So  Mo Di Mi Do Fr Sa So  
               1  2  3  4  5                  1  2   1  2  3  4  5  6  7  
         6  7  8  9 10 11 12   3  4  5  6  7  8  9   8  9 10 11 12 13 14  
        13 14 15 16 17 18 19  10 11 12 13 14 15 16  15 16 17 18 19 20 21  
        20 21 22 23 24 25 26  17 18 19 20 21 22 23  22 23 24 25 26 27 28  
        27 28 29 30 31        24 25 26 27 28 29 30  29 30 31              

                                                          [Snoopy Picture]                                                          

                                                                1582                                                                

       January               February               March                 April                  May                   June          
 Mo Tu We Th Fr Sa Su  Mo Tu We Th Fr Sa Su  Mo Tu We Th Fr Sa Su  Mo Tu We Th Fr Sa Su  Mo Tu We Th Fr Sa Su  Mo Tu We Th Fr Sa Su  
  1  2  3  4  5  6  7            1  2  3  4            1  2  3  4                     1      1  2  3  4  5  6               1  2  3  
  8  9 10 11 12 13 14   5  6  7  8  9 10 11   5  6  7  8  9 10 11   2  3  4  5  6  7  8   7  8  9 10 11 12 13   4  5  6  7  8  9 10  
 15 16 17 18 19 20 21  12 13 14 15 16 17 18  12 13 14 15 16 17 18   9 10 11 12 13 14 15  14 15 16 17 18 19 20  11 12 13 14 15 16 17  
 22 23 24 25 26 27 28  19 20 21 22 23 24 25  19 20 21 22 23 24 25  16 17 18 19 20 21 22  21 22 23 24 25 26 27  18 19 20 21 22 23 24  
 29 30 31              26 27 28              26 27 28 29 30 31     23 24 25 26 27 28 29  28 29 30 31           25 26 27 28 29 30     
                                                                   30                                                                

         July                 August              September              October               November              December        
 Mo Tu We Th Fr Sa Su  Mo Tu We Th Fr Sa Su  Mo Tu We Th Fr Sa Su  Mo Tu We Th Fr Sa Su  Mo Tu We Th Fr Sa Su  Mo Tu We Th Fr Sa Su  
                    1         1  2  3  4  5                  1  2   1  2  3  4 15 16 17   1  2  3  4  5  6  7         1  2  3  4  5  
  2  3  4  5  6  7  8   6  7  8  9 10 11 12   3  4  5  6  7  8  9  18 19 20 21 22 23 24   8  9 10 11 12 13 14   6  7  8  9 10 11 12  
  9 10 11 12 13 14 15  13 14 15 16 17 18 19  10 11 12 13 14 15 16  25 26 27 28 29 30 31  15 16 17 18 19 20 21  13 14 15 16 17 18 19  
 16 17 18 19 20 21 22  20 21 22 23 24 25 26  17 18 19 20 21 22 23                        22 23 24 25 26 27 28  20 21 22 23 24 25 26  
 23 24 25 26 27 28 29  27 28 29 30 31        24 25 26 27 28 29 30                        29 30                 27 28 29 30 31        
 30 31                                                                                                                               

Seed7

<lang seed7>$ include "seed7_05.s7i";

 include "time.s7i";

const func string: center (in string: stri, in integer: length) is

 return ("" lpad (length - length(stri)) div 2 <& stri) rpad length;

const proc: printCalendar (in integer: year, in integer: cols) is func

 local
   var time: date is time.value;
   var integer: dayOfWeek is 0;
   const array string: monthNames is [] ("January", "February", "March", "April", "May", "June",
       "July", "August", "September", "October", "November", "December"); 
   var array array string: monthTable is 12 times 9 times "";
   var string: str is "";
   var integer: month is 0;
   var integer: position is 0;
   var integer: row is 0;
   var integer: column is 0;
   var integer: line is 0;
 begin
   for month range 1 to 12 do
     monthTable[month][1] := " " & center(monthNames[month], 20);
     monthTable[month][2] := " Mo Tu We Th Fr Sa Su";
     date := date(year, month, 1);
     dayOfWeek := dayOfWeek(date);
     for position range 1 to 43 do
       if position >= dayOfWeek and position - dayOfWeek < daysInMonth(date.year, date.month) then
         str := succ(position - dayOfWeek) lpad 3;
       else
         str := "" lpad 3;
       end if;
       monthTable[month][3 + pred(position) div 7] &:= str;
     end for;
   end for;
   writeln(center("[Snoopy Picture]", cols * 24 + 4));
   writeln(center(str(year),cols * 24 + 4));
   writeln;
   for row range 1 to succ(11 div cols) do
     for line range 1 to 9 do
       for column range 1 to cols do
         if pred(row) * cols + column <= 12 then
           write("   " & monthTable[pred(row) * cols + column][line]);
         end if;
       end for;
       writeln;
     end for;
   end for;
 end func;

const proc: main is func

 begin
   printCalendar(1969, 3);
 end func;</lang>

Original source: [1]

The output of this program is:

                              [Snoopy Picture]                              
                                    1969                                    

          January                 February                 March        
    Mo Tu We Th Fr Sa Su    Mo Tu We Th Fr Sa Su    Mo Tu We Th Fr Sa Su
           1  2  3  4  5                    1  2                    1  2
     6  7  8  9 10 11 12     3  4  5  6  7  8  9     3  4  5  6  7  8  9
    13 14 15 16 17 18 19    10 11 12 13 14 15 16    10 11 12 13 14 15 16
    20 21 22 23 24 25 26    17 18 19 20 21 22 23    17 18 19 20 21 22 23
    27 28 29 30 31          24 25 26 27 28          24 25 26 27 28 29 30
                                                    31                  
                  
           April                    May                     June        
    Mo Tu We Th Fr Sa Su    Mo Tu We Th Fr Sa Su    Mo Tu We Th Fr Sa Su
        1  2  3  4  5  6              1  2  3  4                       1
     7  8  9 10 11 12 13     5  6  7  8  9 10 11     2  3  4  5  6  7  8
    14 15 16 17 18 19 20    12 13 14 15 16 17 18     9 10 11 12 13 14 15
    21 22 23 24 25 26 27    19 20 21 22 23 24 25    16 17 18 19 20 21 22
    28 29 30                26 27 28 29 30 31       23 24 25 26 27 28 29
                                                    30                  
                  
            July                   August                September      
    Mo Tu We Th Fr Sa Su    Mo Tu We Th Fr Sa Su    Mo Tu We Th Fr Sa Su
        1  2  3  4  5  6                 1  2  3     1  2  3  4  5  6  7
     7  8  9 10 11 12 13     4  5  6  7  8  9 10     8  9 10 11 12 13 14
    14 15 16 17 18 19 20    11 12 13 14 15 16 17    15 16 17 18 19 20 21
    21 22 23 24 25 26 27    18 19 20 21 22 23 24    22 23 24 25 26 27 28
    28 29 30 31             25 26 27 28 29 30 31    29 30               
                                                                        
                  
          October                 November                December      
    Mo Tu We Th Fr Sa Su    Mo Tu We Th Fr Sa Su    Mo Tu We Th Fr Sa Su
           1  2  3  4  5                    1  2     1  2  3  4  5  6  7
     6  7  8  9 10 11 12     3  4  5  6  7  8  9     8  9 10 11 12 13 14
    13 14 15 16 17 18 19    10 11 12 13 14 15 16    15 16 17 18 19 20 21
    20 21 22 23 24 25 26    17 18 19 20 21 22 23    22 23 24 25 26 27 28
    27 28 29 30 31          24 25 26 27 28 29 30    29 30 31            
                                                                        
                  

Tcl

Due to the prevalence of 80 column devices instead of 132 column ones, this code produces 3 months at a time instead of 4. <lang tcl>package require Tcl 8.5

  1. Produce information about the days in a month, without any assumptions about
  2. what those days actually are.

proc calMonthDays {timezone locale year month} {

   set days {}
   set moment [clock scan [format "%04d-%02d-00 12:00" $year $month] \

-timezone $timezone -locale $locale -format "%Y-%m-%d %H:%M"]

   while 1 {

set moment [clock add $moment 1 day] lassign [clock format $moment -timezone $timezone -locale $locale \ -format "%m %d %u"] m d dow if {[scan $m %d] != $month} { return $days } lappend days $moment [scan $d %d] $dow

   }

}

proc calMonth {year month timezone locale} {

   set dow 0
   set line ""
   set lines {}
   foreach {t day dayofweek} [calMonthDays $timezone $locale $year $month] {

if {![llength $lines]} {lappend lines $t} if {$dow > $dayofweek} { lappend lines [string trimright $line] set line "" set dow 0 } while {$dow < $dayofweek-1} { append line " " incr dow } append line [format "%2d " $day] set dow $dayofweek

   }
   lappend lines [string trimright $line]

}

proc cal3Month {year month timezone locale} {

   # Extract the month data
   set d1 [lassign [calMonth $year $month $timezone $locale] t1]; incr month
   set d2 [lassign [calMonth $year $month $timezone $locale] t2]; incr month
   set d3 [lassign [calMonth $year $month $timezone $locale] t3]
   # Print the header line of month names
   foreach t [list $t1 $t2 $t3] {

set m [clock format $t -timezone $timezone -locale $locale -format "%B"] set l [expr {10 + [string length $m]/2}] puts -nonewline [format "%-25s" [format "%*s" $l $m]]

   }
   puts ""
   # Print the month days
   foreach l1 $d1 l2 $d2 l3 $d3 {

puts [format "%-25s%-25s%s" $l1 $l2 $l3]

   }

}

proc cal {{year ""} {timezone :localtime} {locale en}} {

   if {$year eq ""} {

set year [clock format [clock seconds] -format %Y]

   }
   puts [format "%40s" "-- $year --"]
   foreach m {1 4 7 10} {

puts "" cal3Month $year $m $timezone $locale

   }

}

proc snoopy {} {

   puts [format "%43s\n" {[Snoopy Picture]}]

}

snoopy cal</lang> Which produces this output:

                           [Snoopy Picture]

                              -- 2011 --

      January                  February                  March             
                1  2         1  2  3  4  5  6         1  2  3  4  5  6
 3  4  5  6  7  8  9      7  8  9 10 11 12 13      7  8  9 10 11 12 13
10 11 12 13 14 15 16     14 15 16 17 18 19 20     14 15 16 17 18 19 20
17 18 19 20 21 22 23     21 22 23 24 25 26 27     21 22 23 24 25 26 27
24 25 26 27 28 29 30     28                       28 29 30 31
31                                                

       April                     May                      June             
             1  2  3                        1            1  2  3  4  5
 4  5  6  7  8  9 10      2  3  4  5  6  7  8      6  7  8  9 10 11 12
11 12 13 14 15 16 17      9 10 11 12 13 14 15     13 14 15 16 17 18 19
18 19 20 21 22 23 24     16 17 18 19 20 21 22     20 21 22 23 24 25 26
25 26 27 28 29 30        23 24 25 26 27 28 29     27 28 29 30
                         30 31                    

        July                    August                 September           
             1  2  3      1  2  3  4  5  6  7               1  2  3  4
 4  5  6  7  8  9 10      8  9 10 11 12 13 14      5  6  7  8  9 10 11
11 12 13 14 15 16 17     15 16 17 18 19 20 21     12 13 14 15 16 17 18
18 19 20 21 22 23 24     22 23 24 25 26 27 28     19 20 21 22 23 24 25
25 26 27 28 29 30 31     29 30 31                 26 27 28 29 30

      October                  November                 December           
                1  2         1  2  3  4  5  6               1  2  3  4
 3  4  5  6  7  8  9      7  8  9 10 11 12 13      5  6  7  8  9 10 11
10 11 12 13 14 15 16     14 15 16 17 18 19 20     12 13 14 15 16 17 18
17 18 19 20 21 22 23     21 22 23 24 25 26 27     19 20 21 22 23 24 25
24 25 26 27 28 29 30     28 29 30                 26 27 28 29 30 31
31                                                

If a different year is chosen, it's printed… <lang tcl>snoopy cal 1969</lang>

                           [Snoopy Picture]

                              -- 1969 --

      January                  February                  March             
       1  2  3  4  5                     1  2                     1  2
 6  7  8  9 10 11 12      3  4  5  6  7  8  9      3  4  5  6  7  8  9
13 14 15 16 17 18 19     10 11 12 13 14 15 16     10 11 12 13 14 15 16
20 21 22 23 24 25 26     17 18 19 20 21 22 23     17 18 19 20 21 22 23
27 28 29 30 31           24 25 26 27 28           24 25 26 27 28 29 30
                                                  31

       April                     May                      June             
    1  2  3  4  5  6               1  2  3  4                        1
 7  8  9 10 11 12 13      5  6  7  8  9 10 11      2  3  4  5  6  7  8
14 15 16 17 18 19 20     12 13 14 15 16 17 18      9 10 11 12 13 14 15
21 22 23 24 25 26 27     19 20 21 22 23 24 25     16 17 18 19 20 21 22
28 29 30                 26 27 28 29 30 31        23 24 25 26 27 28 29
                                                  30

        July                    August                 September           
    1  2  3  4  5  6                  1  2  3      1  2  3  4  5  6  7
 7  8  9 10 11 12 13      4  5  6  7  8  9 10      8  9 10 11 12 13 14
14 15 16 17 18 19 20     11 12 13 14 15 16 17     15 16 17 18 19 20 21
21 22 23 24 25 26 27     18 19 20 21 22 23 24     22 23 24 25 26 27 28
28 29 30 31              25 26 27 28 29 30 31     29 30

      October                  November                 December           
       1  2  3  4  5                     1  2      1  2  3  4  5  6  7
 6  7  8  9 10 11 12      3  4  5  6  7  8  9      8  9 10 11 12 13 14
13 14 15 16 17 18 19     10 11 12 13 14 15 16     15 16 17 18 19 20 21
20 21 22 23 24 25 26     17 18 19 20 21 22 23     22 23 24 25 26 27 28
27 28 29 30 31           24 25 26 27 28 29 30     29 30 31

The code also handles Julian/Gregorian switch dates correctly, but must be told what locale to format the switch as and for what timezone in that case. For example, Spain was one of the first countries to make the change: <lang tcl>snoopy cal 1582 :Europe/Madrid es_ES</lang>

                           [Snoopy Picture]

                              -- 1582 --

       enero                   febrero                   marzo             
 1  2  3  4  5  6  7               1  2  3  4               1  2  3  4
 8  9 10 11 12 13 14      5  6  7  8  9 10 11      5  6  7  8  9 10 11
15 16 17 18 19 20 21     12 13 14 15 16 17 18     12 13 14 15 16 17 18
22 23 24 25 26 27 28     19 20 21 22 23 24 25     19 20 21 22 23 24 25
29 30 31                 26 27 28                 26 27 28 29 30 31

       abril                     mayo                    junio             
                   1         1  2  3  4  5  6                  1  2  3
 2  3  4  5  6  7  8      7  8  9 10 11 12 13      4  5  6  7  8  9 10
 9 10 11 12 13 14 15     14 15 16 17 18 19 20     11 12 13 14 15 16 17
16 17 18 19 20 21 22     21 22 23 24 25 26 27     18 19 20 21 22 23 24
23 24 25 26 27 28 29     28 29 30 31              25 26 27 28 29 30
30                                                

       julio                    agosto                 septiembre          
                   1            1  2  3  4  5                     1  2
 2  3  4  5  6  7  8      6  7  8  9 10 11 12      3  4  5  6  7  8  9
 9 10 11 12 13 14 15     13 14 15 16 17 18 19     10 11 12 13 14 15 16
16 17 18 19 20 21 22     20 21 22 23 24 25 26     17 18 19 20 21 22 23
23 24 25 26 27 28 29     27 28 29 30 31           24 25 26 27 28 29 30
30 31                                             

      octubre                 noviembre                diciembre           
 1  2  3  4 15 16 17      1  2  3  4  5  6  7            1  2  3  4  5
18 19 20 21 22 23 24      8  9 10 11 12 13 14      6  7  8  9 10 11 12
25 26 27 28 29 30 31     15 16 17 18 19 20 21     13 14 15 16 17 18 19
                         22 23 24 25 26 27 28     20 21 22 23 24 25 26
                         29 30                    27 28 29 30 31

As can be seen, a Real Programmer has many intricacies to deal with!