Source code for dran.utils.time_utils
# =========================================================================== #
# File: time_utils.py #
# Author: Pfesesani V. van Zyl #
# Email: pfesi24@gmail.com #
# =========================================================================== #
# Library imports
# --------------------------------------------------------------------------- #
from datetime import date, datetime, timedelta
import re
# =========================================================================== #
# --------------------------------------------------------------------------- #
# Module-level constants
# --------------------------------------------------------------------------- #
_DOY_TIMESTAMP_RE = re.compile(
r"^(?P<year>\d{4})d(?P<doy>\d{3})_(?P<hour>\d{2})h(?P<minute>\d{2})m(?P<second>\d{2})s$"
)
def _is_leap_year(year: int) -> bool:
return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)
def _validate_doy(year: int, doy: int) -> None:
max_doy = 366 if _is_leap_year(year) else 365
if doy < 1 or doy > max_doy:
raise ValueError(f"DOY must be in 1..{max_doy} for year {year}.")
[docs]
def doy_to_date(year: int, doy: int) -> date:
"""
Convert day-of-year to a calendar date.
Args:
year: Four-digit year, for example 2024.
doy: Day of year, starting at 1.
Returns:
datetime.date instance.
"""
_validate_doy(year, doy)
return date(year, 1, 1) + timedelta(days=doy - 1)
[docs]
def parse_doy_timestamp(value: str) -> datetime:
"""
Parse a timestamp in the form YYYYdDOY_HHhMMmSSs.
Example input:
2023d281_00h01m59s
Returns:
datetime.datetime instance in UTC-like naive time.
"""
match = _DOY_TIMESTAMP_RE.match(value)
if not match:
raise ValueError("Invalid format. Expected YYYYdDOY_HHhMMmSSs")
year = int(match.group("year"))
doy = int(match.group("doy"))
hour = int(match.group("hour"))
minute = int(match.group("minute"))
second = int(match.group("second"))
_validate_doy(year, doy)
if not (0 <= hour <= 23):
raise ValueError("Hour must be in 0..23.")
if not (0 <= minute <= 59):
raise ValueError("Minute must be in 0..59.")
if not (0 <= second <= 59):
raise ValueError("Second must be in 0..59.")
base_date = date(year, 1, 1) + timedelta(days=doy - 1)
return datetime(
year=base_date.year,
month=base_date.month,
day=base_date.day,
hour=hour,
minute=minute,
second=second,
)