def _build_occurrence(self): """ Figure out which occurrence values are set and add them to the occurrence. Returns a string. """ rr = self.__rrule bymonth = rr._bymonth # Tuple. Which month a yearly event recurs in. byweekno = rr._byweekno # Tuple. Which week number a yearly event recurs in. byyearday = rr._byyearday # Tuple. Which day of the year a yearly event recurs in. byweekday = rr._byweekday # Tuple. Which weekday an event recurs in. bynweekday = rr._bynweekday # Tuple. By the nth weekday, e.g., FR(3) is the third Friday of the period. Only used if freq is < MONTHLY byeaster = rr._byeaster # Tuple. bymonthday = rr._bymonthday # Tuple. Relative day of the month bynmonthday = rr._bynmonthday # Tuple. Relative day of the month, if negative (counting from the end of the month) bysetpos = rr._bysetpos # Tuple. For sets of seconds/minutes/hours/days/weeks/months/years, specifies which position in the list to pay attention to. byhour = rr._byhour # Tuple. The hour of the occurrence. byminute = rr._byminute # Tuple. The minutes of the occurrence. bysecond = rr._bysecond # Tuple. The second of the occurrence. o = "" if bymonth: for month in bymonth: o = " ".join([o, MONTH_MAP[month]]) for i, monthday in enumerate(bymonthday): o = "".join([", " if i>0 else "", o, " ", str(monthday)]) if bymonthday: if not bymonth: s = "" for i, monthday in enumerate(bymonthday): s = "".join([" and " if i>0 else "", "the %s day of the month", human_rrule.int_as_ordinal(bymonthday[0])]) o = "".join([o, s]) # if bynmonthday: s = "" for i, nmonthday in enumerate(bynmonthday): s = "".join([" and " if i>0 else "", "the %s to last day of the month" % human_rrule.int_as_ordinal(bynmonthday)]) o = "".join([o, s]) # if byweekday: s = "" for i, p_weekday in enumerate(byweekday): s = "".join([", " if i>0 else "", WEEKDAY_MAP[unicode(weekday(p_weekday))]]) o = "".join([o, s]) if byweekno: o = " ".join([o, "the %s week" % human_rrule.int_as_ordinal(byweekno)]) if byyearday: o = " ".join([o, "the %s year" % human_rrule.int_as_ordinal(byyearday)]) if bynweekday: for rule_pair in bynweekday: # Get the ordinal. TODO handle multiple ordinals ord_text = [] ord_text.append(human_rrule.int_as_ordinal(rule_pair[1])) # Get the weekday name p_weekday = weekday(rule_pair[0]) name = [WEEKDAY_MAP[unicode(p_weekday)]] ord_text.extend(name) o = " ".join([o, ord_text[0], ord_text[1]]) return o
def dict_to_object(self, d): if '__type__' not in d: return d objtype = d.pop('__type__') if objtype == 'datetime': return datetime(tzinfo=timezone.utc, **d) if objtype == 'interval': return schedule(run_every=d['every'], relative=d['relative']) if objtype == 'crontab': return crontab(**d) if objtype == 'weekday': return weekday(**d) if objtype == 'rrule': # Decode timestamp values into datetime objects for key, tz_key in [('dtstart', 'dtstart_tz'), ('until', 'until_tz')]: timestamp = d.get(key) tz_minutes = d.pop(tz_key, 0) if timestamp is not None: d[key] = from_timestamp(timestamp, tz_minutes) return rrule(**d) d['__type__'] = objtype return d
def to_python(self, value): """ Converts value assigned or read from database into correct format: set of day numbers. """ if value is None or value == "": return None if isinstance(value, basestring): value = int(value) # and then process as usual if isinstance(value, int): # value is a mask of days if not 0 <= value <= 0b01111111: raise ValueError(repr(value)) days = set() for i in xrange(7): if value & (1 << i): days.add(weekday(i)) else: days = set(value) if any(not isinstance(x, weekday) for x in days): raise ValueError("Days should be weekday objects: " + repr(days)) if len(set(day.weekday for day in days)) != len(days): raise ValueError("Duplicate days: " + repr(days)) return days
def test_weekday(self): d = self.weekday() result = self.loads(json.dumps(d)) d.pop('__type__') self.assertEqual(result, dateutil_rrule.weekday(5))
def handle_byweekday(self): """ a fork from dateutil.rrule source code """ if not self.byweekday: return False weekday_dict = { "MO": 0, "TU": 1, "WE": 2, "TH": 3, "FR": 4, "SA": 5, "SU": 6 } weekdays_constants = [] for wday in self.byweekday: i = 0 for i in range(len(wday)): if wday[i] not in '+-0123456789': break n = wday[:i] or None w = wday[i:] if n: n = int(n) weekdays_constants.append(weekday(weekday_dict[w], n)) return weekdays_constants
def calc_recurrence_pattern(start_date: dt.datetime, end_date: dt.datetime): """ 予定の繰り返しパターン文字列を生成する Args: start_date (datetime.datetime): パターンの開始日 end_date (datetime.datetime): パターンの終了日 Returns; str : RFC5545に準拠した繰り返しパターンの文字列 Examples: calc_recurrence_pattern(date, "mon") """ pattern = str( rr.rrule( freq=rr.WEEKLY, wkst=rr.SU, byweekday=rr.weekday(wkday=start_date.weekday()))).split("\n")[1] pattern = "".join([ pattern, ";UNTIL=", end_date.astimezone(dt.timezone.utc).strftime("%Y%m%dT%H%M%SZ"), ]) return pattern
def testPassThruWkst(self): newWkst = (calendar.firstweekday() + 2) % 7 self.failUnlessParseMatches( "FREQ=WEEKLY;INTERVAL=2;BYDAY=SU,FR;WKST=%s" % ( rrule.weekday(newWkst)), True, {'freq':rrule.WEEKLY, 'interval':2, 'byweekday':(rrule.SU, rrule.FR)}, {'wkst': newWkst} # Test passthru of wkst )
def _next_due_date(self) -> datetime.date: """ Calculates the next time item will appear in master list. """ due_date, *_ = rrule( freq=self.todo_list.frequency, interval=self.todo_list.interval, count=1, dtstart=self.due_date + relativedelta(days=1), byweekday=(*[weekday(i) for i in self.todo_list.weekdays], ), ) return due_date.date()
def _get_start_of_period(self, dt): if self.rrule_type == 'weekly': lang = self.env['res.lang']._lang_get(self.env.user.lang) week_start = int(lang.week_start) # lang.week_start ranges from '1' to '7' week_start = rrule.weekday(week_start - 1) # expects an int from 0 to 6 start = dt + relativedelta(weekday=week_start(-1)) elif self.rrule_type == 'monthly': start = dt + relativedelta(day=1) else: start = dt return start
def get_rrule(self): if self._rrule is None: # if it isn't cached, create it params = model_to_dict(self, fields=['dtstart', 'freq','until','count','interval','wkst','byweekday']) params['wkst'] = rrule.weekday(params['wkst']) if len(self.byweekday) == 0: params.pop('byweekday') if not params['count'] and not params['until']: params['count'] = 1 self._rrule = rrule.rrule(**params) return self._rrule
def week_day_every_month_generator(start, interval, local_date): start_week = (local_date.day - 1) / 7 + 1 start_wday = start.weekday() # Use "Last" for 5-th week wday = weekday(start_wday, -1 if start_week == 5 else start_week) rule = rrule(freq=MONTHLY, dtstart=start, count=MAX_YEARS * 12, interval=interval, byweekday=wday) it = iter(rule) while True: yield next(it).date()
def testPassThruWkst(self): newWkst = (calendar.firstweekday() + 2) % 7 self.failUnlessParseMatches( "FREQ=WEEKLY;INTERVAL=2;BYDAY=SU,FR;WKST=%s" % (rrule.weekday(newWkst)), True, { 'freq': rrule.WEEKLY, 'interval': 2, 'byweekday': (rrule.SU, rrule.FR) }, {'wkst': newWkst} # Test passthru of wkst )
def _get_week_days(self): """ :return: tuple of rrule weekdays for this recurrence. """ return tuple( rrule.weekday(weekday_index) for weekday_index, weekday in { rrule.MO.weekday: self.mon, rrule.TU.weekday: self.tue, rrule.WE.weekday: self.wed, rrule.TH.weekday: self.thu, rrule.FR.weekday: self.fri, rrule.SA.weekday: self.sat, rrule.SU.weekday: self.sun, }.items() if weekday)
def get_occurrences(self, start, end): """ Returns start datetimes of occurrences of the event that overlap with [start, end] Expects start < end :type start: datetime :type end: datetime :return: """ event_start = datetime.combine( self.start_date, self.start_time if self.start_time else time.min) event_end = datetime.combine( self.end_date, self.end_time if self.end_time else time.max) event_span = event_end - event_start recurrence = self.recurrence_rule if recurrence: until = min( end, datetime.combine( recurrence.end_date if recurrence.end_date else date.max, time.max)) if recurrence.frequency in (frequency.DAILY, frequency.YEARLY): occurrences = rrule(recurrence.frequency, dtstart=event_start, interval=recurrence.interval, until=until) elif recurrence.frequency == frequency.WEEKLY: occurrences = rrule(recurrence.frequency, dtstart=event_start, interval=recurrence.interval, byweekday=recurrence.weekdays_for_weekly, until=until) else: occurrences = rrule(recurrence.frequency, dtstart=event_start, interval=recurrence.interval, byweekday=weekday( recurrence.weekday_for_monthly, recurrence.week_for_monthly), until=until) return [ x for x in occurrences if start <= x <= end or start <= x + event_span <= end ] return [ event_start ] if start <= event_start <= end or start <= event_end <= end else []
def _rrule_parse(self, rule_str, date_start): # LUL TODO clean this mess data = {} day_list = ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun'] if 'Z' in rule_str and date_start and not date_start.tzinfo: date_start = pytz.utc.localize(date_start) rule = rrule.rrulestr(rule_str, dtstart=date_start) data['rrule_type'] = freq_to_select(rule._freq) data['count'] = rule._count data['interval'] = rule._interval data['until'] = rule._until # Repeat weekly if rule._byweekday: for weekday in day_list: data[weekday] = False # reset for weekday_index in rule._byweekday: weekday = rrule.weekday(weekday_index) data[weekday_to_field(weekday.weekday)] = True data['rrule_type'] = 'weekly' # Repeat monthly by nweekday ((weekday, weeknumber), ) if rule._bynweekday: data['weekday'] = day_list[list(rule._bynweekday)[0][0]].upper() data['byday'] = str(list(rule._bynweekday)[0][1]) data['month_by'] = 'day' data['rrule_type'] = 'monthly' if rule._bymonthday: data['day'] = list(rule._bymonthday)[0] data['month_by'] = 'date' data['rrule_type'] = 'monthly' # Repeat yearly but for odoo it's monthly, take same information as monthly but interval is 12 times if rule._bymonth: data['interval'] *= 12 if data.get('until'): data['end_type'] = 'end_date' elif data.get('count'): data['end_type'] = 'count' else: data['end_type'] = 'forever' return data
def parse_rrule(rule): """ Parse the given C{rrule}, returning a 3-element C{tuple}: element 0: C{true_rep}: A C{boolean}: That indicates if this rule can be represented exactly in the UI. In practice, this is used to pop up a confirmation dialog before editing. element 1: C{canonical}: A C{dict} of keywords you can use to initialize a C{dateutil.rrule.rrule}. element 2: C{passthru}: A C{dict} of keywords that the UI ignores, but won't result in data loss when you edit. Examples include C{'until'}, C{'byhour'}. C{EditRecurrence} includes these values in the returned C{dict}. """ true_rep = True canonical = {} passthru = {} if rule._count: # We don't support COUNT rules true_rep = False wkst = rule._wkst if wkst != calendar.firstweekday( ): # sic (rrule inserts this automatically) passthru['wkst'] = wkst for attr in "hour", "minute", "second": # rrule creates these automatically, so bypass the automatically # set values. val = getattr(rule, '_by' + attr, None) # default value is a 1-element tuple with value # matching _dtstart. if val and val != (getattr(rule._dtstart, attr), ): passthru['by' + attr] = val for attr in 'until', : val = getattr(rule, '_' + attr, None) if val is not None: passthru[attr] = val canonical.update(interval=rule._interval) # _byweekday: tuple of ints # _bynweekday: tuple of (day, n) ints # We combine these into a tuple of rrule.weekday objects # Only support of BYSETPOS is in the "alternate byweekday rule "below, # and we only support a single value here. bysetposException = (len(rule._byweekday or ()) == 1 == len(rule._bysetpos or ())) byweekday = [] if rule._bynweekday: if len(rule._bynweekday) > 1: true_rep = False elif rule._bynweekday[0][1] not in (1, 2, 3, 4, -1): true_rep = False byweekday.extend(rrule.weekday(*t) for t in rule._bynweekday) if bysetposException: byweekday.append(rrule.weekday(rule._byweekday[0], rule._bysetpos[0])) elif rule._byweekday and rule._byweekday != (rule._dtstart.weekday(), ): byweekday.extend(rrule.weekday(day) for day in rule._byweekday) if byweekday: canonical.update(byweekday=tuple(byweekday)) if (rule._bysetpos and not bysetposException): true_rep = False if rule._bymonth and rule._bymonth != (rule._dtstart.month, ): canonical.update(bymonth=rule._bymonth) # rule._bymonthday == days of month counted from start # rule._bynmonthday == days of month counted from end (unsupported) bymonthday = rule._bymonthday + rule._bynmonthday if bymonthday and bymonthday != (rule._dtstart.day, ): canonical.update(bymonthday=tuple(sorted(bymonthday))) if rule._bynmonthday: # We only support -1 for _bynmonthday if not all(day == -1 for day in rule._bynmonthday): true_rep = False # rule._byweekno (unsupported) if rule._byweekno: true_rep = False canonical.update(byweekno=rule._byweekno) if rule._byyearday: true_rep = False canonical.update(byyearday=rule._byyearday) canonical.update(freq=rule._freq) customKeys = set(canonical.keys()) - set(['freq', 'interval']) if rule._freq == rrule.YEARLY: if customKeys - set(['bymonth', 'byweekday']): true_rep = False elif rule._freq == rrule.MONTHLY: # For monthly, we support exactly one of bymonthday and # byweekday if len(customKeys) > 1: true_rep = False elif customKeys - set(['bymonthday', 'byweekday']): true_rep = False # Note, not supporting multiple _bynweekday is handled earlier elif rule._freq == rrule.WEEKLY: if customKeys - set(['byweekday']): true_rep = False elif rule._freq == rrule.DAILY: if customKeys: true_rep = False else: # We don't support HOURLY/MINUTELY/SECONDLY true_rep = False return true_rep, canonical, passthru
def _get_lang_week_start(self): lang = self.env['res.lang']._lang_get(self.env.user.lang) week_start = int( lang.week_start) # lang.week_start ranges from '1' to '7' return rrule.weekday(week_start - 1) # rrule expects an int from 0 to 6
def _parseBooking(self, booking): start = self._date_parse(booking['startdate']) # if start.date() != date(2017,12, 11): # continue # print(json.dumps(booking, indent=2)) start_date = datetime.datetime.combine(start, datetime.time(0, 0)) end = self._date_parse(booking['enddate']) duration = end - start rules = rruleset() repeat_id = int(booking['repeat_id']) if repeat_id != 0 and repeat_id != 999: repeat_until = self._date_parse(booking['repeat_until']) repeat_freq = int(booking['repeat_frequence']) # print("Type: %s, Freq: %s, Start: %s, Until: %s" % (repeat_id, repeat_freq, start, repeat_until)) if repeat_id == 1: # daily rules.rrule(rrule(DAILY, dtstart=start_date, until=repeat_until, interval=repeat_freq)) if repeat_id == 7: # weekly # print("Start: %s\nUntil: %s\nInterval: %s" % (start, repeat_until, repeat_freq)) rules.rrule(rrule(WEEKLY, dtstart=start_date, interval=repeat_freq, until=repeat_until)) elif repeat_id == 31: # monthly by datetime rules.rrule(rrule(MONTHLY, dtstart=start_date, until=repeat_until, interval=repeat_freq)) elif repeat_id == 32: # monthly by weekday repeat_option_id = int(booking['repeat_option_id']) if booking['repeat_option_id'] else 0 if repeat_option_id == 6: raise NotImplementedError( "Error: repeat_option_id == 6 not implemented! Booking:\n%s" % json.dumps(booking, indent=2)) # nthweekOfMonth = (start.day - 1) // 7 + 1 nthweekOfMonth = repeat_option_id # print(nthweekOfMonth) rule = rrule(MONTHLY, dtstart=start_date, until=repeat_until, interval=repeat_freq, byweekday=weekday(start.weekday())(+nthweekOfMonth)) # print(rule) rules.rrule(rule) elif repeat_id == 365: rules.rrule(rrule(YEARLY, dtstart=start_date, until=repeat_until, interval=repeat_freq)) elif repeat_id in [0, 999]: # print("Simple: %s" % start_date) rules.rdate(start_date) else: raise NotImplementedError( "Error: Repeat Frequency is not implemented! Booking:\n%s" % json.dumps(booking, indent=2)) print("Error: Repeat Frequency %s:\n%s" % (repeat_freq, json.dumps(booking, indent=2))) if 'additions' in booking and booking['additions']: adds = booking['additions'] for a in adds: addition = adds[a] add_date = datetime.datetime.combine(self._date_parse(addition['add_date']), datetime.time(0, 0)) # print("Addition: %s" % add_date) rules.rdate(add_date) if 'exceptions' in booking and booking['exceptions']: # print(booking) exceptions = booking['exceptions'] for exc in exceptions: exception = exceptions[exc] exc_start = self._date_parse(exception['except_date_start']) exc_end = self._date_parse(exception['except_date_end']) # print("Exception: %s" % exc_start) if exc_start != exc_end: print("Exception has different start and end: %s" % exception) rules.exdate(datetime.datetime.combine(exc_start, datetime.time(0, 0))) return rules, start, duration
def parse_rrule(rule): """ Parse the given C{rrule}, returning a 3-element C{tuple}: element 0: C{true_rep}: A C{boolean}: That indicates if this rule can be represented exactly in the UI. In practice, this is used to pop up a confirmation dialog before editing. element 1: C{canonical}: A C{dict} of keywords you can use to initialize a C{dateutil.rrule.rrule}. element 2: C{passthru}: A C{dict} of keywords that the UI ignores, but won't result in data loss when you edit. Examples include C{'until'}, C{'byhour'}. C{EditRecurrence} includes these values in the returned C{dict}. """ true_rep = True canonical = {} passthru = {} if rule._count: # We don't support COUNT rules true_rep = False wkst = rule._wkst if wkst != calendar.firstweekday(): # sic (rrule inserts this automatically) passthru['wkst'] = wkst for attr in "hour", "minute", "second": # rrule creates these automatically, so bypass the automatically # set values. val = getattr(rule, '_by' + attr, None) # default value is a 1-element tuple with value # matching _dtstart. if val and val != (getattr(rule._dtstart, attr),): passthru['by' + attr] = val for attr in 'until',: val = getattr(rule, '_' + attr, None) if val is not None: passthru[attr] = val canonical.update(interval=rule._interval) # _byweekday: tuple of ints # _bynweekday: tuple of (day, n) ints # We combine these into a tuple of rrule.weekday objects # Only support of BYSETPOS is in the "alternate byweekday rule "below, # and we only support a single value here. bysetposException = ( len(rule._byweekday or ()) == 1 == len(rule._bysetpos or ())) byweekday = [] if rule._bynweekday: if len(rule._bynweekday) > 1: true_rep = False elif rule._bynweekday[0][1] not in (1, 2, 3, 4, -1): true_rep = False byweekday.extend(rrule.weekday(*t) for t in rule._bynweekday) if bysetposException: byweekday.append(rrule.weekday(rule._byweekday[0], rule._bysetpos[0])) elif rule._byweekday and rule._byweekday != (rule._dtstart.weekday(),): byweekday.extend(rrule.weekday(day) for day in rule._byweekday) if byweekday: canonical.update(byweekday=tuple(byweekday)) if (rule._bysetpos and not bysetposException): true_rep = False if rule._bymonth and rule._bymonth != (rule._dtstart.month,): canonical.update(bymonth=rule._bymonth) # rule._bymonthday == days of month counted from start # rule._bynmonthday == days of month counted from end (unsupported) bymonthday = rule._bymonthday + rule._bynmonthday if bymonthday and bymonthday != (rule._dtstart.day,): canonical.update(bymonthday=tuple(sorted(bymonthday))) if rule._bynmonthday: # We only support -1 for _bynmonthday if not all(day == -1 for day in rule._bynmonthday): true_rep = False # rule._byweekno (unsupported) if rule._byweekno: true_rep = False canonical.update(byweekno=rule._byweekno) if rule._byyearday: true_rep = False canonical.update(byyearday=rule._byyearday) canonical.update(freq=rule._freq) customKeys = set(canonical.keys()) - set(['freq', 'interval']) if rule._freq == rrule.YEARLY: if customKeys - set(['bymonth', 'byweekday']): true_rep = False elif rule._freq == rrule.MONTHLY: # For monthly, we support exactly one of bymonthday and # byweekday if len(customKeys) > 1: true_rep = False elif customKeys - set(['bymonthday', 'byweekday']): true_rep = False # Note, not supporting multiple _bynweekday is handled earlier elif rule._freq == rrule.WEEKLY: if customKeys - set(['byweekday']): true_rep = False elif rule._freq == rrule.DAILY: if customKeys: true_rep = False else: # We don't support HOURLY/MINUTELY/SECONDLY true_rep = False return true_rep, canonical, passthru
def do_t_recurs(self, line): """Make a task recurs t_recurs <id> yearly <dd/mm> <HH:MM> t_recurs <id> monthly <dd> <HH:MM> t_recurs <id> monthly <first/second/third/last> <mo, tu, we, th, fr, sa, su> <hh:mm> t_recurs <id> quarterly <dd> <HH:MM> t_recurs <id> quarterly <first/second/third/last> <mo, tu, we, th, fr, sa, su> <hh:mm> t_recurs <id> weekly <mo, tu, we, th, fr, sa, su> <hh:mm> t_recurs <id> daily <HH:MM> t_recurs <id> none (remove recurrence)""" tokens = parseutils.simplifySpaces(line).split() if len(tokens) < 2: raise YokadiException("You should give at least two arguments: <task id> <recurrence>") task = self.getTaskFromId(tokens[0]) # Define recurrence: freq = byminute = byhour = byweekday = bymonthday = bymonth = None tokens[1] = tokens[1].lower() if tokens[1] == "none": if task.recurrence: self.session.delete(task.recurrence) task.recurrence = None return elif tokens[1] == "daily": if len(tokens) != 3: raise YokadiException("You should give time for daily task") freq = rrule.DAILY byhour, byminute = ydateutils.getHourAndMinute(tokens[2]) elif tokens[1] == "weekly": freq = rrule.WEEKLY if len(tokens) != 4: raise YokadiException("You should give day and time for weekly task") byweekday = ydateutils.getWeekDayNumberFromDay(tokens[2].lower()) byhour, byminute = ydateutils.getHourAndMinute(tokens[3]) elif tokens[1] in ("monthly", "quarterly"): if tokens[1] == "monthly": freq = rrule.MONTHLY else: # quarterly freq = rrule.YEARLY bymonth = [1, 4, 7, 10] if len(tokens) < 4: raise YokadiException("You should give day and time for %s task" % (tokens[1],)) try: bymonthday = int(tokens[2]) byhour, byminute = ydateutils.getHourAndMinute(tokens[3]) except ValueError: POSITION = {"first": 1, "second": 2, "third": 3, "fourth": 4, "last":-1} if tokens[2].lower() in list(POSITION.keys()) and len(tokens) == 5: byweekday = rrule.weekday(ydateutils.getWeekDayNumberFromDay(tokens[3].lower()), POSITION[tokens[2]]) byhour, byminute = ydateutils.getHourAndMinute(tokens[4]) bymonthday = None # Default to current day number - need to be blanked else: raise YokadiException("Unable to understand date. See help t_recurs for details") elif tokens[1] == "yearly": freq = rrule.YEARLY rDate = ydateutils.parseHumaneDateTime(" ".join(tokens[2:])) bymonth = rDate.month bymonthday = rDate.day byhour = rDate.hour byminute = rDate.minute else: raise YokadiException("Unknown frequency. Available: daily, weekly, monthly and yearly") if task.recurrence is None: task.recurrence = Recurrence() rr = rrule.rrule(freq, byhour=byhour, byminute=byminute, byweekday=byweekday, bymonthday=bymonthday, bymonth=bymonth) task.recurrence.setRrule(rr) task.dueDate = task.recurrence.getNext() self.session.merge(task) self.session.commit()
def test_weekday(self): w = dateutil_rrule.weekday(5) result = self.dumps(w) self.assertEqual(json.loads(result), self.weekday())
def get_occurrences(self, start, end): """ Get occurrences of the event that overlap with ``[start, end]``. Expects ``start < end``. :param start: the begin of the requested interval. :type start: ~datetime.datetime :param end: the end of the requested interval. :type end: ~datetime.datetime :return: start datetimes of occurrences of the event that are in the given timeframe :rtype: list [ ~datetime.datetime ] """ event_start = datetime.combine( self.start_date, self.start_time if self.start_time else time.min ) event_end = datetime.combine( self.end_date, self.end_time if self.end_time else time.max ) event_span = event_end - event_start recurrence = self.recurrence_rule if recurrence is not None: until = min( end, datetime.combine( recurrence.recurrence_end_date if recurrence.recurrence_end_date else date.max, time.max, ), ) if recurrence.frequency in (frequency.DAILY, frequency.YEARLY): occurrences = rrule( recurrence.frequency, dtstart=event_start, interval=recurrence.interval, until=until, ) elif recurrence.frequency == frequency.WEEKLY: occurrences = rrule( recurrence.frequency, dtstart=event_start, interval=recurrence.interval, byweekday=recurrence.weekdays_for_weekly, until=until, ) else: occurrences = rrule( recurrence.frequency, dtstart=event_start, interval=recurrence.interval, byweekday=weekday( recurrence.weekday_for_monthly, recurrence.week_for_monthly ), until=until, ) return [ x for x in occurrences if start <= x <= end or start <= x + event_span <= end ] return ( [event_start] if start <= event_start <= end or start <= event_end <= end else [] )
def do_t_recurs(self, line): """Make a task recurs t_recurs <id> yearly <dd/mm> <HH:MM> t_recurs <id> monthly <dd> <HH:MM> t_recurs <id> monthly <first/second/third/last> <mo, tu, we, th, fr, sa, su> <hh:mm> t_recurs <id> quarterly <dd> <HH:MM> t_recurs <id> quarterly <first/second/third/last> <mo, tu, we, th, fr, sa, su> <hh:mm> t_recurs <id> weekly <mo, tu, we, th, fr, sa, su> <hh:mm> t_recurs <id> daily <HH:MM> t_recurs <id> none (remove recurrence)""" tokens = parseutils.simplifySpaces(line).split() if len(tokens) < 2: raise YokadiException( "You should give at least two arguments: <task id> <recurrence>" ) task = self.getTaskFromId(tokens[0]) # Define recurrence: freq = byminute = byhour = byweekday = bymonthday = bymonth = None tokens[1] = tokens[1].lower() if tokens[1] == "none": if task.recurrence: task.recurrence.destroySelf() task.recurrence = None return elif tokens[1] == "daily": if len(tokens) != 3: raise YokadiException("You should give time for daily task") freq = rrule.DAILY byhour, byminute = ydateutils.getHourAndMinute(tokens[2]) elif tokens[1] == "weekly": freq = rrule.WEEKLY if len(tokens) != 4: raise YokadiException( "You should give day and time for weekly task") byweekday = ydateutils.getWeekDayNumberFromDay(tokens[2].lower()) byhour, byminute = ydateutils.getHourAndMinute(tokens[3]) elif tokens[1] in ("monthly", "quarterly"): if tokens[1] == "monthly": freq = rrule.MONTHLY else: # quarterly freq = rrule.YEARLY bymonth = [1, 4, 7, 10] if len(tokens) < 4: raise YokadiException( "You should give day and time for %s task" % (tokens[1], )) try: bymonthday = int(tokens[2]) byhour, byminute = ydateutils.getHourAndMinute(tokens[3]) except ValueError: POSITION = { "first": 1, "second": 2, "third": 3, "fourth": 4, "last": -1 } if tokens[2].lower() in POSITION.keys() and len(tokens) == 5: byweekday = rrule.weekday( ydateutils.getWeekDayNumberFromDay(tokens[3].lower()), POSITION[tokens[2]]) byhour, byminute = ydateutils.getHourAndMinute(tokens[4]) bymonthday = None # Default to current day number - need to be blanked else: raise YokadiException( "Unable to understand date. See help t_recurs for details" ) elif tokens[1] == "yearly": freq = rrule.YEARLY rDate = ydateutils.parseHumaneDateTime(" ".join(tokens[2:])) bymonth = rDate.month bymonthday = rDate.day byhour = rDate.hour byminute = rDate.minute else: raise YokadiException( "Unknown frequency. Available: daily, weekly, monthly and yearly" ) if task.recurrence is None: task.recurrence = Recurrence() rr = rrule.rrule(freq, byhour=byhour, byminute=byminute, byweekday=byweekday, bymonthday=bymonthday, bymonth=bymonth) task.recurrence.setRrule(rr) task.dueDate = task.recurrence.getNext()
def text(self, date_format="%B %d, %Y", time_format="%I:%M %p"): """Return a recurrence rule in plain English (or whatever language, once translation is supported. :) `date_format` An optional argument that specifies the format to print dates using strftime formatting rules. `time_format` An optional argument that specifies the format to print times using strftime formatting rles. """ dtstart = self._dtstart # datetime of when each occurrence starts. Defaults to now, down to the second. freq = self._freq # when the recurrence recurs, secondly through yearly. Required. interval = self._interval # how often the recurrence happens, each time through every nth time. Defaults to 1. wkst = self._wkst # Week start day, ie, an int representing which day of the week starts the week, usually Sunday or Monday. Defaults to calendar.firstweekday(). until = self._until # datetime until which the recurrence continues. Only it or count is set, not both. count = self._count # Number of times the event happens before it stops. Only it or until is set, not both. tzinfo = self._tzinfo # Time zone information. Defaults to the tzinfo of dtstart. bymonth = self._bymonth # Which month a yearly event recurs in. byweekno = self._byweekno # Which week number a yearly event recurs in. byyearday = self._byyearday # Which day of the year a yearly event recurs in. byweekday = self._byweekday # Which weekday an event recurs in. bynweekday = self._bynweekday # byeaster = self._byeaster bymonthday = self._bymonthday # Relative day of the month bynmonthday = self._bynmonthday # Negative relative day of the month bysetpos = self._bysetpos # Must be between -366 and -1 or 1 and 366. byhour = self._byhour byminute = self._byminute bysecond = self._bysecond # YEARLY needs to have bymonth and bymonthday set # MONTHLY needs to have bymonthday set # WEEKLY needs to have byweekday set text_description = [] if freq == YEARLY: pass elif freq == MONTHLY: # Get the interval. "Each", "Every other", "Every third", etc. p_interval = rrule2text.INTERVAL[interval-1][1] text_description.append(p_interval) # bynweekday is a tuple of (weekday, week_in_month) tuples for rule_pair in bynweekday: # Get the ordinal. for ord in rrule2text.ORDINAL: if ord[0] == rule_pair[1]: text_description.append(ord[1]) break # Get the weekday name p_weekday = weekday(rule_pair[0]) name = rrule2text.WEEKDAY_MAP[unicode(p_weekday)] text_description.append(name) text_description.append("at") text_description.append(dtstart.strftime(time_format)) # tack on "and interval" for the next item in the list text_description.extend(["and", p_interval]) # remove the last "and interval" because it's hanging off the end # TODO improve this text_description = text_description[:-2] elif freq == WEEKLY: pass elif freq == DAILY: pass elif freq == HOURLY: pass elif freq == MINUTELY: pass elif freq == SECONDLY: pass else: raise Rrule2textError, "Frequency value of %s is not valid." % freq if count: text_description.append("%s %s" % (int2word(count).rstrip(), "times")) elif until: text_description.extend(["until", until.strftime(date_format)]) return map(unicode, text_description)
def test_to_weekday_from_dateutil_weekday(): day = weekday(1) assert recurrence.to_weekday(day) == recurrence.Weekday(1)
def create_recurring_events(start, end, component): """ Unfold a reoccurring events to its occurrances into the given time frame. :param start: start of the time frame :param end: end of the time frame :param component: iCal component :return: occurrances of the event """ start = normalize(component['DTSTART'].dt) end = normalize(end) rule = component.get('rrule') unfolded = [] first = create_event(component) # unfolded.append(first) freq = str(rule.get('FREQ')[0]) last = find_last(rule, freq, end, first) if last < start: return elif end < last: limit = end else: limit = last # import pdb; pdb.set_trace() logger.debug('summary: %s; freq: %s; start: %s; limit: %s; rule: %s', component['summary'], freq, start, limit, rule) kwargs = {} if rule.get('BYDAY'): byday = [ getattr(rrule, s) if len(s) == 2 else rrule.weekday(getattr(rrule, s[-2:]).weekday, int(s[:-2])) for s in rule['BYDAY'] ] kwargs['byweekday'] = byday if rule.get('INTERVAL'): kwargs['interval'] = rule['INTERVAL'][0] logger.debug('kwargs: %s', kwargs) rrule_obj = rrule.rrule( getattr(rrule, freq), dtstart=start, until=limit, **kwargs ) rrule_set = rrule.rruleset() rrule_set.rrule(rrule_obj) if 'EXDATE' in component: for exdate in component['EXDATE'].dts: rrule_set.exdate(exdate.dt) for dt in list(rrule_set): logger.debug('dt: %s', dt) unfolded.append(first.copy_to(dt)) return in_range(unfolded, start, end) current = first if freq == 'YEARLY': while True: current = current.copy_to(next_year_at(current.start)) if current.start < limit: unfolded.append(current) else: break if freq == 'MONTHLY': while True: current = current.copy_to(next_month_at(current.start)) if current.start < limit: unfolded.append(current) else: break else: if freq == 'DAILY': delta = timedelta(days=1) elif freq == 'WEEKLY': delta = timedelta(days=7) else: return by_day = rule.get('BYDAY') if by_day: day_deltas = generate_day_deltas_by_weekday(set(by_day)) else: day_deltas = None while True: if day_deltas is not None: delta = timedelta(days=day_deltas.get(current.start.weekday())) current = current.copy_to(current.start + delta) if current.start < limit: unfolded.append(current) else: break return in_range(unfolded, start, end)