Skip to content

Fix TT2000 leap-second read; promote DateTime/Timestamp to the CDF epoch types - #74

Merged
Beforerr merged 2 commits into
mainfrom
timestamp
Oct 2, 2026
Merged

Beforerr merged 2 commits into
mainfrom
timestamp

Conversation

@Beforerr

@Beforerr Beforerr commented Oct 2, 2026 •

Copy link
Copy Markdown
Member

Two commits.

fix: TT2000 to DateTime off by 1 s before each leap second. The inverse lookup compared the TT-shifted count against UTC leap-second thresholds, so for the ~30 s before each leap second since 1972, reads came back 1 s early (e.g. DateTime(TT2000(DateTime("2016-12-31T23:59:30"))) gave 23:59:29). Regression values come from cdflib.cdfepoch.compute_tt2000.

feat!: promote DateTime/Timestamp to the CDF epoch types (adds Durations.jl 1.4 for Timestamp).

  • Date, DateTime and Timestamp promote to TT2000 / Epoch16 / Epoch rather than the epoch promoting to DateTime. UTC -> TT2000 is injective, while TT2000 -> UTC folds each inserted leap second onto the following second, so comparing in UTC would equate distinct instants and under-count differences across a leap second. A TT2000 0.4 ms after DateTime(2020) no longer compares == to it.
  • Different CDF epoch types compare with each other (TT2000 > Epoch16 > Epoch).
  • Timestamp{P}(x) / convert(Timestamp{P}, x) for all CDF epochs. TT2000 parses, prints and exposes calendar fields to the nanosecond through Timestamp{Nanosecond}. Epoch and Epoch16 span years 0-9999, beyond Timestamp{Nanosecond} (1677-2262), and keep DateTime for calendar fields and printing.
  • DateTime(x) for TT2000/Epoch16 floors to the millisecond (Epoch16 used to round).
  • Epoch16 ordering and ± Period; Date, Time and day-based accessors.

Fixes found while probing:

  • Leap-second lookup branches and floors on the nanosecond count. Rounding to a DateTime threw for instants within 0.5 ms before 1972-01-01 and used the next day's offset within 0.5 ms before earlier midnights.
  • TT2000(::DateTime) covers the full TT2000 range (to 2292).
  • FILLVAL display: TT2000 uses the ISTP fill (typemin(Int64)) rather than 9999; the Epoch16 fill no longer throws.

Known limits: comparing a TT2000 with a DateTime outside the TT2000 range throws; string of a TT2000 in 2262-2292 throws; floor of an instant inside a leap second lands on the following second.

Breaking (promotion and equality semantics). CDFDatasets' tests pass locally against this branch; downstream compat needs widening on release.

The inverse lookup compared the TT-shifted ns count against UTC leap-second
thresholds, so instants within the leap offset before each threshold read
back one second early. A TT2000 inside an inserted leap second now maps onto
the following UTC second, as cdflib does.
@codecov

codecov Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.00000% with 3 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/epochs.jl 95.58% 3 Missing ⚠️

📢 Thoughts on this report? Let us know!

@github-actions

github-actions Bot commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

Benchmark Results (Julia v1)

Time benchmarks
main d63a4a3... main / d63a4a3...
elx/full_load 0.0367 ± 0.0023 ms 0.0366 ± 0.0021 ms 1 ± 0.084
elx/sum_array 2.87 ± 0.95 μs 2.62 ± 1.1 μs 1.09 ± 0.58
elx/sum_lazy 2.91 ± 0.4 μs 2.79 ± 0.54 μs 1.04 ± 0.25
elx/sum_var_access 2.19 ± 1.1 μs 2.02 ± 0.96 μs 1.08 ± 0.73
elx/var_access 0.24 ± 0.001 μs 0.241 ± 0.001 μs 0.996 ± 0.0059
epochs/DateTime_to_TT2000 0.0622 ± 0.00029 ms 0.0621 ± 0.00032 ms 1 ± 0.007
epochs/Epoch16_parse 0.305 ± 0.01 ms 0.309 ± 0.01 ms 0.988 ± 0.047
epochs/Epoch16_vs_DateTime 2.92 ± 0.011 μs 2.92 ± 0.01 μs 1 ± 0.0051
epochs/TT2000_parse 0.352 ± 0.01 ms 0.357 ± 0.01 ms 0.987 ± 0.04
epochs/TT2000_string 5.78 ± 1.8 ms 5.73 ± 0.36 ms 1.01 ± 0.33
epochs/TT2000_to_DateTime 0.112 ± 0.00064 ms 0.112 ± 0.00051 ms 1 ± 0.0074
epochs/TT2000_vs_DateTime 0.0556 ± 1.1e-05 ms 0.0525 ± 1e-05 ms 1.06 ± 0.00029
mms/full_load 21.6 ± 2.1 μs 21.3 ± 2.2 μs 1.01 ± 0.14
mms/sum_array 0.0952 ± 0.0017 s 0.0952 ± 0.0016 s 1 ± 0.025
mms/sum_slice 3.53 ± 0.48 ms 3.51 ± 0.054 ms 1.01 ± 0.14
mms/sum_var_access 3.51 ± 0.46 ms 3.51 ± 0.049 ms 1 ± 0.13
mms/var_access 0.08 ± 0.01 μs 0.08 ± 0.01 μs 1 ± 0.18
time_to_load 0.166 ± 0.002 s 0.167 ± 0.0012 s 0.995 ± 0.014
Memory benchmarks
main d63a4a3... main / d63a4a3...
elx/full_load 0.217 k allocs: 24.8 kB 0.217 k allocs: 24.8 kB 1
elx/sum_array 12 allocs: 28 kB 12 allocs: 28 kB 1
elx/sum_lazy 12 allocs: 28 kB 12 allocs: 28 kB 1
elx/sum_var_access 14 allocs: 28.3 kB 14 allocs: 28.3 kB 1
elx/var_access 1 allocs: 0.234 kB 1 allocs: 0.234 kB 1
epochs/DateTime_to_TT2000 3 allocs: 0.0764 MB 3 allocs: 0.0764 MB 1
epochs/Epoch16_parse 3 allocs: 0.153 MB 3 allocs: 0.153 MB 1
epochs/Epoch16_vs_DateTime 0 allocs: 0 B 0 allocs: 0 B
epochs/TT2000_parse 3 allocs: 0.0764 MB 3 allocs: 0.0764 MB 1
epochs/TT2000_string 0.2 M allocs: 7.02 MB 0.2 M allocs: 7.02 MB 1
epochs/TT2000_to_DateTime 3 allocs: 0.0764 MB 3 allocs: 0.0764 MB 1
epochs/TT2000_vs_DateTime 0 allocs: 0 B 0 allocs: 0 B
mms/full_load 0.065 k allocs: 6.45 kB 0.065 k allocs: 6.45 kB 1
mms/sum_array 1.04 k allocs: 31.7 MB 1.04 k allocs: 31.7 MB 1
mms/sum_slice 0.069 k allocs: 1.29 MB 0.069 k allocs: 1.29 MB 1
mms/sum_var_access 0.069 k allocs: 1.29 MB 0.069 k allocs: 1.29 MB 1
mms/var_access 1 allocs: 0.219 kB 1 allocs: 0.219 kB 1
time_to_load 0.2 k allocs: 11.8 kB 0.2 k allocs: 11.8 kB 1

@Beforerr
Beforerr force-pushed the timestamp branch 3 times, most recently from 0cf666f to 14fbc19 Compare October 2, 2026 17:21
@Beforerr Beforerr changed the title Fix TT2000 leap-second read; promote TT2000/Epoch16 to Timestamp{Nanosecond} Fix TT2000 leap-second read; promote DateTime/Timestamp to the CDF epoch types Oct 2, 2026
@Beforerr
Beforerr force-pushed the timestamp branch 6 times, most recently from c505cf9 to 40959c2 Compare October 2, 2026 18:00
UTC -> TT2000 is injective, TT2000 -> UTC folds each inserted leap second
onto the following second, so mixed comparisons and differences are done
in the CDF type. Timestamp{Nanosecond} (Durations.jl) backs TT2000
parsing, printing and calendar fields; Epoch and Epoch16 span years
0-9999 and keep DateTime for those.

Also: comparison across CDF epoch types, Epoch16 ordering and
Epoch16 ± Period, Date/Time/days accessors, TT2000(::DateTime) up to
2292, leap-second lookup on the nanosecond count (crashed just below
1972), ISTP FILLVAL display for TT2000 and Epoch16.
@Beforerr
Beforerr merged commit dac3caf into main Oct 2, 2026
5 checks passed
@Beforerr
Beforerr deleted the timestamp branch October 2, 2026 19:28
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.

1 participant