Time Maps

Relative Time Map

The RelativeTimeMap maps strings of relative times to seconds. It tries to be as liberal as possible so there has to be a fair amount of certainty that the string is in fact a time and not something similar but different. ‘5 yolks’ will match ‘5’ years, for instance, and if that is not the desired behavior then something else has to do a check first to filter out bad strings.

It uses dateutil to calculate everything but the seconds because dateutil will handle the ambiguous values like years (which have leap-years) and months which have 28, 29, 30, or 31 days.

The UML Model

RelativeTimeMap --|> BaseClass
RelativeTimeMap : re.RegexObject year_expression
RelativeTimeMap : re.RegexObject month_expression
RelativeTimeMap : re.RegexObject week_expression
RelativeTimeMap : re.RegexObject day_expression
RelativeTimeMap : re.RegexObject hour_expression
RelativeTimeMap : re.RegexObject minute_expression
RelativeTimeMap : re.RegexObject second_expression

RelativeTimeMap() A converter from strings with relative times to seconds

Relative Time Map Groups

The RelativeTime

This is an attempt to extend the timedelta with weeks, hours, and minutes. The original intention was to also allow months and years, requiring the use of the dateutil package, but at this point I can’t see an immediate use for it, so I’ll stop at weeks, since it doesn’t need special cases the way months and years do.

RelativeTime -|> BaseClass
RelativeTime o-- RelativeTimeMap
RelativeTime : __init__(source)
RelativeTime : int days
RelativeTime : int seconds
RelativeTime : int microseconds
RelativeTime : datetime.timedelta
RelativeTime : float total_seconds

By and large the intention is to use this like a time-delta object but with extra fields, so the operators will be overloaded too.

RelativeTime([source]) A timedeltas extension
RelativeTime.time_map A relative time map instance to parse the source.
RelativeTime.days
RelativeTime.seconds
RelativeTime.microseconds
RelativeTime.reset() Resets the attributes (undoes populate_fields)
RelativeTime.total_seconds(*args, **kwargs)
source_required(method) Catches AttributeErrors and TypeErrors and raises ApeErrors in their place so the operators can recover

I’ve run into a circular call problem – my intention was to defer the calculations of the properties until someone called them, but since the larger properties cascade into the smaller ones (e.g. 1.5 minutes would add 30 to seconds to get rid of the fraction) I have to have a single point where the calculations are done so that all of them are done. To do this without requiring anyone to do an explicit method call I’m doing the calculations on the setting of the source. Since I am averse to forcing calculations on construction of an object, the source parameter has been changed to optional and setting it to None should reset the fields.

To make it more obvious that the fields have not been populated they will have the default of None. This way if the source has not been set and a calculation is attempted using the fields it will raise an error.

Reading the timedelta information more closely, it appears that it actually accepts anything I want to give it (up to weeks), it just converts them to days, seconds, and microseconds to store them. So the cascading that I was trying to do (separating out the fraction and propagating it to another unit) is actually unnecessary unless there’s a need to preserve the separate units. To make it easier I won’t.

Well... now that I think about it, I’ve changed things so much that it has brought me back around to where using the relativedelta might make sense. Unfortunately I still need the hack, but this should allow it to accept months and years.

relativetimedelta Attributes
Constructor Attribute
years  
months  
weeks  
days days
hours  
minutes  
seconds seconds
microseconds microseconds

The timedelta only stores three attributes – days, seconds, and microseconds, athough it takes the other attributes on construction of the object and then converts them to the three permanent attributes.

Warning

timedelta takes floats (like 3.2) but the extra attributes for relativedelta (months and years) have to be integers.

ApeError Translators

Since the operations are expected to raise ApeErrors whenever possible, these decorators will translate standard exceptions to ApeErrors.

The Timedelta

timedelta Difference between two datetime values.
timedelta.days Number of days.
timedelta.seconds Number of seconds (>= 0 and less than 1 day).
timedelta.microseconds Number of microseconds (>= 0 and less than 1 second).
timedelta.total_seconds Total seconds in the duration.

Since the timedelta supports operations, the RelativeTime has overloaded the following operations so that it behaves like a timedelta object.

Timedelta Operations
Operation Description  
t1 + t2 Sums two timedeltas  
t1 - t2 Subtracts one timedelta from another  
t1 * int Mulitplies a timedelta by an integer  
t1 // int Calculates the floor of a timedelta throws away the remainder
+t1 Makes the timedelta positive  
-t1 Negates the timedelta  
abs(t1) Depends on number of days  
str(t) string  
repr(t) representation string  

Equality and inequality have also been implemented but there are two warnings:

  • If you have too many decimal places for parameters fed to a timedelta they might not equate with the RelativeTime, even if given the same parameters
  • This only works if the RelativeTime is on the left-hand-side (RelativeTime == timedelta)

AbsoluteTime

This is a class to get datetime objects based on a string input. It really is just a pass-through to dateutil.parser.parse but holds persistent values so they don’t have to be passed in for every function call by the user.

parse(timestr[, parserinfo])
datetime datetime(year, month, day[, hour[, minute[, second[, microsecond[,tzinfo]]]]])

AbsoluteTime -|> BaseClass
AbsoluteTime o-- dateutil.parse
AbsoluteTime : datetime default
AbsoluteTime : boolean ignoretz
AbsoluteTime : (function or dict) tzinfos
AbsoluteTime : boolean dayfirst
AbsoluteTime : boolean yearfirst
AbsoluteTime : boolean fuzzy
AbsoluteTime : parserinfo parserinfo
AbsoluteTime : datetime __call__(string)

AbsoluteTime
AbsoluteTime.__call__

The AbsoluteTime Attributes

These are the same as the parse function’s arguments. I think in most cases the defaults are all that you’ll need, but I’ll at least document how the dayfirst and yearfirst arguments affect the format precedence. If the timestamp is unambiguous, they won’t matter, but for the cases where the fields are ambiguous (e.g. ‘11-11-23’) the two parameters decide what to assume about the timestamp.

Format Precedence
dayfirst yearfirst Order of Precedence (Left to Right)
False False MM-DD-YY, DD-MM-YY, YY-MM-DD (default)
False True YY-MM-DD, MM-DD-YY, DD-MM-YY
True False DD-MM-YY, MM-DD-YY, YY-MM-DD
True True YY-MM-DD, DD-MM-YY, MM-DD-yy

Time Validator

Since I’m switching from the ConfigurationMap to ConfigObj, I need a validator that knows about time conversions. ..’

time_validator = Validator({'relative_time':RelativeTime,
                       'absolute_time':AbsoluteTime()})