Skip to content

API tour

A guided tour of everything IsoWeek and IsoWeekDate can do, grouped by kind of operation. Every output on this page is generated by running the code shown.

If you are new to the library, work through the quickstart first. For signatures, argument types and raised exceptions, see the API reference pages for IsoWeek and IsoWeekDate.

The two classes share most of their behaviour through their common parent class BaseIsoWeek, so this page covers the shared operations first and the class specific ones afterwards.

Both classes are available from the top-level module:

from datetime import date, datetime

from iso_week_date import IsoWeek, IsoWeekDate

iw = IsoWeek("2023-W01")
iwd = IsoWeekDate("2023-W01-1")

print(iw, iwd)
2023-W01 2023-W01-1

str() returns the value, while repr() also reports the offset:

print(str(iw))
print(repr(iw))
2023-W01
IsoWeek(2023-W01) with offset 0:00:00

Parsing from other types

An instance can be initialized from several types:

print(IsoWeek("2023-W01"))
print(IsoWeekDate("2023-W01-1"))
2023-W01
2023-W01-1
print(IsoWeek.from_string("2023-W01"))
print(IsoWeekDate.from_string("2023-W01-1"))
2023-W01
2023-W01-1
print(IsoWeek.from_compact("2023W01"))
print(IsoWeekDate.from_compact("2023W011"))
2023-W01
2023-W01-1
print(IsoWeek.from_date(date(2023, 1, 2)))
print(IsoWeekDate.from_date(date(2023, 1, 2)))
2023-W01
2023-W01-1
print(IsoWeek.from_datetime(datetime(2023, 1, 2, 12)))
print(IsoWeekDate.from_datetime(datetime(2023, 1, 2, 12)))
2023-W01
2023-W01-1
print(IsoWeek.from_values(year=2023, week=1))
print(IsoWeekDate.from_values(year=2023, week=1, weekday=1))
2023-W01
2023-W01-1
print(IsoWeek.from_today() == IsoWeek.from_date(date.today()))
True

The compact format has no separators

The compact format drops every separator: IsoWeekDate.from_compact expects YYYYWNND (e.g. "2023W011"), not "2023W01-1". from_today also accepts an optional time_zone argument.

Converting to other types

In the opposite direction, an instance can be converted to multiple types:

print(iw.to_string())
print(iwd.to_string())
2023-W01
2023-W01-1
print(iw.to_compact())
print(iwd.to_compact())
2023W01
2023W011
print(iw.to_date())
print(iwd.to_date())
2023-01-02
2023-01-02
print(iw.to_datetime())
print(iwd.to_datetime())
2023-01-02 00:00:00
2023-01-02 00:00:00
print(iw.to_values())  # (year, week)
print(iwd.to_values())  # (year, week, weekday)
(2023, 1)
(2023, 1, 1)

IsoWeek to date/datetime

IsoWeek.to_date and IsoWeek.to_datetime accept an optional weekday argument, which defaults to 1 (first weekday), and can be used to get the date of a specific day of the week:

print(iw.to_date(2))
print(iw.to_datetime(3))
2023-01-03
2023-01-04 00:00:00

IsoWeekDate already carries a weekday, so its to_date and to_datetime take no argument.

Properties

Both classes expose the components of their value as integers, plus the class name:

print(iw.year, iwd.year)
2023 2023
print(iw.week, iwd.week)
1 1
print(iw.quarter, iwd.quarter)
print(IsoWeek("2023-W40").quarter)
1 1
4
print(iw.name, iwd.name)
IsoWeek IsoWeekDate

Comparison operations

Both classes implement all the comparison operators (==, !=, <, <=, >, >=) for instances of the same class:

print(iw == IsoWeek("2023-W01"))
print(iw < IsoWeek("2023-W02"))
print(iwd > IsoWeekDate("2023-W02-2"))
True
True
False

Comparing across classes is rejected, because an IsoWeek and an IsoWeekDate do not represent the same kind of quantity:

print(iw == iwd)  # equality is False rather than an error

try:
    iw < iwd
except TypeError as e:
    print(f"TypeError: {e}")
False
TypeError: Cannot compare `IsoWeek` with type `<class 'iso_week_date.isoweekdate.IsoWeekDate'>`, comparison is supported only with other `IsoWeek` objects

Ordering works by first checking that both operands share the same class and the same offset, then comparing their string values, which is correct because the ISO week date format is lexicographically ordered. See why iso-week-date? for why that holds.

Predicates

The comparison operators also come in a named form, which reads better inside filters, and include a bounded check:

print(iw.is_before(IsoWeek("2023-W02")))
print(iw.is_after(IsoWeek("2022-W52")))
True
True
print(iw.is_between(IsoWeek("2022-W52"), IsoWeek("2023-W02")))
print(iw.is_between(IsoWeek("2023-W01"), IsoWeek("2023-W02"), inclusive="neither"))
True
False

is_between accepts inclusive="both" (the default), "left", "right" or "neither".

Addition and subtraction

operations with ints

The two classes treat the int type differently in addition and subtraction:

  • for IsoWeek it is interpreted as weeks
  • for IsoWeekDate it is interpreted as days

Adding an int shifts the value; adding an iterable of ints returns a generator of shifted values:

print(iw + 1)
print(tuple(iw + (1, 2, 3)))
print(iwd + 1)
2023-W02
(IsoWeek(2023-W02) with offset 0:00:00, IsoWeek(2023-W03) with offset 0:00:00, IsoWeek(2023-W04) with offset 0:00:00)
2023-W01-2
print(iw - 1)
print(iw - IsoWeek("2022-W52"))  # distance as int
print(tuple(iw - (1, 2, 3)))
print(iwd - 1)
print(iwd - IsoWeekDate("2022-W52-3"))
2022-W52
1
(IsoWeek(2022-W52) with offset 0:00:00, IsoWeek(2022-W51) with offset 0:00:00, IsoWeek(2022-W50) with offset 0:00:00)
2022-W52-7
5

Subtracting another instance of the same class returns the distance between the two as an int: weeks for IsoWeek, days for IsoWeekDate.

The operators are also available as the named methods add and sub, which accept exactly the same arguments and are handier when passed around as callables:

print(iw.add(2))
print(iw.sub(2))
print(tuple(iw.add((1, 2))))
2023-W03
2022-W51
(IsoWeek(2023-W02) with offset 0:00:00, IsoWeek(2023-W03) with offset 0:00:00)

Stepping by one

For the common case of moving a single step, use next and previous:

print(iw.next(), iw.previous())
print(iwd.next(), iwd.previous())
2023-W02 2022-W52
2023-W01-2 2022-W52-7

Both classes also implement __next__, so the next() builtin works on an instance. It returns a new object rather than advancing the existing one:

print(next(iw), iw)
2023-W02 2023-W01

Arithmetic is bounded by ISO years 0001 and 9999

Values are backed by datetime.date, so stepping past either end raises the standard library's own error rather than a domain one: OverflowError from the timedelta paths (next, previous, +, -, weeksout, daysout) and ValueError from the strptime ones (days, nth, to_date, to_datetime). Guard with except (OverflowError, ValueError) if you work near the bounds. This is deliberate: a bounds check on every arithmetic call would cost every caller something to protect a range essentially nobody reaches.

9999-W52 is the last representable week and a partial one, since it runs into year 10000 from its sixth day on:

from iso_week_date import IsoWeek

print(IsoWeek("9999-W52").nth(5))

try:
    IsoWeek("9999-W52").nth(6)
except (OverflowError, ValueError) as e:
    print(f"day 6: {type(e).__name__}")
9999-12-31
day 6: ValueError

Replacing components

replace returns a new instance with some components swapped out, leaving the others untouched. Its arguments are keyword-only:

print(iw.replace(year=2024))
print(iwd.replace(week=2, weekday=5))
2024-W01
2023-W02-5

Ranges

range is a classmethod that generates every value between two endpoints, with a configurable step and inclusiveness. Endpoints can be passed as strings, dates, datetimes or instances:

_range = IsoWeek.range(
    start="2023-W01", end="2023-W07", step=2, inclusive="both", as_str=True
)
print(tuple(_range))
('2023-W01', '2023-W03', '2023-W05', '2023-W07')
_range = IsoWeekDate.range(
    start="2023-W01-1", end="2023-W03-3", step=3, inclusive="left", as_str=True
)
print(tuple(_range))
('2023-W01-1', '2023-W01-4', '2023-W01-7', '2023-W02-3', '2023-W02-6', '2023-W03-2')

as_str=False yields class instances instead of strings, and inclusive accepts the same four values as is_between.

The generated values always sit on a grid anchored at start, and inclusive removes the endpoints from that grid rather than shifting it. With a step greater than 1 the grid may skip over end entirely, in which case there is no end for inclusive to keep or drop:

for inclusive in ("both", "left", "right", "neither"):
    weeks = IsoWeek.range(start="2023-W01", end="2023-W05", step=2, inclusive=inclusive)
    print(f"{inclusive:8} {tuple(weeks)}")
both     ('2023-W01', '2023-W03', '2023-W05')
left     ('2023-W01', '2023-W03')
right    ('2023-W03', '2023-W05')
neither  ('2023-W03',)

IsoWeek specific

days property and nth method

days returns the tuple of dates in the week, and nth picks one of them (1-indexed, Monday first):

print(iw.days)
print(iw.nth(1), iw.nth(7))
(datetime.date(2023, 1, 2), datetime.date(2023, 1, 3), datetime.date(2023, 1, 4), datetime.date(2023, 1, 5), datetime.date(2023, 1, 6), datetime.date(2023, 1, 7), datetime.date(2023, 1, 8))
2023-01-02 2023-01-08

weeksout method

weeksout generates the weeks that are up to n_weeks after the given week:

print(tuple(iw.weeksout(3)))
print(tuple(iw.weeksout(6, step=2, as_str=False)))
('2023-W02', '2023-W03', '2023-W04')
(IsoWeek(2023-W02) with offset 0:00:00, IsoWeek(2023-W04) with offset 0:00:00, IsoWeek(2023-W06) with offset 0:00:00)

contains method and in operator

contains checks whether a week, string, date or datetime (or an iterable of them) falls inside the given week:

print(iw.contains("2023-W01"))
print(iw.contains(date(2023, 1, 1)))
print(iw.contains((IsoWeek("2023-W01"), date(2023, 1, 1), date(2023, 1, 2))))
True
False
(True, False, True)

A single value is also accepted by the in operator, via __contains__:

print(date(2023, 1, 1) in iw)
print(date(2023, 1, 2) in iw)
False
True

IsoWeekDate specific

weekday and isoweek properties

print(iwd.weekday)  # 1 is Monday, 7 is Sunday
print(iwd.isoweek)  # the week this date belongs to, as a string
1
2023-W01

day is a deprecated alias

IsoWeekDate.day still works as an alias of weekday, kept for backward compatibility. Prefer weekday in new code.

daysout method

daysout generates the dates that are up to n_days after the given date:

print(tuple(iwd.daysout(3)))
print(tuple(iwd.daysout(6, step=3, as_str=False)))
('2023-W01-2', '2023-W01-3', '2023-W01-4')
(IsoWeekDate(2023-W01-2) with offset 0:00:00, IsoWeekDate(2023-W01-5) with offset 0:00:00)

Where to next