Search code examples
pythonpython-dateutilrelativedelta

What are differences between the singular and plural argument in 'dateutil.relativedelta.relativedelta'?


Excerpt from the description of dateutil.relativedelta.relativedelta,

year, month, day, hour, minute, second, microsecond:

Absolute information (argument is singular); adding or subtracting a
relativedelta with absolute information does not perform an aritmetic
operation, but rather REPLACES the corresponding value in the
original datetime with the value(s) in relativedelta.

years, months, weeks, days, hours, minutes, seconds, microseconds:

Relative information, may be negative (argument is plural); adding
or subtracting a relativedelta with relative information performs
the corresponding aritmetic operation on the original datetime value
with the information in the relativedelta.

I can see the differences from the following example when it comes to adding and subtracting.

>>> from datetime import datetime
>>> from dateutil.relativedelta import relativedelta
>>> now = datetime.now()
>>> str(now)
'2016-05-23 22:32:48.427269'
>>> singular = relativedelta(month=3)
>>> plural = relativedelta(months=3)

# subtracting 
>>> str(now - singular)             # replace the corresponding value in the original datetime with the value(s) in relativedelta
'2016-03-23 22:32:48.427269'
>>> str(now - plural)               # perform the corresponding aritmetic operation on the original datetime value with the information in the relativedelta.
'2016-02-23 22:32:48.427269'

# adding
>>> str(now + singular)             # replace the corresponding value in the original datetime with the value(s) in relativedelta
'2016-03-23 22:32:48.427269'
>>> str(now + plural)               # perform the corresponding aritmetic operation on the original datetime value with the information in the relativedelta.
'2016-08-23 22:32:48.427269'

Beside this, what are other differences between the singular and plural argument in relativedelta?


Solution

  • Singular arguments are absolute information, essentially you can think of relativedelta(month=3) to mean "the month of March, relative to whatever date and time it is applied to" (i.e. replace the month keyword). This is not amenable to operations like multiplication and division, so these operations have no effect on absolute information:

    >>> relativedelta.relativedelta(month=3) * 3
    relativedelta(month=3)
    

    Plural arguments are relative offsets, so they are saying, "give me this many months after/before the date". Since these are an offset, they are amenable to multiplication and division:

    >>> relativedelta.relativedelta(months=3) * 3
    relativedelta(months=9)
    

    A good example of where this is used would be in the tzrange class, which uses relativedelta to emulate the behavior of POSIX-style TZ strings. You can see this test, which uses the following object:

    tz.tzrange('EST', -18000, 'EDT', -14400,
           start=relativedelta(hours=+2, month=4, day=1, weekday=SU(+1)),
           end=relativedelta(hours=+1, month=10, day=31, weekday=SU(-1)))
    

    This constructs a time zone equivalent to 'EST5EDT', where the start relativedelta is added to any date in a given year to find the start of DST for that year, and end is added to any date in a given year to find the end of DST for that year.

    Breaking it down:

    • month gives you a date in April
    • day starts you out at the first of the month
    • weekday=SU(+1) gives you the first Sunday on or after the specified date (this is applied after the month and day parameters, so when the day is set to 1, you get the first Sunday of the month).
    • hours=+2 - this will give you 2 hours after all the other stuff has been applied. Probably in this case hour=2 makes more sense for demonstrating the concept, though, since where these relativedeltas actually gets used I think strips out the time portion first (giving a date at midnight), so this is really trying to encode 2AM.