如何更改Windows文件的创建日期?

55

我该如何使用Python修改Windows文件的文件创建日期?


5
os.utime()可以改变文件的修改时间访问时间。详情请见http://docs.python.org/library/os.html#os.utime - jfs
@Sebastian:感谢提供链接。我看了一下SO的问题,他们说没有平台无关的方法来做到这一点,例如Linux不存储文件创建时间。 - Claudiu
@David:哈哈,好的,我会记住的。你对于注释有什么做法? - Claudiu
@Claudiu:我发布了这篇文章,是为了那些在谷歌搜索“python修改Windows文件日期”的读者。你的问题是第二个链接。 - jfs
2
关于 Linux 的同样问题:https://dev59.com/m3NA5IYBdhLWcg3wn_bD - Tobias Kienzler
12个回答

44

为了胜利而进行的“剃牦牛毛”

import pywintypes, win32file, win32con
def changeFileCreationTime(fname, newtime):
    wintime = pywintypes.Time(newtime)
    winfile = win32file.CreateFile(
        fname, win32con.GENERIC_WRITE,
        win32con.FILE_SHARE_READ | win32con.FILE_SHARE_WRITE | win32con.FILE_SHARE_DELETE,
        None, win32con.OPEN_EXISTING,
        win32con.FILE_ATTRIBUTE_NORMAL, None)

    win32file.SetFileTime(winfile, wintime, None, None)

    winfile.close()

6
如果你遇到 ImportError 并想知道在哪里可以找到 pywintypes(就像我一样):http://sourceforge.net/projects/pywin32/ - netvope
@Delta的解决方案更简单。 - Ethan Furman
我确实喜欢这个“剃牦牛毛”的说法,因为这正是我正在尝试做的事情,以支持Wintendo操作系统。 :) - Tomachi
1
“GENERIC_WRITE”请求了不必要的数据访问,如果现有的打开不共享写入数据访问,则可能无法被文件安全性所授予,或者可能导致共享冲突。该操作应仅请求“FILE_WRITE_ATTRIBUTES”元数据访问权限,其中不需要数据访问共享,例如“hfile = win32file.CreateFile(fname, ntsecuritycon.FILE_WRITE_ATTRIBUTES, 0, None, win32con.OPEN_EXISTING, 0, None)”。 - Eryk Sun

41

我不想为了设置一个文件的创建时间而仅仅带上整个pywin32 / win32file库,因此我制作了win32-setctime包来完成这个任务。

pip install win32-setctime

然后就可以像这样使用它:

from win32_setctime import setctime

setctime("my_file.txt", 1561675987.509)

基本上,该函数可以在不需要任何依赖项的情况下仅缩减为几行代码,除了 Python 内置库中的 ctypes 库:

from ctypes import windll, wintypes, byref

# Arbitrary example of a file and a date
filepath = "my_file.txt"
epoch = 1561675987.509

# Convert Unix timestamp to Windows FileTime using some magic numbers
# See documentation: https://support.microsoft.com/en-us/help/167296
timestamp = int((epoch * 10000000) + 116444736000000000)
ctime = wintypes.FILETIME(timestamp & 0xFFFFFFFF, timestamp >> 32)

# Call Win32 API to modify the file creation date
handle = windll.kernel32.CreateFileW(filepath, 256, 0, None, 3, 128, None)
windll.kernel32.SetFileTime(handle, byref(ctime), None, None)
windll.kernel32.CloseHandle(handle)

要进行高级管理(例如错误处理),请参阅win32_setctime.py的源代码


如果解释了魔数的含义,这段代码会更有用。我的猜测是:
  • epoch 是以秒为单位的时间?从什么时候开始?也许无关紧要?
  • 10000000 是秒和 Windows 的 100 纳秒时间单位之间的转换因子
  • 116444736000000000 是……好吧,我不知道。它似乎是大约 369 年的时间跨度,但我无法确定。
- Bruce Dawson
1
@BruceDawson 这来自于微软文档:如何将UNIX time_t转换为Win32 FILETIME或SYSTEMTIME - Delgan
1
@BruceDawson 当然可以,我已经给代码片段添加了一些注释。;) - Delgan
使用ctypes通过FFI调用GetLastError()会在原始FFI调用和GetLastError() FFI调用之间留下大量执行的代码,因为Python是一种解释性脚本语言。使用use_last_error=True加载DLL会自动配置从中创建的所有函数指针(例如kernel32.CreateFileW)以使用线程本地变量在C中设置和捕获FFI调用前后的GetLastError()值。ctypes.get_last_error()返回此线程本地最后一个错误值,而ctypes.set_last_error(error_code)则设置它。 - Eryk Sun
这非常有用,感谢您的制作和解释。 - Roger Heathcote
显示剩余4条评论

10

我的简单的filedate模块或许能够满足您的需求。

优点:

  • 非常简洁的接口
  • 与平台无关
  • 支持各种格式的日期字符串
  • Date Holder 工具

安装

pip install filedate

使用方法

import filedate
Path = "~/Documents/File.txt"

filedate.File(Path).set(
    created = "1st February 2003, 12:30",
    modified = "3:00 PM, 04 May 2009",
    accessed = "08/07/2014 18:30:45"
)

1
我今天使用了它,对于我的用例来说效果很好。感谢您的制作和分享! - Zertrin

8

首先安装pywin32扩展 https://sourceforge.net/projects/pywin32/files/pywin32/Build%20221/

import win32file
import pywintypes

# main logic function
def changeFileCreateTime(path, ctime):
    # path: your file path
    # ctime: Unix timestamp

    # open file and get the handle of file
    # API: http://timgolden.me.uk/pywin32-docs/win32file__CreateFile_meth.html
    handle = win32file.CreateFile(
        path,                          # file path
        win32file.GENERIC_WRITE,       # must opened with GENERIC_WRITE access
        0,
        None,
        win32file.OPEN_EXISTING,
        0,
        0
    )

    # create a PyTime object
    # API: http://timgolden.me.uk/pywin32-docs/pywintypes__Time_meth.html
    PyTime = pywintypes.Time(ctime)

    # reset the create time of file
    # API: http://timgolden.me.uk/pywin32-docs/win32file__SetFileTime_meth.html
    win32file.SetFileTime(
        handle,
        PyTime
    )

# example
changeFileCreateTime('C:/Users/percy/Desktop/1.txt',1234567789)

2
为什么很难发现win32filepywin32的一部分呢?谷歌毫无帮助,这意味着其他答案都没有用处;它们假设你已经安装了它。感谢您在答案顶部提供的有用提示。 - Mark Ransom
任何需要从datetime对象获取时间戳的人都可以在这里找到答案:https://dev59.com/kmsz5IYBdhLWcg3wg4Cv - Mark Ransom

4
这里是更加健壮的已接受答案的版本。它还有相反的getter函数。这解决了创建,修改和访问日期时间的问题。它可以处理日期时间参数作为datetime.datetime对象或作为“自纪元以来的秒数”(getter返回的内容)。此外,它还调整了夏令时,而已接受的答案则没有。如果没有这个调整,在您设置实际系统时间相反相位的冬季或夏季时间时,您的时间将不会正确设置。
这个答案的主要弱点是它仅适用于Windows(回答了提出的问题)。将来,我将尝试发布一个跨平台的解决方案。
def isWindows() :
  import platform
  return platform.system() == 'Windows' 

def getFileDateTimes( filePath ):        
    return ( os.path.getctime( filePath ), 
             os.path.getmtime( filePath ), 
             os.path.getatime( filePath ) )

def setFileDateTimes( filePath, datetimes ):
    try :
        import datetime
        import time 
        if isWindows() :
            import win32file, win32con
            ctime = datetimes[0]
            mtime = datetimes[1]
            atime = datetimes[2]
            # handle datetime.datetime parameters
            if isinstance( ctime, datetime.datetime ) :
                ctime = time.mktime( ctime.timetuple() ) 
            if isinstance( mtime, datetime.datetime ) :
                mtime = time.mktime( mtime.timetuple() ) 
            if isinstance( atime, datetime.datetime ) :
                atime = time.mktime( atime.timetuple() )             
            # adjust for day light savings     
            now = time.localtime()
            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)            
            # change time stamps
            winfile = win32file.CreateFile(
                filePath, win32con.GENERIC_WRITE,
                win32con.FILE_SHARE_READ | win32con.FILE_SHARE_WRITE | win32con.FILE_SHARE_DELETE,
                None, win32con.OPEN_EXISTING,
                win32con.FILE_ATTRIBUTE_NORMAL, None)
            win32file.SetFileTime( winfile, ctime, atime, mtime )
            winfile.close()
        else : """MUST FIGURE OUT..."""
    except : pass    

3
这段代码适用于Python 3,不会出现“ValueError: astimezone() cannot be applied to a naive datetime”错误:
wintime = datetime.datetime.utcfromtimestamp(newtime).replace(tzinfo=datetime.timezone.utc)
winfile = win32file.CreateFile(
    fname, win32con.GENERIC_WRITE,
    win32con.FILE_SHARE_READ | win32con.FILE_SHARE_WRITE | win32con.FILE_SHARE_DELETE,
    None, win32con.OPEN_EXISTING,
    win32con.FILE_ATTRIBUTE_NORMAL, None)
win32file.SetFileTime(winfile, wintime)
winfile.close()

使用Python 3时出现错误:类型对象'datetime.datetime'没有属性'datetime'。 - Vlad
1
@Vlad,使用错误的导入语句会导致错误。对象是datetime.datetime,就像我的示例一样,而不是你试图使用的datetime.datetime.datetime。 - panda-34
你是对的 - 我已经更改了导入语句,但现在datetime.datetime.utcfromtimestamp(newtime)出现了“需要浮点数”的错误。希望这可以在Python 3中正常工作。 - Vlad

2
这是一个适用于Python 3.5和Windows 7的解决方案。非常简单。我承认这是一种粗糙的编码方式...但它能够工作。欢迎您进行优化。我只需要一个快速的解决方案。
import pywintypes, win32file, win32con, datetime, pytz

def changeFileCreationTime(fname, newtime):
    wintime = pywintypes.Time(newtime)
    winfile = win32file.CreateFile(fname, win32con.GENERIC_WRITE,
                                   win32con.FILE_SHARE_READ | 
                                   win32con.FILE_SHARE_WRITE | 
                                   win32con.FILE_SHARE_DELETE,
                                   None, 
                                   win32con.OPEN_EXISTING,
                                   win32con.FILE_ATTRIBUTE_NORMAL, 
                                   None)

    win32file.SetFileTime(      winfile,  wintime,  wintime,     wintime)
    # None doesnt change args = file,     creation, last access, last write
    # win32file.SetFileTime(None, None, None, None) # does nonething
    winfile.close()

if __name__ == "__main__":
    local_tz = pytz.timezone('Antarctica/South_Pole')
    start_date = local_tz.localize(datetime.datetime(1776,7,4), is_dst=None)
    changeFileCreationTime(r'C:\homemade.pr0n', start_date )

1
有关目录:请参见此处:https://dev59.com/5VTTa4cB1Zd3GeqPrVNM! - Tomasz Gandor

2
import os
os.utime(path, (accessed_time, modified_time))

http://docs.python.org/library/os.html

这至少可以在不使用win32模块的情况下更改修改时间。

解释:这段话意思是通过OS模块中提供的函数更改文件的修改时间,而不需要使用win32模块。


21
很遗憾,我很确定这只会改变文件的修改时间和访问时间,而不是OP想要的文件创建时间。 - JJC
1
至少在XP上,它设置了创建时间。 - Ethan Furman
6
在我的XP或Win7上无法实现任何功能,atime设置访问时间,mtime设置修改时间,但两者都无法设置创建时间。 - SilverbackNet

0

我找不到 Python 的确切答案,因此我留下了一个答案,供任何搜索如何修改目录(或文件,感谢此主题中的答案)的日期的人使用。

import os, win32con, win32file, pywintypes


def changeCreationTime(path, time):

    try:
        wintime = pywintypes.Time(time)
        # File
        if os.path.isfile(path):
            winfile = win32file.CreateFile(path,
                                           win32con.GENERIC_WRITE,
                                           win32con.FILE_SHARE_READ |
                                           win32con.FILE_SHARE_WRITE |
                                           win32con.FILE_SHARE_DELETE,
                                           None,
                                           win32con.OPEN_EXISTING,
                                           win32con.FILE_ATTRIBUTE_NORMAL,
                                           None)
            win32file.SetFileTime(winfile, wintime, wintime, wintime)
            winfile.close()
            print(f'File {path} modified')
        # Directory
        elif os.path.isdir(path):
            windir = win32file.CreateFile(path,
                                          win32con.GENERIC_WRITE,
                                          win32con.FILE_SHARE_WRITE |
                                          win32con.FILE_SHARE_DELETE |
                                          win32con.FILE_SHARE_READ,
                                          None,
                                          win32con.OPEN_EXISTING,
                                          win32con.FILE_FLAG_BACKUP_SEMANTICS,
                                          None)
            win32file.SetFileTime(windir, wintime, wintime, wintime)
            windir.close()
            print(f"Directory {path} modified")
    except BaseException as err:
        print(err)

例子:

# Create a folder named example and a text file named example.txt in C:\example
changeCreationTime(r'C:\example', 1338587789)
changeCreationTime(r'C:\example\example.txt', 1338587789)


0

如果你想要使用日期而不是纪元时间,可以使用以下代码,我使用了win32-setctime和attrs包,首先需要安装这些包:

pip install win32-setctime
pip install attrs

然后你可以运行我的代码,记得更新FILEPATH、DATE、MONTH和YEAR。

from datetime import datetime

import attr
from win32_setctime import setctime

FILEPATH = r'C:\Users\jakub\PycharmProjects\date_creation_change\doc.docx'
DAY, MONTH, YEAR = (9, 5, 2020)


@attr.s
class TimeCounter:
    """
    Class calculates epochs
    """
    day = attr.ib(converter=str)
    month = attr.ib(converter=str)
    year = attr.ib(converter=str)

    def create_datetime(self):
        date_time_obj = datetime.strptime(r'{}/{}/{}'.format(self.day,
                                                             self.month,
                                                             self.year), '%d/%m/%Y')

        unix_start = datetime(1970, 1, 1)
        return (date_time_obj - unix_start).days

    def count_epoch(self):
        days = self.create_datetime()
        return days * 86400


@attr.s
class DateCreatedChanger:
    """
    Class changes the creation date of the file
    """
    file_path = attr.ib()

    def change_creation_date(self):
        epoch_obj = TimeCounter(day=DAY,
                                month=MONTH,
                                year=YEAR)
        epoch = epoch_obj.count_epoch()
        setctime(self.file_path, epoch)


if __name__ == '__main__':
    changer = DateCreatedChanger(FILEPATH)
    changer.change_creation_date()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接