Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Challenge: Compute the number of Tuesdays since a given date
#17
Ok guys, here's my solution. I used a bunch of date handling routines that I already had. The actual Tuesday logic is a small FOR loop that's very simple. A more clever algorithm might be used, but why spend the energy.

You run the program with a command-line parameter of the "from" date you want in the format YYYYMMDD.
Try it out.
*****
Code:
REM *** Program to count number of Tuesdays from input date to today-1.***
DEFINT A-Z

DECLARE FUNCTION IsLeapYear% (Z)  

DIM YEAR.MIN     AS INTEGER  'Minimum valid year for dates (default=0)
DIM DATE.FACTOR  AS SINGLE   'Number of days given date is from day zero.
DIM WEEK.DAY     AS INTEGER  'Day of week value: 1=Sunday....7=Saturday.
DIM WEEK.NUM     AS INTEGER  'Week number within year (1 to 54).
DIM JULIAN.DAY   AS INTEGER  'Day  number within year (1 to 366).
DIM DATE.OK      AS INTEGER  'Valid date indicator: -1=True, 0=False.
Z$               =  ""       'Date string as YYYYMMDD.
DIM ZYY          AS INTEGER  'Value of the 4 digit year.
DIM ZMM          AS INTEGER  'Value of the 2 digit month.
DIM ZDD          AS INTEGER  'Value of the 2 digit day.
DIM ZDWORK       AS LONG     'Variable    internal to date routines.
DIM ZFSAVE       AS SINGLE   'Variable    internal to date routines.
DIM ZFSAVE2      AS SINGLE   'Variable    internal to date routines.
ZTEMP$           =  ""       'Work string internal to date routines.

'NOTE: The following variable ZMO() is internal to date routines.

DIM ZMO(1 TO 12) AS INTEGER
DATA 31,28,31,30,31,30,31,31,30,31,30,31
FOR ZMM=1 TO 12:READ ZMO(ZMM):NEXT

dim factor1 as single
dim factor2 as single
dim dtfact as single
dim tcount as long

REM *** MAIN LINE *************************************************************

param1$ = ltrim$(rtrim$(command$))  'input date (from)
gosub date.today:param2$=z$

print param1$
print param2$

z$=Param1$
gosub date.factor
factor1=date.factor
if not(date.ok) then print "Invalid input date, must be YYYYMMDD":system
if param1$ >= param2$ then print "Input date >= today, nothing to do":system

z$=Param2$
gosub date.factor
factor2=date.factor

for dtfact=factor1 to factor2-1
    date.factor=dtfact
    gosub compute.weekday
    if week.day=3 then tcount=tcount+1  'count Tuesdays (3)
next dtfact

print:print "Number of Tuesdays between ";param1$;" and today is ";tcount

SYSTEM

REM ************************  DATE.FACTOR  ************************************
REM *
REM *** PRINCIPAL DATE SUBROUTINE:
REM *   =========================
REM *   - Validate input date string.
REM *   - Compute number of days (date.factor) from year 0, month 0, day 0.
REM *   - Compute day of week.
REM *   - Compute week number.
REM *   - Compute "julian" day of year.
REM *
REM *  INPUT:
REM *  =====
REM *  Z$         = Date string formatted as YYYYMMDD.
REM *  YEAR.MIN   = Minimum year user wishes to allow (default 0)
REM *
REM * OUTPUT:
REM * ======
REM * DATE.OK       = -1 if input date VALID.   (true)
REM *               =  0 if Input date INVALID. (false)                    
REM * NOTE: IF VALID,   THE FOLLOWING VARIABLES AR BASED ON INPUT DATE.
REM *       IF INVALID, THE VALUES MAY HAVE CHANGED AND ARE MEANINGLESS.
REM * DATE.FACTOR   = Number of cumulative days from year/month/day 0.
REM * WEEK.DAY      = 1 to   7 is Sunday to Saturday respectively.  
REM * WEEK.NUM      = 1 TO  54 is week number within year.            
REM * JULIAN.DAY    = 1 TO 366 is day  number within year.            
REM * ZYY           = Value of of 4 digit year.        
REM * ZMM           = Value of month.                    
REM * ZDD           = Value of day.                                
REM * EASTERSUNDAY$ = Date of Easter for given year.                
REM * Z$            = (unchanged).
REM * YEAR.MIN      = (unchanged).
REM *
REM *
REM * Date factor logic adopted from a Texas Instruments calculator manual.
REM *
DATE.FACTOR:
  gosub Date.Check                     'check input date
  if not(date.ok) then RETURN          'exit if invalid

  zmm=1:zdd=1                          'set to January 1st
  gosub Compute.Factor                 'compute factor of Jan 1st
  zfsave=date.factor                   'save factor   of Jan 1st
  gosub Compute.Weekday                'week.day now has day of week of Jan 1st

  zdd=val(right$(z$,2))                'Restore input date's day + month  
  zmm=val(mid$(z$,5,2))  
  gosub Compute.Factor                 'compute factor of input date

  '* Julian day is input date minus Jan 1st of same year +1
  julian.day=date.factor-zfsave+1  

  '* Compute the week number: (week.day-1 is week day of Jan 1st relative to 0)
  week.num=int((julian.day+(week.day-1)-1)/7)+1

  '* Compute the day of the week of input date:
  gosub Compute.Weekday
RETURN

COMPUTE.FACTOR:
  DATE.FACTOR=365!*ZYY+ZDD+31*(ZMM-1)  'NOTE: WON'T WORK WITHOUT ! AFTER 365.
  IF ZMM<3 THEN
     DATE.FACTOR=DATE.FACTOR+INT((ZYY-1)/4)-INT(3/4*(INT((ZYY-1)/100)+1))
  ELSE
     DATE.FACTOR=DATE.FACTOR-INT(.4*ZMM+2.3)+INT(ZYY/4)-INT(3/4*(INT(ZYY/100)+1))
  END IF
RETURN

COMPUTE.WEEKDAY:
  '* Compute the day of the week:
  WEEK.DAY=DATE.FACTOR-INT(DATE.FACTOR/7)*7    'Modulo 7.
  IF WEEK.DAY=0 THEN WEEK.DAY=7                'WEEK.DAY=1=Sunday.
RETURN
REM ******************  DATE.TODAY  *******************************************
REM *
REM *** SUBROUTINE TO GET TODAY'S DATE AND FORMAT AS YYYYMMDD.
REM *
REM *  INPUT: (None)
REM *
REM * OUTPUT: Z$ = Today's date, string as YYYYMMDD.
REM *
DATE.TODAY:
  z$=date$                                'Date is mm-dd-yyyy
  if left$(time$,2)="00" then z$=date$    'make sure date didn't just roll over
  Z$=right$(z$,4)+left$(z$,2)+mid$(z$,4,2)        'in YYYYMMDD format
RETURN
REM *********************  DATE.CHECK  ****************************************
REM *
REM *** VALIDATE A DATE IN YYYYMMDD FORMAT.
REM *
REM *  INPUT: Z$       = Given date in format YYYYMMDD.
REM *         YEAR.MIN = Minimum valid year allowed. (default=0)
REM *
REM * OUTPUT: DATE.OK = -1 if input date is VALID.   (true)
REM *                    0 if input date is INVALID. (false)                    
REM *         (if VALID):
REM *         ZYY      = Value of 4 digit year.        
REM *         ZMM      = Value of month.                              
REM *         ZDD      = Value of day.                                
REM *
REM *
DATE.CHECK:
  DATE.OK = 0      'preset to false
  ZTEMP$="1"+Z$+"1"
  IF LEN(Z$)<>8 OR MID$(STR$(VAL(ZTEMP$)),2)<>ZTEMP$ THEN RETURN
  ZDD=VAL(RIGHT$(Z$,2))                'Set day                
  ZMM=VAL(MID$(Z$,5,2))                'Set month.
  ZYY=VAL(LEFT$(Z$,4))                 'Set year.
  IF ZMM<1 OR ZMM>12 OR ZDD<1 OR ZDD>31 OR ZYY<YEAR.MIN THEN RETURN
  IF ZMO(ZMM)+1*(-(ZMM=2 AND ISLEAPYEAR(ZYY))) < ZDD THEN RETURN
  '   If expression (month=2 and is leapyear) is TRUE which is -1, then
  '   taking the negative of this issues a plus 1. Conversely, the FALSE    
  '   always gives a zero. Multiplying the +1 by this result of 1 or 0
  '   will either add 1 or not to the number of days in the month.
  '   The routine wants to add 1 only when it is February and leap year.
  DATE.OK = -1        '-1=valid (true)
RETURN      
REM ***************************************************************************

END

' ====================== ISLEAPYEAR ==========================
'         Determines if a year is a leap year or not.
' ============================================================
'
FUNCTION IsLeapYear (Z) STATIC

   ' If the year is evenly divisible by 4 and not divisible
   ' by 100, or if the year is evenly divisible by 400, then
   ' it's a leap year:
   IsLeapYear = (Z MOD 4 = 0 AND Z MOD 100 <> 0) OR (Z MOD 400 = 0)
END FUNCTION
Reply


Messages In This Thread
Challenge: Compute the number of Tuesdays since a given date - by Moneo - 06-14-2004, 06:43 AM

Forum Jump:


Users browsing this thread: 1 Guest(s)