Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Modify file create / access / write timestamp with python under windows

I tried to find an easy way to modifiy a file timestamp under windows using python, but there was not much clear information on the web. After searching a while I got a solution. To shorten the search for others, the code follows here.

It might be done easier and more beautiful, but it works. The only thing I didn't solve is the summer time - winter time issue, i.e. if a time in summer is given, the result differs by one hour. Maybe someone can add a correction?

from win32file import CreateFile, SetFileTime, GetFileTime, CloseHandle 
from win32file import GENERIC_READ, GENERIC_WRITE, OPEN_EXISTING
from pywintypes import Time
import time

import sys
import os

if len(sys.argv)<5:
  pfile = os.path.basename(sys.argv[0])
  print "USAGE:\n\t%s <createTime> <modifyTime> <accessTime> <FileName>\n" % pfile
  print "EXAMPLE:"
  print '%s "01.01.2000 00:00:00" "01.01.2000 00:00:00" "01.01.2000 00:00:00" file' % (pfile) 
  sys.exit()  

# get arguments  
cTime = sys.argv[1] # create
mTime = sys.argv[2] # modify
aTime = sys.argv[3] # access
fName = sys.argv[4]

# specify time format
format = "%d.%m.%Y %H:%M:%S"
offset = 0 # in seconds

# create struct_time object
cTime_t = time.localtime(time.mktime(time.strptime(cTime,format))+offset)
mTime_t = time.localtime(time.mktime(time.strptime(mTime,format))+offset)
aTime_t = time.localtime(time.mktime(time.strptime(aTime,format))+offset)

# visually check if conversion was ok
print
print "FileName: %s" % fName
print "Create  : %s --> %s OK" % (cTime,time.strftime(format,cTime_t))
print "Modify  : %s --> %s OK" % (mTime,time.strftime(format,mTime_t))
print "Access  : %s --> %s OK" % (aTime,time.strftime(format,aTime_t))
print

# change timestamp of file
fh = CreateFile(fName, GENERIC_READ | GENERIC_WRITE, 0, None, OPEN_EXISTING, 0, 0) 
createTime, accessTime, modifyTime = GetFileTime(fh) 
print "Change Create from",createTime,"to %s" % (time.strftime(format,cTime_t))
print "Change Modify from",modifyTime,"to %s" % (time.strftime(format,mTime_t))
print "Change Access from",accessTime,"to %s" % (time.strftime(format,aTime_t))
print

createTime = Time(time.mktime(cTime_t))
accessTime   = Time(time.mktime(aTime_t))
modifyTime    = Time(time.mktime(mTime_t))
SetFileTime(fh, createTime, accessTime, modifyTime) 
CloseHandle(fh)

# check if all was ok
ctime = time.strftime(format,time.localtime(os.path.getctime(fName)))
mtime = time.strftime(format,time.localtime(os.path.getmtime(fName)))
atime = time.strftime(format,time.localtime(os.path.getatime(fName)))

print "CHECK MODIFICATION:"
print "FileName: %s" % fName
print "Create  : %s" % (ctime)
print "Modify  : %s" % (mtime)
print "Access  : %s" % (atime)
like image 475
Michael Hecht Avatar asked Jan 16 '14 07:01

Michael Hecht


2 Answers

There are two places where you might want to correct for winter/summer difference of one hour. In both cases, we make use of the tm_isdst field, which time.localtime conveniently calculates to tell us whether Daylight Savings Time (DST) was in effect for a particular timestamp.

Input Correction

If you are setting a winter timestamp during summer, or vice versa, it will become off by an hour when its matching season comes around unless you compensate before calling SetFileTime:

now = time.localtime()
createTime = Time(time.mktime(cTime_t) + 3600 * (now.tm_isdst - cTime_t.tm_isdst))
accessTime = Time(time.mktime(aTime_t) + 3600 * (now.tm_isdst - aTime_t.tm_isdst))
modifyTime = Time(time.mktime(mTime_t) + 3600 * (now.tm_isdst - mTime_t.tm_isdst))
SetFileTime(fh, createTime, accessTime, modifyTime) 

Output Correction

To make Python reports match Windows Explorer, we apply the correction before calling strftime:

# check if all was ok
now = time.localtime()
ctime = os.path.getctime(fName)
mtime = os.path.getmtime(fName)
atime = os.path.getatime(fName)
ctime += 3600 * (now.tm_isdst - time.localtime(ctime).tm_isdst)
mtime += 3600 * (now.tm_isdst - time.localtime(mtime).tm_isdst)
atime += 3600 * (now.tm_isdst - time.localtime(atime).tm_isdst)
ctime = time.strftime(format,time.localtime(ctime))
mtime = time.strftime(format,time.localtime(mtime))
atime = time.strftime(format,time.localtime(atime))

Both Corrections

Beware, if you apply both, your Python output will again seem to mismatch your input. This may be desirable (see below), but if it bothers you:

  • Choose only Input Correction if you prefer timestamps that look right at their native time of year.
  • Choose only Output Correction if you're used to seeing them jump an hour twice a year as DST takes effect and then goes away.

Why is DST so inconsistent?

Python and Windows have chosen different methods to convert timestamps between UTC and the local time zone:

  • Python uses the DST code that was in effect at the timestamp. This way, the time stamp has a consistent representation year-round.

  • Windows uses the DST code in effect right now. This way, all time stamps shown have the same implicit code.

This is evident if you use '%Z' to include the time zone in the converted string (PST vs. PDT, for example) but since most apps (including Windows Explorer) do not, an apparent one-hour inconsistency can manifest.

Example

When printed with explicit time codes, it becomes clear that the stamps in each column really do all represent the same instant in time:

File #1 (January)        File #2 (June)
2000-01-30 20:00:00 UTC  2000-06-22 20:00:00 UTC

observed in January in California:
2000-01-30 12:00:00 PST  2000-06-30 13:00:00 PDT  [Python]
2000-01-30 12:00:00 PST  2000-06-30 12:00:00 PST  [Windows]

observed in June in California:
2000-01-30 12:00:00 PST  2000-06-30 13:00:00 PDT  [Python]
2000-01-30 13:00:00 PDT  2000-06-30 13:00:00 PDT  [Windows]

observed in June in New York:
2000-01-30 15:00:00 EST  2000-06-30 16:00:00 EDT  [Python]
2000-01-30 16:00:00 EDT  2000-06-30 16:00:00 EDT  [Windows]

It would be nice if we could ask strftime to honor the tm_isdst field, to match Windows Explorer and most other apps that display file timestamps, but at least there's a simple workaround to do the calculation ourselves.

def adjustForDST (seconds):
    now = time.localtime()
    correction = 60*60 * (now.tm_isdst - time.localtime(seconds).tm_isdst)
    return seconds + correction

time.strftime(format, time.localtime(adjustforDST(mtime)))

Sources:

http://bytes.com/topic/python/answers/655606-python-2-5-1-broken-os-stat-module http://search.cpan.org/~shay/Win32-UTCFileTime-1.58/lib/Win32/UTCFileTime.pm

If the cpan link breaks again with a new revision, find it this way:

https://www.google.com/search?q=UTCFileTime.pm

like image 96
Sean Gugler Avatar answered Oct 01 '22 02:10

Sean Gugler


Using os.utime, you can change the atime, mtime (no ctime).

>>> import time
>>> import os
>>> t = time.mktime(time.strptime('16.01.2014 00:00:00', '%d.%m.%Y %H:%M:%S'))
>>> t
1389798000.0
>>> os.utime('..\path\to\file', (t,t)) # <---
>>> os.path.getmtime('..\path\to\file')
1389798000.0
like image 45
falsetru Avatar answered Oct 01 '22 03:10

falsetru