0

我创建了一个 python 脚本,它从 Word 文档中的 OLE 流中提取数据,但是在将 OLE2 格式的时间戳转换为更易于阅读的东西时遇到了麻烦:(

被拉出的时间戳是 12760233021 但我不能终生将其转换为 2007 年 3 月 12 日或类似的日期。

任何帮助是极大的赞赏。

编辑:好的,我已经在我的一个 word 文档上运行了脚本,该文档是在31/10/2009, 10:05:00创建的。OLE DocumentSummaryInformation 流中的创建日期是12901417500

另一个示例是在 2009 年 10 月 27 日 15:33:00 创建的 word doc,在 OLE DocumentSummaryInformation 流中给出了 12901091580 的创建日期。

有关这些 OLE 流属性的 MSDN 文档是http://msdn.microsoft.com/en-us/library/aa380376%28VS.85%29.aspx

将这些流拉出的 def 如下所示:

import OleFileIO_PL as ole

def enumerateStreams(item):
    # item is an arbitrary file
    if ole.isOleFile('%s' % item):
        loader = ole.OleFileIO('%s' % item)
        # enumerate all the OLE streams in the office file
        streams = loader.listdir()
        streamProps = []
        for stream in streams:
            if stream[0] == '\x05SummaryInformation':
                # get all the properties fro the SummaryInformation OLE stream
                streamProps.append(loader.getproperties(stream))
            elif stream[0] == '\x05DocumentSummaryInformation':
                # get all the properties from the DocumentSummaryInformation stream
                streamProps.append(loader.getproperties(stream))
     return streamProps
4

2 回答 2

2

(0)请澄清“像 2007 年 3 月 12 日或类似”:您的意思是您希望 11 位 int 转换为 2007 年 3 月 12 日,还是“2007 年 3 月 12 日”只是为了传达您想要的格式显示日期?如果是后者,您不能通过使用 MS Word 或 OpenOffice.org 的文字处理小工具检查某些文件来提供预期的结果吗?您打算如何验证所提供的任何解决方案是否确实有效?

(1) 请提供多于一对(OLE,预期),以便可以更有把握地验证任何提议的解决方案的正确操作。如果可能,您能否创建具有已知预期值的示例,例如 2000 年 1 月 1 日、2001 年 1 月 1 日、2001 年 1 月 2 日、2001 年 2 月 2 日?

(2) 从“从 OLE 流中提取数据”中,您是否需要 OLE2 复合文档标题中的文件创建等时间戳,或者是否需要内容中存在的时间戳,这一点并不明显。请说出您在哪里搜索时间戳。如果您可以参考与您感兴趣的时间戳相关的 MS 文档,这也会有很大帮助......当然它必须告诉您格式是什么,即使它通过一两个内部/外部间接地这样做- 记录啤酒花。

(3) 请说明你是如何把它拉出来的——它是一根绳子吗?固定11个字节?或者它是 str(您从 64 位字段转换的一些 int)?如何转换??除了描述之外,还要显示您的转换代码。不要从内存中重新输入代码;使用复制/粘贴。

请通过编辑您的问题提供所请求的信息,而不是作为评论。

等待信息时更新:

OLE 复合文档标题中的文件创建和修改时间戳似乎是 64 位 little-endian 整数,表示(自 1601-01-01T00:00:00 以来的秒数)* 10 ** 7。

OLE2 数据中的数据中使用的 DATE 类型似乎是 64 位 little-endian IEEE 754 浮点数,表示自 1899-12-30T00:00:00 以来的(天数及其一小部分)。是的,这一天是 30,而不是 31。

检查提供的 2 个示例后更新:

两个观察到的时间戳(将是您的本地时间)之间的差异是 325920 秒:

>>> import datetime
>>> t0 = datetime.datetime(2009,10,27,15,33,0)
>>> t1 = datetime.datetime(2009,10,31,10,5,0)
>>> t1-t0
datetime.timedelta(3, 66720)
>>> secs = 3 * 24 * 60 * 60 + 66720
>>> secs
325920

这与两个幻数之间的差异相同:

>>> 12901417500 - 1290191580
325920

所以神奇的数字代表自某个时代以来的秒数......

>>> m1 = 12901417500
>>> days, seconds = divmod(m1, 60*60*24)
>>> epoch = t1 - datetime.timedelta(days, seconds)
>>> epoch
datetime.datetime(1601, 1, 1, 11, 0)

因此,幻数表示自 1601-01-01T00:00:00Z 以来的秒数,而您的 TZ 距离 UTC 有 11 小时的路程。

这两个幻数不适合 32 位......看起来(a)它以 64 位存储为自 1601 年以来的秒数(浪费了大约 29 位!)或(b)它存储为( 100 纳秒单位)自 1601 年以来如预期的那样,但在你看到它之前,有些东西将它除以 10**7。

您提供的文档参考仅说它是一种VF_FILETIME (UTC)类型。谷歌搜索,我发现了一些关于调用 Windows 函数来操作时间戳的 MS 线索,但据我所知没有定义。然而,有两个第 3 方注释(来自 perlmonks 和 Apache POI 项目)说了很多相同的事情:“”“这看起来像一个 WindowsVT_FILETIME数据类型,它是一个 64 位无符号整数,表示自 1 月 1 日以来经过的 100 纳秒数1601"""

犯罪现场更新:

似乎您正在使用OleFileIO_PL读取文件。快速翻阅唯一的源文件可以发现:

    elif type == VT_FILETIME:
        value = long(i32(s, offset+4)) + (long(i32(s, offset+8))<<32)
        # FIXME: this is a 64-bit int: "number of 100ns periods
        # since Jan 1,1601".  Should map this to Python time
        value = value / 10000000L # seconds
于 2009-11-30T05:52:12.943 回答
2

这个问题很老但仍然有用。我最近改进了 OleFileIO_PL,通过将日期自动转换为 Python 日期时间来解决这个问题。

请参阅此页面上的文档,尤其是有关 get_metadata 和 get_properties 的部分: https ://bitbucket.org/decalage/olefileio_pl

使用 get_metadata 时,标准属性流中的所有时间戳(例如 '\x05SummaryInformation')都将转换为 Python 日期时间。如果您需要改用 get_properties,请使用 convert_time 选项:

p = ole.getproperties('specialprops', convert_time=True)

菲利普。

于 2014-02-06T14:37:49.857 回答