Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Zeller's congruence pass 3: test day-of-week calculation algorythms for accuracy
#1
After my recent debacles with getting a Zeller's congruence (determine day of week from date) formula, I repeated my foray into software archeology, by searching my collection of 90,142 files (many being zip files) and more than 12 gb of Basic source and basic-related files, I found an old file that purports to return day of week from date. It was old enough to be from the days when Basic programs had to have line numbers:
Code: (Select All)
_Title "Possibly_accurate_zellers_congruence"
10 ' DAYOPFWK = Calculates the day of the week given date
20 '
30 Cls: Print
40 Print "  This routine calculates the day of the week given the date"
50 Dim DAYS$(6): For I = 0 To 6: Read DAY$(I): Next
60 Data Saturday,Sunday,Monday,Tuesday,Wednesday,Thursday,Friday
70 Line Input "Enter date as mm/dd/yyyy "; EDATE$: S$ = EDATE$
80 PS = InStr(S$, "/"): MONTH = Val(Left$(S$, PS - 1)): S$ = Mid$(S$, PS + 1)
90 PS = InStr(S$, "/"): DAY = Val(Left$(S$, PS - 1)): S$ = Mid$(S$, PS + 1)
100 YEAR = Val(S$)
110 '
120 If MONTH > 2 Then 140
130 MONTH = MONTH + 12: YEAR = YEAR - 1
140 N = DAY + 2 * MONTH + Int(.6 * (MONTH + 1)) + YEAR + Int(YEAR / 4) - Int(YEAR / 100) + Int(YEAR / 400) + 2
150 N = Int((N / 7 - Int(N / 7)) * 7 + .5)
160 Print DAY$(N)
A few tests seem to confirm it is accurate, but let's really put it to the test. Rerun it thousands, or hundreds of thousands of times, and see if it's right. I have my Zeller's Congruence test harness; let's "strap it in, fire up the motor and see if it lights, or explodes on the test site."

The operative part of the above program is lines numbered 120-150. So, I have this:

Code: (Select All)
' Date_testbed.bas - load every date and test a Zeller's congrence algorithm for correctness
' Paul Robinson October 23, 2024

Option _Explicit
_Title "Zeller_testbed"

' The system time function can be used from QB64, I was mistaken it could not
Type SYSTEMTIME
    wYear As Integer
    wMonth As Integer
    wDayOfWeek As Integer
    wDay As Integer
    wHour As Integer
    wMinute As Integer
    wSecond As Integer
    wMilliseconds As Integer
End Type

Declare Dynamic Library "kernel32"
    Function GetLocalTime& (lpSystemTime As SYSTEMTIME)
    Function GetLastError& ()
    Function FormatMessageA& (ByVal f As Long, f$, Byval e As Long, Byval d As Long, g$, Byval s As Long, h$)
End Declare

Dim As SYSTEMTIME StartTime, EndTime
Dim TimeString As String


Color 15
Cls
Const Saturday = 0
Const Sunday = Saturday + 1
Const Monday = Sunday + 1
Const Tuesday = Monday + 1
Const Wednesday = Tuesday + 1
Const Thursday = Wednesday + 1
Const Friday = Thursday + 1

Const January = 1, February = 2, March = 3, April = 4, May = 5, June = 6
Const July = 7, August = 8, September = 9, October = 10, November = 11, December = 12

Const LimitYear = 9999


Dim Shared YD(1800 To LimitYear, December, 31) As Integer
'    Dim As Integer Month
Dim Shared As Integer Year, Month, Day, Hour, Minute, Second, Leap
'Dim Shared Year$, Month$, Day$, WeekDay$, Minute$, Second$, AmPm$
'Dim Shared As String DateString
Dim As Integer DayStart, I, WindowsOffset, ZellersOffset
Dim As Long RecCount

' these use 1-12 for convenience
Dim Shared MonthDays(1, December) As Integer, MonthNames(December) As String
' This one uses 0
Dim Shared DayNames(12) As String
Data "January",31,"February",28,"March",31,"April",30,"May",31
Data "June",30,"July",31,"August",31,"September",30
Data "October",31,"November",30,"December",31

Data "Saturday","Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday","Monday","Tuesday","Wednesday","Thursday"

For Month = January To December: Read MonthNames(Month), MonthDays(0, Month): Next
For Month = January To December: MonthDays(1, Month) = MonthDays(0, Month): Next ' Leap years
For Day = 0 To 12: Read DayNames(Day): Next

' We start Saturday =0 as most Zeller's congruence algorhythms do
' If either the test routine or Windows starts any other day, add the offset

WindowsOffset = 1 ' Windows starts Sunday
ZellersOffset = 0 ' The Zeller's Congruence starts Saturday

MonthDays(1, February) = 29 'Adjust for leap year

' Start the clock
I = GetLocalTime(StartTime)

TimeString = DayNames(StartTime.wDayOfWeek + WindowsOffset) + ", "
TimeString = TimeString + " " + MonthNames(StartTime.wMonth)
TimeString = TimeString + Str$(StartTime.wDay) + "," + Str$(StartTime.wYear) + " at"
TimeString = TimeString + Str$(StartTime.wHour) + ":"
TimeString = TimeString + Right$("0" + LTrim$(Str$(StartTime.wMinute)), 2) + ":"
TimeString = TimeString + Right$("0" + LTrim$(Str$(StartTime.wSecond)), 2)
Open "L:\ZteatResults.txt" For Output As #1
DPrint "Program begins, " + TimeString
DPrint "Initialization: Collecting dates from 1-1-1800 to 12-31-" + LTrim$(Str$(LimitYear))
DPrint "Please wait while initialization is completed"
Dim As _Byte ok, PP, Sc, InitialDay
ok = -1
InitialDay = Wednesday ' January 1, 1800
DayStart = InitialDay
RecCount = 0
PP = 0
For Year = 1800 To LimitYear
    '    Leap Year Calcultions
    GoSub GetLeap

    ' Now count the days
    For Month = January To December
        For Day = 1 To MonthDays(Leap, Month) ' if leap year, uses alternate
            YD(Year, Month, Day) = DayStart
            DayStart = DayStart + 1
            RecCount = RecCount + 1

            If DayStart = 7 Then DayStart = 0
        Next
    Next
Next


I = GetLocalTime(EndTime) ' Stop the clock

' Compute elapsed time

Hour = EndTime.wHour
If StartTime.wHour < EndTime.wHour Then Hour = Hour + 24
Hour = Hour - StartTime.wHour

Minute = EndTime.wMinute
If StartTime.wMinute < EndTime.wMinute Then Minute = Minute + 60: Hour = Hour - 1
Minute = Minute - StartTime.wMinute

Second = EndTime.wSecond
If StartTime.wSecond < EndTime.wSecond Then Minute = Minute - 1: Second = Second + 60
Second = Second - StartTime.wSecond
Dim As Single St, Et
Et = EndTime.wMilliseconds / 1000
St = StartTime.wMilliseconds / 1000
If Et < St Then Second = Second - 1: Et = Et + 1

Et = (Et - St)

TimeString = ""
If Hour > 0 Then
    TimeString = Str$(Hour) + "hour"
    If Hour <> 1 Then TimeString = TimeString + "s"
    TimeString = TimeString + " "
End If
If Minute > 0 Then
    TimeString = TimeString + Str$(Minute) + "minute"
    If Minute <> 1 Then TimeString = TimeString + "s"
    TimeString = TimeString + " "
End If
If TimeString <> "" Then TimeString = TimeString + "and "
If Second < 0 Then Second = 0
TimeString = TimeString + LTrim$(Str$(Second + Et)) + " seconds."

DPrint "Initialization completed;" + Str$(RecCount) + " records initialized. "
DPrint "Initialization took " + TimeString
Second = Hour * 3600 + Minute * 60 + Second
Dim As Integer ProcessCount
If Second + Et > 3 Then
    ProcessCount = RecCount / (Second + Et)
    DPrint "Initilization rate was approx. " + Str$(ProcessCount) + "records per second."
End If
DPrint ""
DPrint "A check will be made to see how accurate the Zeller's congruence algorhythm is."
' Insert code to test here
Dim DOW As Single

Month = 1
Day = 1
Year = 1800

I = 0

GoSub Zeller
DPrint "January 1, 1800 was a " + DayNames(YD(Year, Month, Day)) + ", the algorhythm says it was " + DayNames(DOW) + "."
If YD(Year, Month, Day) <> DOW Then DPrint "": DPrint "Algorhythm fails.": Close: End

Locate 10, 1
DPrint "Now let's see how accurate it is:"

Locate 12, 1
Print "Year"
RecCount = 0

For Year = 1800 To LimitYear
    Locate 12, 5
    Print Year
    Leap = 0
    GoSub GetLeap
    For Month = January To December
        For Day = 1 To MonthDays(Leap, Month)
            GoSub Zeller
            If DOW <> YD(Year, Month, Day) + ZellersOffset Then
                Locate 15, 1
                Color 4
                DPrint "Mismatch on " + MonthNames(Month) + Str$(Day) + ", " + Str$(Year)
                DPrint "Precalc is :" + DayNames(YD(Year, Month, Day) + ZellersOffset) + " but Zeller says " + DayNames(DOW + ZellersOffset)
                Color 15
                I = I + 1
            End If

        Next
    Next
Next
Locate 17, 1
Dim ERCMsg$
If I = 0 Then ERCMsg$ = "NO " Else ERCMsg$ = Str$(I)
DPrint "Completed with " + ERCMsg$ + "errors."

DPrint "There were " + Str$(RecCount) + " date computations."
DPrint ""
If I = 0 Then
    DPrint ""
    DPrint "I believe this certifies the algorhythm is accurate."
Else
    DPrint "Algorhythm fails."
End If

Close
End

GetLeap:
'    Leap Year Calcultions
Leap = 0
' Year mod 4 = 0 for a leap year (in most cases)
If Year Mod 4 = 0 Then ' It is virtually certain it is a leap year with one exception

    ' If it is a century year (year where last two digits are 00)
    If Year Mod 100 <> 0 Then ' Not a century year
        Leap = 1 ' regular leap year
    Else ' It is a century year, it must also be divisible by 400
        If Year Mod 400 = 0 Then Leap = 1 ' Century leap year
    End If
End If
Return


Dim As Integer Zmonth, ZYear
Zeller:
If Month < 3 Then
    Zmonth = Month + 12
    ZYear = Year - 1
Else
    Zmonth = Month
    ZYear = Year
End If
DOW = Day + 2 * Zmonth + Int(.6 * (Zmonth + 1)) + ZYear + Int(ZYear / 4) - Int(ZYear / 100) + Int(ZYear / 400) + 2
DOW = Int((DOW / 7 - Int(DOW / 7)) * 7 + .5)

RecCount = RecCount + 1
Return

' Text output can't be captured off screen through
' mouse or hilighting, so we also print to a file
Sub DPrint (P$)
    Print P$
    Print #1, P$
End Sub
Note I did not change the original algorhythm even if there might be more succinct ways of doing it, for one simple reason: i believe it works, and I can always come back later and "modernize" it

Let's run it and see how accurate it is:

Program begins, Wednesday,  October 23, 2024 at 12:55:05
Initialization: Collecting dates from 1-1-1800 to 12-31-9999
Please wait while initialization is completed
Initialization completed; 2994988 records initialized.
Initialization took .09 seconds.

A check will be made to see how accurate the Zeller's congruence algorhythm is.
January 1, 1800 was a Wednesday, the algorhythm says it was Wednesday.
Now let's see how accurate it is:
Completed with NO errors.
There were  2994988 date computations.


I believe this certifies the algorhythm is accurate.

While 1
   Fix Bugs
   report all bugs fixed
   receive bug report
end while
Reply




Users browsing this thread: 2 Guest(s)