[MERGE] Use win32file.FindFiles instead of os.lstat when available

Mark Hammond mhammond at skippinet.com.au
Fri Jun 27 00:30:26 BST 2008


> Hmm... I seem to be having some *really* weird stuff happening vi
> win32file and PyTime. Specifically, if I do:
> 
> info = win32file.FindFilesW('path\\to\\file')[0]
> ctime, atime, wtime = info[1:4]
> 
> This seems okay, and I now have 3 PyTime structures.
> 
> However if I do:
> 
> |>> int(ctime)
> 1210366208
> |>> float(ctime)
> 39577.659814814811
> 
> 
> As near as I can tell, that value is the "variant" time. Described
> here:
> http://msdn.microsoft.com/en-us/library/ms221646(VS.85).aspx
> 
> The value is basically the time in "days".

Yeah - that code is quite old (well before datetime) and the exact details escape me, but using int and float to get the info out the object caused problems.

> Which basically, converts the FileTime => SystemTime and then
> SystemTime
> => VariantTime. Which is a bit odd, considering all of the places that
> I
> see it call VariantTimeToSystemTime before it tries to actually
> continue.

Yeah - that date object was designed to use with COM Variant and it turned into a generic date object for pywin32.

> 
> Just to compare quickly... Python's os.stat does:
> 
> WIN32_FIND_DATAW FileData;
> hFindFile = FindFirstFileW(pszFile, &FileData);
> 
> Which it then assigns into a WIN32_FILE_ATTRIBUTE_DATA structure.
> It passes this off to
> FILE_TIME_to_time_t_nsec()
> 
> which pretends that FILETIME is actually a 64-bit integer (it is 2
> 32-bit integers, so it is actually reasonable), and then does:
> 
> *nsec_out = (int)(in % 10000000) * 100; /* FILETIME is in units of 100
> nsec. */
> /* XXX Win32 supports time stamps past 2038; we currently don't */
> *time_out = Py_SAFE_DOWNCAST((in / 10000000) - secs_between_epochs,
> __int64, int);
> 
> Where:
> secs_between_epochs = 11644473600; /* Seconds between 1.1.1601 and
> 1.1.1970 */
> 
> So os.lstat() seems to be returning the raw time stored by the disk.
> 
> While win32file seems to be doing:
> grab the raw WIN32_FIND_DATA, with its FILETIME structure 'ft'.
> FileTimeToSystemTime => st
> SystemTimeToVariantTime => m_time
> VariantTimeToSystemTime => st
> copy st => a struct tm object
> struct tm tm = { 0 };
> tm.tm_sec = st.wSecond;
> tm.tm_min = st.wMinute;
> tm.tm_hour = st.wHour;
> tm.tm_mday = st.wDay;
> tm.tm_mon = st.wMonth - 1;
> tm.tm_year = st.wYear - 1900;
> tm.tm_isdst = -1;   /* have the library figure it out */
> 
> long result = (long)mktime(&tm);
> 
> Now, mktime() assumes that the structure is in local time.
> 
> |>> help(time.mktime)
> mktime(...)
> ~    mktime(tuple) -> floating point number
> 
> ~    Convert a time tuple in local time to seconds since the Epoch.
> 
> 
> ...
> 
> So in summary 'os.lstat("foo")' is returning the raw time given by the
> disk, which seems to be in local time. And win32file is going through a
> whole *lot* of convolutions to convert that time into the Epoch time.
> 
> Except, if I do:
> |>> time.time()
> 1214504550.293
> |>> open('filename.txt', 'wb').close()
> |>> os.lstat('filename.txt').st_mtime
> 1214504561.2620001
> |>> int(win32file.FindFiles('filename.txt')[0][3])
> 1214522561
> 
> I'm *very* tempted to say that pywin32 is in the wrong here. I'm
> guessing it actually needs to be doing:
> SystemTimeToTzSpecificLocalTime(NULL, &stUTC, &stLocal);

Yeah - how about not trying to use int/float at all, but instead attributes on the item?  Eg, the object has year, month, weekday, day, hour, minute, seconds and msec attributes.  I haven't looked at the initial patch yet, but FindFilesIterator() might be a better way to iterate a directory that may have many items.

> Mark, can you comment on what pywin32 is doing here?

The date object in pywin32 sucks.  I'd be more interested in having pywin32 work more closely with the datetime object than trying to breath more life into that object.

Cheers,

Mark




More information about the bazaar mailing list