Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Challenge: Compute the number of Tuesdays since a given date
#11
Xhantt, you didn't answer my questions. I already understand about Gregorian and Julian dates.

I could not access the site you gave me yesterday, and I still can't today.
*****
Reply
#12
Let me trya again

The parameters are year, month, day, ut (universal time zone my is GMT-3 so is -3, in GMT is 0 Big Grin ).

When i put fix(a) i mean it to be the greatest integer less than a. Ej. 1.34 -> 1, -2.3 -> -3, 1.75 -> 1, -2.7 -> -3.

I tune my programin knowing that may 25th of 1810 was friday. Then i try current date and my birthday date.

Code:
a#  = JulianDate#(2004, 6, 11, 0) '-> current date is friday
b# = JulianDate#(1900, 1, 1, 0) '-> date to test
d& = FIX(a# - b#) ' -> days elapsed
PRINT "January 1th, 1900 was ";
SELECT CASE d& MOD 7 ' -> we get day of the week
  CASE 0: PRINT "friday" '-> same day of the week
  CASE 1: PRINT "thursday" '-> b is the day after so b is thursday
  CASE 2: PRINT "wednesday" '-> and so on
  CASE 3: PRINT "tuesday"
  CASE 4: PRINT "monday"
  CASE 5: PRINT "sunday"
  CASE 6: PRINT "saturday"
END SELECT

Google "julian date" and pick up Eric Weisstein's world of astronomy. I dont know why the link doesnt work i copy from the address bar :???:.

I think it should work.
Reply
#13
Xhantt: The link does work. just so ya know.
Reply
#14
Xhantt,

I did some testing with your original version of Wednesday June 9.
I almost works. The number of Tuesdays or Thurdays is one less than the correct number. For example, if you put in a date of Monday June 7, you get a count of zero.
Maybe the FIX(dias/7) is not working just right.

Anyway, you get an "A" for effort, and so far you're the only winner.
*****
Reply
#15
Thanks, i'm glad to help you.
Reply
#16
Xhantt,

I looked up the FIX command. For positive numbers it's the same as using an INT. For negative numbers it truncates the decimals, whereas INT will truncate but increment the whole number part by one.

However, your code still has a problem. Your statement:

PRINT "Han pasado "; FIX(dias / 7); " jueves"

will print the truncated number of weeks between the two dates. The number of weeks is not necessarily the number of Tuesdays or Thurdays that transpired, because the first date could have started on a Tuesday and the end date could end on a Wednesday which means that you have 2 extra Tuesdays in the count.

You need to add a little routine that computes the day of the week for a given value which you call JulianDate#.

I suggest you do some more testing with real, small dates that can be checked manually with a calendar.
*****
Reply
#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
#18
Last code. I fix the 'fix' problem and count the last tuesday :-)
Code:
FUNCTION JulianDate# (y%, m%, d%, ut%)
  DIM a, b, c
  a = INT(7# * (y% + INT((m% + 9#) / 12#)) / 4#)
  b = INT(3# * (INT((y% + (m% - 9#) / 7#) / 100#) + 1#) / 4#)
  c = INT(275 * m% / 9#) + d% + 1721028.5# + ut% / 24#
  JulianDate# = 367# * y% - a - b + c
END FUNCTION

'Tuesdays elapsed between y1-m1-d1 and y2-m2-d2, including y1-m1-d1
' but not including y2-m2-d2
FUNCTION TuesdaysElapsed# (y1%, m1%, d1%, y2%, m2%, d2%)
  DIM j1, j2, j3 AS DOUBLE
  DIM WE AS DOUBLE
  DIM TE AS DOUBLE
  j1 = JulianDate#(y1%, m1%, d1%, 0)
  j2 = JulianDate#(y2%, m2%, d2%, 0)
  'Weeks elapsed
  WE = INT((j2 - j1) / 7)
  'for each complete week there's one tuesday
  TE = WE
  'next tuesday not counted
  j3 = j1 + WE * 7 + (7 - (INT(j1) MOD 7)) MOD 7
  'if next tuesdays is before y2-m2-d2 count it
  IF j3 < j2 THEN TE = TE + 1
  TuesdaysElapsed# = TE
END FUNCTION
Reply
#19
Xhantt,

Looks much better. Give me a few days to test it out.
*****
Reply
#20
Xhantt,

I inserted your functions into a test program and they run fine. I ran some parallel tests with my solution and the results were the same.

So, congratulations, you are the winner!
*****
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)