Skip to content

Commit fca8931

Browse files
dylanbstoreyDylan Bobby Storey
andauthored
metis: file DST gaps as follow-up bugs (T-0342/0343/0344) + cross-link T-0341 (#89)
Co-authored-by: Dylan Bobby Storey <dstorey@dstorey-personal-m3.local>
1 parent 5682285 commit fca8931

4 files changed

Lines changed: 202 additions & 0 deletions

File tree

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
---
2+
id: dst-duration-across-transition
3+
level: task
4+
title: "DST: duration across a DST transition ignores the gained/lost hour"
5+
short_code: "GQLITE-T-0342"
6+
created_at: 2026-06-01T00:00:00.000000+00:00
7+
updated_at: 2026-06-01T00:00:00.000000+00:00
8+
parent:
9+
blocked_by: []
10+
archived: false
11+
tags:
12+
- "#task"
13+
- "#phase/backlog"
14+
- "#bug"
15+
exit_criteria_met: false
16+
initiative_id: NULL
17+
---
18+
19+
# DST: duration across a DST transition ignores the gained/lost hour
20+
21+
## Type
22+
- [x] Bug
23+
24+
## Priority
25+
- [ ] P2 - Medium (TCK conformance; blocks the "100% TCK" goal)
26+
27+
## Objective
28+
`duration.inSeconds` / `duration.between` over an interval that crosses a DST
29+
transition must account for the gained/lost hour (wall-clock vs elapsed time).
30+
Closes **Temporal10 [8]** (8 examples).
31+
32+
## Impact Assessment
33+
- **Affected:** openCypher TCK Temporal10 [8] "Should handle durations at daylight
34+
saving time day" — 8 example rows.
35+
- **Reproduction:**
36+
```cypher
37+
RETURN duration.inSeconds(
38+
datetime({year:2017, month:10, day:29, hour:0, timezone:'Europe/Stockholm'}),
39+
localdatetime({year:2017, month:10, day:29, hour:4})
40+
) AS d
41+
```
42+
- **Expected vs Actual:** expected `PT5H` (2017-10-29 is the Stockholm fall-back
43+
day; 00:00->04:00 wall clock = 5 real hours because 03:00 falls back to 02:00);
44+
actual `PT4H` (flat UTC diff using a single offset, missing the extra hour).
45+
Also `... date({year:2017,month:10,day:30})` expects `PT25H`, we give `PT24H`.
46+
47+
## Root cause (shared — see also [[GQLITE-T-0343]] / [[GQLITE-T-0344]])
48+
`apply_duration_to_temporal` / the temporal-diff helpers compute elapsed time with
49+
ONE offset for the whole interval. Across a DST transition the offset changes
50+
mid-interval, so the elapsed seconds differ from `end_utc - start_utc` computed
51+
with a single offset. Correct handling needs the exact transition INSTANT for the
52+
named zone, which requires real IANA tzdata (see T-0344). Implementation analysis
53+
in [[GQLITE-T-0341]] (initiative [[GQLITE-I-0049]]).
54+
55+
## Acceptance Criteria
56+
- [ ] Temporal10 [8] all 8 examples pass
57+
- [ ] Zero TCK regressions (rigorous full pass-set diff); unit 944/944; functional clean
58+
59+
## Notes
60+
Deferred from the +30 Temporal push (PR #88). Likely depends on the IANA-tzdata
61+
work in [[GQLITE-T-0344]] (need transition instants, not just an offset-by-date).
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
---
2+
id: dst-named-zone-offset-approximation
3+
level: task
4+
title: "DST: named-zone offset uses coarse month approximation, not historical rules"
5+
short_code: "GQLITE-T-0343"
6+
created_at: 2026-06-01T00:00:00.000000+00:00
7+
updated_at: 2026-06-01T00:00:00.000000+00:00
8+
parent:
9+
blocked_by: []
10+
archived: false
11+
tags:
12+
- "#task"
13+
- "#phase/backlog"
14+
- "#bug"
15+
exit_criteria_met: false
16+
initiative_id: NULL
17+
---
18+
19+
# DST: named-zone offset uses coarse month approximation, not historical rules
20+
21+
## Type
22+
- [x] Bug
23+
24+
## Priority
25+
- [ ] P2 - Medium (TCK conformance; blocks "100% TCK")
26+
27+
## Objective
28+
Resolve a named zone's UTC offset for a given date using the zone's actual DST
29+
transition dates, including historical changes — not the current coarse
30+
"April–September = summer" month heuristic. Closes **Temporal3 [10]** (2 remaining
31+
tz-conversion examples) and is a prerequisite for clean DST elsewhere.
32+
33+
## Impact Assessment
34+
- **Affected:** `named_tz_offset()` in `src/backend/runtime/udf_helpers.c`, used by
35+
datetime construction, parsing, `duration.between`, the `.offset`/`.epochSeconds`
36+
accessors, and time rendering.
37+
- **Reproduction:**
38+
```cypher
39+
RETURN datetime({year:1984, month:3, day:28, hour:12, timezone:'Europe/Stockholm'})
40+
```
41+
Expected `1984-03-28T12:00+02:00[Europe/Stockholm]` (DST started 1984-03-25);
42+
the coarse month rule yields `+01:00` for March. (Direct construction was made
43+
to work; the SELECT/tz-conversion forms in Temporal3 [10] ex31/32 remain.)
44+
45+
## Root cause + the trap (CRITICAL)
46+
The coarse heuristic is **load-bearing**. A naive "modern" last-Sunday-of-March..
47+
October EU rule REGRESSED **-52 examples**, because the TCK uses HISTORICALLY
48+
accurate IANA data: pre-1996 EU DST ended the last Sunday of SEPTEMBER (so
49+
`1984-10-11 Stockholm = +01:00`, not `+02:00`). A regression-free improvement was
50+
prototyped — historical end-month `(y>=1996)?Oct:Sep` + threading the real base
51+
date into `_gql_tz_offset_for(tz, _gql_date_compose(... $.date, $.datetime))` — and
52+
it fixes individual examples but flips no full scenario (so it was reverted to keep
53+
PR #88 clean). Full correctness needs real IANA transition tables (see
54+
[[GQLITE-T-0344]]). Detailed analysis: [[GQLITE-T-0341]] / [[GQLITE-I-0049]].
55+
56+
## Acceptance Criteria
57+
- [ ] Temporal3 [10] all examples pass
58+
- [ ] No regression in Temporal1 [10], Temporal3 [3]/[9]/[11] (the -52 trap)
59+
- [ ] Rigorous full pass-set diff zero regressions; unit 944/944; functional clean
60+
61+
## Notes
62+
Re-derive the regression-free foundation from the 2026-06-01 note in
63+
[[GQLITE-T-0341]] when picking this up. Shares root cause with [[GQLITE-T-0342]],
64+
[[GQLITE-T-0344]].
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
---
2+
id: dst-named-zone-string-parse-lmt
3+
level: task
4+
title: "DST: datetime string parse doesn't resolve named-zone historical/LMT offset"
5+
short_code: "GQLITE-T-0344"
6+
created_at: 2026-06-01T00:00:00.000000+00:00
7+
updated_at: 2026-06-01T00:00:00.000000+00:00
8+
parent:
9+
blocked_by: []
10+
archived: false
11+
tags:
12+
- "#task"
13+
- "#phase/backlog"
14+
- "#bug"
15+
exit_criteria_met: false
16+
initiative_id: NULL
17+
---
18+
19+
# DST: datetime string parse doesn't resolve named-zone historical/LMT offset
20+
21+
## Type
22+
- [x] Bug
23+
24+
## Priority
25+
- [ ] P3 - Low (needs an embedded IANA tz database; largest/most data-heavy)
26+
27+
## Objective
28+
Parsing a datetime string that carries a named zone must insert the resolved
29+
numeric offset (including pre-standardization Local Mean Time) before the
30+
`[Region]` suffix. Closes **Temporal2 [6]** (2 examples) and is the umbrella
31+
"embed IANA tzdata" work the other two DST bugs depend on.
32+
33+
## Impact Assessment
34+
- **Affected:** `gql_normalize_datetime_func` in `src/backend/runtime/udf_helpers.c`.
35+
- **Reproduction / Expected vs Actual:**
36+
- `datetime('2015-07-21T21:40:32.142[Europe/London]')`
37+
expected `2015-07-21T21:40:32.142+01:00[Europe/London]`,
38+
actual `2015-07-21T21:40:32.142[Europe/London]` (no resolved offset inserted).
39+
- `datetime('1818-07-21T21:40:32.142[Europe/Stockholm]')`
40+
expected `1818-07-21T21:40:32.142+00:53:28[Europe/Stockholm]`**Local Mean
41+
Time** (Stockholm's longitude-based offset before standardized zones in 1879).
42+
43+
## Root cause
44+
1. The datetime string normalizer keeps the `[Region]` bracket but never computes
45+
and inserts the offset for the value's date.
46+
2. `+00:53:28` cannot be produced by ANY rule — it is historical LMT data. This is
47+
the hard floor: full Temporal DST conformance requires embedding the real IANA
48+
tz database (transition instants + historical/LMT offset records). A rule-based
49+
approximation (see [[GQLITE-T-0343]]) cannot reach these cases.
50+
51+
## Suggested approach
52+
- Embed a compact subset of IANA tzdata for the zones the TCK exercises
53+
(Europe/Stockholm, Europe/London, Europe/Paris, Europe/Berlin, America/New_York,
54+
America/Los_Angeles, Pacific/Honolulu, America/Anchorage, Asia/Tokyo,
55+
Asia/Shanghai, Australia/Sydney, Pacific/Auckland, Australia/Eucla, ...),
56+
including historical transitions and pre-standard LMT, OR vendor a tz library.
57+
- Replace `named_tz_offset` + the transition logic with lookups against it; this
58+
then unblocks [[GQLITE-T-0342]] (across-transition elapsed) and
59+
[[GQLITE-T-0343]] (offset-by-date).
60+
61+
## Acceptance Criteria
62+
- [ ] Temporal2 [6] examples pass (incl. the 1818 LMT case)
63+
- [ ] Rigorous full pass-set diff zero regressions; unit 944/944; functional clean
64+
65+
## Notes
66+
This is the foundational DST item; [[GQLITE-T-0342]] and [[GQLITE-T-0343]] likely
67+
collapse into it once real tzdata is available. Context: [[GQLITE-T-0341]],
68+
[[GQLITE-I-0049]].

.metis/initiatives/GQLITE-I-0049/tasks/GQLITE-T-0341.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,3 +189,12 @@ Dug into the 12 remaining DST scenarios empirically. Findings:
189189
- RECOMMENDATION: close T-0341 as "Temporal non-DST done (+30)"; open a separate
190190
task "embed IANA tzdata for full temporal DST conformance" (large, data-heavy)
191191
for the 12 DST scenarios.
192+
193+
### 2026-06-01: DST gaps filed as bugs
194+
Non-DST Temporal closed via PR #88 (main 3758, +30). The 12 DST scenarios are now
195+
tracked as follow-up bugs:
196+
- [[GQLITE-T-0342]] — duration across a DST transition (Temporal10 [8], 8)
197+
- [[GQLITE-T-0343]] — named-zone offset coarse approximation / historical rules
198+
(Temporal3 [10], 2); documents the -52-regression trap
199+
- [[GQLITE-T-0344]] — datetime string parse named-zone historical/LMT offset
200+
(Temporal2 [6], 2); the foundational "embed IANA tzdata" item the others depend on

0 commit comments

Comments
 (0)