将timedelta转换为时间或整数并存储在GAE(Python)数据存储中

4 投票
4 回答
7579 浏览
提问于 2025-04-15 20:13

看起来这个问题在其他讨论中已经提到过一些,但我还是有点困惑,不太清楚该怎么做。我的经验不足也没能帮上什么忙。

我有两个时间属性 - 开始时间(StartTime)和结束时间(EndTime)。我想通过把结束时间减去开始时间来得到持续时间(Duration)。根据我之前的问题(感谢所有回答的人!),这个操作似乎会产生一个时间差(timedelta)。

在GAE的数据存储中,似乎没有简单的方法可以直接存储时间差,这意味着我需要把它转换成毫秒的整数、秒的浮点数或者时间格式。

我之后还需要进行其他计算,比如算平均持续时间。根据这一点,现在我觉得用整数来表示最合适。

请问有什么好的方法可以做到这一点,或者有没有可以让我练习的教程?

谢谢!

4 个回答

0

最后这个方法成功了:

delta = StartTime - EndTime

event_record.Duration = int((delta.microseconds)/1000)

其实就是需要从时间差中提取微秒,然后把它转换成毫秒。

1

如果你打算把它存储为 datetime(我觉得这是个好主意),我建议你扩展一下 DateTimeProperty,这样你就能免费获得各种解析和验证功能。

另外,把 timedelta 存储为一个距离参考时间点的 datetime 可能比这里提到的其他方法要简单得多,这样差值就代表了时间差。这得益于 datetime 模块提供的运算符重载,让这变得非常容易。

from datetime import datetime, timedelta
from google.appengine.ext import db

class TimeDeltaProperty(db.DateTimeProperty):
    # Use a reference datetime half way between the min and max possible
    # datetimes, so that we can support both +ve and -ve timedeltas
    ref_datetime = (datetime.max - datetime.min) / 2 + datetime.min

    def get_value_for_datastore(self, model_instance):
        # Get the timedelta instance assigned to this property
        td = super(TimeDeltaProperty, self).get_value_for_datastore(model_instance)
        if td is not None:
            # datetime + timedelta = datetime
            return self.ref_datetime + td

    def make_value_from_datastore(self, dt):
        if dt is not None:
            # datetime - datetime = timedelta
            return dt - self.ref_datetime

如果你对 NDB API 感兴趣,这里有一个等效的实现:

from datetime import datetime, timedelta
from google.appengine.ext import ndb

class TimeDeltaProperty(ndb.DateTimeProperty):
    # Use a reference datetime half way between the min and max possible
    # datetimes, so that we can support both +ve and -ve timedeltas
    ref_datetime = (datetime.max - datetime.min) / 2 + datetime.min

    def _validate(self, value):
        if not isinstance(value, timedelta):
            raise TypeError('expected a datetime.timedelta, got %r' % value)

    def _to_base_type(self, value):
        # datetime + timedelta = datetime
        return self.ref_datetime + td

    def _from_base_type(self, value):
        # datetime - datetime = timedelta
        return dt - self.ref_datetime

准确性

在 Python 中,timedelta 可以处理大约 +/-270 万年的时间差。然而,datetime 只覆盖大约 10,000 年的范围。要在 datetime 中存储更大的时间差,你需要做一些调整,并牺牲一些准确性。

上面的方法将时间差限制在这个范围的一半,大约 +/-5000 年,因为选择了参考时间点。

如果你知道你的时间差总是正值,可以使用 ref_datetime = datetime.min(或者如果你知道它总是负值,可以使用 ref_datetime = datetime.max)来获取大约 10,000 年的完整范围。

12

为了让这个过程尽可能简单,有两个步骤:将时间差(timedelta)转换成整数或浮点数,然后把它存储到数据存储中。首先,我们要把时间差转换成微秒时间(microtime):

def timedelta_to_microtime(td):
  return td.microseconds + (td.seconds + td.days * 86400) * 1000000

不过,你不需要自己去做这个转换——你可以定义一个自定义的数据存储属性,这样就可以直接把时间差存储到你的模型里:

class TimeDeltaProperty(db.Property):
  def get_value_for_datastore(self, model_instance):
    value = self.__get__(model_instance, model_instance.__class__)
    if value is not None:
      return timedelta_to_microtime(value)

  def make_value_from_datastore(self, value):
    if value is not None:
      return datetime.timedelta(microseconds=value)

现在你可以像使用其他属性一样使用这个属性了:

class MyModel(db.Model):
  td = TimeDeltaProperty(required=True)

entity = MyModel(td=datetime.datetime.now()-some_datetime)
key = entity.put()

entity = db.get(key)
print entity.td

撰写回答