Skip to content

Fix UTC formatting at near-boundary second carries - #1157

Open
heathsanchez wants to merge 1 commit into
skyfielders:masterfrom
metalogiclabs:fix-utc-rounding-boundaries
Open

heathsanchez wants to merge 1 commit into
skyfielders:masterfrom
metalogiclabs:fix-utc-rounding-boundaries

Conversation

@heathsanchez

Copy link
Copy Markdown

Problem

Rounding a UTC instant near the end of a minute can produce a civil second that does not exist. On current master:

from skyfield.api import load
ts = load.timescale()

print(ts.utc(2016, 12, 30, 23, 59, 59.9995).utc_iso(places=3))
# Before: 2016-12-30T23:59:60.000Z (no leap second that day)

print(ts.utc(2016, 12, 31, 23, 59, 60.9995).utc_iso(places=3))
# Before: 2016-12-31T23:59:61.000Z (no second 61, even on a leap day)

utc_jpl() exhibits the same rollover problem at four decimal places, with 59.99995 and 60.99995. These are formatting labels, not evidence that Skyfield's internal time calculations are incorrect.

Cause and fix

_utc_seconds(offset) adds half a display unit before splitting the TAI seconds. Near the boundary, the fractional remainder can be just below 1.0 (for example, 0.9999999999999976), then round up when _utc_tuple() adds it to an integer civil second. That last rounding happens after the leap-offset/calendar decision, yielding impossible :60 or :61 labels.

This patch normalizes the near-integer carry before applying the leap-second offset, and adds native regression tests for ordinary-minute and leap-minute rollovers in ISO and JPL formatting. The change is limited to skyfield/timelib.py and skyfield/tests/test_timelib.py.

Verification

  • Original source at 31a6ed7584cd09b6e48e07546a4b5921e1cd5bb4: existing native suite 584/584 pass, but the new boundary witness fails.
  • This patch: native assay --batch skyfield.tests 586/586 pass (Python 3.12, NumPy 1.26.4), pyflakes skyfield clean, and git diff --check clean.
  • Separate controls exercised scalar/vector agreement, ordinary midnight, fractional leap seconds, and the 1973, 2012 and 2016 leap-second insertions.
  • Upstream GitHub CI and behavior across all floating-point edge cases remain to be verified by this PR.

The report and correction were prepared with AI assistance. The unchanged upstream source was used for the original-head reproduction, and native controls and tests were run before submission. The claim is limited to impossible UTC labels. Maintainer feedback on the near-carry threshold or a preferred alternative is welcome.

@brandon-rhodes

Copy link
Copy Markdown
Member

Oh, that's a very bad behavior — thanks for letting me know! I'll plan to look at this sometime this week, and hopefully get it merged. I'll have to plan for a Skyfield release near the end of the month, I suppose.

@heathsanchez

Copy link
Copy Markdown
Author

No problem Brandon, it's a brilliant project- happy to help. Have a great week!

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.

2 participants