Python OLE2 日期格式转换
我写了一个Python脚本,可以从Word文档中的OLE流提取数据,但我在把OLE2格式的时间戳转换成更容易理解的日期时遇到了麻烦 :(
我提取出的时间戳是12760233021,但我怎么也无法把它转换成像2007年3月12日这样的日期。
任何帮助都非常感谢。
编辑: 好的,我在我的一个Word文档上运行了这个脚本,这个文档的创建时间是2009年10月31日,10:05:00。在OLE DocumentSummaryInformation流中的创建日期是12901417500。
另一个例子是一个在2009年10月27日,15:33:00创建的Word文档,它在OLE DocumentSummaryInformation流中的创建日期是12901091580。
关于这些OLE流属性的MSDN文档可以在这里找到:http://msdn.microsoft.com/en-us/library/aa380376%28VS.85%29.aspx
下面是提取这些流的函数:
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
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)
Philippe.
(0) 请你解释一下“像2007年3月12日或类似的”是什么意思:你是说你希望这个11位的整数能转换成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个字节?还是从64位字段转换过来的某个整数的字符串?是怎么转换的?除了描述之外,请展示你的转换代码。不要凭记忆重新输入代码;请使用复制/粘贴。
请通过编辑你的问题提供所需的信息,而不是作为评论。
在等待信息时的更新:
在OLE复合文档头中的文件创建和修改时间戳似乎是64位小端整数,表示自1601年1月1日00:00:00以来的秒数乘以10的7次方。
在OLE2数据中使用的日期类型似乎是64位小端IEEE 754浮点数,表示自1899年12月30日00:00:00以来的天数及其小数部分。是的,日期是30号,而不是31号。
在检查了提供的两个例子后的更新:
观察到的两个时间戳之间的差异(将会是你的本地时间)是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年1月1日00:00:00Z以来的秒数,而你的时区比UTC晚11小时。
这两个魔法数字无法用32位表示……看起来要么(a)它以64位存储,自1601年以来的秒数(浪费了大约29位!),要么(b)它按预期以(自1601年以来的100纳秒单位数量)存储,但在你看到之前被除以10的7次方。
你提供的文档参考仅仅说这是一个VF_FILETIME (UTC)
类型。通过谷歌搜索,我找到了一些关于调用Windows函数来处理时间戳的MS线索,但没有找到定义。不过有两个第三方的说明(来自perlmonks和Apache POI项目)说的差不多:“这看起来像是Windows的VT_FILETIME
数据类型,它是一个64位无符号整数,表示自1601年1月1日以来经过的100纳秒的数量。”
来自现场的更新:
看起来你正在使用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