calendarcalculationdate-arithmeticforthgforth

Get day name from a user specified date in Gforth


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?


Solution

  • 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        !