Упростить код, касающийся времени и продолжительности, который в настоящее время использует дату и время? - PullRequest
0 голосов
/ 14 сентября 2018

Код ниже должен:

  • парсинг входной строки с некоторой продолжительностью
  • подтвердить, что продолжительность не пустая, не отрицательная, не более 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

Ответы [ 2 ]

0 голосов
/ 17 сентября 2018

Вот альтернативное решение, которое я использовал в итоге:

    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')
0 голосов
/ 14 сентября 2018

Хорошо, поэтому, если вам абсолютно необходимо использовать анализ продолжительности 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

Если вам не нужен анализ изодата (и я подозреваю, что вы этого не делаете), вы можете просто сделать

@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

ипередайте годы / месяцы / недели / дни как целые числа и сравните их, как раньше.

...