pydantic / pydantic-extra-types

Extra Pydantic types.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

`ISOWeek`, `ISOWeekDate` and `OrdinalDate`

FBruzzesi opened this issue Β· comments

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