monotime 0.5.0 → 0.7.1
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 +4 -4
- data/.github/workflows/ci.yml +16 -0
- data/.rubocop.yml +28 -1
- data/CHANGELOG.md +108 -0
- data/README.md +75 -45
- data/lib/monotime/duration.rb +307 -0
- data/lib/monotime/include.rb +5 -0
- data/lib/monotime/instant.rb +179 -0
- data/lib/monotime/version.rb +2 -1
- data/lib/monotime.rb +3 -467
- data/monotime.gemspec +4 -3
- metadata +30 -13
- data/.travis.yml +0 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8a9395967e506d690912041251bc7b6afd1a373aa49d0b4425ec08ca611e5238
|
4
|
+
data.tar.gz: '09ec6a1e96c9ce434d4847d275d85f7514c4a9bb3d9c71d205b04ab0f06e6d5b'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a68a338484a8534a8953abf5bec0a98a6b7a2dc6a506cd1f5fb1e3b29e1b534fa0d41e4175aa81598273ee5d706d16caeb60ce1b89a92b5e8192f87a960265f3
|
7
|
+
data.tar.gz: f9f329d74339eccc1b4c50007d2cb93cc87132ce6a5841faa327d4db48dfb347df8faee3f9777551cb686e90001def65d72c8a77c6b6df4dfed1d847598ac685
|
@@ -0,0 +1,16 @@
|
|
1
|
+
name: CI
|
2
|
+
on: [push, pull_request]
|
3
|
+
jobs:
|
4
|
+
test:
|
5
|
+
runs-on: ubuntu-latest
|
6
|
+
strategy:
|
7
|
+
fail-fast: false
|
8
|
+
matrix:
|
9
|
+
ruby: ['2.5', '2.6', '2.7', '3.0', jruby, truffleruby]
|
10
|
+
steps:
|
11
|
+
- uses: actions/checkout@v2
|
12
|
+
- uses: ruby/setup-ruby@v1
|
13
|
+
with:
|
14
|
+
ruby-version: ${{ matrix.ruby }}
|
15
|
+
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
|
16
|
+
- run: bundle exec rake
|
data/.rubocop.yml
CHANGED
@@ -1,6 +1,18 @@
|
|
1
|
-
|
1
|
+
AllCops:
|
2
|
+
NewCops: enable
|
3
|
+
Exclude:
|
4
|
+
- "bin/*"
|
5
|
+
- "test/*"
|
6
|
+
- "*.gemspec"
|
7
|
+
- "Rakefile"
|
8
|
+
- "Gemfile"
|
9
|
+
|
10
|
+
Layout/LineLength:
|
2
11
|
Max: 96
|
3
12
|
|
13
|
+
Metrics/ClassLength:
|
14
|
+
Max: 120
|
15
|
+
|
4
16
|
Style/AsciiComments:
|
5
17
|
Enabled: false
|
6
18
|
|
@@ -9,3 +21,18 @@ Style/AccessModifierDeclarations:
|
|
9
21
|
|
10
22
|
Style/FormatStringToken:
|
11
23
|
Enabled: false
|
24
|
+
|
25
|
+
Style/HashEachMethods:
|
26
|
+
Enabled: true
|
27
|
+
|
28
|
+
Style/HashTransformKeys:
|
29
|
+
Enabled: true
|
30
|
+
|
31
|
+
Style/HashTransformValues:
|
32
|
+
Enabled: true
|
33
|
+
|
34
|
+
Style/ExplicitBlockArgument:
|
35
|
+
Enabled: false
|
36
|
+
|
37
|
+
Style/MixinUsage:
|
38
|
+
Enabled: false
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
## 0.7.1 - 2021-10-22
|
4
|
+
### Added
|
5
|
+
- `simplecov` introduced to test suite.
|
6
|
+
- `monotime/include.rb` to auto-include types globally.
|
7
|
+
|
8
|
+
### Changed
|
9
|
+
- All `Instant` and `Duration` instances are now frozen.
|
10
|
+
- Migrate from Travis CI to Github Actions
|
11
|
+
- Update development dependency on `rake`.
|
12
|
+
|
13
|
+
## [0.7.0] - 2019-04-24
|
14
|
+
### Added
|
15
|
+
- `Duration.with_measure`, which yields and returns an array containing its
|
16
|
+
evaluated return value and its `Duration`.
|
17
|
+
|
18
|
+
### Changed
|
19
|
+
- Break `Duration` and `Instant` into their own files.
|
20
|
+
- Rename `Monotime::VERSION` to `Monotime::MONOTIME_VERSION` to reduce
|
21
|
+
potential for collision if the module is included.
|
22
|
+
- Update to bundler 2.0.
|
23
|
+
- Rework README.md. Includes fix for [issue #1] (added a "See Also" section).
|
24
|
+
|
25
|
+
## [0.6.1] - 2018-10-26
|
26
|
+
### Fixed
|
27
|
+
- Build gem from a clean git checkout, not my local development directory.
|
28
|
+
No functional changes.
|
29
|
+
|
30
|
+
## [0.6.0] - 2018-10-26
|
31
|
+
### Added
|
32
|
+
- This `CHANGELOG.md` by request of [@celsworth].
|
33
|
+
- Aliases for `Duration.from_*` and `Duration#to_*` without the prefix. e.g.
|
34
|
+
`Duration.from_secs(42).to_secs == 42` can now be written as
|
35
|
+
`Duration.secs(42).secs == 42`.
|
36
|
+
- `Duration#nonzero?`.
|
37
|
+
- `Instant#in_past?` and `Instant#in_future?`.
|
38
|
+
|
39
|
+
## [0.5.0] - 2018-10-13
|
40
|
+
### Added
|
41
|
+
- `Duration#abs` to make a `Duration` positive.
|
42
|
+
- `Duration#-@` to invert the sign of a `Duration`.
|
43
|
+
- `Duration#positive?`
|
44
|
+
- `Duration#negative?`
|
45
|
+
- `Duration#zero?`
|
46
|
+
|
47
|
+
### Changed
|
48
|
+
- `Instant#sleep` with no argument now sleeps until the `Instant`.
|
49
|
+
- `Duration.from_*` no longer coerce their argument to `Float`.
|
50
|
+
- `Duration#==` checks value via `#to_nanos`, not type.
|
51
|
+
- `Duration#eql?` checks value and type.
|
52
|
+
- `Duration#<=>` compares value via `#to_nanos`.
|
53
|
+
|
54
|
+
## [0.4.0] - 2018-10-09
|
55
|
+
### Added
|
56
|
+
- `Instant#sleep` - sleep to a given `Duration` past an `Instant`.
|
57
|
+
- `Instant#sleep_secs` and `Instant#sleep_millis` convenience methods.
|
58
|
+
- `Duration#sleep` - sleep for the `Duration`.
|
59
|
+
- `Duration#*` - multiply a `Duration` by a number.
|
60
|
+
- `Duration#/` - divide a `Duration` by a number.
|
61
|
+
|
62
|
+
### Changed
|
63
|
+
- More `#to_nanos` `Duration` duck-typing.
|
64
|
+
|
65
|
+
## [0.3.0] - 2018-10-04
|
66
|
+
### Added
|
67
|
+
- `#to_nanos` is now used to duck-type `Duration` everywhere.
|
68
|
+
|
69
|
+
### Changed
|
70
|
+
- Make `<=>` return nil on invalid types, rather than raising a `TypeError`.
|
71
|
+
|
72
|
+
### Removed
|
73
|
+
- Dependency on `dry-equalizer`.
|
74
|
+
|
75
|
+
## [0.2.0] - 2018-10-03
|
76
|
+
### Added
|
77
|
+
- `Instant#to_s` as an alias for `#elapsed.to_s`
|
78
|
+
- `Duration#to_nanos`, with some limited duck-typing.
|
79
|
+
|
80
|
+
### Changed
|
81
|
+
- Switch to microseconds internally.
|
82
|
+
- `Duration#to_{secs,millis,micros}` now return a `Float`.
|
83
|
+
- `Instant#ns` is now `protected`.
|
84
|
+
|
85
|
+
### Fixed
|
86
|
+
- `Duration#to_s` zero-stripping with precision=0.
|
87
|
+
- `Instant#-` argument ordering with other `Instant`.
|
88
|
+
- `Duration#to_micros` returns microseconds, not picoseconds.
|
89
|
+
|
90
|
+
### Removed
|
91
|
+
- `Instant` and `Duration` maths methods no longer support passing an `Integer`
|
92
|
+
number of nanoseconds.
|
93
|
+
|
94
|
+
## [0.1.0] - 2018-10-02
|
95
|
+
### Added
|
96
|
+
- Initial release
|
97
|
+
|
98
|
+
|
99
|
+
[0.1.0]: https://github.com/Freaky/monotime/commits/v0.1.0
|
100
|
+
[0.2.0]: https://github.com/Freaky/monotime/commits/v0.2.0
|
101
|
+
[0.3.0]: https://github.com/Freaky/monotime/commits/v0.3.0
|
102
|
+
[0.4.0]: https://github.com/Freaky/monotime/commits/v0.4.0
|
103
|
+
[0.5.0]: https://github.com/Freaky/monotime/commits/v0.5.0
|
104
|
+
[0.6.0]: https://github.com/Freaky/monotime/commits/v0.6.0
|
105
|
+
[0.6.1]: https://github.com/Freaky/monotime/commits/v0.6.1
|
106
|
+
[0.7.0]: https://github.com/Freaky/monotime/commits/v0.7.0
|
107
|
+
[issue #1]: https://github.com/Freaky/monotime/issues/1
|
108
|
+
[@celsworth]: https://github.com/celsworth
|
data/README.md
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
[](https://badge.fury.io/rb/monotime)
|
2
|
-
|
2
|
+

|
3
|
+
[](http://inch-ci.org/github/Freaky/monotime)
|
4
|
+
[](https://www.rubydoc.info/gems/monotime)
|
3
5
|
|
4
6
|
# Monotime
|
5
7
|
|
@@ -21,66 +23,75 @@ Or install it yourself as:
|
|
21
23
|
|
22
24
|
$ gem install monotime
|
23
25
|
|
24
|
-
|
26
|
+
`Monotime` is tested on Ruby 2.5—3.0 and recent JRuby 9.x releases.
|
25
27
|
|
26
|
-
|
27
|
-
this pile of nonsense:
|
28
|
+
## Usage
|
28
29
|
|
29
30
|
```ruby
|
30
|
-
|
31
|
-
|
32
|
-
|
31
|
+
require 'monotime'
|
32
|
+
# or, to automatically include Monotime::* in the global scope,
|
33
|
+
# as used by these examples:
|
34
|
+
require 'monotime/include'
|
33
35
|
```
|
34
36
|
|
35
|
-
|
36
|
-
|
37
|
+
`Monotime` offers a `Duration` type for describing spans of time, and an
|
38
|
+
`Instant` type for describing points in time. Both operate at nanosecond
|
39
|
+
resolution to the limits of whatever your Ruby implementation supports.
|
37
40
|
|
38
|
-
`
|
41
|
+
For example, to measure an elapsed time, either create an `Instant` to mark the
|
42
|
+
start point, perform the action and then ask for the `Duration` that has elapsed
|
43
|
+
since:
|
39
44
|
|
40
45
|
```ruby
|
41
|
-
include Monotime
|
42
|
-
|
43
46
|
start = Instant.now
|
44
47
|
do_something
|
45
48
|
elapsed = start.elapsed
|
49
|
+
```
|
46
50
|
|
47
|
-
|
51
|
+
Or use a convenience method:
|
52
|
+
|
53
|
+
```ruby
|
48
54
|
elapsed = Duration.measure { do_something }
|
55
|
+
# or
|
56
|
+
return_value, elapsed = Duration.with_measure { compute_something }
|
49
57
|
```
|
50
58
|
|
51
|
-
`
|
52
|
-
|
53
|
-
|
59
|
+
`Duration` offers formatting:
|
60
|
+
|
61
|
+
```ruby
|
62
|
+
Duration.millis(42).to_s # => "42ms"
|
63
|
+
Duration.nanos(12345).to_s # => "12.345μs"
|
64
|
+
Duration.secs(1.12345).to_s(2) # => "1.12s"
|
65
|
+
```
|
54
66
|
|
55
|
-
|
67
|
+
Conversions:
|
56
68
|
|
57
69
|
```ruby
|
58
|
-
Duration.
|
59
|
-
Duration.
|
60
|
-
Duration.from_secs(1.12345).to_s(2) # => "1.12s"
|
70
|
+
Duration.secs(10).millis # => 10000.0
|
71
|
+
Duration.micros(12345).secs # => 0.012345
|
61
72
|
```
|
62
73
|
|
63
|
-
And
|
74
|
+
And basic mathematical operations:
|
64
75
|
|
65
76
|
```ruby
|
66
|
-
(Duration.
|
67
|
-
(Duration.
|
68
|
-
(Duration.
|
69
|
-
(Duration.
|
77
|
+
(Duration.millis(42) + Duration.secs(1)).to_s # => "1.042s"
|
78
|
+
(Duration.millis(42) - Duration.secs(1)).to_s # => "-958ms"
|
79
|
+
(Duration.secs(42) * 2).to_s # => "84s"
|
80
|
+
(Duration.secs(42) / 2).to_s # => "21s"
|
70
81
|
```
|
71
82
|
|
72
83
|
`Instant` does some simple maths too:
|
73
84
|
|
74
85
|
```ruby
|
75
86
|
# Instant - Duration => Instant
|
76
|
-
(Instant.now - Duration.
|
87
|
+
(Instant.now - Duration.secs(1)).elapsed.to_s # => "1.000014627s"
|
77
88
|
|
78
89
|
# Instant - Instant => Duration
|
79
90
|
(Instant.now - Instant.now).to_s # => "-5.585μs"
|
80
91
|
```
|
81
92
|
|
82
93
|
`Duration` and `Instant` are also `Comparable` with other instances of their
|
83
|
-
type, and
|
94
|
+
type, and can be used in hashes, sets, and similar structures.
|
84
95
|
|
85
96
|
## Sleeping
|
86
97
|
|
@@ -89,35 +100,38 @@ is not yet implemented):
|
|
89
100
|
|
90
101
|
```ruby
|
91
102
|
# Equivalent
|
92
|
-
sleep(Duration.
|
93
|
-
|
94
|
-
Duration.from_secs(1).sleep # => 1
|
103
|
+
sleep(Duration.secs(1).secs) # => 1
|
104
|
+
Duration.secs(1).sleep # => 1
|
95
105
|
```
|
96
106
|
|
97
107
|
So can `Instant`, taking a `Duration` and sleeping until the given `Duration`
|
98
|
-
past the time the `Instant` was created, if any.
|
99
|
-
|
108
|
+
past the time the `Instant` was created, if any. This can be useful for
|
109
|
+
maintaining a precise candence between tasks:
|
100
110
|
|
101
111
|
```ruby
|
102
|
-
|
112
|
+
interval = Duration.secs(60)
|
113
|
+
start = Instant.now
|
103
114
|
loop do
|
104
|
-
|
105
|
-
|
106
|
-
start
|
107
|
-
# alternative: start.sleep_secs(60)
|
115
|
+
do_stuff
|
116
|
+
start.sleep(interval)
|
117
|
+
start += interval
|
108
118
|
end
|
109
119
|
```
|
110
120
|
|
111
|
-
Or you can declare
|
121
|
+
Or you can declare an `Instant` in the future and sleep to that point:
|
112
122
|
|
113
123
|
```ruby
|
114
|
-
|
115
|
-
|
116
|
-
|
124
|
+
interval = Duration.secs(60)
|
125
|
+
deadline = Instant.now + interval
|
126
|
+
loop do
|
127
|
+
do_stuff
|
128
|
+
deadline.sleep
|
129
|
+
deadline += interval
|
130
|
+
end
|
117
131
|
```
|
118
132
|
|
119
|
-
`Instant#sleep` returns a `Duration` which was slept, or a negative `Duration`
|
120
|
-
the desired sleep
|
133
|
+
`Instant#sleep` returns a `Duration` which was slept, or a negative `Duration`
|
134
|
+
indicating that the desired sleep point was in the past.
|
121
135
|
|
122
136
|
## Duration duck typing
|
123
137
|
|
@@ -133,8 +147,8 @@ class Numeric
|
|
133
147
|
end
|
134
148
|
end
|
135
149
|
|
136
|
-
(Duration.
|
137
|
-
(Instant.now - 42).to_s
|
150
|
+
(Duration.secs(1) + 41).to_s # => "42s"
|
151
|
+
(Instant.now - 42).to_s # => "42.000010545s"
|
138
152
|
```
|
139
153
|
|
140
154
|
## Development
|
@@ -150,3 +164,19 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/Freaky
|
|
150
164
|
## License
|
151
165
|
|
152
166
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
167
|
+
|
168
|
+
## See Also
|
169
|
+
|
170
|
+
### Core Ruby
|
171
|
+
|
172
|
+
For a zero-dependency alternative, see
|
173
|
+
[`Process.clock_gettime`](https://ruby-doc.org/core-2.6.3/Process.html#method-c-clock_gettime).
|
174
|
+
`monotime` currently only uses `Process::CLOCK_MONOTONIC`, but others may offer higher precision
|
175
|
+
depending on platform.
|
176
|
+
|
177
|
+
### Other Gems
|
178
|
+
|
179
|
+
[hitimes](https://rubygems.org/gems/hitimes) is a popular and mature alternative
|
180
|
+
which also includes a variety of features for gathering statistics about
|
181
|
+
measurements, and may offer higher precision on some platforms.
|
182
|
+
|
@@ -0,0 +1,307 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Monotime
|
4
|
+
# A type representing a span of time in nanoseconds.
|
5
|
+
class Duration
|
6
|
+
include Comparable
|
7
|
+
|
8
|
+
# Create a new +Duration+ of a specified number of nanoseconds, zero by
|
9
|
+
# default.
|
10
|
+
#
|
11
|
+
# Users are strongly advised to use +#from_nanos+ instead.
|
12
|
+
#
|
13
|
+
# @param nanos [Integer]
|
14
|
+
# @see #from_nanos
|
15
|
+
def initialize(nanos = 0)
|
16
|
+
@ns = Integer(nanos)
|
17
|
+
freeze
|
18
|
+
end
|
19
|
+
|
20
|
+
class << self
|
21
|
+
# Generate a new +Duration+ measuring the given number of seconds.
|
22
|
+
#
|
23
|
+
# @param secs [Numeric]
|
24
|
+
# @return [Duration]
|
25
|
+
def from_secs(secs)
|
26
|
+
new(Integer(secs * 1_000_000_000))
|
27
|
+
end
|
28
|
+
|
29
|
+
alias secs from_secs
|
30
|
+
|
31
|
+
# Generate a new +Duration+ measuring the given number of milliseconds.
|
32
|
+
#
|
33
|
+
# @param millis [Numeric]
|
34
|
+
# @return [Duration]
|
35
|
+
def from_millis(millis)
|
36
|
+
new(Integer(millis * 1_000_000))
|
37
|
+
end
|
38
|
+
|
39
|
+
alias millis from_millis
|
40
|
+
|
41
|
+
# Generate a new +Duration+ measuring the given number of microseconds.
|
42
|
+
#
|
43
|
+
# @param micros [Numeric]
|
44
|
+
# @return [Duration]
|
45
|
+
def from_micros(micros)
|
46
|
+
new(Integer(micros * 1_000))
|
47
|
+
end
|
48
|
+
|
49
|
+
alias micros from_micros
|
50
|
+
|
51
|
+
# Generate a new +Duration+ measuring the given number of nanoseconds.
|
52
|
+
#
|
53
|
+
# @param nanos [Numeric]
|
54
|
+
# @return [Duration]
|
55
|
+
def from_nanos(nanos)
|
56
|
+
new(Integer(nanos))
|
57
|
+
end
|
58
|
+
|
59
|
+
alias nanos from_nanos
|
60
|
+
|
61
|
+
# Return a +Duration+ measuring the elapsed time of the yielded block.
|
62
|
+
#
|
63
|
+
# @example
|
64
|
+
# Duration.measure { sleep(0.5) }.to_s # => "512.226109ms"
|
65
|
+
#
|
66
|
+
# @return [Duration]
|
67
|
+
def measure
|
68
|
+
Instant.now.tap { yield }.elapsed
|
69
|
+
end
|
70
|
+
|
71
|
+
# Return the result of the yielded block alongside a +Duration+.
|
72
|
+
#
|
73
|
+
# @example
|
74
|
+
# Duration.with_measure { "bloop" } # => ["bloop", #<Monotime::Duration: ...>]
|
75
|
+
#
|
76
|
+
# @return [Object, Duration]
|
77
|
+
def with_measure
|
78
|
+
start = Instant.now
|
79
|
+
ret = yield
|
80
|
+
[ret, start.elapsed]
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# Add another +Duration+ or +#to_nanos+-coercible object to this one,
|
85
|
+
# returning a new +Duration+.
|
86
|
+
#
|
87
|
+
# @example
|
88
|
+
# (Duration.from_secs(10) + Duration.from_secs(5)).to_s # => "15s"
|
89
|
+
#
|
90
|
+
# @param [Duration, #to_nanos]
|
91
|
+
# @return [Duration]
|
92
|
+
def +(other)
|
93
|
+
raise TypeError, 'Not one of: [Duration, #to_nanos]' unless other.respond_to?(:to_nanos)
|
94
|
+
|
95
|
+
Duration.new(to_nanos + other.to_nanos)
|
96
|
+
end
|
97
|
+
|
98
|
+
# Subtract another +Duration+ or +#to_nanos+-coercible object from this one,
|
99
|
+
# returning a new +Duration+.
|
100
|
+
#
|
101
|
+
# @example
|
102
|
+
# (Duration.from_secs(10) - Duration.from_secs(5)).to_s # => "5s"
|
103
|
+
#
|
104
|
+
# @param [Duration, #to_nanos]
|
105
|
+
# @return [Duration]
|
106
|
+
def -(other)
|
107
|
+
raise TypeError, 'Not one of: [Duration, #to_nanos]' unless other.respond_to?(:to_nanos)
|
108
|
+
|
109
|
+
Duration.new(to_nanos - other.to_nanos)
|
110
|
+
end
|
111
|
+
|
112
|
+
# Divide this duration by a +Numeric+.
|
113
|
+
#
|
114
|
+
# @example
|
115
|
+
# (Duration.from_secs(10) / 2).to_s # => "5s"
|
116
|
+
#
|
117
|
+
# @param [Numeric]
|
118
|
+
# @return [Duration]
|
119
|
+
def /(other)
|
120
|
+
Duration.new(to_nanos / other)
|
121
|
+
end
|
122
|
+
|
123
|
+
# Multiply this duration by a +Numeric+.
|
124
|
+
#
|
125
|
+
# @example
|
126
|
+
# (Duration.from_secs(10) * 2).to_s # => "20s"
|
127
|
+
#
|
128
|
+
# @param [Numeric]
|
129
|
+
# @return [Duration]
|
130
|
+
def *(other)
|
131
|
+
Duration.new(to_nanos * other)
|
132
|
+
end
|
133
|
+
|
134
|
+
# Unary minus: make a positive +Duration+ negative, and vice versa.
|
135
|
+
#
|
136
|
+
# @example
|
137
|
+
# -Duration.from_secs(-1).to_s # => "1s"
|
138
|
+
# -Duration.from_secs(1).to_s # => "-1s"
|
139
|
+
#
|
140
|
+
# @return [Duration]
|
141
|
+
def -@
|
142
|
+
Duration.new(-to_nanos)
|
143
|
+
end
|
144
|
+
|
145
|
+
# Return a +Duration+ that's absolute (positive).
|
146
|
+
#
|
147
|
+
# @example
|
148
|
+
# Duration.from_secs(-1).abs.to_s # => "1s"
|
149
|
+
# Duration.from_secs(1).abs.to_s # => "1s"
|
150
|
+
#
|
151
|
+
# @return [Duration]
|
152
|
+
def abs
|
153
|
+
return self if positive? || zero?
|
154
|
+
|
155
|
+
Duration.new(to_nanos.abs)
|
156
|
+
end
|
157
|
+
|
158
|
+
# Compare the *value* of this +Duration+ with another, or any +#to_nanos+-coercible
|
159
|
+
# object, or nil if not comparable.
|
160
|
+
#
|
161
|
+
# @param [Duration, #to_nanos, Object]
|
162
|
+
# @return [-1, 0, 1, nil]
|
163
|
+
def <=>(other)
|
164
|
+
to_nanos <=> other.to_nanos if other.respond_to?(:to_nanos)
|
165
|
+
end
|
166
|
+
|
167
|
+
# Compare the equality of the *value* of this +Duration+ with another, or
|
168
|
+
# any +#to_nanos+-coercible object, or nil if not comparable.
|
169
|
+
#
|
170
|
+
# @param [Duration, #to_nanos, Object]
|
171
|
+
# @return [Boolean]
|
172
|
+
def ==(other)
|
173
|
+
other.respond_to?(:to_nanos) && to_nanos == other.to_nanos
|
174
|
+
end
|
175
|
+
|
176
|
+
# Check equality of the value and type of this +Duration+ with another.
|
177
|
+
#
|
178
|
+
# @param [Duration, Object]
|
179
|
+
# @return [Boolean]
|
180
|
+
def eql?(other)
|
181
|
+
other.is_a?(Duration) && to_nanos == other.to_nanos
|
182
|
+
end
|
183
|
+
|
184
|
+
# Generate a hash for this type and value.
|
185
|
+
#
|
186
|
+
# @return [Integer]
|
187
|
+
def hash
|
188
|
+
self.class.hash ^ to_nanos.hash
|
189
|
+
end
|
190
|
+
|
191
|
+
# Return this +Duration+ in seconds.
|
192
|
+
#
|
193
|
+
# @return [Float]
|
194
|
+
def to_secs
|
195
|
+
to_nanos / 1_000_000_000.0
|
196
|
+
end
|
197
|
+
|
198
|
+
alias secs to_secs
|
199
|
+
|
200
|
+
# Return this +Duration+ in milliseconds.
|
201
|
+
#
|
202
|
+
# @return [Float]
|
203
|
+
def to_millis
|
204
|
+
to_nanos / 1_000_000.0
|
205
|
+
end
|
206
|
+
|
207
|
+
alias millis to_millis
|
208
|
+
|
209
|
+
# Return this +Duration+ in microseconds.
|
210
|
+
#
|
211
|
+
# @return [Float]
|
212
|
+
def to_micros
|
213
|
+
to_nanos / 1_000.0
|
214
|
+
end
|
215
|
+
|
216
|
+
alias micros to_micros
|
217
|
+
|
218
|
+
# Return this +Duration+ in nanoseconds.
|
219
|
+
#
|
220
|
+
# @return [Integer]
|
221
|
+
def to_nanos
|
222
|
+
@ns
|
223
|
+
end
|
224
|
+
|
225
|
+
alias nanos to_nanos
|
226
|
+
|
227
|
+
# Return true if this +Duration+ is positive.
|
228
|
+
#
|
229
|
+
# @return [Boolean]
|
230
|
+
def positive?
|
231
|
+
to_nanos.positive?
|
232
|
+
end
|
233
|
+
|
234
|
+
# Return true if this +Duration+ is negative.
|
235
|
+
#
|
236
|
+
# @return [Boolean]
|
237
|
+
def negative?
|
238
|
+
to_nanos.negative?
|
239
|
+
end
|
240
|
+
|
241
|
+
# Return true if this +Duration+ is zero.
|
242
|
+
#
|
243
|
+
# @return [Boolean]
|
244
|
+
def zero?
|
245
|
+
to_nanos.zero?
|
246
|
+
end
|
247
|
+
|
248
|
+
# Return true if this +Duration+ is non-zero.
|
249
|
+
#
|
250
|
+
# @return [Boolean]
|
251
|
+
def nonzero?
|
252
|
+
to_nanos.nonzero?
|
253
|
+
end
|
254
|
+
|
255
|
+
# Sleep for the duration of this +Duration+. Equivalent to
|
256
|
+
# +Kernel.sleep(duration.to_secs)+.
|
257
|
+
#
|
258
|
+
# @example
|
259
|
+
# Duration.from_secs(1).sleep # => 1
|
260
|
+
# Duration.from_secs(-1).sleep # => raises NotImplementedError
|
261
|
+
#
|
262
|
+
# @raise [NotImplementedError] negative +Duration+ sleeps are not yet supported.
|
263
|
+
# @return [Integer]
|
264
|
+
# @see Instant#sleep
|
265
|
+
def sleep
|
266
|
+
raise NotImplementedError, 'time travel module missing' if negative?
|
267
|
+
|
268
|
+
Kernel.sleep(to_secs)
|
269
|
+
end
|
270
|
+
|
271
|
+
DIVISORS = [
|
272
|
+
[1_000_000_000.0, 's'],
|
273
|
+
[1_000_000.0, 'ms'],
|
274
|
+
[1_000.0, 'μs'],
|
275
|
+
[0, 'ns']
|
276
|
+
].map(&:freeze).freeze
|
277
|
+
|
278
|
+
private_constant :DIVISORS
|
279
|
+
|
280
|
+
# Format this +Duration+ into a human-readable string, with a given number
|
281
|
+
# of decimal places.
|
282
|
+
#
|
283
|
+
# The exact format is subject to change, users with specific requirements
|
284
|
+
# are encouraged to use their own formatting methods.
|
285
|
+
#
|
286
|
+
# @example
|
287
|
+
# Duration.from_nanos(100).to_s # => "100ns"
|
288
|
+
# Duration.from_micros(100).to_s # => "100μs"
|
289
|
+
# Duration.from_millis(100).to_s # => "100ms"
|
290
|
+
# Duration.from_secs(100).to_s # => "100s"
|
291
|
+
# Duration.from_nanos(1234567).to_s # => "1.234567ms"
|
292
|
+
# Duration.from_nanos(1234567).to_s(2) # => "1.23ms"
|
293
|
+
#
|
294
|
+
# @param precision [Integer] the maximum number of decimal places
|
295
|
+
# @return [String]
|
296
|
+
def to_s(precision = 9)
|
297
|
+
precision = Integer(precision).abs
|
298
|
+
div, unit = DIVISORS.find { |d, _| to_nanos.abs >= d }
|
299
|
+
|
300
|
+
if div.zero?
|
301
|
+
format('%d%s', to_nanos, unit)
|
302
|
+
else
|
303
|
+
format("%#.#{precision}f", to_nanos / div).sub(/\.?0*\z/, '') << unit
|
304
|
+
end
|
305
|
+
end
|
306
|
+
end
|
307
|
+
end
|