def _cycle_end_date(self, reference_date=None, ignore_trial=None, granulate=None): ignore_trial_default = False granulate_default = False ignore_trial = ignore_trial or ignore_trial_default granulate = granulate or granulate_default if reference_date is None: reference_date = timezone.now().date() real_cycle_start_date = self._cycle_start_date(reference_date, ignore_trial, granulate) # we need a current start date in order to compute a current end date if not real_cycle_start_date: return None # during trial and trial cycle is not separated into intervals if self.on_trial(reference_date) and not ( self.separate_cycles_during_trial or granulate): return min(self.trial_end, (self.ended_at or datetime.max.date())) if self.plan.interval == self.plan.INTERVALS.YEAR: relative_delta = {'years': self.plan.interval_count} elif self.plan.interval == self.plan.INTERVALS.MONTH: relative_delta = {'months': self.plan.interval_count} elif self.plan.interval == self.plan.INTERVALS.WEEK: relative_delta = {'weeks': self.plan.interval_count} else: # plan.INTERVALS.DAY relative_delta = {'days': self.plan.interval_count} maximum_cycle_end_date = real_cycle_start_date + relativedelta( **relative_delta) - ONE_DAY # We know that the cycle end_date is the day before the next cycle start_date, # therefore we check if the cycle start_date for our maximum cycle end_date is the same # as the initial cycle start_date. while True: reference_cycle_start_date = self._cycle_start_date( maximum_cycle_end_date, ignore_trial, granulate) # it means the cycle end_date we got is the right one if reference_cycle_start_date == real_cycle_start_date: return min(maximum_cycle_end_date, (self.ended_at or datetime.max.date())) elif reference_cycle_start_date < real_cycle_start_date: # This should never happen in normal conditions, but it may stop infinite looping return None maximum_cycle_end_date = reference_cycle_start_date - ONE_DAY
def _cycle_end_date(self, reference_date=None, ignore_trial=None, granulate=None): ignore_trial_default = False granulate_default = False ignore_trial = ignore_trial or ignore_trial_default granulate = granulate or granulate_default if reference_date is None: reference_date = timezone.now().date() real_cycle_start_date = self._cycle_start_date(reference_date, ignore_trial, granulate) # we need a current start date in order to compute a current end date if not real_cycle_start_date: return None # during trial and trial cycle is not separated into intervals if self.on_trial(reference_date) and not (self.separate_cycles_during_trial or granulate): return min(self.trial_end, (self.ended_at or datetime.max.date())) if self.plan.interval == self.plan.INTERVALS.YEAR: relative_delta = {'years': self.plan.interval_count} elif self.plan.interval == self.plan.INTERVALS.MONTH: relative_delta = {'months': self.plan.interval_count} elif self.plan.interval == self.plan.INTERVALS.WEEK: relative_delta = {'weeks': self.plan.interval_count} else: # plan.INTERVALS.DAY relative_delta = {'days': self.plan.interval_count} maximum_cycle_end_date = real_cycle_start_date + relativedelta(**relative_delta) - ONE_DAY # We know that the cycle end_date is the day before the next cycle start_date, # therefore we check if the cycle start_date for our maximum cycle end_date is the same # as the initial cycle start_date. while True: reference_cycle_start_date = self._cycle_start_date(maximum_cycle_end_date, ignore_trial, granulate) # it means the cycle end_date we got is the right one if reference_cycle_start_date == real_cycle_start_date: return min(maximum_cycle_end_date, (self.ended_at or datetime.max.date())) elif reference_cycle_start_date < real_cycle_start_date: # This should never happen in normal conditions, but it may stop infinite looping return None maximum_cycle_end_date = reference_cycle_start_date - ONE_DAY
def test_relative_delta(self): # TODO: this from silver.utils.dates import relativedelta, ONE_DAY from dateutil.rrule import rrule, MONTHLY from datetime import datetime, timedelta from calendar import monthrange # Billing by the last day of each month, with a start date that # includes all possible months. (Same basic result) # Set up the timescale. cycle_start_dates = dt.date(2018, 1, 29) # These are each month on the same day. # [datetime.datetime(), ...] intervals = list( rrule(MONTHLY, count=12, bymonthday=-1, dtstart=cycle_start_dates)) relative_delta = relativedelta(months=1) for interval in intervals: end_date = interval + relative_delta - ONE_DAY