Skip to content

feat(is_dst): real per-backend implementation (item 65) - #291

Merged
discreteds merged 3 commits into
developfrom
feature/65-is-dst-real
Aug 17, 2026
Merged

feat(is_dst): real per-backend implementation (item 65)#291
discreteds merged 3 commits into
developfrom
feature/65-is-dst-real

Conversation

@discreteds

Copy link
Copy Markdown
Member

Summary

Replaces the is_dst placeholder (return False on every backend) with a real per-backend implementation, gated by a capability fact.

  • Build time: timezone is now required — is_dst() with no timezone raises InvalidOptionValueError.
  • Polars: native dt.convert_time_zone(tz).dt.dst_offset() != 0.
  • Narwhals: hand-rolled two-reference-offset algorithm (no native dst_offset equivalent); verified against zoneinfo across 7 zones, both hemispheres, on narwhals-polars and narwhals-pandas. Constructed to avoid a narwhals 2.24.0 + pandas 3.0.5 bug where a second convert_time_zone chained onto a America/New_York/America/Los_Angeles-targeted expression raises AttributeError.
  • Ibis: declared_unsupported — no DST/timezone-offset primitive exists at all. Enforced via a new is_dst.timezone CapabilityFact (family default + ibis-duckdb).

Spec / Plan

  • Spec: mountainash-central/.../specs/2026-08-15-is-dst-real-implementation-design.md (rev 2, adversarially reviewed + empirically verified against pinned library versions)
  • Plan: mountainash-central/.../plans/2026-08-15-is-dst-real-implementation.md (rev 2, 6 tasks)

Verification

  • New cross-backend test file tests/expressions/cross_backend/test_datetime_is_dst.py: 11 cases (7 temporal backends × hemisphere pairs + 3 ibis capability-gate cases), all passing.
  • Full suite: 22543 passed, 0 failed (up from 22517 baseline + this PR's new tests).
  • ruff check: clean. mypy: only pre-existing baseline import-untyped/found-twice noise, no errors in touched code.
  • Regenerated docs/reference/expression-coverage.{md,json} for the new capability fact.

🤖 Generated with SDD (spec → plan → execute)

- require timezone at build time (InvalidOptionValueError when omitted)
- protocol/backend signatures tightened to timezone: str (no default)
- polars: native dt.convert_time_zone + dt.dst_offset() != 0
- narwhals: hand-rolled two-reference-offset algorithm (no native dst_offset
  equivalent); avoids the narwhals 2.24.0 + pandas 3.0.5 chained-convert_time_zone
  bug for America/New_York and America/Los_Angeles by never deriving utc_naive
  by converting back from local_naive
- ibis: declared_unsupported (no DST/timezone-offset primitive at all); new
  is_dst.timezone UNSUPPORTED capability fact on ibis family-default + ibis-duckdb
- new cross-backend test file, 11 cases, all passing (7 temporal backends x
  hemisphere pairs + 3 ibis capability-gate cases)
- fixed two pre-existing tests written for the old permissive no-arg/placeholder
  behavior (test_is_dst_builder_validation, test_remaining_ops_results docstring)
…zone (item 65)

- add IS_DST smoke override (America/New_York UTC pair) to _smoke_helpers.py
  so the generic introspection-driven smoke/reachability/signature-conformance
  suites synthesize a valid IANA timezone instead of the generic 'x' placeholder
- register is_dst.timezone in the disposition-matrix open-value domains
  (option_disposition.py OPTION_DTYPES + _MA_OPTION_VALUE_DOMAINS), mirroring
  to_timezone exactly (same value class, same ibis-unsupported shape)
- add a full is_dst OptionCell/OptionProbeRegistration/InvalidOptionRejection
  registration section to test_arg_types_datetime.py so the new ibis capability
  fact is backed by a declared disposition cell, and every (backend, timezone)
  combination gets a runtime probe assertion
- fix test_is_dst AST test and test_is_dst_builder_validation (cross_backend)
  to pass the now-required timezone argument
- regenerate docs/reference/expression-coverage.{md,json} + scoped variant
  for the new is_dst.timezone ibis capability fact
- ruff --fix: drop unused Optional imports in the four is_dst signature files

Full suite: 22543 passed, 0 failed. ruff clean. mypy: 97 pre-existing
import-untyped/found-twice baseline errors only, no errors in touched code.
@discreteds
discreteds merged commit c03076e into develop Aug 17, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant