【问题标题】:Simplify code dealing with time and duration that currently uses datetime and isodate?简化处理当前使用 datetime 和 isodate 的时间和持续时间的代码?
【发布时间】:2019-02-19 08:07:57
【问题描述】:

下面的代码应该是:

  • 将输入字符串解析为某种持续时间
  • 验证持续时间不为空、不为负、不超过 10 年

输入字符串示例如下:

duration_string = "P10W"
duration_string = "P1Y"

这里是代码

    duration = isodate.parse_duration(duration_string)

    if isinstance(duration, datetime.timedelta):
        if not duration > datetime.timedelta(0):
            raise Exception('duration invalid')
        if duration > datetime.timedelta(3660):
            raise Exception('duration cannot be longer than 10 years')
    elif isinstance(duration, isodate.Duration):
        if not duration > 0:
            raise Exception('duration invalid')
        if duration > isodate.duration.Duration(0, 0, 0, years=10, months=0):
            log.debug("duration %s isodate %s" % (duration, isodate.duration.Duration(0, 0, 0, years=10, months=0)))
            raise Exception('duration cannot be longer than 10 years')

有没有比我制作的怪物更简单的方法?

除了需要简化之外,duration > isodate.duration.Duration(0, 0, 0, years=10, months=0) 这一行也不起作用。

我正在使用 Python 2.7

【问题讨论】:

  • 30 天比一个月短还是长(想想 2 月和 8 月)?这些值之间的比较没有意义。如果你想比较时间长度,你必须先把它们变成一致的单位。
  • @FHTMitchell:这是一个很好的观点。精确性如果不是超级重要的话。我可以用 365D=12M=52W=1Y
  • OK,除非52 weeks == 365 days 然后1 week == 7.019230769230769 days。这真的是你想要的吗?你能明白为什么没有标准功能可以做到这一点吗?
  • @FHTMitchell:我想不出另一种方法来将持续时间限制在合理的范围内。你能想出更好的方法吗?
  • 仅在几秒钟或几天内要求输入?这个持续时间指的是什么?它是从今天开始的持续时间(因为我们将从现在开始持续 6 个月?)。有什么日期可以与此相关联吗?

标签: python python-2.7 time duration


【解决方案1】:

好的,所以如果您绝对必须使用 isodate 持续时间解析,请保留 isodate 库。但是我要提一下,isodate 库是不完整的,有许多糟糕的设计决策,而且通常都很糟糕。

但是如果你必须使用他们的解析工具,这可能是一个好方法。

import isodate
import functools

@functools.total_ordering  # if we implement < ==, will implement <=, >, >=
class Duration(isodate.Duration):
    # inherit from isodate.Duration -- gives us ==

    # constants 
    seconds_in_day = 60**2 * 24
    approx_days_in_month = 30
    approx_days_in_year = 365

    def approx_total_seconds(self):
        """approx total seconds in duration"""
        # self.months and self.years are stored as `Decimal`s for some reason...
        return self.tdelta.total_seconds() \
               + float(self.months) * self.approx_days_in_month *  self.seconds_in_day \
               + float(self.years) * self.approx_days_in_year * self.seconds_in_day

    def __lt__(self, other):
        """defines self < other"""
        if not isinstance(other, Duration):
            return NotImplemented
        return self.approx_total_seconds() < other.approx_total_seconds()

    @classmethod
    def parse_duration(cls, datestring):
        """a version of isodate.parse_duration that returns out class"""

        iso_dur = isodate.parse_duration(datestring)

        # iso_date.parse_duration can return either a Duration or a timedelta...
        if isinstance(iso_dur, isodate.Duration):
            return cls(seconds=iso_dur.tdelta.total_seconds(),
                       months=iso_dur.months, years=iso_dur.years)
        else:
            return cls(seconds=iso_dur.total_seconds())


ten_weeks = Duration.parse_duration('P10W')
one_year = Duration.parse_duration('P1Y')

print(ten_weeks.approx_total_seconds())
print(one_year.approx_total_seconds())

print(ten_weeks < one_year)
print(ten_weeks > one_year)

输出

6048000.0
31536000.0
True
False

如果您不需要 isodate 解析(我怀疑您不需要),您可以这样做

@functools.TotalOrdering
class ApproxTimeDelta:

    approx_days_in_week = 7
    approx_days_in_month = 30
    approx_days_in_year = 365

    def __init__(self, days, weeks, months, years):
        self.days = days + \
                    weeks * self.approx_days_in_week + \
                    months * self.approx_days_in_month + \
                    years * self.approx_days_in_year

    def __eq__(self, other):
        return self.days == other.days

    def __lt__(self, other):
        return self.days < other.days

并将年/月/周/日作为整数传递并像以前一样进行比较。

【讨论】:

    【解决方案2】:

    这是我最终使用的另一种解决方案:

        if isinstance(duration, datetime.timedelta):
            if not duration > 0:
                raise Exception('duration invalid')
            if duration > 3650:
                raise Exception('maximum duration is 3650 days')
        elif isinstance(duration, isodate.Duration):
            if duration.years > 10:
                raise Exception('maximum duration is 10 years')
            if duration.months > 120:
                raise Exception('maximum duration is 120 months')
    

    【讨论】:

      猜你喜欢
      • 2022-01-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-10-18
      • 2011-10-15
      相关资源
      最近更新 更多