5.7. Datetime Timedelta

5.7.1. Timedelta object

Shifting datetime objects:

>>> from datetime import datetime
>>>
>>>
>>> gagarin = datetime(1961, 4, 12, 6, 7)
>>> armstrong = datetime(1969, 7, 21, 2, 56, 15)
>>>
>>> between_dates = armstrong - gagarin
>>>
>>> str(between_dates)
'3021 days, 20:49:15'
>>> between_dates
datetime.timedelta(days=3021, seconds=74955)
>>> between_dates.days
3021
>>> between_dates.seconds
74955
>>> between_dates.total_seconds()  # (days * seconds per day + seconds)
261089355.0

5.7.2. Simple Time Shift

>>> from datetime import timedelta, datetime
>>>
>>>
>>> gagarin = datetime(1961, 4, 12)
>>>
>>> gagarin - timedelta(minutes=15)
datetime.datetime(1961, 4, 11, 23, 45)
>>>
>>> gagarin + timedelta(minutes=10)
datetime.datetime(1961, 4, 12, 0, 10)
>>> from datetime import timedelta, datetime
>>>
>>>
>>> armstrong = datetime(1969, 7, 21, 2, 56, 15)
>>>
>>> armstrong - timedelta(hours=21)
datetime.datetime(1969, 7, 20, 5, 56, 15)
>>>
>>> armstrong + timedelta(hours=5)
datetime.datetime(1969, 7, 21, 7, 56, 15)
>>> from datetime import timedelta, date
>>>
>>>
>>> sputnik = date(1957, 10, 4)
>>>
>>> sputnik + timedelta(days=5)
datetime.date(1957, 10, 9)
>>>
>>> sputnik - timedelta(days=3)
datetime.date(1957, 10, 1)
>>> from datetime import datetime, timedelta
>>>
>>>
>>> gagarin = datetime(1961, 4, 12)
>>>
>>> gagarin + timedelta(weeks=2)
datetime.datetime(1961, 4, 26, 0, 0)
>>>
>>> gagarin - timedelta(weeks=3)
datetime.datetime(1961, 3, 22, 0, 0)

5.7.3. Complex Shifts

>>> from datetime import timedelta, datetime
>>>
>>>
>>> armstrong = datetime(1969, 7, 21, 2, 56, 15)
>>>
>>> armstrong - timedelta(days=2, hours=21)
datetime.datetime(1969, 7, 18, 5, 56, 15)
>>> from datetime import timedelta, datetime
>>>
>>>
>>> armstrong = datetime(1969, 7, 21, 2, 56, 15)
>>>
>>> duration = timedelta(
...     weeks=3,
...     days=2,
...     hours=21,
...     minutes=5,
...     seconds=12,
...     milliseconds=10,
...     microseconds=55)
>>>
>>> duration
datetime.timedelta(days=23, seconds=75912, microseconds=10055)
>>>
>>> armstrong - duration
datetime.datetime(1969, 6, 27, 5, 51, 2, 989945)

5.7.4. Month Shifts

>>> from datetime import timedelta, date
>>>
>>>
>>> MONTH = timedelta(days=30.4375)
>>>
>>> gagarin = date(1961, 4, 12)
>>> gagarin - MONTH
datetime.date(1961, 3, 13)
>>> from calendar import _monthlen as monthlen
>>> from datetime import timedelta, date
>>>
>>>
>>> def month_before(dt):
...     MONTH = monthlen(dt.year, dt.month)
...     return dt - timedelta(days=MONTH)
>>>
>>>
>>> gagarin = date(1961, 4, 12)
>>> month_before(gagarin)
datetime.date(1961, 3, 13)

5.7.5. Duration

  • Period between two datetimes

>>> from datetime import datetime
>>>
>>>
>>> SECOND = 1
>>> MINUTE = 60 * SECOND
>>> HOUR = 60 * MINUTE
>>> DAY = 24 * HOUR
>>> MONTH = 30.4375 * DAY  # Average days a month in solar calendar
>>> YEAR = 365.25 * DAY  # Solar calendar
>>>
>>>
>>> def duration(td):
...     years, seconds = divmod(td.total_seconds(), YEAR)
...     months, seconds = divmod(seconds, MONTH)
...     days, seconds = divmod(seconds, DAY)
...     hours, seconds = divmod(td.seconds, HOUR)
...     minutes, seconds = divmod(seconds, MINUTE)
...     return {
...         'years': int(years),
...         'months': int(months),
...         'days': int(days),
...         'hours': int(hours),
...         'minutes': int(minutes),
...         'seconds': int(seconds)}
>>>
>>>
>>> gagarin = datetime(1961, 4, 12, 6, 7)
>>> armstrong = datetime(1969, 7, 21, 2, 56, 15)
>>>
>>> td = armstrong - gagarin
>>> td
datetime.timedelta(days=3021, seconds=74955)
>>>
>>> duration(td)
{'years': 8, 'months': 3, 'days': 8, 'hours': 20, 'minutes': 49, 'seconds': 15}

5.7.6. Further Reading

5.7.7. Assignments

Code 5.23. Solution
"""
* Assignment: Datetime Timedelta Duration
* Complexity: easy
* Lines of code: 2 lines
* Time: 3 min

English:
    1. Calculate how many years passed between Gagarin's launch
       and Armstrong's first step on the Moon
    2. Assume:
        a. year = 365.25 days
        b. month = 30.4375 days
    3. Result round to one decimal place
    4. Run doctests - all must succeed

Polish:
    1. Podany jest czas, który upłynął między startem Gagarina
       a pierwszym krokiem Armstronga na Księżycu
    2. Uwzględnij założenie:
        a. rok = 365.25 dni
        b. miesiąc = 30.4375 dni
    3. Rezultat zaokrąglij jednego miejsca po przecinku
    4. Uruchom doctesty - wszystkie muszą się powieść

Tests:
    >>> import sys; sys.tracebacklimit = 0

    >>> assert type(result) is float, \
    'Variable `result` has invalid type, must be a float'

    >>> result
    8.3
"""

from datetime import datetime


DAY = 1
MONTH = 30.4375 * DAY
YEAR = 365.25 * DAY

GAGARIN = datetime(1961, 4, 12, 6, 7)
ARMSTRONG = datetime(1969, 7, 21, 2, 56, 15)

# Time between GAGARIN and ARMSTRONG
# type: timedelta
duration = ...

# Number of years rounded to 1 decimal place
# type: float
result = ...

Code 5.24. Solution
"""
* Assignment: Datetime Timedelta Age
* Complexity: easy
* Lines of code: 6 lines
* Time: 5 min

English:
    1. How old was Yuri Gagarin when he was launched to space?
    2. How old was Neil Armstrong when he made a first step on the Moon?
    3. Result round to full years
    4. Mind, that there are two different objects: `date` and `datetime`
    5. Run doctests - all must succeed

Polish:
    1. Ile miał lat Juri Gagarin kiedy wystartował w kosmos?
    2. Ile lat miał Neil Armstrong kiedy zrobił pierwszy krok na Księżycu?
    3. Rezultat zaokrąglij do pełnych lat
    4. Zwróć uwagę, że tam są dwa różne obiekty: `date` i `datetime`
    5. Uruchom doctesty - wszystkie muszą się powieść

Tests:
    >>> import sys; sys.tracebacklimit = 0

    >>> assert type(gagarin_age) is int, \
    'Variable `gagarin_age` has invalid type, must be a int'

    >>> assert type(armstrong_age) is int, \
    'Variable `armstrong_age` has invalid type, must be a int'

    >>> gagarin_age
    27

    >>> armstrong_age
    38
"""

from datetime import date, datetime


DAY = 1
MONTH = 30.4375 * DAY
YEAR = 365.25 * DAY

GAGARIN_BIRTHDATE = date(1934, 3, 9)
GAGARIN_LAUNCH = datetime(1961, 4, 12, 6, 7)
ARMSTRONG_BIRTHDATE = date(1930, 8, 5)
ARMSTRONG_STEP = datetime(1969, 7, 21, 2, 56, 15)


# Gagarin's age when he was launched to space
# type: int
gagarin_age = ...

# Armstrong's age when he made a first step on the Moon
# type: int
armstrong_age = ...


Code 5.25. Solution
"""
* Assignment: Datetime Timedelta Period
* Complexity: easy
* Lines of code: 2 lines
* Time: 5 min

English:
    1. Between the first flight to space and the first step on the moon passed:
        * 8 years
        * 3 months
        * 8 days
        * 13 hours
        * 19 minutes
        * 15 seconds
    2. Assumption:
        a. year = 365.25 days
        b. month = 30.4375 days
    3. Define `result: timedelta` representing given period
    4. Run doctests - all must succeed

Polish:
    1. Między pierwszym lotem w kosmos a pierwszym krokiem na Księżycu minęło:
        * 8 lat
        * 3 miesięcy
        * 8 dni
        * 13 godzin
        * 19 minut
        * 15 sekund
    2. Założenie:
        a. rok = 365.25 dni
        b. miesiąc = 30.4375 dni
    3. Zdefiniuj `result: timedelta` reprezentujące dany okres
    4. Uruchom doctesty - wszystkie muszą się powieść


Tests:
    >>> import sys; sys.tracebacklimit = 0

    >>> assert type(result) is timedelta, \
    'Variable `result` has invalid type, must be a timedelta'

    >>> result
    datetime.timedelta(days=3021, seconds=74955)
"""

from datetime import timedelta


SECOND = 1
MINUTE = 60 * SECOND
HOUR = 60 * MINUTE
DAY = 24 * HOUR
MONTH = 30.4375 * DAY
YEAR = 365.25 * DAY


# - 8 years
# - 3 months
# - 8 days
# - 13 hours
# - 19 minutes
# - 15 seconds
# type: timedelta
result = ...


Code 5.26. Solution
"""
* Assignment: Datetime Timedelta Dict
* Complexity: easy
* Lines of code: 13 lines
* Time: 5 min

English:
    1. `DATA` is the time between Gagarin launch and Armstrong
       first step on the Moon
    2. Assume:
        a. year = 365.25 days
        b. month = 30.4375 days
    3. Define `result: dict[str, int]` representing period
    4. Run doctests - all must succeed

Polish:
    1. `DATA`, to czas który upłynął między startem Gagarina
       a pierwszym krokiem Armstronga na Księżycu
    2. Uwzględnij założenie:
        a. rok = 365.25 dni
        b. miesiąc = 30.4375 dni
    3. Zdefiniuj `result: dict[str, int]` reprezentujący okres
    4. Uruchom doctesty - wszystkie muszą się powieść

Given:
    * 8 years
    * 3 months
    * 8 days
    * 20 hours
    * 49 minutes
    * 15 seconds

Tests:
    >>> import sys; sys.tracebacklimit = 0

    >>> assert type(result) is dict, \
    'Variable `result` has invalid type, must be a dict'

    >>> result.keys()
    dict_keys(['years', 'months', 'days', 'hours', 'minutes', 'seconds'])

    >>> assert all(type(value) is int for value in result.values()), \
    'All elements in `result` must be an int'

    >>> result  # doctest: +NORMALIZE_WHITESPACE
    {'years': 8,
     'months': 3,
     'days': 8,
     'hours': 20,
     'minutes': 49,
     'seconds': 15}
"""

from datetime import timedelta


SECOND = 1
MINUTE = 60 * SECOND
HOUR = 60 * MINUTE
DAY = 24 * HOUR
MONTH = 30.4375 * DAY
YEAR = 365.25 * DAY

DATA = timedelta(days=3021, seconds=74955)

# dict[str, int]
result = {
    'years': ...,
    'months': ...,
    'days': ...,
    'hours': ...,
    'minutes': ...,
    'seconds': ...,
}