doubledate.offset#

doubledate.offset(date: date, days: int = None, weekdays: int = None, weeks: int = None, months: int = None, years: int = None, to: str = None, handle=0)#

Returns the date offset either by a number of frequencies or else to the nearest frequency. Only one of the days, weekdays, weeks, months, years or to must be provided.

Parameters:
  • date (datetime-like) – the reference date to offset

  • days (int, optional) – to offset the reference date by n calendar days

  • weekdays (int, optional) – to offset the reference date by n weekdays

  • weeks (int, optional) – to offset the reference date by n weeks

  • months (int, optional) – to offset the reference date by n months. See notes below on handling out-of-range dates (e.g. 31 Jan + 1m)

  • years (int, optional) – to offset the reference date by n months. See notes below if the reference date is 29 February

  • to (str, optional) – to offset the reference date to a given period start or period end

  • handle (int, function, optional) – optional callback function (or integer) to customize the way out-of-range dates are handled (e.g. 31 Jan + 1m). See note below

Raises:

ValueError – on invalid arguments

Notes

When adding months or years, there may ambiguity as to what the function should return when the “intended” target date is out-of-range. For example, 31 January + 1m should - in a perfect world - yield 31 February… which is out-of-range.

By default, the function will handle these out-of-range cases by returning the last calendar date in the target month (e.g. 28 February, or 29 February if the year is a leap year).

>>> offset(datetime.date(2020, 1, 31), months=1)
datetime.date(2020, 2, 29)

You can can customize this behavior using the handle argument, which can take either an integer or a callback function:

  • if given an integer (e.g. 1), the function adds that number of days to the end of target month (e.g. 28 February + 1 day is 1 March)

>>> offset(datetime.date(2020, 1, 31), months=1, handle=0)
datetime.date(2020, 2, 29)

>>> offset(datetime.date(2020, 1, 31), months=1, handle=1)
datetime.date(2020, 3, 1)

>>> offset(datetime.date(2020, 1, 31), months=1, handle=2)
datetime.date(2020, 3, 2)

>>> offset(datetime.date(2020, 1, 31), months=1, handle=-1)
datetime.date(2020, 2, 28)

>>> offset(datetime.date(2020, 2, 29), years=1, handle=1)
datetime.date(2021, 3, 1)
  • if given a callback function (e.g. lambda function), the function must accept the end of the month (e.g. 28 February) and the number of gap days between the “intended” target date (e.g. 31 February) and the most recent feasible date (29 February) and return an integer (e.g. 1). The result of the callback function is then added to end of the month.

>>> offset(datetime.date(2020, 1, 31), months=1, handle=lambda eom, gap: 0)
datetime.date(2020, 2, 29)

>>> offset(datetime.date(2020, 1, 31), months=1, handle=lambda eom, gap: 1)
datetime.date(2020, 3, 1)

>>> offset(datetime.date(2020, 1, 31), months=1, handle=lambda eom, gap: 2)
datetime.date(2020, 3, 2)

>>> offset(datetime.date(2020, 1, 31), months=1, handle=lambda eom, gap: gap)
datetime.date(2020, 3, 2) #gap is 2 as 2020 is a leap year

>>> offset(datetime.date(2021, 1, 31), months=1, handle=lambda eom, gap: gap)
datetime.date(2020, 3, 3) #gap is 3 as 2021 is not a leap year

>>> offset(datetime.date(2020, 8, 31), months=1, handle=lambda eom, gap: gap)
datetime.date(2020, 10, 1) #gap is 1

Examples

>>> today = datetime.date(2020, 1, 10)
>>> today
datetime.date(2020, 1, 10) #Friday
>>> offset(today, days=1)
datetime.date(2020, 1, 11) #Saturday
>>> offset(today, weekdays=1)
datetime.date(2020, 1, 13) #Monday
>>> offset(today, weekdays=-1)
datetime.date(2020, 1, 9) #Thursday
>>> offset(today, weeks=1)
datetime.date(2020, 1, 17) #Friday next week
>>> offset(today, weekdays=5)
datetime.date(2020, 1, 17) #also Friday next week
>>> offset(today, months=1)
datetime.date(2020, 2, 10)
>>> jan31 = datetime.date(2020, 1, 31)
>>> offset(jan31, months=1)
datetime.date(2020, 2, 29) #defaults to end of month
>>> offset(jan31, months=1, handle=1)
datetime.date(2020, 3, 1)
>>> offset(jan31, months=1, handle=lambda eom, days: 1)
datetime.date(2020, 3, 1) #handle returns 1
>>> offset(jan31, months=1, handle=lambda eom, days: days)
datetime.dte(2020, 3, 2) #handle returns the size of the gap