-
-
Save atr000/150952 to your computer and use it in GitHub Desktop.
Revisions
-
zacharyvoase revised this gist
Jun 21, 2009 . 1 changed file with 69 additions and 38 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -37,7 +37,7 @@ ... StopIteration >>> g3 = daterange(start, step='2 days') >>> g3.next() datetime.date(2009, 6, 21) >>> g3.next() @@ -47,8 +47,7 @@ >>> g3.next() datetime.date(2009, 6, 27) >>> g4 = daterange(start, to=datetime.date(2009, 6, 25), step='2 days') >>> g4.next() datetime.date(2009, 6, 21) >>> g4.next() @@ -67,6 +66,7 @@ def daterange(date, to=None, step=datetime.timedelta(days=1)): """ Similar to the built-in ``xrange()``, only for datetime objects. @@ -78,27 +78,38 @@ def daterange(date, to=None, step=datetime.timedelta(days=1)): If the ``step`` keyword is provided, this will be used as the step size instead of the default of 1 day. It should be either an instance of ``datetime.timedelta``, an integer, a string representing an integer, or a string representing a ``delta()`` value (consult the documentation for ``delta()`` for more information). If it is an integer (or string thereof) then it will be interpreted as a number of days. If it is not a simple integer string, then it will be passed to ``delta()`` to get an instance of ``datetime.timedelta()``. Note that, due to the similar interfaces of both objects, this function will accept both ``datetime.datetime`` and ``datetime.date`` objects. If a date is given, then the values yielded will be dates themselves. A caveat is in order here: if you provide a date, the step should have at least a ‘days’ component; otherwise the same date will be yielded forever. """ if to is None: condition = lambda d: True else: condition = lambda d: (d <= to) if isinstance(step, (int, long)): # By default, integers are interpreted in days. For more granular # steps, use a `datetime.timedelta()` instance. step = datetime.timedelta(days=step) elif isinstance(step, basestring): # If the string if re.match(r'^(\d+)$', str(step)): step = datetime.timedelta(days=int(step)) else: try: step = delta(step) except ValueError: pass if not isinstance(step, datetime.timedelta): raise TypeError('Invalid step value: %r' % (step,)) @@ -109,9 +120,10 @@ def daterange(date, to=None, step=datetime.timedelta(days=1)): date += step class delta(object): """ Build instances of ``datetime.timedelta`` using short, friendly strings. ``delta()`` allows you to build instances of ``datetime.timedelta`` in fewer characters and with more readability by using short strings instead @@ -139,31 +151,50 @@ def delta(delta_string): 'microseconds') If an illegal specifier is present, the parser will raise a ValueError. This utility is provided as a class, but acts as a function (using the ``__new__`` method). This is so that the names and aliases for units are stored on the class object itself: as ``UNIT_NAMES``, which is a mapping of names to aliases, and ``UNIT_ALIASES``, the converse. """ UNIT_NAMES = { ## unit_name: unit_aliases 'days': 'd day'.split(), 'hours': 'h hr hrs hour'.split(), 'minutes': 'm min mins minute'.split(), 'seconds': 's sec secs second'.split(), 'microseconds': 'ms microsec microsecs microsecond'.split(), } # Turn `UNIT_NAMES` inside-out, so that unit aliases point to canonical # unit names. UNIT_ALIASES = {} for cname, aliases in UNIT_NAMES.items(): for alias in aliases: UNIT_ALIASES[alias] = cname # Make the canonical unit name point to itself. UNIT_ALIASES[cname] = cname def __new__(cls, string): specifiers = (specifier.strip() for specifier in string.split(',')) kwargs = {} for specifier in specifiers: match = re.match(r'^(\d+)\s*(\w+)$', specifier) if not match: raise ValueError('Invalid delta specifier: %r' % (specifier,)) number, unit_alias = match.groups() number, unit_alias = int(number), unit_alias.lower() unit_cname = cls.UNIT_ALIASES.get(unit_alias) if not unit_cname: raise ValueError('Invalid unit: %r' % (unit_alias,)) kwargs[unit_cname] = kwargs.get(unit_cname, 0) + number return datetime.timedelta(**kwargs) if __name__ == '__main__': -
zacharyvoase revised this gist
Jun 21, 2009 . 1 changed file with 64 additions and 2 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -84,7 +84,7 @@ def daterange(date, to=None, step=datetime.timedelta(days=1)): Note that, due to the similar interfaces of both objects, this function will accept both ``datetime.datetime`` and ``datetime.date`` objects. """ if to is None: condition = lambda d: True else: @@ -94,16 +94,78 @@ def daterange(date, to=None, step=datetime.timedelta(days=1)): # By default, integers are interpreted in days. For more granular # steps, use a `datetime.timedelta()` instance. step = datetime.timedelta(days=int(step)) elif isinstance(step, basestring): try: step = delta(step) except ValueError: pass if not isinstance(step, datetime.timedelta): raise TypeError('Invalid step value: %r' % (step,)) # The main generation loop. while condition(date): yield date date += step def delta(delta_string): """ Build instances of ``datetime.timedelta`` using shorter spec strings. ``delta()`` allows you to build instances of ``datetime.timedelta`` in fewer characters and with more readability by using short strings instead of a long sequence of keyword arguments. A typical (but very precise) spec string looks like this: '1 day, 4 hours, 5 minutes, 3 seconds, 120 microseconds' ``datetime.timedelta`` doesn’t allow deltas containing months or years, because of the differences between different months, leap years, etc., so this function doesn’t support them either. The parser is very simple; it takes a series of comma-separated values, each of which represents a number of units of time (such as one day, four hours, five minutes, et cetera). These ‘specifiers’ consist of a number and a unit of time, optionally separated by whitespace. The units of time accepted are (case-insensitive): * Days ('d', 'day', 'days') * Hours ('h', 'hr', 'hrs', 'hour', 'hours') * Minutes ('m', 'min', 'mins', 'minute', 'minutes') * Seconds ('s', 'sec', 'secs', 'second', 'seconds') * Microseconds ('ms', 'microsec', 'microsecs' 'microsecond', 'microseconds') If an illegal specifier is present, the parser will raise a ValueError. """ specifiers = (specifier.strip() for specifier in delta_string.split(',')) kwargs = {} for specifier in specifiers: try: number, unit = re.match(r'^(\d+)\s*(\w+)$', specifier).groups() except AttributeError: raise ValueError('Invalid delta specifier: %r' % (specifier,)) number, unit = int(number), unit.lower() # Throughout this we use `kwargs.get(unit, 0) + num` so that multiple # appearances of the same unit will be summed together. if unit in 'd day days'.split(): kwargs['days'] = kwargs.get('days', 0) + number elif unit in 'h hr hrs hour hours'.split(): kwargs['hours'] = kwargs.get('hours', 0) + number elif unit in 'm min mins minute minutes'.split(): kwargs['minutes'] = kwargs.get('minutes', 0) + number elif unit in 's sec secs second seconds'.split(): kwargs['seconds'] = kwargs.get('seconds', 0) + number elif unit in 'ms microsec microsecs microsecond microseconds'.split(): kwargs['microseconds'] = kwargs.get('microseconds', 0) + number return datetime.timedelta(**kwargs) if __name__ == '__main__': import doctest doctest.testmod() -
zacharyvoase revised this gist
Jun 21, 2009 . 1 changed file with 91 additions and 32 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,50 +1,109 @@ # -*- coding: utf-8 -*- """ Example Usage ============= >>> import datetime >>> start = datetime.date(2009, 6, 21) >>> g1 = daterange(start) >>> g1.next() datetime.date(2009, 6, 21) >>> g1.next() datetime.date(2009, 6, 22) >>> g1.next() datetime.date(2009, 6, 23) >>> g1.next() datetime.date(2009, 6, 24) >>> g1.next() datetime.date(2009, 6, 25) >>> g1.next() datetime.date(2009, 6, 26) >>> g2 = daterange(start, to=datetime.date(2009, 6, 25)) >>> g2.next() datetime.date(2009, 6, 21) >>> g2.next() datetime.date(2009, 6, 22) >>> g2.next() datetime.date(2009, 6, 23) >>> g2.next() datetime.date(2009, 6, 24) >>> g2.next() datetime.date(2009, 6, 25) >>> g2.next() Traceback (most recent call last): ... StopIteration >>> g3 = daterange(start, step=datetime.timedelta(days=2)) >>> g3.next() datetime.date(2009, 6, 21) >>> g3.next() datetime.date(2009, 6, 23) >>> g3.next() datetime.date(2009, 6, 25) >>> g3.next() datetime.date(2009, 6, 27) >>> g4 = daterange(start, to=datetime.date(2009, 6, 25), ... step=datetime.timedelta(days=2)) >>> g4.next() datetime.date(2009, 6, 21) >>> g4.next() datetime.date(2009, 6, 23) >>> g4.next() datetime.date(2009, 6, 25) >>> g4.next() Traceback (most recent call last): ... StopIteration """ import datetime import re def daterange(date, to=None, step=datetime.timedelta(days=1)): """ Similar to the built-in ``xrange()``, only for datetime objects. If called with just a ``datetime`` object, it will keep yielding values forever, starting with that date/time and counting in steps of 1 day. If the ``to_date`` keyword is provided, it will count up to and including that date/time (again, in steps of 1 day by default). If the ``step`` keyword is provided, this will be used as the step size instead of the default of 1 day. It should be either an instance of ``datetime.timedelta``, an integer, or a string representing an integer. If it is either of the latter two, it will be interpreted as a number of days. Note that, due to the similar interfaces of both objects, this function will accept both ``datetime.datetime`` and ``datetime.date`` objects. """ if to is None: condition = lambda d: True else: condition = lambda d: (d <= to) if (isinstance(step, (int, long)) or re.match(r'^([\d]+)$', str(step))): # By default, integers are interpreted in days. For more granular # steps, use a `datetime.timedelta()` instance. step = datetime.timedelta(days=int(step)) if not isinstance(step, datetime.timedelta): raise TypeError('Invalid step value: %r' % (step_size,)) # The main generation loop. while condition(date): yield date date += step if __name__ == '__main__': import doctest doctest.testmod() -
zacharyvoase renamed this gist
Jun 21, 2009 . 1 changed file with 0 additions and 0 deletions.There are no files selected for viewing
File renamed without changes. -
zacharyvoase created this gist
Jun 21, 2009 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,50 @@ # -*- coding: utf-8 -*- import datetime def daterange(*args): """ Similar to the built-in ``xrange()``, only for dates. The function can be called with between one and three arguments. If called with one, it will yield all dates *from* the one provided, ad infinitum. This is markedly different from the built-in ``xrange()``. If called with two dates, it will yield all dates from the first, up to and including the last. If called with three arguments, the third should be an instance of ``datetime.timedelta`` or an integer specifying the step size in days. It will yield all dates which lie between the first and second arguments inclusive, with the given step. """ # Set up a default step value. step = datetime.timedelta(days=1) if len(args) == 0: raise TypeError('daterange() expects at least 1 argument') if len(args) >= 1: date = args[0] condition = lambda d: True if len(args) >= 2: up_to_date = args[1] condition = lambda d: (d <= up_to_date) if len(args) >= 3: step_size = args[2] if (isinstance(step_size, (int, long)) or re.match(r'([\d]+)', str(step_size))): step = datetime.timedelta(days=int(step_size)) elif isinstance(step_size, datetime.timedelta): step = step_size else: raise TypeError('Invalid step value: %r' % (step_size,)) # The main generation loop. while condition(date): yield date date += step