Repository navigation
Codegen train: timestamp date strings with a UTC or GMT zone word - #330
Conversation
timestamp("01 Feb 2024 00:00 UTC+24"), the same string with GMT-24 and with
UTC+25 are accepted by TradingView, which reads the zone word as plain
arithmetic on the wall-clock fields. The translator stops on them with an
internal ValueError instead of emitting the constant.
The new file pins the three values TradingView's exported trade lists decode
to (1706659200000, 1706832000000 and 1706655600000 ms), the same string as an
input.time default, the build of the emitted C++ for the three strings, and
six neighbouring forms that already translate (no zone, a bare UTC word,
UTC+2, GMT-5:30, UTC+23, UTC-23) so that the repair can be seen to move none
of them.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
…trings
timestamp("01 Feb 2024 00:00 UTC+24") raised an internal ValueError because
the zone word was turned into a datetime.timezone, which refuses an offset of
24 hours or more. TradingView treats the word as plain arithmetic on the date
fields: its exported trade lists for UTC+24, GMT-24 and UTC+25 decode to the
fields read as UTC minus the signed offset. The offset is now kept as a
timedelta and subtracted from the UTC reading of the fields.
The value for an offset under 24 hours, a bare UTC word, a string without a
zone and a string that carries its own numeric offset is unchanged.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
TradingView's exported trade lists for fourteen date strings with a UTC or GMT zone word decode to values the translator does not give. A negative zone with a zero hour (GMT-00:30, UTC-0:30) loses its sign, minutes of 60 or more (UTC+00:60, UTC+01:60) are added instead of dropped, a number without a sign after the word (UTC30) is added as minutes, and with a numeric offset and a word in one string (+0000 UTC+2) the numeric offset is used instead of the last zone token. Two of the strings (UTC5 and UTC+2 +0000) are refused as unparseable. The new file pins the fourteen values, which are decoded from the trade lists and not computed by the translator, the build of the emitted C++ for each, and six neighbouring forms that already agree with TradingView and must not move. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
TradingView's exported trade lists for fourteen date strings with a UTC or GMT zone word decode to values the translator did not give. The zone word is now read the way those lists show: - a signed zone keeps its sign even when its hour is zero (GMT-00:30 and UTC-0:30 are west of UTC; the sign was lost in an int conversion of "-00"); - minutes of 60 or more are dropped and the hours kept (UTC+01:60 is UTC+1, UTC+00:90 is UTC); - a number without a sign after the word is ignored (UTC30, UTC5); - with a numeric offset and a word in one string the last zone token decides (+0000 UTC+2 is UTC+2, UTC+2 +0000 is UTC). UTC5 and UTC+2 +0000 are no longer refused as unparseable. A string without a zone word, a zone word with a plain signed offset and a string that carries only a numeric offset translate to the same value as before. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
There was a problem hiding this comment.
Graphify reviewed this change.
No issues found by static checks — no coupling regressions or blocking issues in the code graph. That is not the same as safe to merge: see what was not checked.
Not checked: tests were not run; formal verification proved 0 of 1 changed function(s) (1 sampled, not proven).
Formal verification. PR-changed functions: 1/1 verified (0 proven, 1 may-equivalent, 0 distinguished) · 0 not verified.
Graphify review — findings
Fixes timestamp("...") date strings with a UTC/GMT zone word so the value matches TradingView. A signed zero-hour offset keeps its sign, minutes of 60 or more are dropped, an unsigned number after the word is ignored, and the last zone token wins when a numeric offset and a word share one string. UTC5 and UTC+2 +0000 now translate instead of being refused. Replaces the datetime.timezone offset with plain arithmetic on the fields read as UTC, so zone words of 24 hours or more (UTC+24, GMT-24, UTC+25) emit a constant instead of stopping with an internal ValueError. The "last token decides" handling only strips a trailing ±HHMM that sits next to the word, so an ISO-style offset followed by a later zone word is the case that can still resolve to the wrong zone.
No blocking issues surfaced. 2 lower-confidence candidates did not survive cross-model review.
Analysis details — impact, health, verification
Impact & health
Graphify review
Impact — 216 functions depend on the 210 functions this change touches.
Health — grade A; no new coupling hotspots.
Verification — 216 functions in the blast radius were not formally verified this run (proofs are advisory here).
Gate & verification
graphify gate
PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.
Advisory (not blocking):
- verification_scope: 216 function(s) in the blast radius were not formally verified this run
Cross-repo impact
Cross-repo impact
No cross-repo impact detected among the indexed peer repo(s).
1 peer repo(s) in this group are not yet indexed — cross-repo impact may be incomplete.
Cross-repo linkage is derived (SCIP-moniker matches are sound; name/contract matches are heuristic) and ADVISORY — it never affects this check's conclusion.
Formal verification
No difference found (not proven): No behavior difference found in \_parse\_pine\_datestring\_ms (not a proof).
The verifier ran both versions of \_parse\_pine\_datestring\_ms on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.
Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.
Note: An input the sampler did not try could still differ.
Two files conflicted, both in how timestamp(dateString) reads a date string. pineforge_codegen/codegen/visit_call.py, _parse_pine_datestring_ms: the branch had added full month names and kept the written sign of a zero-hour zone (GMT-00:30) by reading the old two-group zone regex; main (#330) replaced that regex and the zone arithmetic with the zone-word grammar that reads the sign, hours and minutes separately and lets the last zone token decide. The merge keeps the branch's full month names (the docstring wording and the %B formats merged without conflict) and takes main's zone-word code, which already keeps the written sign of a negative zone with a zero hour, so both behaviours hold. CHANGELOG.md: both sides added entries under "Unreleased"; all of them are kept. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
After merging codegen main into this branch, four rows of tests/test_timestamp_zero_hour_offset.py failed: UTC and GMT with +24:00 and -24:00 were expected to keep raising ValueError. That error came from building the zone word as a datetime.timezone, which refuses an offset of 24 hours or more. Main replaced it (the zone-word change in #330): the offset is plain arithmetic on the date fields, as in TradingView, whose exported trade lists for UTC+24, GMT-24 and UTC+25 match that rule. So the rows now pin the value instead of the old failure: the fields read as UTC minus the signed offset (2024-01-01 00:00 +24:00 is 2023-12-31 00:00 UTC, -24:00 is 2024-01-02 00:00 UTC), with the expected fields written independently of the parser like the rows above them. The +-24:00 spelling is the same rule applied; the exported lists cover the spellings UTC+24 and GMT-24. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Two changes to how
timestamp("...")reads theUTC/GMTzone word of a date string, in this order on top of main. The first was opened as #329 and checked on its own; the second was checked on top of it. This pull request carries both.Closes #329.
1. A zone word of 24 hours or more no longer stops the translator (#329)
timestamp("01 Feb 2024 00:00 UTC+24"), the same string withGMT-24and withUTC+25stopped the translator with an internalValueError(offset must be a timedelta strictly between -timedelta(hours=24) and timedelta(hours=24)), because the zone word was turned into adatetime.timezone, which Python limits to under 24 hours. TradingView accepts these strings and reads the word as plain arithmetic on the date fields. Its exported trade lists for scripts built on them decode to the values below (01 Feb 2024 00:00read as UTC is 1706745600000 ms):01 Feb 2024 00:00 UTC+2401 Feb 2024 00:00 GMT-2401 Feb 2024 00:00 UTC+25The zone word is now kept as a
timedeltaand subtracted from the UTC reading of the fields.2. Zone word forms that gave another value than TradingView
TradingView's trade lists for fourteen more date strings decode to values the translator did not give; two of them were refused as unparseable. The zone word is now read the way the lists show:
GMT-00:30,GMT-0:45,UTC-0030,UTC-0:30int("-00"))UTC+00:60,GMT+00:60,UTC+00:90,UTC+01:60UTC30,GMT30,UTC5UTC5refused+0000 UTC+2,+0000 GMT-3,UTC+2 +0000UTC+2 +0000refusedA string without a zone word, a zone word with a plain signed offset (
UTC+2,GMT-5:30,UTC-23) and a string that carries only a numeric offset (+0300) translate to the same value as before; the tests pin six such neighbours.Verification
All runs below are against engine main
9ee1cc7d. The state with both fixes was run on a tree identical to this branch's head (tree008b207ab1932ce2e69c298d3ddc6310a5a6e403); the states with only the first fix, or only the tests, were run on the trees of the corresponding commits of this branch.tests/test_timestamp_zone_offsets.py(13 tests) on main with only its tests: 7 fail with theValueErrorabove, 6 pass.tests/test_timestamp_zone_grammar.py(34 tests) on the first fix with only its tests: 16 fail (12 with another value, 4 withPF-E5027because two forms are refused) and 18 pass. With both fixes: 47 passed, none skipped, including the 17 rows that build the emitted C++ against the engine headers. The expected values in both files are the TradingView values above, not values computed by the fixed function.timestamp(...)call: 1300 passed, none skipped, with and without each fix.UTCorGMTzone word, run on the engine and joined to each list by entry time. Before the second fix 21 probes were as they are now; after it 14 move and every move is toward the list: the seven two-entry probes go from 1 of 2 to 2 of 2 exact quantities, the five twelve-entry probes from 9 to 11 of 12, andUTC5andUTC+2 +0000go from refused to measured at 11 of 12.UTC+24andGMT-24match 2 of 2 entries;UTC+25matches 11 of 12.Limit of the evidence: the TradingView lists cover date strings written as
DD Mon YYYY hh:mm <zone>. The zone word is peeled off before the date part is parsed, so the same rules apply to the other accepted date forms (YYYY-MM-DD hh:mm <zone>,Mon DD YYYY ...); the forms in the second table have no list of their own in those spellings.Known difference, not caused by this change
One entry of every twelve-entry probe still differs from TradingView: the
UTC+25probe, the seven other date-string probes of that kind, and eleven probes that use the timezone-first formtimestamp("UTC+13:59", 2024, 2, 1, 0, 0), which this change does not touch. The probes computeint(math.floor(x)) % 1000for a value of about 1.7e12; the generated code reads that cast as the integerna(a value outside the 32-bit range), TradingView gives 1700000000000. It is the same entry with the same quantity on all nineteen, so it does not depend on the zone word, and it is tracked separately.🤖 Generated with Claude Code