Форум сайта python.su
Python неправильно получает timestamp'ы для файлов, которые были изменены при “зимнем” часовом поясе ДО ОТМЕНЫ ПЕРЕВОДА ВРЕМЕНИ.
Столкнулся с данной проблемой еще год назад, но отложит решение… теперь вопрос опять актуален для меня.
Проявляется на Windows (установлен Python 2.7.3), на Linux все нормально.
Допустим есть файл, который изменен “2008-12-16 07:55:22” (это локальное время).
Файловые менеджеры (Explorer и T-Commander) показывают время корректно, а вот Python выдает “2008-12-16 08:55:22”.
написал год назад тестовый код:
# -*- coding: utf-8 -*- import logging, sys, os, hashlib, struct, time from os import path from datetime import datetime def getFileTimes(fname, gmt=True, checkMT=True): try: fs = "%Y-%m-%d %H:%M:%S" os.stat_float_times(False) if gmt: ct = time.gmtime(os.path.getctime(fname)) mt = time.gmtime(os.path.getmtime(fname)) else: ct = time.localtime(os.path.getctime(fname)) mt = time.localtime(os.path.getmtime(fname)) if checkMT: pass # TODO: if ct > mt: ct = mt return time.strftime(fs,ct), time.strftime(fs,mt) except: return "", "" files = { 'test-file-1-ERR-js.txt': '2009-11-29 21:27:38', 'test-file-2-OK-js.txt' : '2009-05-27 10:46:12', 'test-file-3-ERR-js.txt': '2008-12-16 09:55:22', # после отмены перехода 'test-file-4-OK-py.txt' : '2011-11-22 10:33:44', 'test-file-5-OK-txt.txt': '2012-07-20 20:05:29' } for fname in files.keys(): ftime = getFileTimes(fname, False)[1] ftime2 = getFileTimes(fname, True)[1] shift = int(ftime[11:13]) - int(ftime2[11:13]) res = 'OK: ' if files[fname] == ftime else 'ERR:' print '%s [%s] ftime = %s mustbe = %s shift=%i' % (res, fname, ftime, files[fname], shift)
#include <windows.h>
#include <tchar.h>
#include <strsafe.h>
BOOL GetLastWriteTime(HANDLE hFile, LPTSTR lpszString, DWORD dwSize)
{
FILETIME ftCreate, ftAccess, ftWrite;
SYSTEMTIME stUTC, stLocal;
DWORD dwRet;
// Retrieve the file times for the file.
if (!GetFileTime(hFile, &ftCreate, &ftAccess, &ftWrite))
return FALSE;
// Convert the last-write time to local time.
FileTimeToSystemTime(&ftWrite, &stUTC);
SystemTimeToTzSpecificLocalTime(NULL, &stUTC, &stLocal);
// Build a string showing the date and time.
dwRet = StringCchPrintf(lpszString, dwSize, TEXT(
"%04d-%02d-%02d %02d:%02d:%02d"), stLocal.wYear, stLocal.wMonth,
stLocal.wDay, stLocal.wHour, stLocal.wMinute, stLocal.wSecond);
if (S_OK == dwRet)
return TRUE;
else
return FALSE;
}
int _tmain(int argc, TCHAR *argv[])
{
HANDLE hFile;
TCHAR szBuf[MAX_PATH];
if (argc != 2)
{
printf("This sample takes a file name as a parameter\n");
return 0;
}
hFile = CreateFile(argv[1], GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, 0, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
printf("CreateFile failed with %d\n", GetLastError());
return 0;
}
if (GetLastWriteTime(hFile, szBuf, MAX_PATH))
_tprintf(TEXT("%s"), szBuf);
CloseHandle(hFile);
}
# -*- coding: utf-8 -*- import logging, sys, os, hashlib, struct, time from os import path from datetime import datetime files = { 'test-file-1-ERR-js.txt': '2009-11-29 21:27:38', 'test-file-2-OK-js.txt' : '2009-05-27 10:46:12', 'test-file-3-ERR-js.txt': '2008-12-16 09:55:22', # после отмены перехода 'test-file-4-OK-py.txt' : '2011-11-22 10:33:44', 'test-file-5-OK-txt.txt': '2012-07-20 20:05:29', 'test-file-6.txt': '2013-02-20 19:32:10' } for fname in files.keys(): import subprocess proc = subprocess.Popen('"getFileTime/getFileTime.exe" "%s"' % fname, shell=True, stdout=subprocess.PIPE) out = proc.stdout.readlines() ftime = out[0] shift = 0 res = 'OK: ' if files[fname] == ftime else 'ERR:' print '%s ftime = %s mustbe = %s [%s] shift=%i' % (res, ftime, files[fname], fname, shift)
Офлайн
В “NEWS.txt” нашел такую вещь:
What's New in Python 2.7.4 release candidate 1
==============================================
*Release date: 2013-03-23*
- Issue #13863: Work around buggy 'fstat' implementation on Windows / NTFS that
lead to incorrect timestamps (off by one hour) being stored in .pyc files on
some systems.
Офлайн
Вопрос закрыт.
Сначала запихнул сишный код в DLL, но потом переделал всё на основе ctypes.
Самописная DLL производительнее ctypes-версии всего на 10%, поэтому в ней необходимость отпала.
############################################################################### ## немного доработал getTimestamp(), взятый отсюда: ## http://pyudd.googlecode.com/svn-history/r14/trunk/pyudd.py ############################################################################### def getTimestamp(filename): ctime = ctypes.c_ulonglong(0) atime = ctypes.c_ulonglong(0) mtime = ctypes.c_ulonglong(0) h = ctypes.windll.kernel32.CreateFileA(ctypes.c_char_p(filename), 0, 3, 0, 3, 0x80, 0) ctypes.windll.kernel32.GetFileTime(h, ctypes.pointer(ctime), ctypes.pointer(atime), ctypes.pointer(mtime)) ctypes.windll.kernel32.CloseHandle(h) return ctime.value, mtime.value, atime.value ############################################################################### ## вытащил немного кода отсюда: ## http://pydoc.net/Python/winappdbg/1.4/winappdbg.win32.kernel32/ ############################################################################### WORD = ctypes.c_ushort DWORD = ctypes.c_uint class SYSTEMTIME(Structure): _fields_ = [ ('wYear', WORD), ('wMonth', WORD), ('wDayOfWeek', WORD), ('wDay', WORD), ('wHour', WORD), ('wMinute', WORD), ('wSecond', WORD), ('wMilliseconds', WORD), ] LPSYSTEMTIME = POINTER(SYSTEMTIME) class FILETIME(Structure): _fields_ = [ ('dwLowDateTime', DWORD), ('dwHighDateTime', DWORD), ] LPFILETIME = POINTER(FILETIME) def FileTimeToSystemTime(lpFileTime): _FileTimeToSystemTime = windll.kernel32.FileTimeToSystemTime _FileTimeToSystemTime.argtypes = [LPFILETIME, LPSYSTEMTIME] _FileTimeToSystemTime.restype = bool #_FileTimeToSystemTime.errcheck = RaiseIfZero if isinstance(lpFileTime, FILETIME): FileTime = lpFileTime else: FileTime = FILETIME() FileTime.dwLowDateTime = lpFileTime & 0xFFFFFFFF FileTime.dwHighDateTime = lpFileTime >> 32 SystemTime = SYSTEMTIME() _FileTimeToSystemTime(ctypes.byref(FileTime), ctypes.byref(SystemTime)) return SystemTime ############################################################################### # добавил конвертацию ############################################################################### def convertFileTimeToLocalTimeStr(ftSrc): stUTC = FileTimeToSystemTime(ftSrc) stLocal = SYSTEMTIME() ctypes.windll.kernel32.SystemTimeToTzSpecificLocalTime(0, ctypes.byref(stUTC), ctypes.byref(stLocal)) return "%04d-%02d-%02d %02d:%02d:%02d" % (stLocal.wYear, stLocal.wMonth, stLocal.wDay, stLocal.wHour, stLocal.wMinute, stLocal.wSecond) ############################################################################### # тестируем ############################################################################### if __name__ == '__main__': fname = 'test-file-3-ERR-js.txt' # 'test-file-3-ERR-js.txt': '2008-12-16 09:55:22' ctime, mtime, atime = getTimestamp(fname) print convertFileTimeToLocalTimeStr(ctime), convertFileTimeToLocalTimeStr(mtime)
#include <windows.h>
#include <string>
#define DLLEXPORT extern "C" __declspec(dllexport)
#define PRINTF printf
int convertFileTimeToLocalTimeStr(FILETIME ftSrc, char *outBuf)
{
SYSTEMTIME stUTC, stLocal;
if ( FALSE == ::FileTimeToSystemTime(&ftSrc, &stUTC) )
{
PRINTF("FileTimeToSystemTime failed\n");
return -1;
}
if ( FALSE == ::SystemTimeToTzSpecificLocalTime(NULL, &stUTC, &stLocal) )
{
PRINTF("SystemTimeToTzSpecificLocalTime failed\n");
return -2;
}
int n = sprintf(outBuf, "%04d-%02d-%02d %02d:%02d:%02d"
, stLocal.wYear, stLocal.wMonth, stLocal.wDay, stLocal.wHour, stLocal.wMinute, stLocal.wSecond);
if (n < 0)
{
PRINTF("sprintf failed\n");
return -3;
}
return n;
}
DLLEXPORT int getFileTimes(char* fname, char* createTime, char* writeTime)
{
HANDLE hFile = ::CreateFile(fname, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if (INVALID_HANDLE_VALUE == hFile)
{
PRINTF("CreateFile failed, GetLastError = %u\n", ::GetLastError());
return -1;
}
FILETIME ftCreate, ftAccess, ftWrite;
if (FALSE == ::GetFileTime(hFile, &ftCreate, &ftAccess, &ftWrite))
{
PRINTF("GetFileTime failed\n");
::CloseHandle(hFile);
return -2;
}
int n1 = convertFileTimeToLocalTimeStr(ftCreate, createTime);
int n2 = convertFileTimeToLocalTimeStr(ftWrite, writeTime);
::CloseHandle(hFile);
return (n1 > 0 && n2 > 0) ? 0 : -3;
}
Офлайн