Perpetual Calendar 2068

This file is part of and TS-2068 Computer Programs. Download the collection to get this file.
Developer(s): Imre Auersbacher
Date: 1985
Type: Program
Platform(s): TS 2068
Tags: Calendar

This program implements a perpetual calendar for dates ranging from 1583 to 9999 AD, using Zeller’s Congruence-style arithmetic to calculate the day of the week for any given month and year. The user inputs a month (1–12) and a four-digit year, and the program draws a bordered grid on screen representing a monthly calendar with day labels. Leap year detection handles the standard Gregorian rules: divisible by 4 but not 100, or divisible by 400. The calendar grid is drawn using PLOT and DRAW commands to create a 7-column, 6-row table, and dates are positioned by building a padded string from a pre-formatted date sequence stored in Z$.


Program Analysis

Program Structure

The program is organized into four logical phases:

  1. Input and validation (lines 20–30): prompts for month and year, rejecting out-of-range values with looping GO TOs.
  2. Day-of-week calculation (lines 60–75): computes which weekday column the 1st of the month falls on.
  3. Days-in-month calculation (lines 80–100): sets C to the correct number of days, including Gregorian leap year logic.
  4. Calendar rendering (lines 200–290): draws the grid with PLOT/DRAW, prints column headers and month/year label, then places each date string into the correct cell.

Day-of-Week Algorithm

Lines 60–75 implement a variant of Zeller’s Congruence. The adjusted year E is decremented when the month is January or February (treating them as months 13 and 14 of the previous year). The adjusted month C (line 65) is offset by 12 for those months and incremented by 1. The day index D is then computed as:

  • INT(E/400) - INT(E/100) + INT(1.25*E) + INT(2.6*C)

This combines the Gregorian century correction with the standard formula. Line 75 reduces D modulo 7 and adds 1, yielding a value from 1 (Sunday) to 7 (Saturday), representing the starting column of the first day.

Date String Construction

Line 15 defines Z$ as a 74-character string containing two-character representations of the numbers 1 through 31, with single-digit numbers right-padded with a space (e.g., " 1", " 2", …, "31"). Line 264 constructs C$ of length 74, then prefixes it with blank characters to shift the starting date by the computed day offset: C$ = C$(TO 2*D-2) + Z$. This effectively inserts D-1 blank pairs before day 1, so date extraction in the print loop is straightforward.

Leap Year Logic

Line 100 uses a compound boolean expression typical of Sinclair BASIC. The condition (B/4=INT(B/4))*(B/100<>INT(B/100))+(B/400=INT(B/400)) evaluates to non-zero (true) when the year is a leap year under Gregorian rules: divisible by 4 and not by 100, or divisible by 400. This correctly handles all Gregorian edge cases within the supported range.

Grid Rendering

Lines 200–240 draw the calendar border and internal grid lines using PLOT and DRAW. A 225×112 pixel rectangle is drawn first, followed by horizontal dividers (line 230) and vertical dividers (line 240). The seven column headers (S M T W Th F S) are printed at line 220. The inner grid has 6 row bands and 7 column bands.

Date Placement Loop

Lines 270–290 iterate Z from 1 to C+D-1, printing the two-character substring C$(2*Z-1 TO 2*Z) at screen position (J, K). Column K advances by 4 each iteration. When Z is divisible by 7 (end of a week row), row J is incremented by 2 and K resets to 2. Starting the loop at Z=1 with the pre-padded C$ means the first D-1 cells print blanks, correctly indenting the 1st of the month to its weekday column.

Notable Techniques and Idioms

  • Boolean arithmetic is used throughout as a substitute for IF-THEN within expressions (e.g., A+12*(A<3), (A=4)+(A=6)+…).
  • RESTORE followed by a FOR/READ loop at line 250 loads month names into the DIM M$(12,9) array; the dimension of 9 accommodates “SEPTEMBER” (9 characters), the longest name.
  • Input validation uses BEEP 1,-20 on an invalid year as an audible error cue.
  • DIM C$(74) at line 264 initializes the string to 74 spaces before the concatenation that right-fills it with Z$; slicing with TO 2*D-2 extracts exactly the needed blank prefix.
  • Line 290 uses two consecutive STOP statements; only the first is executed, the second is redundant.
  • The program uses PAPER 6 in the opening PRINT and BORDER 5 for screen coloring consistent with TS2068/Spectrum color attributes.

Variable Summary

VariableRole
AInput month (1–12)
BInput year
CDays in the month; also reused as adjusted month in Zeller calculation
DStarting weekday column (1=Sunday … 7=Saturday)
EAdjusted year for Zeller’s formula
Z$Pre-formatted string of day numbers ” 1″ through “31”
C$Working string with blank prefix + day numbers for grid placement
M$()12×9 array of month name strings
J, KCurrent print row and column within the grid
ZLoop counter for grid rendering and DATA read

Potential Anomaly

The reuse of variable C is worth noting: it holds the adjusted month number (line 65) during the day-of-week calculation, then is overwritten with the days-in-month count (lines 80–100). This works correctly because the overwrite happens after D has been fully computed, but makes the code harder to follow at first glance.

Content

Appears On

Predict eclipses across four millennia, track planetary positions through five centuries, calculate loan amortization schedules, or encode secret messages with modular arithmetic — Imre Auersbacher's collection brings scientific precision and practical utility to the TS 2068.

Related Products

Related Content

Image Gallery

Source Code

    2 REM Perpetual Calendar 2068
    4 REM  \* 1984 I. Auersbacher 
    8 BORDER 5: DIM M$(12,9)
   10 CLS : PRINT PAPER 6;"*** CALENDAR (1583-9999 AD) *** "
   15 LET Z$=" 1 2 3 4 5 6 7 8 910111213141516171819202122232425262728293031": BEEP 0.05,20
   20 INPUT "MONTH (1-12):";A: IF (A<1)+(A>12) THEN GO TO 20
   30 INPUT "YEAR (YYYY):";B: IF B<1582 THEN BEEP 1,-20: GO TO 30
   60 LET B=INT B: LET E=B-(A<3)
   65 LET C=A+12*(A<3)+1
   70 LET D=INT (E/400)-INT (E/100)+INT (1.25*E)+INT (2.6*C)
   75 LET D=D-(7*INT (D/7))+1
   80 IF (A=4)+(A=6)+(A=9)+(A=11) THEN LET C=30
   85 IF (A=1)+(A=3)+(A=5)+(A=7)+(A=8)+(A=10)+(A=12) THEN LET C=31
   90 IF A<>2 THEN GO TO 200
  100 LET C=28: IF (B/4=INT (B/4))*(B/100<>INT (B/100))+(B/400=INT (B/400)) THEN LET C=29
  200 PLOT 10,132: DRAW 225,0: DRAW 0,-112: DRAW -225,0: DRAW 0,112
  210 PLOT 10,118: DRAW 225,0
  220 PRINT AT 6,3;"S   M   T   W  Th   F   S": PRINT AT 3,0;
  230 FOR Z=1 TO 6: PLOT 10,116-(Z-1)*16: DRAW 225,0: NEXT Z
  240 FOR Z=1 TO 6: PLOT 40+(Z-1)*32,132: DRAW 0,-112: NEXT Z
  250 RESTORE : FOR Z=1 TO 12: READ M$(Z): NEXT Z
  252 PRINT TAB 8;M$(A);TAB 18;B
  264 DIM C$(74): LET J=8: LET K=2: LET C$=C$( TO 2*D-2)+Z$
  270 FOR Z=1 TO C+D-1: PRINT AT J,K;C$(2*Z-1 TO 2*Z): LET K=K+4
  280 IF (Z/7=INT (Z/7)) THEN LET J=J+2: LET K=2
  290 NEXT Z: STOP : STOP 
  300 DATA "JANUARY","FEBRUARY","MARCH","APRIL","MAY","JUNE","JULY","AUGUST","SEPTEMBER","OCTOBER","NOVEMBER","DECEMBER"
  999 SAVE "cal" LINE 8

Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.

Scroll to Top