设计 - 如何处理时间戳(存储)及计算时 ; Python
我正在尝试弄清楚,如何最好地存储和处理我的数据,因为我的应用程序需要处理来自不同来源和不同时间区域、格式等的大量数据。
比如,我应该把所有数据都存储为UTC时间吗?这意味着当我获取数据时,我需要确定当前的数据是哪个时区的,如果不是UTC,就要进行必要的转换。(顺便说一下,我在东部标准时间,EST)。
然后,在对数据进行计算时,我应该把数据提取出来(假设是UTC),转换成我的时区(EST),这样看起来更合理吗?还是说我应该保持数据为UTC,所有计算都在UTC下进行?
很多数据是时间序列的,并且会被绘制成图表,而图表会用EST显示。
这是一个Python项目,假设我有一个数据结构是:
"id1": {
"interval": 60, <-- seconds, subDict['interval']
"last": "2013-01-29 02:11:11.151996+00:00" <-- UTC, subDict['last']
},
我需要在这个数据上进行操作,判断当前时间(now())是否大于最后一次时间加上间隔(是否已经过去60秒)?所以在代码中:
lastTime = dateutil.parser.parse(subDict['last'])
utcNow = datetime.datetime.utcnow().replace(tzinfo=tz.tzutc())
if lastTime + datetime.timedelta(seconds=subDict['interval']) < utcNow:
print "Time elapsed, do something!"
这样说清楚了吗?我在所有地方都使用UTC,无论是存储还是计算……
另外,如果有人有关于如何在软件中处理时间戳的好文章链接,我很想看看。可能类似于《Joel On Software》关于应用程序中时间戳使用的内容?
4 个回答
我觉得最好的方法是把所有的时间戳数据都存储为UTC时间。你在读取数据时,立刻把它转换成UTC;在显示之前,再把UTC时间转换成你所在的本地时区。
你甚至可以让你的代码把所有时间戳打印两次,一次是本地时间,另一次是UTC时间……这要看你需要在屏幕上显示多少数据。
我非常喜欢RFC 3339这种时间戳格式。它对人和机器来说都很清晰明了。最棒的是几乎没有可选项,所以它的格式总是一样的:
2013-01-29T19:46:00.00-08:00
我更喜欢把时间戳转换成单一的浮点值来存储和计算,然后再转换回日期时间格式来显示。我不会把钱存成浮点数,但时间戳的值完全在浮点数的精度范围内!
使用时间浮点数可以让很多代码变得简单:
if time_now() >= last_time + interval:
print("interval has elapsed")
看起来你已经差不多是这样做的,所以我也没什么特别的改进建议。
我写了一些库函数,可以把时间戳解析成Python的时间浮点值,并把时间浮点值转换回时间戳字符串。也许这里面的东西对你有用:
http://home.blarg.net/~steveha/pyfeed.html
我建议你看看feed.date.rfc3339
。它是BSD许可证的,所以如果你喜欢,可以直接使用这段代码。
编辑:问题:这对时区有什么帮助?
回答:如果你存储的每个时间戳都是以UTC时间作为Python的时间浮点值(从纪元开始的秒数,带可选的小数部分),你就可以直接比较它们;用一个减去另一个来找出它们之间的间隔;等等。如果你使用RFC 3339的时间戳,那么每个时间戳字符串中都有时区信息,你的代码可以正确地把它转换成UTC时间。如果你在显示之前把浮点值转换成时间戳字符串,时区就会正确显示为本地时间。
而且,正如我所说的,看起来他已经差不多在这样做了,所以我觉得我也没什么特别的建议。
从你提到的情况来看,你似乎没有遇到什么实现上的问题,所以我建议你更关注设计方面,而不是代码和时间戳的格式。我曾参与过一个导航系统的网络支持设计,这个系统是一个分布式系统,运行在局域网中。这个系统的特点是会有很多来自不同来源的数据(有时这些数据会互相矛盾),所以解决可能的冲突并保持数据的完整性是相当棘手的。这些都是我根据那段经历的一些想法。
在分布式系统中,即使有很多计算机,给数据加时间戳通常也不是问题,只要你不需要比系统时间函数提供的更高的精度,或者比操作系统组件提供的更高的时间同步准确度。
在最简单的情况下,使用协调世界时(UTC)是相当合理的,对于大多数任务来说,这已经足够了。不过,从设计的开始就要理解在你的系统中使用时间戳的目的。时间值(无论是Unix时间还是格式化的UTC字符串)有时可能是相同的。如果你需要根据时间戳来解决数据冲突(也就是说,在多个来自不同来源的数据中,总是选择一个更新的(或更旧的)值),你需要明白,如果冲突解决不正确(通常意味着时间戳相同的情况下可能有多种解决方式),这对你的系统设计来说是否是一个致命的问题。可能的选项有:
如果99.99%的冲突在所有节点上都能以相同的方式解决,你就不需要在意剩下的0.01%,而且它们不会破坏数据的完整性。在这种情况下,你可以放心地继续使用类似UTC的时间戳。
如果你必须严格解决所有冲突,那么你需要设计自己的时间戳系统。时间戳可以包括时间(可能不是系统时间,而是某种更高精度的计时器)、序列号(以便即使时间精度不足也能生成唯一的时间戳)和节点标识符(以便系统中的不同节点能够生成完全唯一的时间戳)。
最后,你需要的可能不是基于时间的时间戳。你真的需要能够计算一对时间戳之间的时间差吗?仅仅允许对时间戳进行排序,而不连接到真实的时间点,难道不够吗?如果你不需要时间计算,只需要比较,那么基于顺序计数器的时间戳,而不是基于真实时间的时间戳,是一个不错的选择(详细信息请参见Lamport时间)。
如果你需要严格解决冲突,或者需要非常高的时间精度,你可能需要自己编写一个时间戳服务。
很多想法和线索可以参考A. Tanenbaum的书《分布式系统:原则与范例》。当我遇到类似问题时,这本书对我帮助很大,其中有一章专门讲时间戳的生成。
我觉得你已经在“正确的方向”上做事情了。用户可能会希望在他们的本地时区进行交互(输入和输出),但把日期存储为统一时间(UTC)格式是很正常的,这样可以避免混淆,并简化计算。因此,尽早将时间标准化为UTC,尽量晚一点再转换为本地时间。
关于Python和时区处理的一些基本信息可以在这里找到:
我现在的做法是把日期存储为后端存储中的unix时间戳tv_sec
值,然后在处理时转换为Python的datetime.datetime
对象。处理通常是在UTC时区的datetime
对象中进行,最后在输出之前转换为本地用户的时区。我发现使用像datetime.datetime
这样的丰富对象有助于调试。
处理时区确实很麻烦,你可能需要根据具体情况判断是否值得花精力去正确支持时区。
举个例子,假设你在计算每天使用的带宽。可能会出现一些问题:
- 在夏令时的边界上会发生什么?为了方便计算,你应该假设一天总是24小时,还是需要在每次日常计算时检查一下,看看在夏令时的边界上这一天可能会有更多或更少的小时?
- 在展示本地时间时,如果时间重复,这是否重要?例如,如果你有一个按小时显示的报告,但没有附加时区,用户会不会因为夏令时变化而缺少一个小时的数据,或者重复一个小时的数据而感到困惑?