iers 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4977c2dce8a969f0590cabc8c9c2a992e946b14d5def3fd9e7b6635c76a5cd83
4
- data.tar.gz: fd23f09386c87acbc6f4b25207d023198a41c9f55502c341859188496fc6f66b
3
+ metadata.gz: 4132b4709810d88306f0ea6bde33b1664d1fdbfeef985e0191cba65c98c01f8a
4
+ data.tar.gz: 5e3bef64e668f1320bc9c3876e571bf4bb452ea2df52cd09539303bafbf03ea0
5
5
  SHA512:
6
- metadata.gz: c219975975bd14d24c47c45062868592999f4da0f19d254d73a01676b71b5d881faebd8f73afbb3d3155ef5da341459414846165e4f2540570f78e9487bba435
7
- data.tar.gz: 0b13b6a3dc0daa6d2012e112662c3605b59de6c4d6b1437f4162912095bb424772cd13fb1c0cddb7cb3e234e85bfd0658ecf63ecf977a8281f063e13da05ceaa
6
+ metadata.gz: c2bd5f8b1d3a088bc9aa8d822013a9c04570cea94c6036f080eb4d34d8ad53ac0346e6547092f5ce95fe61f95df6de32006e59371894e3b77768565be15354a1
7
+ data.tar.gz: 8b32049edecc0955026664f02cbf89a1af9d518050374538cbf1b1c3b08d572509b971d522a1d368490d6d07bcbd90bb807bd9fd5c4223d074431a60583244f5
data/CHANGELOG.md CHANGED
@@ -1,5 +1,40 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.0 - 2026-07-25
4
+
5
+ ### Added
6
+
7
+ - `LeapSecond.expires_on`, `LeapSecond.expired?` and
8
+ `LeapSecond.updated_through` expose the expiry date and IERS bulletin that
9
+ `Leap_Second.dat` states in its header. Both were previously discarded with
10
+ the rest of the comment lines. Unlike `Data.status.cache_age`, which measures
11
+ when a file was downloaded, these describe how long the data itself stays
12
+ authoritative.
13
+ - `LeapSecond.at` now answers 1961-01-01 through 1972-01-01, the era when UTC
14
+ was steered by rate adjustments rather than whole leap seconds. It returns
15
+ those values as exact `Rational`s. The coefficients come from the USNO
16
+ `tai-utc.dat` file and are bundled in source, since they are fixed. The
17
+ change is additive: the pre-1972 range previously raised, so no successful
18
+ call returns anything different. `TAI.utc_to_tai` and `TAI.tai_to_utc` gain
19
+ the same range for free.
20
+
21
+ ### Changed
22
+
23
+ - `Parsers::LeapSecond.parse` returns a `Table` of `entries` and `metadata`
24
+ instead of an array of entries. `Parsers` is internal; the public API is
25
+ unaffected.
26
+ - `Parsers::LeapSecond.parse` no longer raises `Encoding::CompatibilityError`
27
+ on a file containing invalid UTF-8. Header metadata is read on a best-effort
28
+ basis and an unreadable data row raises `ParseError` as before.
29
+
30
+ ## 0.1.1 - 2026-06-27
31
+
32
+ ### Changed
33
+
34
+ - Refreshed the bundled `finals2000A.all` snapshot from IERS. Final values now
35
+ extend through 2026-06-25 (previously 2026-02-19) and predictions through
36
+ 2027-08-22.
37
+
3
38
  ## 0.1.0 - 2026-02-27
4
39
 
5
40
  Initial public release.
data/README.md CHANGED
@@ -242,14 +242,28 @@ entries = IERS::EOP.between(
242
242
  Look up TAI−UTC at a given date:
243
243
 
244
244
  ```ruby
245
- IERS::LeapSecond.at(Time.utc(2017, 1, 1)) # => 37.0 (seconds)
245
+ IERS::LeapSecond.at(Time.utc(2017, 1, 1)) # => 37 (seconds)
246
246
  ```
247
247
 
248
+ `at` covers 1961-01-01 onward. From 1972 it returns a whole number of seconds
249
+ as an `Integer`. Between 1961 and 1972, UTC was steered by small rate
250
+ adjustments rather than whole leap seconds, so TAI−UTC was a fraction of a
251
+ second that changed daily. `at` returns those values as exact `Rational`s:
252
+
253
+ ```ruby
254
+ IERS::LeapSecond.at(mjd: 38_900) # => (1910137/500000)
255
+ IERS::LeapSecond.at(mjd: 38_900).to_f # => 3.820274 (seconds, 1965-05-20)
256
+ ```
257
+
258
+ The coefficients for that era are fixed and will never change, so they are
259
+ bundled in the gem rather than downloaded. Anything before 1961-01-01 has no
260
+ published UTC and raises `OutOfRangeError`.
261
+
248
262
  List all leap seconds:
249
263
 
250
264
  ```ruby
251
265
  IERS::LeapSecond.all
252
- # => [#<data IERS::LeapSecond::Entry effective_date=#<Date: 1972-01-01>, tai_utc=10.0>, ...]
266
+ # => [#<data IERS::LeapSecond::Entry effective_date=#<Date: 1972-01-01>, tai_utc=10>, ...]
253
267
  ```
254
268
 
255
269
  Check for a future scheduled leap second:
@@ -258,6 +272,27 @@ Check for a future scheduled leap second:
258
272
  IERS::LeapSecond.next_scheduled # => #<data IERS::LeapSecond::Entry ...> or nil
259
273
  ```
260
274
 
275
+ #### Leap second file validity
276
+
277
+ `Leap_Second.dat` states when it stops being authoritative and which IERS
278
+ bulletin it was updated through. Both are read from the file that is actually
279
+ loaded, so a custom or cached file reports its own header:
280
+
281
+ ```ruby
282
+ IERS::LeapSecond.expires_on # => #<Date: 2026-12-28> or nil
283
+ IERS::LeapSecond.expired? # => false
284
+ IERS::LeapSecond.updated_through # => "IERS Bulletin 71 issued in January 2026"
285
+ ```
286
+
287
+ The stated date is itself still valid, so a file expiring on 28 December 2026
288
+ is expired on the 29th. An expired file is not wrong: it means the IERS has
289
+ published a newer one and a leap second may have been announced since.
290
+
291
+ This is unrelated to `Data.status.cache_age`, which measures when the file was
292
+ downloaded rather than how long it stays valid. A bundled snapshot can be a year
293
+ old and still valid while a file downloaded this morning can be a week from
294
+ expiry.
295
+
261
296
  ### TAI
262
297
 
263
298
  Convert between UTC and TAI time scales:
@@ -295,6 +330,10 @@ end
295
330
 
296
331
  Without `coverage_days_ahead`, the check ensures predictions cover today.
297
332
 
333
+ `ensure_fresh!` is about prediction coverage in `finals2000A.all` only. Leap
334
+ second file validity is a separate question, answered by
335
+ `IERS::LeapSecond.expired?`.
336
+
298
337
  ### Data status and cache management
299
338
 
300
339
  ```ruby
@@ -358,6 +397,9 @@ release a new version, update the version number in `version.rb`, and then run
358
397
  `bundle exec rake release`, which will create a git tag for the version, push
359
398
  git commits and the created tag, and push the `.gem` file to [rubygems.org].
360
399
 
400
+ To refresh the bundled snapshot in `data/`, run `bundle exec rake data:update`.
401
+ A monthly workflow runs this and opens a pull request automatically.
402
+
361
403
  ## License
362
404
 
363
405
  The gem is available as open source under the terms of the [MIT License].
data/Rakefile CHANGED
@@ -10,3 +10,31 @@ require "rubocop/rake_task"
10
10
  RuboCop::RakeTask.new
11
11
 
12
12
  task default: %i[test rubocop]
13
+
14
+ namespace :data do
15
+ desc "Download the latest IERS data"
16
+ task :update do
17
+ require "iers"
18
+
19
+ data_dir = File.expand_path("data", __dir__)
20
+ IERS.configure do |config|
21
+ config.finals_path = File.join(data_dir, "finals2000A.all")
22
+ config.leap_second_path = File.join(data_dir, "Leap_Second.dat")
23
+ end
24
+
25
+ result = IERS::Data.update!
26
+
27
+ unless result.success?
28
+ result.errors.each do |source, error|
29
+ warn "#{source}: #{error.class} - #{error.message}"
30
+ end
31
+ abort "IERS data update failed"
32
+ end
33
+
34
+ IERS::Data.clear_loaded!
35
+ IERS::Data.ensure_fresh!(coverage_days_ahead: 90)
36
+ IERS::Data.leap_second_entries
37
+
38
+ puts "Updated: #{result.updated_files.join(", ")}"
39
+ end
40
+ end