Get day name from a user specified date in Gforth - calendar

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 !

Related

SAS do loop with if statement

I am trying to group by dataset in three month groups, or quarters, but as I'm starting from an arbitrary date, I cannot use the quarter function in sas.
Example data below of what I have and quarter is the column I need to create in SAS.
The start date is always the same, so my initial quarter will be 3rd Sep 2018 - 3rd Dec 2018 and any active date falling in that quarter will be 1, then quarter 2 will be 3rd Dec 2018 - 3rd Mar 2019 and so on. This cannot be coded manually as the start date will change depending on the data, and the number of quarters could be up to 20+.
The code I have attempted so far is below
data test_Data_op;
set test_data end=eof;
%let j = 0;
%let start_date = start_Date;
if &start_Date. <= effective_dt < (&start_date. + 90) then quarter = &j.+1;
run;
This works and gives the first quarter correctly, but I can't figure out how to loop this for every following quarter? Any help will be greatly appreciated!
No need for a DO loop if you already have the start_date and actual event dates. Just count the number of months and divide by three. Use the continuous method of the INTCK() function to handle start dates that are not the first day of a month.
month_number=intck('month',&start_date,mydate,'cont')+1;
qtr_number=floor((month_number-1)/3)+1;
Based on the comment by #Lee. Edited to match the data from the screenshot.
The example shows that May 11 would be in the 3rd quarter since the seed date is September 3.
data have;
input mydate :yymmdd10.;
format mydate yymmddd10.;
datalines;
2018-09-13
2018-12-12
2019-05-11
;
run;
%let start_date='03sep2018'd;
data want;
set have;
quarter=floor(mod((yrdif(&start_date,mydate)*4),4))+1;
run;
If you want the number of quarters to extend beyond 4 (e.g. September 4, 2019 would be in quarter 5 rather than cycle back to 1), then remove the "mod" from the function:
quarter=floor(yrdif(&start_date,mydate)*4)+1;
The traditional use of quarter means a 3 month time period relative to Jan 1. Make sure your audience understands the phrase quarter in your data presentation actually means 3 months relative to some arbitrary starting point.
The funky quarter can be functionally computed from a months apart derived using a mix of INTCK for the baseline months computation and a logical expression for adjusting with relation to the day of the month of the start date. No loops required.
For example:
data have;
do startDate = '11feb2019'd ;
do effectiveDate = startDate to startDate + 21*90;
output;
end;
end;
format startDate effectiveDate yymmdd10.;
run;
data want;
set have;
qtr = 1
+ floor(
( intck ('month', startDate, effectiveDate)
-
(day(effectiveDate) < day(startDate))
)
/ 3
);
format qtr 4.;
run;
Extra
Comparing my method (qtr) to #Tom (qtr_number) for a range of startDates:
data have;
retain seq 0;
do startDate = '01jan1999'd to '15jan2001'd;
seq + 1;
do effectiveDate = startDate to startDate + 21*90;
output;
end;
end;
format startDate effectiveDate yymmdd10.;
run;
data want;
set have;
qtr = 1
+ floor( ( intck ('month', startDate, effectiveDate)
- (day(effectiveDate) < day(startDate))
) / 3 );
month_number=intck('month',startDate,effectiveDate,'cont')+1;
qtr_number=floor((month_number-1)/3)+1;
format qtr: month: 4.;
run;
options nocenter nodate nonumber;title;
ods listing;
proc print data=want;
where qtr ne qtr_number;
run;
dm 'output';
-------- OUTPUT ---------
effective month_ qtr_
Obs seq startDate Date qtr number number
56820 31 1999-01-31 1999-04-30 1 4 2
57186 31 1999-01-31 2000-04-30 5 16 6
57551 31 1999-01-31 2001-04-30 9 28 10
57916 31 1999-01-31 2002-04-30 13 40 14
58281 31 1999-01-31 2003-04-30 17 52 18
168391 90 1999-03-31 1999-06-30 1 4 2
168483 90 1999-03-31 1999-09-30 2 7 3
168757 90 1999-03-31 2000-06-30 5 16 6
168849 90 1999-03-31 2000-09-30 6 19 7
169122 90 1999-03-31 2001-06-30 9 28 10
169214 90 1999-03-31 2001-09-30 10 31 11
169487 90 1999-03-31 2002-06-30 13 40 14
169579 90 1999-03-31 2002-09-30 14 43 15
169852 90 1999-03-31 2003-06-30 17 52 18
169944 90 1999-03-31 2003-09-30 18 55 19
280510 149 1999-05-29 2001-02-28 7 22 8
280875 149 1999-05-29 2002-02-28 11 34 12
281240 149 1999-05-29 2003-02-28 15 46 16
282035 150 1999-05-30 2000-02-29 3 10 4
282400 150 1999-05-30 2001-02-28 7 22 8
282765 150 1999-05-30 2002-02-28 11 34 12

Operator precedence is confusing on long statement

I have worked with a simple C program to find the Day for Given Date. For it, I have written a lot of lines to calculate the day and month and to find the kind of the given year. While Surfing I came to know about a single line code to find the day for the given date. The code is as below
( d += m < 3 ? y --: y- 2, 23 * m / 9 + d + 4 + y / 4 - y / 100 + y / 400) % 7 ;
// 0 - Sunday, 6 - saturday
It gave the correct answer for all inputs, but I couldn't understand the values used in this expression.
Why the sum of day and month is checked for less than 3.
Why the year is reduced by one and the condition fails it decreases the year by 2.
Why the numbers 3, 23 and 9 are used in this expression.
I have confused about the operator precedence on this statement. Can anyone explain how this works?
What I've found so far:
23 * m / 9 results in
1 2 3
2 5 2
3 7 3
4 10 2
5 12 3
6 15 2
7 17 3
8 20 3
9 23 2
10 25 3
11 28 2
12 30 3
This expression adds the days over 28 days of a month.
The expression y / 4 - y / 100 + y / 400 results in:
1995 483 0
1996 484 1
1997 484 1
1998 484 1
1999 484 1
2000 485 2
2001 485 2
with the result, adding one day every 4 years (except leap years)
Because every year with 365 days (mod 7 == 1) increments the weekday by 1, the years are added to the days.
The expression d + (m < 3 ? y --: y- 2) is for correcting the leap year calculation. If we have a leap year, we can correct by one day only if we have a month >= march.

How to setup cron job to run every 5 days?

I want to setup cronjob which will start on for example today and it will run every 5 days.
This is what I have now, is this will work correctly ? If I install this job at 5 o`clock and then every 5 days on 6 AM.
0 6 */5 * * mailx -r root#mail.com -s "Message title" -c "cc#mail.com" primary#mail.com < body.txt
0 0 */5 * * midnight every 5 days.
See this, similar question just asked for every 3 days.
Cron job every three days
Bear with me, this is my first time posting an answer... let me know if anything I put is unclear.
I don't think you have what you need with:
0 0 */5 * * ## <<< WARNING!!! CAUSES UNEVEN INTERVALS AT END OF MONTH!!
Unfortunately, the */5 is setting the interval based on day of the month. See: explanation here. At the end of the month there is recurring issue guaranteed.
1st at 2019-01-01 00:00:00
then at 2019-01-06 00:00:00 << 5 days, etc. OK
then at 2019-01-11 00:00:00
...
then at 2019-01-26 00:00:00
then at 2019-01-31 00:00:00
then at 2019-02-01 00:00:00 << 1 day WRONG
then at 2019-02-06 00:00:00
...
then at 2019-02-26 00:00:00
then at 2019-03-01 00:00:00 << 3 days WRONG
According to this article, you need to add some modulo math to the command being executed to get a TRUE "every N days". For example:
0 0 * * * bash -c '(( $(date +\%s) / 86400 \% 5 == 0 )) && runmyjob.sh
In this example, the job will be checked daily at 12:00 AM, but will only execute when the number of days since 01-01-1970 modulo 5 is 0.
If you want it to be every 5 days from a specific date, use the following format:
0 0 * * * bash -c '(( $(date +\%s -d "2019-01-01") / 86400 \% 5 == 0 )) && runmyjob.sh
The last snippet of Brad will not work because it ignores the actual date.
date +%s -d "2019-01-01"
will always return the same amount of seconds which will end up in either always true or always false depending on which date you choose.
you would have to do an extra calculation like this to run it every 5 days at 00:00 from 2019-01-01 00:00:00 on:
0 0 * * * bash -c '(( ( $(date +\%s) - $(date +\%s -d "2019-01-01 00:00:00") ) / 86400 \% 5 == 0 )) && runmyjob.sh'

Tabbing a Calendar console application using C

I am implementing a simple yearly calendar in traditional format in a console application using ANSI C. The calendar must be tabbed to show in the format of 3 x 4 months. Till now I managed to display all the months beneath each other as shown in the code below. Any help how can I tackled the tabbing part? I tried to split the month[] into 3 according to the column for example Jan, April, July and October will be the 1st column and then work column by column, but I don't know if it is the best thing to do...any help please?
#include<stdio.h>
int main()
{
int d,y,no_lp,n,i=1,j,month[]={0,31,28,31,30,31,30,31,31,30,31,30,31};
printf("Enter year:");
scanf("%d", &y);
if (y%4==0)
{month[2]=29;}
no_lp= (27 + (42/5) + (y-1) + ((y-1)/4) - ((y-1)/100) + ((y-1)/400) + 1);
d= no_lp%7;
n=d;
for(j=1;j<=12;j++)
{
printf("\n\n %s",monthname[j]);
//printf ("\n\n%d",j);
printf("\n Su Mo Tu We Th Fr Sa\n");
while(d--!=0)
printf(" "); //spaces for empty days
while(i<=month[j])
{
if(i<10)
{printf(" %d ",i++);} //formating for dates with 2 digits
else{printf("%d ",i++);}//formatting for dates with 1 digit
n++;
if(n==7) //if 7 is reached start new line
{
n=0;
printf("\n");
}
}
d=n;
i=1; //n will be the 1st day of next month
}
return(0);
}
You can replace
if(i<10)
{printf(" %d ",i++);} //formating for dates with 2 digits
else{printf("%d ",i++);}//formatting for dates with 1 digit
with
printf("%2d ",i++);
In order to print 3 * 4, don't print on the fly
Store those values
char out[12][6][24];
| | |
n months <- | -> string containing week in calendar (e.g 10 11 12 13 14 15 17)
V
Max weeks in a month
and print
week 1 month 1 , week 1 month 2 , week 1 month 3
week 2 month 1 , week 2 month 2 , week 2 month 3
week 3 month 1 , week 3 month 2 , week 3 month 3
...
week 1 month 4 , week 1 month 5 , week 1 month 6
week 2 month 4 , week 2 month 5 , week 2 month 6
week 3 month 4 , week 3 month 5 , week 3 month 6
...
...

How to segregate string into different column in MATLAB?

How to segregate ddmmyyhhmmss (day, month, year, hour, minute, second) 12 digit number and put in the separate 6 column in MATLAB? I need 7 column now in total. For example, I have following string
051210151255 (which is 05 day, 12 month, 2010 year, 15 hours, 12 minute, 55 second)
Now I need 7 column (whole 12 digit number, dd, mm, yy, hh, mm, ss)
d = '051210151255';
output = zeros(1, 6);
for i=1:6
output(1, i) = str2num(d(i*2-1:i*2));
end
output = [str2num(d) output];
gives:
>> output =
051210151255 5 12 10 15 12 55

Resources