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 proper way.
Both require the pydantic extra, see installation:
python -m pip install "iso-week-date[pydantic]"
Every output on this page is generated by running the code shown.
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 pydantic import BaseModel, StringConstraints
from iso_week_date import ISOWEEK_PATTERN, ISOWEEKDATE_PATTERN
T_ISOWeekStr = Annotated[str, StringConstraints(pattern=ISOWEEK_PATTERN.pattern)]
T_ISOWeekDateStr = Annotated[
str, StringConstraints(pattern=ISOWEEKDATE_PATTERN.pattern)
]
class MyModel(BaseModel):
week: T_ISOWeekStr
week_date: T_ISOWeekDateStr
print(MyModel(week="2023-W01", week_date="2023-W01-1"))
print(
MyModel(week="2023-W53", week_date="2023-W01-1")
) # accepted, but 2023 has no week 53
week='2023-W01' week_date='2023-W01-1'
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 purposes, 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. See
why validation is stricter than the regex for the
reasoning.
What happens to such a value downstream depends on your Python version, which is its own reason not to let it through:
import sys
from datetime import datetime
print(f"python {sys.version_info.major}.{sys.version_info.minor}")
# 2023 has 52 weeks
try:
print(datetime.strptime("2023-W53-1", "%G-W%V-%u"))
except ValueError as e:
print(f"ValueError: {e}")
print(datetime.strptime("2024-W01-1", "%G-W%V-%u"))
python 3.14
ValueError: Invalid week: 53
2024-01-01 00:00:00
Up to Python 3.11, strptime accepted 2023-W53-1 and silently normalized it to the same datetime as 2024-W01-1,
turning an impossible week into a plausible date a year off. Since Python 3.12 it raises ValueError instead. Neither
outcome is one you want to discover in production, so it is better for the model field to reject the value at the
boundary.
The proper way
The pydantic submodule implements the T_ISOWeek and T_ISOWeekDate custom types using
custom validation with __get_pydantic_core_schema__,
which run the same validation as the classes themselves. It requires pydantic>=2.4.0.
from pydantic import BaseModel, ValidationError
from iso_week_date.pydantic import T_ISOWeek, T_ISOWeekDate
class MyModel(BaseModel):
week: T_ISOWeek
week_date: T_ISOWeekDate
print(MyModel(week="2023-W01", week_date="2023-W01-1")) # All good here!
try:
MyModel(week="2023-W53", week_date="2023-W01-1")
except ValidationError as e:
print(e)
week='2023-W01' week_date='2023-W01-1'
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]
Which one to use
- Use the regex types when you are validating a shape at a boundary, in volume, and a week number that never existed is an acceptable risk.
- Use the
iso_week_date.pydantictypes when the value has to be a week that really existed. This is the default recommendation.
Compact formats
The compact formats (YYYYWNN, YYYYWNND) are not directly available in the module. However if needed it is possible to compose them from the building blocks:
from typing import Final
from iso_week_date._patterns import ( # These are strings, not compiled patterns
WEEK_MATCH,
WEEKDAY_MATCH,
YEAR_MATCH,
)
ISOWEEK_COMPACT_PATTERN: Final[str] = rf"^{YEAR_MATCH}{WEEK_MATCH}$"
ISOWEEKDATE_COMPACT_PATTERN: Final[str] = rf"^{YEAR_MATCH}{WEEK_MATCH}{WEEKDAY_MATCH}$"
T_ISOWeekCompact = Annotated[str, StringConstraints(pattern=ISOWEEK_COMPACT_PATTERN)]
T_ISOWeekDateCompact = Annotated[
str, StringConstraints(pattern=ISOWEEKDATE_COMPACT_PATTERN)
]
class CompactModel(BaseModel):
week: T_ISOWeekCompact
week_date: T_ISOWeekDateCompact
print(CompactModel(week="2023W01", week_date="2023W011"))
week='2023W01' week_date='2023W011'
_patterns is a private module
iso_week_date._patterns is private: its contents may change or move without a deprecation cycle. If you depend on
it, pin the version and cover it with a test. For validation that is guaranteed to keep working, parse with
IsoWeek.from_compact instead.