I tried to apply Zeller's convergence simplified method to get the day name from a user input date.
Simplified algorithm from
\ Zeller's Congruence
variable year 2 allot
variable day 2 allot
variable month 2 allot
variable century 2 allot
variable daynumber 1 allot
variable k 2 allot
variable j 2 allot
\ Read keyboard word input
: input$ ( n -- addr n )
pad swap accept
pad swap
;
\ Check input type
: input# ( -- u true | false )
0. 16 input$ dup >R
>number nip nip
R> <> dup 0 = if
nip
then
;
\ Get all year month and day to check
: readyear
CR ." Year ? "
input# if
year !
else
cr ." Must be a number" cr
bye
then
year @ dup >r 99 > r> 1 < or if \ more forth way to write it
cr ." Must be lower than 99 and Gregorian date (so also over 1752 September 2cd)" cr
bye
then
;
: readday
CR ." Day ? "
input# if
day !
else
cr ." Must be a number" cr
bye
then
day @ dup >r 31 > r> 1 < or if
cr ." Must be between 1 and 31" cr ( is user stupid ? )
bye
then
;
: ?adaptday
\ NOTE: In this algorithm January and February are
\ counted as months 13 and 14 of the previous
\ year. E.g., if it is 2 February 2010, the
\ algorithm counts the date as the second day
\ of the fourteenth month of 2009 (02/14/2009
\ in DD/MM/YYYY format)
month @ case
1 of
month 13 !
year @ 1- !
endof
2 of
month 14 !
year @ 1- !
endof
endcase
\ 13(m+1) K J
\ daynumber = ( day + (-------) + k + (---) + (---) + 5j ) %7
\ 5 4 4
year 100 mod k !
year 100 / j !
day @ month @ 1 + 13 * 5 / + \ day + ((13*(m-1))/5)
k @ + \ day + ((13*(m-1))/5) + k
k @ 4 / + \ day + ((13*(m-1))/5) + k + k/4
J @ 4 / + \ day + ((13*(m-1))/5) + k + k/4 + J/4
J @ 5 * + \ day + ((13*(m-1))/5) + k + k/4 + J/4 + 5J
7 mod daynumber ! \ (day + ((13*(m-1))/5) + k + k/4 + J/4 + 5J) %7
\ 1 line for each sub calculation just for better mathematical reading
daynumber @ case
0 of cr ." Saturday !" cr bye endof
1 of cr ." Sunday !" cr bye endof
2 of cr ." Monday !" cr bye endof
3 of cr ." Tuesday !" cr bye endof
4 of cr ." Wednesday !" cr bye endof
5 of cr ." Thursday !" cr bye endof
6 of cr ." Friday !" cr bye endof
endcase
;
\ main function
: main
page
cr
>readvars
?adaptday
cr cr
bye
;
main
The syntax seems OK, but method or buggy/failed function may be the root cause.
The input is taken well, but randomly the day obtained is not the good one (even for the same date).
So I may failed to do something & here I un-optimized the code to try to debug it, but I didn't find yet why.
Here is an execution example:
Insert date:
gforth zellersconvergence_bugged.fs
redefined k redefined j
Insert a decomposed date:
Century ? 20
Year ? 21
Mounth ? 6
Day ? 8
Tuesday !
gforth zellersconvergence_bugged.fs
redefined k redefined j
Insert a decomposed date:
Century ? 20
Year ? 21
Mounth ? 6
Day ? 8
Saturday !
gforth zellersconvergence_bugged.fs
redefined k redefined j
Insert a decomposed date:
Century ? 20
Year ? 21
Mounth ? 6
Day ? 8
Monday !
Can it be a stack issue?
Can it be a method issue?
Can it be a misunderstood thing inside the algorithm itself?
You need to fetch the data from the year variable twice, year @ 100 ...
. I think after that ?adaptday
will work. There is forth word within \ n lo hi -- flag ; flag is True if lo <= n < hi
for checking numbers within ranges,
In Forth it's unusual to use so many variables. The values are normally stored on the stack. j
as a variable could override the j used as the outer do loop counter. I've seen k used for the next outer loop too!!
I'd implement it something like this. I can then run the words in the console with stack input to see what is happening to help debug.
: century-ix \ c -- days-ix
dup 4/ swap 5 * +
;
: year-ix \ yy -- days-ix
dup 4/ +
;
: month-ix \ mm - days-ix
1+ 13 * 5 /
;
: weekday \ dd mm yyyy -- dow
over 3 < if
swap 12 + swap 1- \ adjusts Jan and Feb to be month 13 or 14 of previous year.
then
100 /mod ( dd mm yy cc )
century-ix ( dd mm yy days )
swap year-ix + ( dd mm days )
swap month-ix + + \ Calculate for months and days
7 mod
;
: weekday. \ n -- ; -- Prints weekday in English
\ Too useful to hide in another definition.
case
0 of cr ." Saturday !" cr endof
1 of cr ." Sunday !" cr endof
2 of cr ." Monday !" cr endof
3 of cr ." Tuesday !" cr endof
4 of cr ." Wednesday !" cr endof
5 of cr ." Thursday !" cr endof
6 of cr ." Friday !" cr endof
endcase
;
8 6 2021 weekday weekday.
Tuesday !