adriangb commented on PR #11038:
URL: https://github.com/apache/arrow-rs/pull/11038#issuecomment-5626606546

   Self-review QA pass — this is our own PR, so treat this as our own 
adversarial check, not an independent review.
   
   No blocking defect. I re-measured every claim. Four findings, in priority 
order.
   
   ## 1. The stated safety argument for the 24-hour probe is not the 
load-bearing one
   
   The PR justifies the probe with a transition-spacing figure: no two 
transitions in 597 zones are closer than 167 hours. That figure is correct. I 
reproduce it exactly (`America/Boa_Vista` 2000-10-15, 167.00 h). But it is not 
the property that makes the probe resolvable.
   
   Two separate invariants are at work here:
   
   * **Transition spacing above 24 hours.** This makes the probed offset the 
*correct* pre-transition offset. Margin: 167 hours. The PR states this one.
   * **Maximum local gap at or below 24 hours.** This makes the probe land 
*outside* the gap at all. The PR does not state this one, and it has no margin.
   
   Seven zones have a local gap of exactly 24.00 hours. These are the dateline 
changes:
   
   | Zone | Transition (UTC) | Local gap |
   | --- | --- | --- |
   | `Pacific/Kwajalein` | 1993-08-21T12:00Z | 24.00 h |
   | `Kwajalein` | 1993-08-21T12:00Z | 24.00 h |
   | `Pacific/Kiritimati` | 1994-12-31T10:00Z | 24.00 h |
   | `Pacific/Kanton` | 1994-12-31T11:00Z | 24.00 h |
   | `Pacific/Enderbury` | 1994-12-31T11:00Z | 24.00 h |
   | `Pacific/Apia` | 2011-12-30T10:00Z | 24.00 h |
   | `Pacific/Fakaofo` | 2011-12-30T11:00Z | 24.00 h |
   
   The next largest gap is 10.00 h (`Antarctica/Macquarie`). So the 
distribution is bimodal, and the code sits exactly on the upper mode.
   
   For a reading at the last second of such a gap the probe lands one second 
before the gap starts. The real margin is **one second**, not 167 hours. The 
code is correct today. It stops being correct the day tzdb gains a gap above 24 
hours.
   
   I tested all four surviving dateline zones at the first instant of the gap, 
the middle and the last second. All 13 readings match PostgreSQL 17.11 exactly:
   
   ```
   Pacific/Apia       2011-12-30 00:00:00 -> 1325239200   (PG 1325239200)
   Pacific/Apia       2011-12-30 00:00:01 -> 1325239201   (PG 1325239201)
   Pacific/Apia       2011-12-30 12:00:00 -> 1325282400   (PG 1325282400)
   Pacific/Apia       2011-12-30 23:59:59 -> 1325325599   (PG 1325325599)
   Pacific/Kiritimati 1994-12-31 00:00:00 -> 788868000    (PG 788868000)
   Pacific/Kwajalein  1993-08-21 23:59:59 -> 746020799    (PG 746020799)
   Pacific/Fakaofo    2011-12-30 23:59:59 -> 1325329199   (PG 1325329199)
   ...
   ```
   
   Please state the gap-length invariant next to the spacing invariant, both in 
the PR body and in the doc comment on `resolve_local_offset`. A future reader 
who bumps `chrono-tz` needs to know which of the two numbers to re-check.
   
   ## 2. The give-up branch is unreachable, so its callers are untested
   
   `LocalResult::None` from the probe reaches `.earliest()`, which returns 
`None`. I could not construct any input that reaches it. Both invariants above 
exclude it:
   
   * No gap exceeds 24 hours, so the probe never lands in another gap.
   * No fall-back transition sits within 24 hours before a gap, so the probe 
never lands in an ambiguous interval either.
   
   So the branch is dead code with real tzdata. I read the callers instead of 
testing them, and both are correct:
   
   * `safe: false` — `adjust` returns `None`, `try_unary` raises 
`CastError("Cannot cast timezone to different timezone")`. The whole array 
fails, as before.
   * `safe: true` — `unary_opt` writes a null. As before.
   
   The `?` in `adjust` covers three separate `None` sources: `as_datetime`, 
`resolve_local_offset` and `from_naive_datetime`. All three collapse into the 
same message. That is pre-existing and fine, but it means the new `None` source 
has no distinct diagnostic.
   
   I confirmed the two options stay in step on real DST input:
   
   ```
   cast  safe=false [Some(1710055800), Some(1730615400)]
   cast  safe=true  [Some(1710055800), Some(1730615400)]
   ```
   
   ## 3. The fixed-offset claim is over-broad
   
   The PR says PostgreSQL reads a fixed-offset string such as `'+05:30'` with 
the opposite sign. That is true for one spelling only. I measured both on 
PostgreSQL 17.11:
   
   | Spelling | Result | Sign convention |
   | --- | --- | --- |
   | `SET TimeZone='+05:30'` then `'2024-01-01 12:00:00'::timestamptz` | 
`17:30Z` | POSIX, west-positive |
   | `'2024-01-01 12:00:00'::timestamp AT TIME ZONE '+05:30'` | `17:30Z` | 
POSIX, west-positive |
   | `timestamptz '2024-01-01 12:00:00 +05:30'` | `06:30Z` | ISO, east-positive 
|
   
   So PostgreSQL agrees with arrow-rs when the offset sits inside the literal, 
and disagrees when the offset acts as a zone name. DuckDB 1.5.2 rejects the 
zone-name spelling outright (`Not implemented Error: Unknown TimeZone 
'+05:30'`). Please say "as a zone name" in the PR body. The current wording 
reads as though PostgreSQL always disagrees.
   
   ## 4. The dev-dependency removes the no-`chrono-tz` coverage of arrow-cast
   
   `arrow-array = { workspace = true, features = ["chrono-tz"] }` under 
`[dev-dependencies]` unifies across the graph. So `cargo test -p arrow-cast` 
now always builds `Tz` with IANA support, and arrow-cast has no test that 
exercises the offset-only `Tz`. That is exactly why 
`test_cast_string_to_timestamp_invalid_tz` needed the relaxed assertion.
   
   The relaxed assertion is the right call. I raise this only so the lost 
coverage is on the record.
   
   ---
   
   ## What I checked and found correct
   
   **The per-row claim holds.** This was my main concern, because a 
once-per-array offset gives silently wrong values rather than a loud error. I 
built one 15-row array that straddles both 2024 `America/New_York` transitions, 
and compared every element against PostgreSQL 17.11:
   
   | Wall clock reading | arrow | PostgreSQL 17.11 |
   | --- | --- | --- |
   | `2024-01-15 12:00:00` | 1705338000 | 1705338000 |
   | `2024-03-10 01:30:00` | 1710052200 | 1710052200 |
   | `2024-03-10 02:30:00` (gap) | 1710055800 | 1710055800 |
   | `2024-03-10 03:30:00` | 1710055800 | 1710055800 |
   | `2024-06-15 12:00:00` | 1718467200 | 1718467200 |
   | `2024-11-03 00:30:00` | 1730608200 | 1730608200 |
   | `2024-11-03 01:30:00` (ambiguous) | 1730615400 | 1730615400 |
   | `2024-11-03 02:30:00` | 1730619000 | 1730619000 |
   | `2024-12-15 12:00:00` | 1734282000 | 1734282000 |
   
   Three distinct offsets appear in one array. A single offset per array cannot 
produce this.
   
   **A broad independent sweep agrees.** I generated every DST transition of 
every zone from 2020 to 2025, then sampled the start and the midpoint of each 
gap and each repeated interval. That gives 5,012 readings across 226 zones: 
2,506 in a gap, 2,506 ambiguous. Results:
   
   * 0 unresolved readings under `safe: true`.
   * 3,908 of them use a zone PostgreSQL 17.11 accepts. 16 disagree.
   * All 16 are `Europe/Chisinau` and its alias `Europe/Tiraspol`.
   
   That is the chrono-tz tzdata skew the PR already discloses. I confirmed it 
predates this PR. On the base commit, with no change applied:
   
   ```
   main            Europe/Chisinau 2024-03-31 03:30 -> 1711845000
   PostgreSQL 17.11                                -> 1711848600
   DuckDB 1.5.2                                    -> 1711848600
   ```
   
   PostgreSQL and DuckDB agree with each other, so chrono-tz is the outlier. It 
puts the Moldova spring transition at 02:00 local; the other two put it at 
03:00 local. Non-boundary readings agree everywhere. Please add 
`Europe/Tiraspol` to the disclosure — it carries the same skew and the PR names 
only `Europe/Chisinau`.
   
   **The known divergence stays untouched and disclosed.** `git diff` against 
the base commit shows this branch changes nothing under `arrow-array/`. 
`arrow-array/src/types.rs:349` still reads `Ambiguous(dt1, _)` and `None => 
None`. Measured side by side in `America/New_York`:
   
   | `2024-11-03 01:30:00` | Result |
   | --- | --- |
   | `from_naive_datetime` | 1730611800 = `05:30Z`, the **earlier** instant |
   | this PR's cast | 1730615400 = `06:30Z`, the **later** instant |
   
   Opposite, as the PR states. The disclosure in the body is accurate and 
prominent.
   
   **Test coverage.** The six new tests cover the southern-hemisphere gap 
(`Australia/Sydney`), both `safe` modes, the nanosecond path, a unit change and 
a fixed offset. `Australia/Lord_Howe`, `Pacific/Chatham`, `America/Sao_Paulo` 
and `America/Havana` are not in this PR's tests, but #11054 adds them. I 
verified all four against PostgreSQL by hand and they pass on this branch too.
   
   **Suite.** `cargo test -p arrow-cast` passes on this branch.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to