Calendar: Difference between revisions
m (→{{header|C}}: remove strptime call) |
|||
Line 459: | Line 459: | ||
=={{header|C}}== |
=={{header|C}}== |
||
With arbitrary display width (>= 20 though) and auto spacing. |
With arbitrary display width (>= 20 though) and auto spacing. |
||
<lang C># |
<lang C>#include <stdio.h> |
||
#include <time.h> |
|||
#include <stdio.h> |
|||
#include <stdlib.h> |
#include <stdlib.h> |
||
#include <string.h> |
#include <string.h> |
||
Line 491: | Line 489: | ||
void init_months() |
void init_months() |
||
{ |
{ |
||
char buf[128]; |
|||
struct tm tm; |
|||
int i; |
int i; |
||
if ((!(year % 4) && (year % 100)) || !(year % 400)) |
if ((!(year % 4) && (year % 100)) || !(year % 400)) |
||
months[1].days = 29; |
months[1].days = 29; |
||
sprintf(buf, "%d 1 1", year); |
|||
year--; |
|||
strptime(buf, "%Y %m %d", &tm); |
|||
months[0].start_wday |
months[0].start_wday |
||
= (year * 365 + year/4 - year/100 + year/400 + 1) % 7; |
|||
for (i = 1; i < 12; i++) |
for (i = 1; i < 12; i++) |
||
months[i].start_wday = |
months[i].start_wday = |
||
(months[i-1].start_wday + months[i-1].days) % 7; |
(months[i-1].start_wday + months[i-1].days) % 7; |
||
cols = (width + 2) / 22; |
cols = (width + 2) / 22; |
||
while (12 % cols) cols--; |
while (12 % cols) cols--; |
||
Line 567: | Line 566: | ||
for (i = 1; i < c; i++) { |
for (i = 1; i < c; i++) { |
||
if (!strcmp(v[i], "-w")) { |
if (!strcmp(v[i], "-w")) { |
||
if (++i == c || (width = atoi(v[i])) < 20) |
if (++i == c || (width = atoi(v[i])) < 20) |
||
goto bail; |
goto bail; |
||
} else if (!year_set) { |
} else if (!year_set) { |
||
if (!sscanf(v[i], "%d", &year)) |
if (!sscanf(v[i], "%d", &year) || year <= 0) |
||
year = 1969; |
year = 1969; |
||
year_set = 1; |
year_set = 1; |
||
Line 584: | Line 583: | ||
exit(1); |
exit(1); |
||
}</lang> |
}</lang> |
||
=={{header|C sharp|C#}}== |
=={{header|C sharp|C#}}== |
Revision as of 07:18, 10 November 2011
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
<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
With arbitrary display width (>= 20 though) and auto spacing. <lang C>#include <stdio.h>
- include <stdlib.h>
- 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; }
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 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>
Perl
This will call to the external program, cal.
<lang perl>$_=$ARGV[0]//1969;`cal $_ >&2`</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>
- !/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>
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.
<lang ruby>require 'date'
- Creates a calendar of _year_. Returns this calendar as a multi-line
- 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"
- Guess width of terminal.
- 1. Obey environment variable COLUMNS.
- 2. Try to require 'io/console' from Ruby 1.9.3.
- 3. Try to run `tput co`.
- 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.
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
- Produce information about the days in a month, without any assumptions about
- 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!