【问题标题】:Convert datetime back to Windows 64-bit FILETIME将日期时间转换回 Windows 64 位 FILETIME
【发布时间】:2017-01-21 17:10:35
【问题描述】:

我想创建 NT 格式的网络时间戳。

我已经能够使用这个函数将它们转换为可读时间:

NetworkStamp = "\xc0\x65\x31\x50\xde\x09\xd2\x01"

def GetTime(NetworkStamp):
    Ftime = int(struct.unpack('<Q',NetworkStamp)[0])
    Epoch = divmod(Ftime - 116444736000000000, 10000000)
    Actualtime = datetime.datetime.fromtimestamp(Epoch[0])
    return Actualtime, Actualtime.strftime('%Y-%m-%d %H:%M:%S')

print GetTime(NetworkStamp)

输出:

(datetime.datetime(2016, 9, 8, 11, 35, 57), '2016-09-08 11:35:57')

现在我想做相反的事情,将'2016/09/08 11:35:57' sec 转换为这种格式:

 "\xc0\x65\x31\x50\xde\x09\xd2\x01"

【问题讨论】:

    标签: python python-2.x filetime


    【解决方案1】:

    如果您了解如何在一个方向上执行转换,那么反向执行基本上就是以相反的顺序使用每种方法的倒数。只需查看您正在使用的模块/类的文档:

    1. strftimea strptime counterpart
    2. fromtimestamp is matched by timestamp (如果您使用的是 3.3 之前的 Python,timestamp 不存在,但您可以在函数外部定义 FILETIME_epoch = datetime.datetime(1601, 1, 1) - datetime.timedelta(seconds=time.altzone if time.daylight else time.timezone) 以预先计算代表 FILETIME 纪元的 datetime时区,然后使用int((mydatetime - FILETIME_epoch).total_seconds()) 直接获取自FILETIME epoch 以来的int 秒数,无需单独手动调整FILETIME 和Unix epoch 之间的差异)
    3. divmod (你并不真正需要,因为你只使用商,而不是余数,你可以只做Epoch = (Ftime - 116444736000000000) // 10000000 并避免以后索引)是微不足道的可逆(只需乘法和加法,如果加法是不必要的您使用我的技巧直接从#2 转换为FILETIME 纪元秒)
    4. struct.unpackis matched by struct.pack

    我没有提供确切的代码,因为您确实应该自己学习使用这些东西(并在必要时阅读文档);我猜你的正向代码是在不理解它在做什么的情况下编写的,因为如果你理解它,反过来应该很明显;每一步都有一个逆向记录在同一页面上。

    【讨论】:

    【解决方案2】:

        将 Window 的 FILETIME 值转换为 datetime.datetime 的代码并不准确 - 它截断了可能存在的任何小数秒(因为它忽略了 divmod() 结果的其余部分) .这在您的代码创建的可读字符串中并不明显,因为它只显示整秒。
    即使包含小数秒,您也不能完全按照您的意愿进行操作,因为 Windows FILETIME 结构的值间隔为 100 纳秒(0.1 微秒),但 Python 的 datetime 仅支持精确到整微秒。因此,最好的办法是近似原始值,因为即使进行最准确的转换也会涉及信息丢失。

    以下是 Python 2 和 3 的代码,在您的问题中使用 NetworkStamp 测试值演示了这一点:

    import datetime
    import struct
    import time
    
    WINDOWS_TICKS = int(1/10**-7)  # 10,000,000 (100 nanoseconds or .1 microseconds)
    WINDOWS_EPOCH = datetime.datetime.strptime('1601-01-01 00:00:00',
                                               '%Y-%m-%d %H:%M:%S')
    POSIX_EPOCH = datetime.datetime.strptime('1970-01-01 00:00:00',
                                             '%Y-%m-%d %H:%M:%S')
    EPOCH_DIFF = (POSIX_EPOCH - WINDOWS_EPOCH).total_seconds()  # 11644473600.0
    WINDOWS_TICKS_TO_POSIX_EPOCH = EPOCH_DIFF * WINDOWS_TICKS  # 116444736000000000.0
    
    def get_time(filetime):
        """Convert windows filetime winticks to python datetime.datetime."""
        winticks = struct.unpack('<Q', filetime)[0]
        microsecs = (winticks - WINDOWS_TICKS_TO_POSIX_EPOCH) / WINDOWS_TICKS
        return datetime.datetime.fromtimestamp(microsecs)
    
    def convert_back(timestamp_string):
        """Convert a timestamp in Y=M=D H:M:S.f format into a windows filetime."""
        dt = datetime.datetime.strptime(timestamp_string, '%Y-%m-%d %H:%M:%S.%f')
        posix_secs = int(time.mktime(dt.timetuple()))
        winticks = (posix_secs + int(EPOCH_DIFF)) * WINDOWS_TICKS
        return winticks
    
    def int_to_bytes(n, minlen=0):  # helper function
        """ int/long to bytes (little-endian byte order).
            Note: built-in int.to_bytes() method could be used in Python 3.
        """
        nbits = n.bit_length() + (1 if n < 0 else 0)  # plus one for any sign bit
        nbytes = (nbits+7) // 8  # number of whole bytes
        ba = bytearray()
        for _ in range(nbytes):
            ba.append(n & 0xff)
            n >>= 8
        if minlen > 0 and len(ba) < minlen:  # zero pad?
            ba.extend([0] * (minlen-len(ba)))
        return ba  # with low bytes first
    
    def hexbytes(s):  # formatting helper function
        """Convert string to string of hex character values."""
        ba = bytearray(s)
        return ''.join('\\x{:02x}'.format(b) for b in ba)
    
    win_timestamp = b'\xc0\x65\x31\x50\xde\x09\xd2\x01'
    print('win timestamp: b"{}"'.format(hexbytes(win_timestamp)))
    dtime = get_time(win_timestamp)
    readable = dtime.strftime('%Y-%m-%d %H:%M:%S.%f')  # includes fractional secs
    print('datetime repr: "{}"'.format(readable))
    
    winticks = convert_back(readable)  # includes fractional secs
    new_timestamp = int_to_bytes(winticks)
    print('new timestamp: b"{}"'.format(hexbytes(new_timestamp)))
    

    输出:

    win timestamp: b"\xc0\x65\x31\x50\xde\x09\xd2\x01"
    datetime repr: "2016-09-08 07:35:57.996998"
    new timestamp: b"\x80\x44\x99\x4f\xde\x09\xd2\x01"
    

    【讨论】:

    • 鉴于 OP 被截断到秒,缺少 100 ns 的粒度并不重要;你不会得到你开始的东西,但不是因为datetime的任何限制。
    • 另外,为什么divmod 后面跟着撤消divmod?您可以直接将定义quotientremaindermicrosecs 的两行替换为单行计算microsecsmicrosecs = (winticks - WINDOWS_TICKS_TO_POSIX_EPOCH) / WINDOWS_TICKS
    • @ShadowRanger:感谢您指出不需要divmod — 这只是我试图向 OP 展示如何不丢弃其余部分并进行更准确转换的遗物,不像他们自己的代码。至于缺少 0.1 微秒的粒度是否重要:如果您出于某种原因需要获得与开始时完全相同的值,则可能是这样。
    【解决方案3】:

    基本转换:

    from datetime import datetime
    from calendar import timegm
    
    EPOCH_AS_FILETIME = 116444736000000000  # January 1, 1970 as MS file time
    HUNDREDS_OF_NANOSECONDS = 10000000
    
    def dt_to_filetime(dt):
        return EPOCH_AS_FILETIME + (timegm(dt.timetuple()) * HUNDREDS_OF_NANOSECONDS)
    

    答案基于我找到的这个 Gist:https://gist.github.com/Mostafa-Hamdy-Elgiar/9714475f1b3bc224ea063af81566d873

    Gist 增加了对时区和其他方向转换的支持。

    【讨论】:

      猜你喜欢
      • 2014-10-03
      • 2016-02-08
      • 1970-01-01
      • 2011-04-04
      • 1970-01-01
      • 1970-01-01
      • 2010-12-18
      • 2014-09-12
      • 1970-01-01
      相关资源
      最近更新 更多