Calendar: Difference between revisions

From Rosetta Code
Content added Content deleted
(→‎{{header|Ruby}}: Use `tput co` to avoid escape sequences from curses. Copy the output in 132 columns and describe the output in 80 columns.)
(Edit task: Allow output in 132 columns. (The task allowed such output before 12 June 2011.) Remove reference to IBM 1403; the CALENDAR task only requires uppercase code, not uppercase output.)
Line 1: Line 1:
{{task|Date and time}}
{{task|Date and time}}
Create a routine that will generate an text calendar for any date. Test the calendar by generating a calendar for the year 1969 on a [[wp:IBM_3270#Displays|IBM 3278 model 4 terminal]] (80×43 display with accented characters) of the time.
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.
As a test case, target formatting the months of the year to fit nicely across the 80 character width page. Restrict number of lines in test output to 43.
* An [[wp:IBM_3270#Displays|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)
(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.
Kudos (κῦδος) for routines that also correctly transition from Julian to Gregorian calendar in September 1752.
Line 11: Line 12:
THE REAL PROGRAMMER'S NATURAL HABITAT
THE REAL PROGRAMMER'S NATURAL HABITAT
"Taped to the wall is a line-printer Snoopy calender for the year 1969."
"Taped to the wall is a line-printer Snoopy calender for the year 1969."
For further Kudos see task [[Calendar - for "real" programmers|CALENDAR]], for printing on a 132 character width [[wp:IBM 1403|IBM 1403]] printer. Where all code and output is to be in UPPERCASE.
For further Kudos see task [[Calendar - for "real" programmers|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.
For economy of size, do not actually include Snoopy generation in either the code or the output, instead just output a place-holder.

=={{header|ALGOL 68}}==
=={{header|ALGOL 68}}==
{{works with|ALGOL 68|Revision 1 - no extensions to language used}}
{{works with|ALGOL 68|Revision 1 - no extensions to language used}}
Line 401: Line 403:


=={{header|PicoLisp}}==
=={{header|PicoLisp}}==
This "calendar" is nicely formatted, and fits into 20 columns ;-)
{{Template:Needs-review}}
This "calendar" is nicely formated, and fits into 20 columns ;-)
<lang PicoLisp>(de cal (Year)
<lang PicoLisp>(de cal (Year)
(prinl "====== " Year " ======")
(prinl "====== " Year " ======")

Revision as of 01:05, 20 August 2011

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.

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                                             

C

Code reuse:<lang C>#include <stdio.h>

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

int main(int c, char **v) { char *s = (c > 1 && atoi(v[1]) > 0) ? v[1] : "1969"; printf("\t\t\t [insert Snoopy here]\n"); return execlp("cal", "-h", s, (void*)0); }</lang>

D

Works with: D version 2

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

void main() {

   printCalendar(1969, 3);

}

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

   enforce(1 <= cols && cols <= 12);
   
   auto rows = 12 / cols + (12 % cols != 0); 
   auto date = Date(year, 1, 1);
   auto offs = cast(int)date.dayOfWeek();
   auto 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";
       int eom = date.endOfMonthDay();
       foreach (d; 1 .. 43) {
           auto day = d > offs && d <= offs + eom; 
           auto str = day ? format(" %2s", d-offs) : "   ";
           mons[m][2 + (d - 1) / 7] ~= str;
       }
       offs = (offs + eom) % 7;
       date.add!q{months}(1);
   }
   writeln("[Snoopy Picture]".center(cols * 24 + 4));
   writeln(to!string(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(join(s, "\n"), "\n");
   }

}</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

GUISS

In Graphical User Interface Support Script, we utilize applications that are already written. So for this task, we shall just display the calendar that sets the system clock.

<lang guiss>Rightclick:Clock,Adjust Date and Time,Button:Cancel</lang>

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>

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

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>

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.

<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 raise "usage: #{$0} year"

  1. Guess width of terminal device.
  2. 1. Obey environment variable COLUMNS.
  3. 2. Try to run `tput co`.
  4. 3. Assume 80 columns.

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

          Integer(`tput co`) rescue
          80)

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.

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!