pydantic-extra-types
pydantic-extra-types copied to clipboard
`ISOWeek`, `ISOWeekDate` and `OrdinalDate`
Hi everyone 👋🏼, I hope this is the right place to ask 🙃
Lately I have been working a lot with ISO Week, ISO Week Date and Ordinal Dates formats (resp. YYYY-WNN, YYYY-WNN-D, YYYY-DDD, where YYYY indicates the year, NN the week number, D the weekday, DDD the day of the year, and W is just a literal)
In terms of pydantic types, these would just be pattern constrained strings (*), however it could be useful to have them already implemented to plug and play:
(Pseudo)implementation:
from typing import Annotated, Final
from pydantic import BaseModel, StringConstraints
# Year should range between 0001 and 9999
YEAR_MATCH: Final[str] = r"([1-9]\d{3}|0\d{2}[1-9]|0\d[1-9]\d|0[1-9]\d{2})"
# Week should range between 01 and 53
WEEK_MATCH: Final[str] = r"(W0[1-9]|W[1-4]\d|W5[0-3])"
# Weekday should range between 1 and 7
WEEK_DAY_MATCH: Final[str] = r"([1-7])"
# Ordinal day should range between 001 and 366
ORDINAL_DAY_MATCH: Final[str] = r"(36[0-6]|3[0-5]\d|[1-2]\d{2}|0\d[1-9]|0\d[1-9])"
# Compose patterns
ISOWEEK_PATTERN: Final[str] = r"^{}-{}$".format(YEAR_MATCH, WEEK_MATCH)
ISOWEEKDATE_PATTERN: Final[str] = r"^{}-{}-{}$".format(YEAR_MATCH, WEEK_MATCH, WEEK_DAY_MATCH)
ORDINALDATE_PATTERN: Final[str] = r"^{}-{}$".format(YEAR_MATCH, ORDINAL_DAY_MATCH)
# Pydantic compatible types
ISOWeek = Annotated[str, StringConstraints(pattern=ISOWEEK_PATTERN)]
ISOWeekDate = Annotated[str, StringConstraints(pattern=ISOWEEKDATE_PATTERN)]
OrdinalDate = Annotated[str, StringConstraints(pattern=ORDINALDATE_PATTERN)]
(*) While this is not 100% correct because some combinations should not be possible (not every year has 53 weeks and/or 366 days), python datetime module deals with that automatically:
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)
# 2023 has 365 days
datetime.strptime("2023-366", "%Y-%j") # datetime(2024, 1, 1, 0, 0)
datetime.strptime("2024-001", "%Y-%j") # datetime(2024, 1, 1, 0, 0)
Happy to open a PR if this is something you could be interested in adding
Edit: Each of these type could also have a compact version without dashes, i.e. matching the formats YYYYWNN, YYYYWNND and YYYYDDD, which implementation is straightforward from above.
Selected Assignee: @PrettyWood