Problem
period_type is a predefined string enum ("hourly", "daily", "monthly", "yearly") that is used as a branching key in at least five separate modules. Adding any new granularity — bi-weekly (P2W), six-hourly (PT6H), dekadal (P10D) — requires touching core code in multiple places. A custom dataset with an unsupported period type will hit an "unsupported period_type" error at runtime.
The enum already has a parallel ISO 8601 representation: extents.temporal.resolution (e.g. P1D, P1M, PT1H) was introduced in time.py alongside _iso_step_to_approx_hours. Both fields exist in the codebase with no single source of truth.
Where period_type is used today
| File |
Usage |
shared/time.py |
datetime_to_period_string, normalize_period_string, numpy_datetime_to_period_string |
data_manager/services/downloader.py |
Chunk size computation |
ingestions/sync_engine.py |
_next_period_start, _default_target_end |
ingestions/services.py |
Period normalization, default end computation |
providers/availability.py |
Availability cutoff computation |
data_accessor/services/accessor.py |
Coverage metadata |
processing/resample.py |
_frequency_to_period_type, _previous_source_period_start |
stac/services.py |
_period_step — converts period_type → ISO step for STAC |
| Dataset YAMLs |
era5_land.yaml (×2), chirps3.yaml, worldpop.yaml |
What the handling actually does
Most of the branching is string formatting, not logic:
- Period string truncation — decides how many characters to keep from an ISO datetime (
"2020-01-01" → "2020-01" for monthly). Derivable from ISO duration precision.
- Period arithmetic — advances one period for sync. Replaceable with
dateutil.relativedelta or pandas.DateOffset driven by the parsed duration.
- Chunk sizing — already handled by
_iso_step_to_approx_hours; the period_type-based version is redundant.
- Default end computation — "what is today for this period type?" Business logic, not a type system.
- STAC step —
_period_step() converts period_type → ISO duration; eliminated once ISO is canonical.
Target state
extents.temporal.resolution (ISO 8601 duration) is the canonical field in all dataset YAMLs; period_type is removed
- Period string truncation length is derived mechanically from duration precision (
PT1H → 13 chars, P1D → 10, P1M → 7, P1Y → 4)
- Period arithmetic uses
dateutil.relativedelta / pandas.DateOffset driven by the parsed duration — no lookup table of named types
_period_step() in stac/services.py is deleted
- Any dataset with a valid ISO 8601 duration in
extents.temporal.resolution works without core changes
Short-term fix (unblocks #129)
Validate extents.temporal.resolution centrally in time.py — resolve_iso_period_step should verify the string is a well-formed ISO 8601 duration using _iso_step_to_approx_hours, logging a warning and returning None if invalid. This is the fix requested in the #129 review and does not touch period_type.
Related
Problem
period_typeis a predefined string enum ("hourly","daily","monthly","yearly") that is used as a branching key in at least five separate modules. Adding any new granularity — bi-weekly (P2W), six-hourly (PT6H), dekadal (P10D) — requires touching core code in multiple places. A custom dataset with an unsupported period type will hit an"unsupported period_type"error at runtime.The enum already has a parallel ISO 8601 representation:
extents.temporal.resolution(e.g.P1D,P1M,PT1H) was introduced intime.pyalongside_iso_step_to_approx_hours. Both fields exist in the codebase with no single source of truth.Where period_type is used today
shared/time.pydatetime_to_period_string,normalize_period_string,numpy_datetime_to_period_stringdata_manager/services/downloader.pyingestions/sync_engine.py_next_period_start,_default_target_endingestions/services.pyproviders/availability.pydata_accessor/services/accessor.pyprocessing/resample.py_frequency_to_period_type,_previous_source_period_startstac/services.py_period_step— convertsperiod_type→ ISO step for STACera5_land.yaml(×2),chirps3.yaml,worldpop.yamlWhat the handling actually does
Most of the branching is string formatting, not logic:
"2020-01-01"→"2020-01"for monthly). Derivable from ISO duration precision.dateutil.relativedeltaorpandas.DateOffsetdriven by the parsed duration._iso_step_to_approx_hours; theperiod_type-based version is redundant._period_step()convertsperiod_type→ ISO duration; eliminated once ISO is canonical.Target state
extents.temporal.resolution(ISO 8601 duration) is the canonical field in all dataset YAMLs;period_typeis removedPT1H→ 13 chars,P1D→ 10,P1M→ 7,P1Y→ 4)dateutil.relativedelta/pandas.DateOffsetdriven by the parsed duration — no lookup table of named types_period_step()instac/services.pyis deletedextents.temporal.resolutionworks without core changesShort-term fix (unblocks #129)
Validate
extents.temporal.resolutioncentrally intime.py—resolve_iso_period_stepshould verify the string is a well-formed ISO 8601 duration using_iso_step_to_approx_hours, logging a warning and returningNoneif invalid. This is the fix requested in the #129 review and does not touchperiod_type.Related