CREATE CURSOR datedata (dod D, dobS D NULL, nyears N(2,0), nmonths N(2,0), ndays N(3,0), ; ncyears N(3,0), ncmonths N(2,0), ncdays N(3,0)) =RAND(-1) FOR i = 1 TO 1000 dDOD = DATE() - INT(IIF(i <= 500000, 1000, 10000) * RAND()) nyrs = INT(99 * RAND() + 1) nmths = INT(11 * RAND() + 1) ndys = INT(364 * RAND() + 1) INSERT INTO datedata VALUES (dDOD, NULL, nyrs, nmths, ndys, 0, 0, 0) ENDFOR SCAN REPLACE datedata.dobS WITH CalcBdate(datedata.dod, datedata.nyears, datedata.nmonths, datedata.ndays) loage = calcage(datedata.dobS, datedata.dod) REPLACE datedata.ncyears WITH loage.years, datedata.ncmonths WITH loage.months, ; datedata.ncdays WITH loage.days ENDSCAN BROWSE LAST PROCEDURE CalcBdate LPARAMETERS tdTarget,tnYears,tnMonths,tnDays LOCAL ldDate ldDate = GOMONTH(m.tdTarget-m.tnDays,-m.tnMonths) RETURN DATE(YEAR(m.ldDate)-m.tnYears,MONTH(m.ldDate),DAY(m.ldDate)) ENDPROC PROCEDURE calcage LPARAMETERS tdBirth, tdTarget LOCAL ldTemp, ldBirth, lnDrop, loage, lnSelect lnSelect = SELECT() CREATE CURSOR (SYS(2015)) (years i,months i,days i) SCATTER NAME loage USE IN (ALIAS()) SELECT (m.lnSelect) IF m.tdBirth > m.tdTarget ldTemp = m.tdTarget tdTarget = m.tdBirth tdBirth = m.ldTemp ENDIF ldBirth = DATE(YEAR(m.tdTarget),MONTH(m.tdBirth),DAY(m.tdBirth)) lnDrop = 0 IF EMPTY(m.ldBirth) && leap case ldBirth = DATE(YEAR(m.tdTarget),3,1) lnDrop = IIF(MONTH(m.tdTarget)<=2,0,1) ENDIF WITH loage .years = YEAR(m.tdTarget) - YEAR(m.tdBirth) - ; (IIF(m.ldBirth > m.tdTarget,1,0)) .months = (MONTH(m.tdTarget) - MONTH(m.tdBirth) + 12 - ; (IIF(DAY(m.tdBirth)>DAY(m.tdTarget),1,0)))%12 ldTemp = DATE( YEAR(m.tdBirth) + .years, MONTH(m.tdBirth), DAY(m.tdBirth) ) .days = m.tdTarget - GOMONTH(m.ldTemp,.months) - m.lnDrop ENDWITH RETURN loageand I am still seeing wild differences, with maybe 1 in 30 returing the same 'age'. Pretty much the same results I get using Sergey's and Rick's code.