commit 3d7d5bab6c51ba409c386a8ecde9c5ae23148707
parent a4c8a75380f18c3573c7aed1c03d0ad4a3aa3448
Author: triesap <tyson@radroots.org>
Date: Mon, 21 Sep 2026 12:58:52 +0000
core: handle local-time ambiguity and window boundaries
Diffstat:
2 files changed, 67 insertions(+), 0 deletions(-)
diff --git a/src/hyf_core/normalization/dates.mojo b/src/hyf_core/normalization/dates.mojo
@@ -90,3 +90,41 @@ def resolve_relative_expression(
ambiguity="none",
)
return resolve_weekday(reference, lowered, "next")
+
+
+from std.collections import List
+
+
+@fieldwise_init
+struct LocalTimeResolution(Copyable, Movable):
+ var resolution: String
+ var ambiguity: String
+ var offset_minutes: Int
+
+
+def resolve_local_time(candidate_offsets: List[Int]) raises -> LocalTimeResolution:
+ if len(candidate_offsets) == 0:
+ return LocalTimeResolution(
+ resolution="unresolved", ambiguity="missing_zone", offset_minutes=0
+ )
+ if len(candidate_offsets) > 1:
+ return LocalTimeResolution(
+ resolution="unresolved",
+ ambiguity="ambiguous_local",
+ offset_minutes=0,
+ )
+ return LocalTimeResolution(
+ resolution="resolved",
+ ambiguity="none",
+ offset_minutes=candidate_offsets[0],
+ )
+
+
+def window_contains(
+ start_day: Int, end_day: Int, boundary: String, point_day: Int
+) raises -> Bool:
+ if boundary == "inclusive":
+ return point_day >= start_day and point_day <= end_day
+ if boundary == "exclusive":
+ return point_day >= start_day and point_day < end_day
+ raise Error("window boundary must be 'inclusive' or 'exclusive'")
diff --git a/tests/test_domain.mojo b/tests/test_domain.mojo
@@ -278,3 +278,32 @@ def test_source_anchored_relative_date_replay() raises:
assert_equal(next.date.day, 28)
# Replay at a later evaluation time uses the same source reference.
assert_equal(resolve_relative_expression("Friday", monday).date.day, 25)
+
+
+from hyf_core.normalization.dates import (
+ LocalTimeResolution,
+ resolve_local_time,
+ window_contains,
+)
+
+
+def test_local_time_ambiguity_and_window_boundaries() raises:
+ var offsets = List[Int]()
+ offsets.append(-420)
+ offsets.append(-480)
+ var ambiguous = resolve_local_time(offsets)
+ assert_equal(ambiguous.resolution, "unresolved")
+ assert_equal(ambiguous.ambiguity, "ambiguous_local")
+
+ var empty = List[Int]()
+ assert_equal(resolve_local_time(empty).ambiguity, "missing_zone")
+
+ var single = List[Int]()
+ single.append(-420)
+ assert_equal(resolve_local_time(single).resolution, "resolved")
+
+ assert_true(window_contains(20717, 20721, "exclusive", 20721) == False)
+ assert_true(window_contains(20717, 20721, "inclusive", 20721))
+ assert_true(window_contains(20717, 20721, "exclusive", 20720))
+ with assert_raises():
+ _ = window_contains(1, 2, "half-open", 1)