Skip to content

Working with Pydantic

If you want to work with ISO Week (date) format within pydantic v2, i.e. create a model with a string field representing an ISO Week (date) format, there are two options: the easy way and the hard proper way.

The easy way

The easy way to achieve this is via Annotated and StringConstraints with custom regex patterns.

The regex patterns are available in the top level module of iso-week-date, therefore it is possible to use them directly:

from typing import Annotated

from iso_week_date import ISOWEEK_PATTERN, ISOWEEKDATE_PATTERN
from pydantic import BaseModel, StringConstraints

T_ISOWeek = Annotated[str, StringConstraints(pattern=ISOWEEK_PATTERN.pattern)]
T_ISOWeekDate = Annotated[str, StringConstraints(pattern=ISOWEEKDATE_PATTERN.pattern)]

class MyModel(BaseModel):
    week: T_ISOWeek
    week_date: T_ISOWeekDate

m1 = MyModel(week='2023-W01', week_date='2023-W01-1')
m2 = MyModel(week='2023-W53', week_date='2023-W01-1')

Caveat

The caveat of this approach can be seen in the second instance in the example above. Namely the regex patterns could be not strict enough for your purpouses, i.e. they allow for some combinations that are not valid ISO Week (date) formats.

In fact not every combination of year and week number should be possible (not every year has 53 weeks!), but this is not enforced by the regex patterns.

Note

Remark that actual validation happens when instantiating IsoWeek and IsoWeekDate classes.

On the positive side, python datetime module deals with that automagically:

from datetime import datetime

# 2023 has 52 weeks
datetime.strptime("2023-W53-1", "%G-W%V-%u")  # datetime(2024, 1, 1, 0, 0)
datetime.strptime("2024-W01-1", "%G-W%V-%u")  # datetime(2024, 1, 1, 0, 0)

As we can see the datetime module is able to parse both 2023-W53-1 and 2024-W01-1 as the same datetime object (datetime(2024, 1, 1, 0, 0)).

The proper way

As of iso-week-date version 1.2.0, we provide a .pydantic submodule, which implements T_ISOWeek and T_ISOWeekDate custom types using custom validation with __get_pydantic_core_schema__.

Such implementation requires pydantic v2.4.0+ and pydantic-core features, which are under fast and active development.

from iso_week_date.pydantic import T_ISOWeek, T_ISOWeekDate
from pydantic import BaseModel

class MyModel(BaseModel):
    week: T_ISOWeek
    week_date: T_ISOWeekDate

m1 = MyModel(week='2023-W01', week_date='2023-W01-1')  # All good here!
m2 = MyModel(week='2023-W53', week_date='2023-W01-1')  # Raises ValidationError
ValidationError: 1 validation error for MyModel
week
  Invalid week number. Year 2023 has only 52 weeks. [type=T_ISOWeek, input_value='2023-W53', input_type=str]

Compact formats

The compact formats (YYYYWNN, YYYYWNND) are not directly available in the module. However if needed it is possible to composed them with some gymnastic:

from typing import Final
from iso_week_date._patterns import YEAR_MATCH, WEEK_MATCH, WEEKDAY_MATCH  # These are strings, not regex patterns

ISOWEEK_COMPACT_PATTERN: Final[str] = r"^{}{}$".format(YEAR_MATCH, WEEK_MATCH)
ISOWEEKDATE_COMPACT_PATTERN: Final[str] = r"^{}{}{}$".format(YEAR_MATCH, WEEK_MATCH, WEEK_DAY_MATCH)

T_ISOWeekCompact = Annotated[str, StringConstraints(pattern=ISOWEEK_COMPACT_PATTERN)]
T_ISOWeekDateCompact = Annotated[str, StringConstraints(pattern=ISOWEEKDATE_COMPACT_PATTERN)]