calcpace 1.12.0 → 1.13.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: 42ce231ceded47814ccd2f3a0e8ecb79d0e8572253533980a76e11a70afeb6f1
4
- data.tar.gz: a2680a1f0607ef49d5178431b415cce7e0945a722ad3dbec8bb58b2c2a698131
3
+ metadata.gz: 76b9c7242a528134ed36832a7be8fc3e83ccb65fc7e139232d2987a78116498b
4
+ data.tar.gz: f87752c75360bfa9dca40ae93bfdeaafc1522f7592d9ba3ca236cd9fb1d63a56
5
5
  SHA512:
6
- metadata.gz: 836dd745def09ebc585241308d56298b986693c075b4676e12b1d95e57d4847f05b979d41cf203491dedd4e1228561163782c7c14886e4c75a9fe7f577c70820
7
- data.tar.gz: 672cb17a7a142cc6f693c8b3c6d3597223e3391f8d9cd3c6c552259a1eb1a377805bdaa26c95adb8e8e56d3b2acde94da66a8c125d0044c02fee3a11fd13174c
6
+ metadata.gz: 63421f8934eb0c94aa0713056fb6014ee7fa1ef7e1f611d16477ab3c84fb9235909b4f2e82e0e6e38a59d66ce801d109a76215ffbe08dddafd19523330327172
7
+ data.tar.gz: 9754c81c00f31f76e0250ebd0388dd7779cac95312513945364fc8f3efe914efd2f871c47afda9afbe442fdcc68d0c1eee3454348119bbd998f168c258af449c
data/CHANGELOG.md CHANGED
@@ -7,6 +7,37 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [1.13.0] - 2026-08-28
11
+
12
+ ### Added
13
+ - `convert_to_clocktime(seconds, compact: true)` — the display format a runner
14
+ reads, next to the padded format the gem already returned
15
+ - drops the hour when it is zero and the leading zero of the most significant
16
+ component, keeping two digits on everything after it:
17
+ `convert_to_clocktime(292, compact: true)` #=> `'4:52'`,
18
+ `convert_to_clocktime(45, compact: true)` #=> `'0:45'`,
19
+ `convert_to_clocktime(5025, compact: true)` #=> `'1:23:45'`
20
+ - past 24 hours it keeps counting hours (`100_000` #=> `'27:46:40'`) instead
21
+ of the padded format's day prefix (`'1 03:46:40'`) — a day count brings back
22
+ the padding and the extra unit the compact format exists to strip
23
+ - fractional seconds truncate, as they already did in the padded format:
24
+ `292.9` #=> `'4:52'`
25
+
26
+ The default stays `compact: false`, byte-for-byte the previous output, so every
27
+ existing caller is unaffected.
28
+
29
+ ### Fixed
30
+ - `convert_to_clocktime` with a negative number now raises
31
+ `Calcpace::NonPositiveInputError` instead of silently wrapping around
32
+ (`-5` used to return `'23:59:55'`, a `Time.at` artifact). Zero remains a valid
33
+ duration in both formats. Non-numeric input keeps raising as before.
34
+
35
+ ## [1.12.1] - 2026-08-15
36
+
37
+ ### Changed
38
+ - Development dependency bumps: rubocop 1.89, rdoc 8.0, simplecov 1.1, erb 6.0.7.
39
+ No library code changes.
40
+
10
41
  ## [1.12.0] - 2026-08-01
11
42
 
12
43
  ### Added
@@ -294,7 +325,8 @@ predictors are untouched.
294
325
 
295
326
  See git history for changes in earlier versions.
296
327
 
297
- [Unreleased]: https://github.com/0jonjo/calcpace/compare/v1.12.0...HEAD
328
+ [Unreleased]: https://github.com/0jonjo/calcpace/compare/v1.13.0...HEAD
329
+ [1.13.0]: https://github.com/0jonjo/calcpace/compare/v1.12.1...v1.13.0
298
330
  [1.12.0]: https://github.com/0jonjo/calcpace/compare/v1.11.0...v1.12.0
299
331
  [1.11.0]: https://github.com/0jonjo/calcpace/compare/v1.10.0...v1.11.0
300
332
  [1.10.0]: https://github.com/0jonjo/calcpace/compare/v1.9.10...v1.10.0
data/Gemfile CHANGED
@@ -5,6 +5,6 @@ source 'https://rubygems.org'
5
5
  gem 'minitest', '~> 6.0'
6
6
  gem 'rake', '~> 13.2'
7
7
  gem 'rake-compiler', '~> 1.0'
8
- gem 'rdoc', '~> 7.0'
8
+ gem 'rdoc', '~> 8.0'
9
9
  gem 'rubocop', '~> 1.69'
10
- gem 'simplecov', '~> 0.22', require: false
10
+ gem 'simplecov', '~> 1.1', require: false
data/Gemfile.lock CHANGED
@@ -2,35 +2,36 @@ GEM
2
2
  remote: https://rubygems.org/
3
3
  specs:
4
4
  ast (2.4.3)
5
- date (3.5.1)
6
- docile (1.4.1)
7
5
  drb (2.2.3)
8
- erb (6.0.4)
9
- json (2.19.9)
10
- language_server-protocol (3.17.0.5)
6
+ erb (6.0.7)
7
+ json (2.21.2)
8
+ language_server-protocol (3.17.0.6)
11
9
  lint_roller (1.1.0)
10
+ logger (1.7.0)
12
11
  minitest (6.0.6)
13
12
  drb (~> 2.0)
14
13
  prism (~> 1.5)
15
14
  parallel (2.1.0)
16
- parser (3.3.11.1)
15
+ parser (3.3.12.0)
17
16
  ast (~> 2.4.1)
18
17
  racc
19
18
  prism (1.9.0)
20
- psych (5.4.0)
21
- date
22
- stringio
23
19
  racc (1.8.1)
24
20
  rainbow (3.1.1)
25
21
  rake (13.4.2)
26
22
  rake-compiler (1.3.1)
27
23
  rake
28
- rdoc (7.2.0)
24
+ rbs (4.1.3)
25
+ logger
26
+ prism (>= 1.6.0)
27
+ tsort
28
+ rdoc (8.0.0)
29
29
  erb
30
- psych (>= 4.0.0)
30
+ prism (>= 1.6.0)
31
+ rbs (>= 4.0.0)
31
32
  tsort
32
33
  regexp_parser (2.12.0)
33
- rubocop (1.88.0)
34
+ rubocop (1.89.0)
34
35
  json (~> 2.3)
35
36
  language_server-protocol (~> 3.17.0.2)
36
37
  lint_roller (~> 1.1.0)
@@ -41,17 +42,11 @@ GEM
41
42
  rubocop-ast (>= 1.49.0, < 2.0)
42
43
  ruby-progressbar (~> 1.7)
43
44
  unicode-display_width (>= 2.4.0, < 4.0)
44
- rubocop-ast (1.49.1)
45
+ rubocop-ast (1.50.0)
45
46
  parser (>= 3.3.7.2)
46
47
  prism (~> 1.7)
47
48
  ruby-progressbar (1.13.0)
48
- simplecov (0.22.0)
49
- docile (~> 1.1)
50
- simplecov-html (~> 0.11)
51
- simplecov_json_formatter (~> 0.1)
52
- simplecov-html (0.13.2)
53
- simplecov_json_formatter (0.1.4)
54
- stringio (3.2.0)
49
+ simplecov (1.1.1)
55
50
  tsort (0.2.0)
56
51
  unicode-display_width (3.2.0)
57
52
  unicode-emoji (~> 4.1)
@@ -65,9 +60,9 @@ DEPENDENCIES
65
60
  minitest (~> 6.0)
66
61
  rake (~> 13.2)
67
62
  rake-compiler (~> 1.0)
68
- rdoc (~> 7.0)
63
+ rdoc (~> 8.0)
69
64
  rubocop (~> 1.69)
70
- simplecov (~> 0.22)
65
+ simplecov (~> 1.1)
71
66
 
72
67
  BUNDLED WITH
73
68
  4.0.6
data/README.md CHANGED
@@ -367,6 +367,21 @@ calc.convert_to_clocktime(3600) # => "01:00:00"
367
367
  calc.check_time('01:00:00') # => nil (valid)
368
368
  ```
369
369
 
370
+ `convert_to_clocktime` takes a `compact:` keyword for the format a runner reads
371
+ on a screen — no zero hour, no leading zero on the most significant component:
372
+
373
+ ```ruby
374
+ calc.convert_to_clocktime(292, compact: true) # => "4:52"
375
+ calc.convert_to_clocktime(45, compact: true) # => "0:45"
376
+ calc.convert_to_clocktime(5025, compact: true) # => "1:23:45"
377
+ calc.convert_to_clocktime(100_000, compact: true) # => "27:46:40"
378
+ ```
379
+
380
+ Past 24 hours the compact format keeps counting hours, where the padded one
381
+ prefixes a day count (`"1 03:46:40"`). Fractional seconds truncate in both.
382
+ A negative number of seconds raises `Calcpace::NonPositiveInputError`; zero is a
383
+ valid duration (`"00:00:00"` / `"0:00"`).
384
+
370
385
  ---
371
386
 
372
387
  ### Errors
@@ -99,13 +99,38 @@ module Converter
99
99
 
100
100
  # Converts seconds to a clocktime string
101
101
  #
102
- # @param seconds [Numeric] total seconds
103
- # @return [String] time in HH:MM:SS format, or "D HH:MM:SS" for durations over 24 hours
102
+ # The default (padded) format is the machine-readable one: always HH:MM:SS,
103
+ # with a day prefix past 24 hours. The compact format is the one a runner
104
+ # reads on a screen — it drops a zero hour and the leading zero of the most
105
+ # significant component, keeping two digits on everything after it.
104
106
  #
105
- # @example
107
+ # Fractional seconds are truncated, not rounded, in both formats, so a
108
+ # predictor returning 292.9 s prints the same 4:52 either way. Past 24 hours
109
+ # the compact format keeps counting hours ('27:46:40') instead of adding the
110
+ # padded format's day prefix: a day count reintroduces the very padding and
111
+ # the extra unit the compact format exists to strip, and ultra finish times
112
+ # are read as a running hour count.
113
+ #
114
+ # @param seconds [Numeric] total seconds, zero or more
115
+ # @param compact [Boolean] when true, return the compact display format
116
+ # @return [String] time in HH:MM:SS format, or "D HH:MM:SS" for durations over
117
+ # 24 hours; with compact: true, "M:SS" or "H:MM:SS"
118
+ # @raise [Calcpace::NonPositiveInputError] if seconds is negative
119
+ #
120
+ # @example padded (default)
106
121
  # convert_to_clocktime(3600) #=> '01:00:00' (1 hour)
122
+ # convert_to_clocktime(292) #=> '00:04:52'
107
123
  # convert_to_clocktime(100000) #=> '1 03:46:40' (1 day, 3 hours, 46 minutes, 40 seconds)
108
- def convert_to_clocktime(seconds)
124
+ #
125
+ # @example compact
126
+ # convert_to_clocktime(45, compact: true) #=> '0:45'
127
+ # convert_to_clocktime(292, compact: true) #=> '4:52'
128
+ # convert_to_clocktime(5025, compact: true) #=> '1:23:45'
129
+ # convert_to_clocktime(100000, compact: true) #=> '27:46:40'
130
+ def convert_to_clocktime(seconds, compact: false)
131
+ check_not_negative(seconds)
132
+ return compact_clocktime(seconds) if compact
133
+
109
134
  days = (seconds / 86_400).to_i
110
135
  format = days.positive? ? "#{days} %H:%M:%S" : '%H:%M:%S'
111
136
  Time.at(seconds).utc.strftime(format)
@@ -149,6 +174,28 @@ module Converter
149
174
 
150
175
  private
151
176
 
177
+ # Formats a duration without padding the most significant component, dropping
178
+ # the hour when there is none. Hours accumulate past 24 rather than rolling
179
+ # over into a day count.
180
+ def compact_clocktime(seconds)
181
+ total = seconds.to_i
182
+ parts = { hours: total / 3600, minutes: (total % 3600) / 60, seconds: total % 60 }
183
+ return format('%<hours>d:%<minutes>02d:%<seconds>02d', parts) if parts[:hours].positive?
184
+
185
+ format('%<minutes>d:%<seconds>02d', parts)
186
+ end
187
+
188
+ # Guards against negative durations. Unlike Checker#check_positive, zero is a
189
+ # legitimate duration here — a zero split prints as 00:00:00 — so only a
190
+ # negative value is rejected. Non-numeric input is left to raise on its own,
191
+ # as it always has.
192
+ def check_not_negative(seconds)
193
+ return unless seconds.is_a?(Numeric) && seconds.negative?
194
+
195
+ raise Calcpace::NonPositiveInputError,
196
+ 'Seconds must not be a negative number'
197
+ end
198
+
152
199
  # Guards the "race name + distance_unit" combination. A standard race already
153
200
  # carries its own distance, so the keyword can only be a caller mistake —
154
201
  # better to say so than to ignore it silently.
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Calcpace
4
- VERSION = '1.12.0'
4
+ VERSION = '1.13.0'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: calcpace
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.12.0
4
+ version: 1.13.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - João Gilberto Saraiva