duration.rb 0.4.0 → 0.5.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: 9f7d0f0552f3ce6cb16fd75947bad208ccf403bd8e572b23ae262d4b6aada58f
4
- data.tar.gz: 7423385510fa2bb3769521892430705fb036eb12b1a40c276a9b4b5445b7bc94
3
+ metadata.gz: 9e8c196994e5358c6875d3ee17471f379cf8cffe2fa9924856729a6fc64c88d0
4
+ data.tar.gz: de2a825ddec433cb81b9797015833a61eac8b5548166b592686623f86cc9101a
5
5
  SHA512:
6
- metadata.gz: be48eabdcaddff69c28452886f068623646af55255ea06f17809e38b2fafd0c343ccc7592478952bb1a7879ef60b10433ad074fbbfa0d47ee0c54405b0066723
7
- data.tar.gz: 852569a9d5a66c7e576c9c33310c549e6846e369bf934da1eae13d9c54a343a9c6bc06e0dcdbb5da12768fc24f0feabf9c20a1c1b0d3c405b6a40b7f299d9af3
6
+ metadata.gz: e47937b8086f02fd9ab15d4444bc9b9f38c7d7b01e8948c99c500adfaef01697ea17d8898c93cc727b8961610f904810e3aaeb9da00aa762fbee4a1ffd25ed57
7
+ data.tar.gz: 17f71a43aa5680b73bc5ca532e2c172964cf988d74668b2bfa253105c4b46d31a34810e9ee31ff37a0e85d36a91df42fd39194394c7429186ddff34afb1274b1
data/CHANGELOG CHANGED
@@ -1,5 +1,16 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## 20260811
4
+
5
+ 0.5.0: + Multiplication and division, and with them the dimensionless ratio.
6
+
7
+ 1. + Duration::Common#*: a duration times a number is a duration, in the receiver's own unit as the rest of the arithmetic is. A duration times a duration raises TypeError, there being no unit of time squared: refused rather than quietly given some plausible answer.
8
+ 2. + Duration::Common#/: divided by a number a duration stays a duration; divided by a duration it becomes the dimensionless ratio of the two, the units having cancelled. That ratio is the shape of a rate or a speedup, and until now it could not be written at all: a timing had to be dropped back to a bare number before it could be divided, which is the point at which its unit is lost.
9
+ 3. + Duration::Common#scale and #ratio, private, being the two halves of what #* and #/ mean. Division goes through Numeric#quo rather than #/, so that an Integer quantity divides exactly: 1.quo(3) is (1/3) where 1 / 3 is 0. A Float quantity stays a Float, this library manufacturing no precision it was not given.
10
+ 4. + test/Duration/Common_test.rb: multiplication and division, including that 1.second / 3 holds a third rather than nothing, that 1.hour / 30.minutes is 2, and that 1.second / 3.milliseconds is exactly 1000/3.
11
+ 5. ~ Duration::VERSION: /0.4.0/0.5.0/
12
+
13
+
3
14
  ## 20260718
4
15
 
5
16
  0.4.0: + Nanoseconds and Microseconds, arithmetic and comparison, exact conversions without a base, and the Numeric monkeypatch made opt-in.
data/README.md CHANGED
@@ -98,6 +98,20 @@ The examples below use the `Numeric` sugar, so they assume
98
98
  90.seconds > 1.minute # => true
99
99
  [1.hour, 30.seconds, 5.minutes].sort
100
100
  # => [30.seconds, 5.minutes, 1.hour]
101
+
102
+ # Scaled by a number, a duration stays a duration and keeps its unit:
103
+ 5.minutes * 3 # => #<Minutes @minutes=15>
104
+ 1.hour / 4 # => #<Hours @hours=(1/4)>
105
+ 1.second / 3 # => #<Seconds @seconds=(1/3)> (not 0)
106
+
107
+ # Divided by a duration, the units cancel and a plain number is left. It is a
108
+ # Rational when both quantities are exact, as Numeric#quo gives, so it compares
109
+ # and calculates as the number it is; call to_f at the edge.
110
+ 1.hour / 30.minutes # => (2/1)
111
+ 1.second / 3.milliseconds # => (1000/3)
112
+
113
+ # There is no unit of time squared, so this is refused rather than answered:
114
+ 5.minutes * 3.minutes # => TypeError
101
115
  ```
102
116
 
103
117
  ### Time Calculations
@@ -13,6 +13,24 @@ module Duration
13
13
  combine(:-, other)
14
14
  end
15
15
 
16
+ # A duration times a number is a duration. A duration times a duration
17
+ # would be time squared, which has no unit here and few enough anywhere, so
18
+ # it is refused rather than quietly given some plausible answer.
19
+ def *(other)
20
+ if other.is_a?(Duration::Common)
21
+ raise TypeError, "can't multiply #{self.class} by #{other.class}: there is no unit of time squared"
22
+ end
23
+ scale(:*, other)
24
+ end
25
+
26
+ # Divided by a number a duration stays a duration. Divided by a duration it
27
+ # becomes the dimensionless ratio of the two, the units having cancelled,
28
+ # which is how a rate or a speedup is arrived at.
29
+ def /(other)
30
+ return ratio(other) if other.is_a?(Duration::Common)
31
+ scale(:quo, other)
32
+ end
33
+
16
34
  def <=>(other)
17
35
  return nil unless other.is_a?(Duration::Common)
18
36
  quantity <=> other.send(:"to_#{unit}").quantity
@@ -32,5 +50,19 @@ module Duration
32
50
  end
33
51
  self.class.new(quantity.send(operator, other.send(:"to_#{unit}").quantity))
34
52
  end
53
+
54
+ # Division goes through #quo rather than #/ so that an Integer quantity
55
+ # divides exactly: 1.quo(3) is (1/3) where 1 / 3 is 0. A Float quantity
56
+ # stays a Float, this library manufacturing no precision it was not given.
57
+ def scale(operator, other)
58
+ unless other.is_a?(Numeric)
59
+ raise TypeError, "can't scale #{self.class} by #{other.class}"
60
+ end
61
+ self.class.new(quantity.send(operator, other))
62
+ end
63
+
64
+ def ratio(other)
65
+ quantity.quo(other.send(:"to_#{unit}").quantity)
66
+ end
35
67
  end
36
68
  end
@@ -1,3 +1,3 @@
1
1
  module Duration
2
- VERSION = '0.4.0'
2
+ VERSION = '0.5.0'
3
3
  end
@@ -30,6 +30,46 @@ describe Duration::Common do
30
30
  end
31
31
  end
32
32
 
33
+ describe "multiplication" do
34
+ it "scales a duration by a number, keeping the unit" do
35
+ result = 5.minutes * 3
36
+ _(result).must_be_instance_of Duration::Minutes
37
+ _(result.to_i).must_equal 15
38
+ end
39
+
40
+ it "raises upon a duration, there being no unit of time squared" do
41
+ _{5.minutes * 3.minutes}.must_raise TypeError
42
+ end
43
+
44
+ it "raises upon a non-numeric" do
45
+ _{5.minutes * "3"}.must_raise TypeError
46
+ end
47
+ end
48
+
49
+ describe "division" do
50
+ it "scales a duration by a number, keeping the unit" do
51
+ result = 1.hour / 4
52
+ _(result).must_be_instance_of Duration::Hours
53
+ _(result.to_minutes.to_f).must_equal 15.0
54
+ end
55
+
56
+ it "divides exactly rather than as integers" do
57
+ _(1.second / 3).must_equal Duration::Seconds.new(Rational(1, 3))
58
+ end
59
+
60
+ it "returns the dimensionless ratio of two durations" do
61
+ _(1.hour / 30.minutes).must_equal 2
62
+ end
63
+
64
+ it "takes the ratio across units exactly" do
65
+ _(1.second / 3.milliseconds).must_equal Rational(1000, 3)
66
+ end
67
+
68
+ it "raises upon a non-numeric" do
69
+ _{5.minutes / "3"}.must_raise TypeError
70
+ end
71
+ end
72
+
33
73
  describe "comparison" do
34
74
  it "compares durations of different units exactly" do
35
75
  _(5.minutes).must_equal 300.seconds
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: duration.rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - thoran
@@ -107,7 +107,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
107
107
  - !ruby/object:Gem::Version
108
108
  version: '0'
109
109
  requirements: []
110
- rubygems_version: 4.0.16
110
+ rubygems_version: 4.0.18
111
111
  specification_version: 4
112
112
  summary: Objects for handling durations of time.
113
113
  test_files: []