Microsoft KB Archive/250300

= PRB: FoxPro DIR Command Returns Incorrect Year for Years > 1999 =

Article ID: 250300

Article Last Modified on 5/10/2003

-

APPLIES TO


 * Microsoft FoxPro 2.0
 * Microsoft FoxPro 2.5b for MS-DOS
 * Microsoft FoxPro 2.5a
 * Microsoft FoxPro 2.5b for MS-DOS
 * Microsoft FoxPro 2.6 for MS-DOS
 * Microsoft FoxPro 2.6a Standard Edition
 * Microsoft FoxPro 2.5b
 * Microsoft FoxPro 2.5a
 * Microsoft FoxPro 2.5b
 * Microsoft FoxPro 2.6 Standard Edition
 * Microsoft FoxPro 2.6a Standard Edition
 * Microsoft Visual FoxPro 3.0 Standard Edition
 * Microsoft Visual FoxPro 3.0b Standard Edition
 * Microsoft Visual FoxPro 5.0 Standard Edition
 * Microsoft Visual FoxPro 5.0a
 * Microsoft FoxPro 2.5b for Macintosh
 * Microsoft Visual FoxPro 2.5c for Macintosh
 * Microsoft FoxPro 2.6a Professional Edition for Macintosh
 * Microsoft Visual FoxPro 3.0 for Macintosh
 * Microsoft FoxPro 2.6 for SCO/UNIX

-



This article was previously published under Q250300



SYMPTOMS
The FoxPro DIR command returns the century portion of the date in the Last Update column as 19xx for any table modified after 12/31/1999.



CAUSE
The date of the last update is stored in the header of the .dbf file. The DIR command looks at the table header to retrieve the last update information. Because this information is stored in YYMMDD format, FoxPro can not determine the century and assumes the 1900s.



RESOLUTION
Two possible workarounds are described in this section. The first workaround uses the ADIR function to provide basic information about the tables. This code can be modified to return information on all files and not just FoxPro tables. Copy this code into a program and execute it: nCnt=ADIR(aFileList,"*.DBF") FOR x = 1 TO nCnt ? aFileList[x,1], aFileList[x,2], aFileList[x,3] ENDFOR

The second code example provides a more extensive workaround that also uses the ADIR function. Copy the code sample into a program named Zdir.prg and run it with one of the following commands:   DO Zdir

-or-

  DO Zdir with "*.DBF"

-or-

  =Zdir("*.DBF") To retrieve a list of any file type, use the following syntax: DO Zdir WITH "*.*" 

Here is the code sample: PARAMETERS cFileMask PRIVATE cFileName, cRecCount, cFileDate, cFileSize PRIVATE cSetTalk, cSetEscape, cOnError, cSetResource
 * Output strings
 * Environment Settings

PRIVATE nFileCnt, nFileSize, cReadStr, x, hdl, nIdx, lVFP, cByte1, cTempAlias, cFileToOpen PRIVATE nCol, nSRows, nSCols, cJustPath, lDBF, nLen, aFileList[1], nTableType, nCnt PRIVATE lCancel

IF SET('TALK') = "ON" SET TALK OFF cSetTalk = "ON" ELSE cSetTalk = "OFF" ENDIF

cOnError = ON("ERROR")

cSetEscape = SET('ESCAPE') SET ESCAPE OFF

cSetResource = SET('RESOURCE') SET RESOURCE OFF

lCancel = .F.
 * Status of the cancel key

nSRows = SROWS nSCols = SCOLS
 * Used for scaling the rows and columns to the current desktop

lVFP = ( "VISUAL" $ UPPER(VERSION) )
 * Is this VFP - used in determining table type on Fox 2.x

IF TYPE("cFileMask") <> "C" cFileMask = "*.dbf" ENDIF

cJustPath = JUSTPATH(cFileMask) cJustPath = ADDBS(IIF(EMPTY(cJustPath), SYS(5) + SYS(2003), cJustPath))
 * Set the path to either the path passed in or the current folder

lDBF = ( UPPER(JUSTEXT(cFileMask)) = "DBF" )

nFileCnt = ADIR(aFileList, cFileMask) IF nFileCnt = 0 DIMENSION aFileList[1,5] nFileSize = 0 ELSE =ASORT(aFileList) ENDIF
 * NOTE -- DIR puts '_' first, ASORT puts "_" last

nFileSize = 0 nCol = 0 ? && Start with a blank line IF lDBF ? "Database Table/DBF files" ?? " # Records" AT 25 ?? "Last Update" AT 37 ?? "     Size" AT 50 ENDIF IF nFileCnt = 0 ? "None" ENDIF

cTempAlias = SYS(2015) FOR x = 1 TO nFileCnt cFileName = aFileList[x,1] cFileToOpen = ALLTRIM(cJustPath + cFileName) cFileDate = PADL(DTOC(aFileList[x,3]), 11)

IF lDBF hdl = FOPEN(cFileToOpen, 0)

IF hdl > 0 cByte1 = dec2hex(str2long(FREAD(hdl, 1))) && Table type cReadStr = "" FOR nCnt = 7 TO 4 STEP -1 nIdx = FSEEK(hdl, nCnt, 0) cReadStr = cReadStr + dec2hex(str2long(FREAD(hdl, 1))) ENDFOR cRecCount = PADL(STR(Hex2Dec(cReadStr)), 10) && Convert the Hex string to numeric and then format =FCLOSE(hdl)
 * Nobody has the table exclusive, so check the header for record count

DO CASE CASE INLIST(cByte1, "02", "03", "43", "63", "83", "8B", "CB", "F5", "FB") nTableType = 1 CASE INLIST(cByte1, "30") nTableType = 2 OTHERWISE nTableType = 0 ENDCASE IF nTableType = 0 ; OR (nTableType = 2 AND NOT lVFP) cFileName = cFileName + " Not a Fox database" cRecCount = "" ENDIF ELSE cRecCount = "Can't read file" cFileDate = "" ENDIF
 * Check to see if the table is Fox 2.x or VFP or Not a table
 * FoxPro 2.x table
 * Visual FoxPro Table
 * Not a table
 * File cannot be opened at all.

cFileSize = PADL(STR(aFileList[x,2]), 11)

? cFileName ?? cRecCount AT 25 ?? cFileDate AT 37 ?? cFileSize AT 50 ELSE IF nCol = 0 ? cFileName AT nCol*14 ELSE ?? cFileName AT nCol*14 ENDIF nLen = INT(LEN(ALLTRIM(cFileName)) / 13) IF nLen > 0 nCol = nCol + nLen ENDIF nCol = IIF((nCol + 2) * 14 > nSCols, 0, nCol + 1) ENDIF nFileSize = nFileSize + aFileList[x,2]
 * Format the output to fit columns on the desktop.

IF INT(ROW) >= INT(nSRows) - 2 ; AND nCol = 0 WAIT WINDOW lCancel = ( LASTKEY = 27 ) && User hit the escape key IF NOT lCancel CLEAR ENDIF ENDIF IF lCancel EXIT ENDIF ENDFOR IF lCancel ? "*** INTERRUPTED ***" ELSE ?   ? TRANSFORM(nFileSize, "999999999") + " bytes in " + ALLTRIM(STR(nFileCnt)) + " files." ? TRANSFORM(DISKSPACE, "999999999") + " bytes remaining on drive." ENDIF

IF cSetTalk = "ON" SET TALK ON ENDIF IF cSetEscape = "ON" SET ESCAPE ON ENDIF

IF cSetResource = "ON" SET RESOURCE ON ENDIF

RETURN ""

FUNCTION Hex2Dec PARAMETER cHexStr PRIVATE nDecimal cHexStr = UPPER(cHexStr) nDecimal = 0 FOR i=1 TO LEN(cHexStr) nDecimal = nDecimal + IIF(ISDIGIT(SUBSTR(cHexStr, i, 1)), ;       VAL(SUBSTR(cHexStr, i, 1)), ;        ASC(SUBSTR(cHexStr, i, 1)) - ASC('A') + 10) * 16 ^ (LEN(cHexStr) - i) ENDFOR
 * passed: 1-byte integer (up to 255)
 * returns: 1-byte hex string (up to FF)
 * example:
 * nDecimal = 111
 * cHex = dec2hex(nDecimal)  returns "6F"
 * cHex = dec2hex(nDecimal)  returns "6F"

RETURN nDecimal

FUNCTION dec2hex PARAMETER nDecimal PRIVATE cBit1, cBit2, cHexStr cBit1 = INT(nDecimal/16) cBit2 = nDecimal - (cBit1 * 16)
 * passed: 1-byte integer (up to 255)
 * returns: 1-byte hex string (up to FF)
 * example:
 * nDecimal = 111
 * cHex = dec2hex(nDecimal)  returns "6F"
 * cHex = dec2hex(nDecimal)  returns "6F"

cHexStr = CHR(IIF(cBit1 > 9, 55, 48) + cBit1) + ; CHR(IIF(cBit2 > 9, 55, 48) + cBit2) RETURN cHexStr

FUNCTION str2long
 * passed: 4-byte character string (m.longstr) in low-high ASCII format
 * returns: long integer value
 * example:
 * m.longstr = "1111"
 * m.longval = str2long(m.longstr)
 * m.longval = str2long(m.longstr)

PARAMETERS cLongStr PRIVATE i, nDecimal nDecimal = 0 FOR i = 0 TO 24 STEP 8 nDecimal = nDecimal + (ASC(cLongStr) * (2^i)) cLongStr = RIGHT(cLongStr, LEN(cLongStr) - 1) NEXT RETURN nDecimal

FUNCTION JUSTPATH PARAMETERS m.FilName m.FilName = ALLTRIM(UPPER(m.FilName)) IF "\" $ m.FilName m.FilName = SUBSTR(m.FilName,1,RAT("\",m.FilName)) IF RIGHT(m.FilName,1) = "\" AND LEN(m.FilName) > 1 ; AND SUBSTR(m.FilName,LEN(m.FilName)-1,1) <> ":" FilName = SUBSTR(m.FilName,1,LEN(m.FilName)-1) ENDIF RETURN m.FilName ELSE RETURN "" ENDIF
 * Returns just the pathname.

FUNCTION JUSTEXT PARAMETERS m.FilName PRIVATE m.ext m.FilName = JUSTFNAME(m.FilName)  && prevents problems with ..\ paths m.ext = "" IF AT(".", m.FilName) > 0 m.ext = SUBSTR(m.FilName, AT(".", m.FilName) + 1, 3) ENDIF RETURN UPPER(m.ext)
 * Return just the extension from "filname"

FUNCTION JUSTFNAME PARAMETERS m.FilName IF RAT("\",m.FilName) > 0 m.FilName = SUBSTR(m.FilName,RAT("\",m.FilName)+1,255) ENDIF IF AT(":",m.FilName) > 0 m.FilName = SUBSTR(m.FilName,AT(":",m.FilName)+1,255) ENDIF RETURN ALLTRIM(UPPER(m.FilName))
 * Return just the filename (i.e., no path) from "filname"

FUNCTION ADDBS PARAMETER m.pathname PRIVATE m.separator m.separator = IIF(_MAC,":","\") m.pathname = ALLTRIM(UPPER(m.pathname)) IF !(RIGHT(m.pathname,1) $ '\:') AND !EMPTY(m.pathname) m.pathname = m.pathname + m.separator ENDIF RETURN m.pathname
 * Add a backslash unless there is one already there.



STATUS
Microsoft has confirmed that this is a problem in the Microsoft products that are listed at the beginning of this article.



Steps to Reproduce the Behavior
 Change the system date of the computer to 02/01/2000. The specific date is not critical as long as the year is 2000 or later.</li>  In the FoxPro Command window, type the following without the comments: SET CENTURY ON USE        && Name of the table. APPEND BLANK           && Appends a blank record to the table.DIR USE                    && This closes the table. DIR An incorrect answer is listed for the modified table: 02/01/1900. </li></ol>

NOTE: Remember to reset the system date of your computer back to today.

<div class="references_section">