duration 0.0.4 → 0.1.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.
- data/LICENSE +21 -0
- data/README +152 -0
- data/lib/duration.rb +188 -499
- data/lib/duration/holidays.rb +3 -0
- data/lib/duration/locale.rb +3 -0
- data/lib/duration/localizations.rb +42 -0
- data/lib/duration/localizations/english.rb +14 -0
- data/lib/duration/localizations/korean.rb +14 -0
- data/lib/duration/numeric.rb +63 -0
- data/lib/duration/time.rb +7 -0
- data/lib/duration/version.rb +9 -0
- metadata +27 -11
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2007 Matthew Harris
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README
ADDED
@@ -0,0 +1,152 @@
|
|
1
|
+
= Project
|
2
|
+
== Duration
|
3
|
+
== http://rubyforge.org/projects/duration
|
4
|
+
|
5
|
+
= Author
|
6
|
+
Matthew Harris <matt@matthewharris.org>
|
7
|
+
http://matthewharris.org
|
8
|
+
|
9
|
+
= Synopsis
|
10
|
+
Duration objects are simple mechanisms that allow you to operate on durations
|
11
|
+
of time.
|
12
|
+
|
13
|
+
They allow you to know how much time has passed since a certain
|
14
|
+
point in time, or they can tell you how much time something is (when given as
|
15
|
+
seconds) in different units of time measurement.
|
16
|
+
|
17
|
+
Durations would particularly be useful for those scripts or applications that
|
18
|
+
allow you to know the uptime of themselves or perhaps provide a countdown until
|
19
|
+
a certain event.
|
20
|
+
|
21
|
+
As of version 0.1.0, the duration library has been completely rewritten with
|
22
|
+
better knowledge.
|
23
|
+
|
24
|
+
= Features
|
25
|
+
Duration is packed with many features and has plans for many more.
|
26
|
+
|
27
|
+
=== Current features
|
28
|
+
* Duck typing objects to work with Duration
|
29
|
+
* Localization support of formatted strings (feel free to submit your own!)
|
30
|
+
* Capability to compare durations to any object (duck typing)
|
31
|
+
* Arithmetic operations from Duration instances are legal (duck typing)
|
32
|
+
* Convenience methods for creating durations from Time objects
|
33
|
+
|
34
|
+
=== Soon to come features
|
35
|
+
* Collection of holiday-related methods (creating durations since/until a holiday)
|
36
|
+
* A larger collection of locales
|
37
|
+
* Definitely have to put more Rubyisms in there too!
|
38
|
+
* ...and much more! (can't think of any yet)
|
39
|
+
|
40
|
+
= Credits
|
41
|
+
I want to thank everyone who downloaded duration, tried it, and sent me e-mails
|
42
|
+
of appreciation, feedback, bug reports and suggestions.
|
43
|
+
|
44
|
+
Your gratitude has motivated me to rework this almost-dead library and continue
|
45
|
+
to maintain it.
|
46
|
+
|
47
|
+
Thanks!
|
48
|
+
|
49
|
+
= Usage
|
50
|
+
Using the duration library is fairly simple and straight-forward. The base
|
51
|
+
class most of you will be using is the Duration class.
|
52
|
+
|
53
|
+
=== Initialization
|
54
|
+
There are two ways to initialize the Duration class. One way is to pass an
|
55
|
+
object capable of responding to the #to_i method. That object is expected to
|
56
|
+
return an integer or float that represents the number of seconds for the
|
57
|
+
duration object.
|
58
|
+
|
59
|
+
The second way to initialize the Duration class is to pass a key=>value paired
|
60
|
+
Hash object. The hash's keys will be scanned for units of measurement defiend
|
61
|
+
in Duration::UNITS or (the keys of) Duration::MULTIPLES.
|
62
|
+
|
63
|
+
Here's an example of using the formerly mentioned solution:
|
64
|
+
duration = Duration.new(Time.now)
|
65
|
+
=> #<Duration: 1981 weeks, 3 hours, 50 minutes and 44 seconds>
|
66
|
+
|
67
|
+
As you can see, it gave us the duration since the UNIX epoch. This works fine
|
68
|
+
because Time objects respond to the #to_i method. And Time#to_i returns a
|
69
|
+
UNIX timestamp (which is the number of seconds that have passed since the UNIX
|
70
|
+
epoch, January 1, 1970).
|
71
|
+
|
72
|
+
Before I move on to demonstrating the second solution, I'd like to note that
|
73
|
+
there are Time#to_duration, Time#duration and Duration.since_epoch for
|
74
|
+
accomplishing similar tasks.
|
75
|
+
|
76
|
+
Alright, the second solution is to pass in a Hash object, as previously
|
77
|
+
mentioned.
|
78
|
+
|
79
|
+
For example:
|
80
|
+
duration = Duration.new(:hours => 12, :minutes => 58, :days => 14)
|
81
|
+
=> #<Duration: 2 weeks, 12 hours and 58 minutes>
|
82
|
+
|
83
|
+
So, it works. But also notice that I gave it `:days => 14', but it spat out
|
84
|
+
`2 weeks'. Yes, Duration knows what it's doing.
|
85
|
+
|
86
|
+
There are more keys you can pass. Check all the keys available in the
|
87
|
+
Duration::MULTIPLES hash to see. All those keys are valid to pass. Any
|
88
|
+
duplicated multiples will merely be added to each other, so be careful if that's
|
89
|
+
not the desired behavior.
|
90
|
+
|
91
|
+
=== Formatting
|
92
|
+
The Duration class provides Duration#format (alias: Duration#strftime) for
|
93
|
+
formatting the time units into a string.
|
94
|
+
|
95
|
+
For more information on the identifiers it supports, check out Duration#format.
|
96
|
+
|
97
|
+
Here's an example of how to format the known minutes in the duration:
|
98
|
+
duration = Duration.since_epoch
|
99
|
+
duration.format('%~m passed: %m')
|
100
|
+
=> "minutes passed: 4"
|
101
|
+
|
102
|
+
Now, this may look a bit tricky at first, but it's somewhat similar to the
|
103
|
+
standard strftime() function you see in many lanuages, with the exception of the
|
104
|
+
weird "%~m" type identifiers. These identifiers are the correct terminology for
|
105
|
+
the corresponding time unit.
|
106
|
+
|
107
|
+
The identifiers are locale-sensitive and can be easily changed to another
|
108
|
+
language. Localization is done internally from the duration library, and does
|
109
|
+
not use the system's locales.
|
110
|
+
|
111
|
+
If you would like to write a locale for your language (if it doesn't exist),
|
112
|
+
you can simply read the source code of the existing locales lying within the
|
113
|
+
"duration/localizations" directory. They are straight-forward 10-15 line
|
114
|
+
modules and are not complex at all.
|
115
|
+
|
116
|
+
=== Arithmetic
|
117
|
+
Duration objects support arithmetic against any object that response to the
|
118
|
+
#to_i method. This is pretty self-explanatory. What happens when you divide
|
119
|
+
a duration by 2?
|
120
|
+
|
121
|
+
duration = Duration.since_epoch
|
122
|
+
=> #<Duration: 1981 weeks, 4 hours, 12 minutes and 54 seconds>
|
123
|
+
|
124
|
+
duration / 2
|
125
|
+
=> #<Duration: 990 weeks, 3 days, 14 hours, 6 minutes and 27 seconds>
|
126
|
+
|
127
|
+
Pretty simple, right?
|
128
|
+
|
129
|
+
=== Magical Methods
|
130
|
+
Finally, Duration has a few magical methods that are handled through the
|
131
|
+
#method_missing override. Duration instances support all method calls to such
|
132
|
+
methods as `round_<unit>_to_<unit>' and `<unit>_to_<unit>'. These methods
|
133
|
+
basically convert their <unit> to another <unit> and spit back the numbers.
|
134
|
+
|
135
|
+
The `round_<unit>_to_<unit>' method rounds the converted unit.
|
136
|
+
|
137
|
+
Duration.since_epoch.weeks_to_days
|
138
|
+
=> 13867.0
|
139
|
+
|
140
|
+
Duration.since_epoch.round_weeks_to_days
|
141
|
+
=> 13867
|
142
|
+
|
143
|
+
Duration.since_epoch.weeks_to_days.round
|
144
|
+
=> 13867
|
145
|
+
|
146
|
+
= Feedback
|
147
|
+
Well, I hope you learned a lot from the above explanations. Time for you to get
|
148
|
+
creative on your own now!
|
149
|
+
|
150
|
+
If you have any feedback, bug reports, suggestions or locale submissions, just
|
151
|
+
e-mail me at matt@matthewharris.org or shugotenshi@gmail.com and I will be glad
|
152
|
+
to answer you back.
|
data/lib/duration.rb
CHANGED
@@ -1,515 +1,204 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
# = Usage
|
15
|
-
#
|
16
|
-
# require 'duration'
|
17
|
-
# => true
|
18
|
-
# d = Duration.new(60 * 60 * 24 * 10 + 120 + 30)
|
19
|
-
# => #<Duration: 1 week, 3 days, 2 minutes and 30 seconds>
|
20
|
-
# d.to_s
|
21
|
-
# => "1 week, 3 days, 2 minutes and 30 seconds"
|
22
|
-
# [d.weeks, d.days]
|
23
|
-
# => [1, 3]
|
24
|
-
# d.days = 7; d
|
25
|
-
# => #<Duration: 2 weeks, 2 minutes and 30 seconds>
|
26
|
-
# d.strftime('%w w, %d d, %h h, %m m, %s s')
|
27
|
-
# => "2 w, 0 d, 0 h, 2 m, 30 s"
|
28
|
-
#
|
1
|
+
require 'duration/version'
|
2
|
+
require 'duration/numeric'
|
3
|
+
require 'duration/time'
|
4
|
+
require 'duration/localizations'
|
5
|
+
Duration::Localizations.load_all
|
6
|
+
|
7
|
+
# Duration objects are simple mechanisms that allow you to operate on durations
|
8
|
+
# of time. They allow you to know how much time has passed since a certain
|
9
|
+
# point in time, or they can tell you how much time something is (when given as
|
10
|
+
# seconds) in different units of time measurement. Durations would particularly
|
11
|
+
# be useful for those scripts or applications that allow you to know the uptime
|
12
|
+
# of themselves or perhaps provide a countdown until a certain event.
|
29
13
|
class Duration
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
'h' => @hours ,
|
101
|
-
'm' => @minutes,
|
102
|
-
's' => @seconds,
|
103
|
-
't' => @total ,
|
104
|
-
'x' => to_s}
|
105
|
-
|
106
|
-
fmt.gsub(/%?%(w|d|h|m|s|t|x)/) do |match|
|
107
|
-
match.size == 3 ? match : h[match[1..1]]
|
108
|
-
end.gsub('%%', '%')
|
109
|
-
end
|
110
|
-
|
111
|
-
# Get the number of seconds of a given part, or simply just get the number of
|
112
|
-
# seconds.
|
113
|
-
#
|
114
|
-
# *Example*
|
115
|
-
#
|
116
|
-
# d = Duration.new(:weeks => 1, :days => 1, :hours => 1, :seconds => 30)
|
117
|
-
# => #<Duration: 1 week, 1 day, 1 hour and 30 seconds>
|
118
|
-
# d.seconds(:weeks)
|
119
|
-
# => 604800
|
120
|
-
# d.seconds(:days)
|
121
|
-
# => 86400
|
122
|
-
# d.seconds(:hours)
|
123
|
-
# => 3600
|
124
|
-
# d.seconds
|
125
|
-
# => 30
|
126
|
-
#
|
127
|
-
def seconds(part = nil)
|
128
|
-
# Table mapping
|
129
|
-
h = {:weeks => WEEK, :days => DAY, :hours => HOUR, :minutes => MINUTE}
|
130
|
-
|
131
|
-
if [:weeks, :days, :hours, :minutes].include? part
|
132
|
-
__send__(part) * h[part]
|
133
|
-
else
|
134
|
-
@seconds
|
135
|
-
end
|
136
|
-
end
|
137
|
-
|
138
|
-
# For iterating through the duration set of weeks, days, hours, minutes, and
|
139
|
-
# seconds.
|
140
|
-
#
|
141
|
-
# *Example*
|
142
|
-
#
|
143
|
-
# Duration.new(:weeks => 1, :seconds => 30).each do |part, time|
|
144
|
-
# puts "part: #{part}, time: #{time}"
|
145
|
-
# end
|
146
|
-
#
|
147
|
-
# _Output_
|
148
|
-
#
|
149
|
-
# part: weeks, time: 1
|
150
|
-
# part: days, time: 0
|
151
|
-
# part: hours, time: 0
|
152
|
-
# part: minutes, time: 0
|
153
|
-
# part: seconds, time: 30
|
154
|
-
#
|
155
|
-
def each
|
156
|
-
[['weeks' , @weeks ],
|
157
|
-
['days' , @days ],
|
158
|
-
['hours' , @hours ],
|
159
|
-
['minutes' , @minutes],
|
160
|
-
['seconds' , @seconds]].each do |part, time|
|
161
|
-
# Yield to block
|
162
|
-
yield part, time
|
14
|
+
include Comparable
|
15
|
+
include Enumerable
|
16
|
+
|
17
|
+
# Unit multiples
|
18
|
+
MULTIPLES = {
|
19
|
+
:seconds => 1,
|
20
|
+
:minutes => 60,
|
21
|
+
:hours => 3600,
|
22
|
+
:days => 86400,
|
23
|
+
:weeks => 604800,
|
24
|
+
:second => 1,
|
25
|
+
:minute => 60,
|
26
|
+
:hour => 3600,
|
27
|
+
:day => 86400,
|
28
|
+
:week => 604800
|
29
|
+
}
|
30
|
+
|
31
|
+
# Unit names
|
32
|
+
UNITS = [:seconds, :minutes, :hours, :days, :weeks]
|
33
|
+
|
34
|
+
attr_reader :total, :seconds, :minutes, :hours, :days, :weeks
|
35
|
+
|
36
|
+
# Change the locale Duration will use when converting itself to a string
|
37
|
+
def Duration.change_locale(locale)
|
38
|
+
@@locale = Localizations.locales[locale.to_sym] or raise LocaleError, "undefined locale '#{locale}'"
|
39
|
+
end
|
40
|
+
|
41
|
+
# Load default locale
|
42
|
+
change_locale(Localizations::DEFAULT_LOCALE)
|
43
|
+
|
44
|
+
# Constructs a Duration instance that represents the duration since the UNIX
|
45
|
+
# epoch (1970-01-01T00:00:00Z)
|
46
|
+
def Duration.since_epoch
|
47
|
+
new(Time.now)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Initialize a duration. `args' can be a hash or anything else. If a hash is
|
51
|
+
# passed, it will be scanned for a key=>value pair of time units such as those
|
52
|
+
# listed in the Duration::UNITS array or Duration::MULTIPLES hash.
|
53
|
+
#
|
54
|
+
# If anything else except a hash is passed, #to_i is invoked on that object
|
55
|
+
# and expects that it return the number of seconds desired for the duration.
|
56
|
+
def initialize(args = 0)
|
57
|
+
# Two types of arguments are accepted. If it isn't a hash, it's converted
|
58
|
+
# to an integer.
|
59
|
+
if args.kind_of?(Hash)
|
60
|
+
@seconds = 0
|
61
|
+
MULTIPLES.each do |unit, multiple|
|
62
|
+
unit = unit.to_sym
|
63
|
+
@seconds += args[unit] * multiple if args.key?(unit)
|
64
|
+
end
|
65
|
+
else
|
66
|
+
@seconds = args.to_i
|
67
|
+
end
|
68
|
+
|
69
|
+
# Calculate duration
|
70
|
+
calculate!
|
71
|
+
end
|
72
|
+
|
73
|
+
# Calculates the duration from seconds and figures out what the actual
|
74
|
+
# durations are in specific units. This method is called internally, and
|
75
|
+
# does not need to be called by user code.
|
76
|
+
def calculate!
|
77
|
+
multiples = [MULTIPLES[:weeks], MULTIPLES[:days], MULTIPLES[:hours], MULTIPLES[:minutes], MULTIPLES[:seconds]]
|
78
|
+
units = []
|
79
|
+
@total = @seconds.to_f.round
|
80
|
+
multiples.inject(@total) do |total, multiple|
|
81
|
+
# Divide into largest unit
|
82
|
+
units << total / multiple
|
83
|
+
total % multiple # The remainder will be divided as the next largest
|
163
84
|
end
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
# 5.days == 24.hours * 5
|
171
|
-
# => true
|
172
|
-
#
|
85
|
+
|
86
|
+
# Gather the divided units
|
87
|
+
@weeks, @days, @hours, @minutes, @seconds = units
|
88
|
+
end
|
89
|
+
|
90
|
+
# Compare this duration to another (or objects that respond to #to_i)
|
173
91
|
def <=>(other)
|
174
92
|
@total <=> other.to_i
|
175
93
|
end
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
end
|
241
|
-
|
242
|
-
# Friendly, human-readable string representation of the duration.
|
243
|
-
#
|
244
|
-
# *Example*
|
245
|
-
#
|
246
|
-
# d = Duration.new(:seconds => 140)
|
247
|
-
# => #<Duration: 2 minutes and 20 seconds>
|
248
|
-
# d.to_s
|
249
|
-
# => "2 minutes and 20 seconds"
|
250
|
-
#
|
251
|
-
def to_s
|
252
|
-
str = ''
|
253
|
-
|
254
|
-
each do |part, time|
|
255
|
-
# Skip any zero times.
|
256
|
-
next if time.zero?
|
257
|
-
|
258
|
-
# Concatenate the part of the time and the time itself.
|
259
|
-
str << "#{time} #{time == 1 ? part[0..-2] : part}, "
|
260
|
-
end
|
261
|
-
|
262
|
-
str.chomp(', ').sub(/(.+), (.+)/, '\1 and \2')
|
263
|
-
end
|
264
|
-
|
265
|
-
# Inspection string--Similar to #to_s except that it has the class name.
|
266
|
-
#
|
267
|
-
# *Example*
|
268
|
-
#
|
269
|
-
# Duration.new(:seconds => 140)
|
270
|
-
# => #<Duration: 2 minutes and 20 seconds>
|
271
|
-
#
|
94
|
+
|
95
|
+
# Convenient iterator for going through each duration unit from lowest to
|
96
|
+
# highest. (Goes from seconds...weeks)
|
97
|
+
def each
|
98
|
+
UNITS.each { |unit| yield unit, __send__(unit) }
|
99
|
+
end
|
100
|
+
|
101
|
+
# Format a duration into a human-readable string.
|
102
|
+
#
|
103
|
+
# %w => weeks
|
104
|
+
# %d => days
|
105
|
+
# %h => hours
|
106
|
+
# %m => minutes
|
107
|
+
# %s => seconds
|
108
|
+
# %t => total seconds
|
109
|
+
# %H => zero-padded hours
|
110
|
+
# %M => zero-padded minutes
|
111
|
+
# %S => zero-padded seconds
|
112
|
+
# %~s => locale-dependent "seconds" terminology
|
113
|
+
# %~m => locale-dependent "minutes" terminology
|
114
|
+
# %~h => locale-dependent "hours" terminology
|
115
|
+
# %~d => locale-dependent "days" terminology
|
116
|
+
# %~w => locale-dependent "weeks" terminology
|
117
|
+
#
|
118
|
+
def format(format_str)
|
119
|
+
identifiers = {
|
120
|
+
'w' => @weeks,
|
121
|
+
'd' => @days,
|
122
|
+
'h' => @hours,
|
123
|
+
'm' => @minutes,
|
124
|
+
's' => @seconds,
|
125
|
+
't' => @total,
|
126
|
+
'H' => @hours.to_s.rjust(2, '0'),
|
127
|
+
'M' => @minutes.to_s.rjust(2, '0'),
|
128
|
+
'S' => @seconds.to_s.rjust(2, '0'),
|
129
|
+
'~s' => @seconds == 1 ? @@locale.singulars[0] : @@locale.plurals[0],
|
130
|
+
'~m' => @minutes == 1 ? @@locale.singulars[1] : @@locale.plurals[1],
|
131
|
+
'~h' => @hours == 1 ? @@locale.singulars[2] : @@locale.plurals[2],
|
132
|
+
'~d' => @days == 1 ? @@locale.singulars[3] : @@locale.plurals[3],
|
133
|
+
'~w' => @weeks == 1 ? @@locale.singulars[4] : @@locale.plurals[4]
|
134
|
+
}
|
135
|
+
|
136
|
+
format_str.gsub(/%?%(w|d|h|m|s|t|H|M|S|~(?:s|m|h|d|w))/) do |match|
|
137
|
+
match['%%'] ? match : identifiers[match[1..-1]]
|
138
|
+
end.gsub('%%', '%')
|
139
|
+
end
|
140
|
+
|
141
|
+
def method_missing(m, *args, &block)
|
142
|
+
units = UNITS.join('|')
|
143
|
+
match = /(round_)?(#{units})_to_(#{units})$/.match(m.to_s)
|
144
|
+
if match
|
145
|
+
seconds = ((__send__(match[2].to_sym) * MULTIPLES[match[2].to_sym]) / MULTIPLES[match[3].to_sym].to_f)
|
146
|
+
match[1] ? seconds.round : seconds
|
147
|
+
else
|
148
|
+
super
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
# String representation of the Duration object.
|
153
|
+
def to_s
|
154
|
+
@@locale.format.call(self)
|
155
|
+
end
|
156
|
+
|
157
|
+
# Inspect Duration object.
|
272
158
|
def inspect
|
273
159
|
"#<#{self.class}: #{(s = to_s).empty? ? '...' : s}>"
|
274
160
|
end
|
275
|
-
|
276
|
-
# Add to Duration.
|
277
|
-
#
|
278
|
-
# *Example*
|
279
|
-
#
|
280
|
-
# d = Duration.new(30)
|
281
|
-
# => #<Duration: 30 seconds>
|
282
|
-
# d + 30
|
283
|
-
# => #<Duration: 1 minute>
|
284
|
-
#
|
161
|
+
|
285
162
|
def +(other)
|
286
|
-
|
163
|
+
Duration.new(@total + other.to_i)
|
287
164
|
end
|
288
|
-
|
289
|
-
# Subtract from Duration.
|
290
|
-
#
|
291
|
-
# *Example*
|
292
|
-
#
|
293
|
-
# d = Duration.new(30)
|
294
|
-
# => #<Duration: 30 seconds>
|
295
|
-
# d - 15
|
296
|
-
# => #<Duration: 15 seconds>
|
297
|
-
#
|
165
|
+
|
298
166
|
def -(other)
|
299
|
-
|
167
|
+
Duration.new(@total - other.to_i)
|
300
168
|
end
|
301
|
-
|
302
|
-
# Multiply two Durations.
|
303
|
-
#
|
304
|
-
# *Example*
|
305
|
-
#
|
306
|
-
# d = Duration.new(30)
|
307
|
-
# => #<Duration: 30 seconds>
|
308
|
-
# d * 2
|
309
|
-
# => #<Duration: 1 minute>
|
310
|
-
#
|
169
|
+
|
311
170
|
def *(other)
|
312
|
-
|
171
|
+
Duration.new(@total * other.to_i)
|
313
172
|
end
|
314
|
-
|
315
|
-
# Divide two Durations.
|
316
|
-
#
|
317
|
-
# *Example*
|
318
|
-
#
|
319
|
-
# d = Duration.new(30)
|
320
|
-
# => #<Duration: 30 seconds>
|
321
|
-
# d / 2
|
322
|
-
# => #<Duration: 15 seconds>
|
323
|
-
#
|
173
|
+
|
324
174
|
def /(other)
|
325
|
-
|
326
|
-
end
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
:minutes => MINUTE,
|
355
|
-
:seconds => SECOND}
|
356
|
-
|
357
|
-
# Loop through each valid part, ignore all others.
|
358
|
-
seconds = seconds_or_attr.inject(0) do |sec, args|
|
359
|
-
# Grab the part of the duration (week, day, whatever) and the number of seconds for it.
|
360
|
-
part, time = args
|
361
|
-
|
362
|
-
# Map each part to their number of seconds and the given value.
|
363
|
-
# {:weeks => 2} maps to h[:weeks] -- so... weeks = WEEK * 2
|
364
|
-
if h.key?(prt = part.to_s.to_sym) then sec + time * h[prt] else 0 end
|
365
|
-
end
|
366
|
-
else
|
367
|
-
seconds = seconds_or_attr
|
368
|
-
end
|
369
|
-
|
370
|
-
@total, array = seconds.to_f.round, []
|
371
|
-
@seconds = [YEAR, MONTH, WEEK, DAY, HOUR, MINUTE].inject(@total) do |left, part|
|
372
|
-
array << left / part; left % part
|
373
|
-
end
|
374
|
-
|
375
|
-
@years, @months, @weeks, @days, @hours, @minutes = array
|
376
|
-
end
|
377
|
-
|
378
|
-
# BigDuration variant of Duration#strftime.
|
379
|
-
#
|
380
|
-
# *Identifiers: BigDuration*
|
381
|
-
#
|
382
|
-
# %y -- Number of years
|
383
|
-
# %m -- Number of months
|
384
|
-
#
|
385
|
-
def strftime(fmt)
|
386
|
-
h = {'y' => @years, 'M' => @months}
|
387
|
-
super(fmt.gsub(/%?%(y|M)/) { |match| match.size == 3 ? match : h[match[1..1]] })
|
388
|
-
end
|
389
|
-
|
390
|
-
# Similar to Duration#each except includes years and months in the interation.
|
391
|
-
#
|
392
|
-
def each
|
393
|
-
[['years' , @years ],
|
394
|
-
['months' , @months ],
|
395
|
-
['weeks' , @weeks ],
|
396
|
-
['days' , @days ],
|
397
|
-
['hours' , @hours ],
|
398
|
-
['minutes' , @minutes],
|
399
|
-
['seconds' , @seconds]].each do |part, time|
|
400
|
-
# Yield to block
|
401
|
-
yield part, time
|
402
|
-
end
|
403
|
-
end
|
404
|
-
|
405
|
-
# Derived from Duration#seconds, but supports `:years' and `:months' as well.
|
406
|
-
#
|
407
|
-
def seconds(part = nil)
|
408
|
-
h = {:years => YEAR, :months => MONTH}
|
409
|
-
if [:years, :months].include? part
|
410
|
-
__send__(part) * h[part]
|
411
|
-
else
|
412
|
-
super(part)
|
413
|
-
end
|
414
|
-
end
|
415
|
-
|
416
|
-
# Set the number of years in the BigDuration.
|
417
|
-
#
|
418
|
-
def years=(n)
|
419
|
-
initialize(:years => n, :seconds => @total - seconds(:years))
|
420
|
-
end
|
421
|
-
|
422
|
-
# Set the number of months in the BigDuration.
|
423
|
-
#
|
424
|
-
def months=(n)
|
425
|
-
initialize(:months => n, :seconds => @total - seconds(:months))
|
426
|
-
end
|
427
|
-
end
|
428
|
-
|
429
|
-
# The following important additions are made to Numeric:
|
430
|
-
#
|
431
|
-
# Numeric#weeks -- Create a Duration object with given weeks
|
432
|
-
# Numeric#days -- Create a Duration object with given days
|
433
|
-
# Numeric#hours -- Create a Duration object with given hours
|
434
|
-
# Numeric#minutes -- Create a Duration object with given minutes
|
435
|
-
# Numeric#seconds -- Create a Duration object with given seconds
|
436
|
-
#
|
437
|
-
# BigDuration support:
|
438
|
-
#
|
439
|
-
# Numeric#years -- Create a BigDuration object with given years
|
440
|
-
# Numeric#months -- Create a BigDuration object with given months
|
441
|
-
#
|
442
|
-
# BigDuration objects can be created from regular weeks, days, hours, etc by
|
443
|
-
# providing `:big' as an argument to the above Numeric methods.
|
444
|
-
#
|
445
|
-
class Numeric
|
446
|
-
alias __numeric_old_method_missing method_missing
|
447
|
-
|
448
|
-
# Create a Duration object using self where self could represent weeks, days,
|
449
|
-
# hours, minutes, and seconds.
|
450
|
-
#
|
451
|
-
# *Example*
|
452
|
-
#
|
453
|
-
# 10.duration(:weeks)
|
454
|
-
# => #<Duration: 10 weeks>
|
455
|
-
# 10.duration
|
456
|
-
# => #<Duration: 10 seconds>
|
457
|
-
#
|
458
|
-
def duration(part = nil, klass = Duration)
|
459
|
-
if [:years, :months, :weeks, :days, :hours, :minutes, :seconds].include? part
|
460
|
-
klass.new(part => self)
|
461
|
-
else
|
462
|
-
klass.new(self)
|
463
|
-
end
|
464
|
-
end
|
465
|
-
|
466
|
-
# Intercept calls to .weeks, .days, .hours, .minutes and .seconds because
|
467
|
-
# Rails defines its own methods, so I'd like to prevent any redefining of
|
468
|
-
# Rails' methods. If these methods don't get captured, then alternatively
|
469
|
-
# Numeric#duration can be used.
|
470
|
-
#
|
471
|
-
# BigDuration methods include .years and .months, also BigDuration objects
|
472
|
-
# can be created from any time such as weeks or minutes and even seconds.
|
473
|
-
#
|
474
|
-
# *Example: BigDuration*
|
475
|
-
#
|
476
|
-
# 5.years
|
477
|
-
# => #<BigDuration: 5 years>
|
478
|
-
# 10.minutes(:big)
|
479
|
-
# => #<BigDuration: 10 minutes>
|
480
|
-
#
|
481
|
-
# *Example*
|
482
|
-
#
|
483
|
-
# 140.seconds
|
484
|
-
# => #<Duration: 2 minutes and 20 seconds>
|
485
|
-
#
|
486
|
-
def method_missing(method, *args)
|
487
|
-
if [:weeks, :days, :hours, :minutes, :seconds].include? method
|
488
|
-
if args.size > 0 && args[0] == :big
|
489
|
-
duration(method, BigDuration)
|
490
|
-
else
|
491
|
-
duration(method)
|
492
|
-
end
|
493
|
-
elsif [:years, :months].include? method
|
494
|
-
duration(method, BigDuration)
|
495
|
-
else
|
496
|
-
__numeric_old_method_missing(method, *args)
|
497
|
-
end
|
498
|
-
end
|
499
|
-
end
|
500
|
-
|
501
|
-
# Time#duration has been added to convert the UNIX timestamp into a Duration.
|
502
|
-
# See Time#duration for an example.
|
503
|
-
#
|
504
|
-
class Time
|
505
|
-
# Create a Duration object from the UNIX timestamp.
|
506
|
-
#
|
507
|
-
# *Example*
|
508
|
-
#
|
509
|
-
# Time.now.duration
|
510
|
-
# => #<Duration: 1898 weeks, 6 days, 1 hour, 12 minutes and 1 second>
|
511
|
-
#
|
512
|
-
def duration(type = nil)
|
513
|
-
if type == :big then BigDuration.new(to_i) else Duration.new(to_i) end
|
514
|
-
end
|
175
|
+
Duration.new(@total / other.to_i)
|
176
|
+
end
|
177
|
+
|
178
|
+
def %(other)
|
179
|
+
Duration.new(@total % other.to_i)
|
180
|
+
end
|
181
|
+
|
182
|
+
def seconds=(seconds)
|
183
|
+
initialize :seconds => (@total + seconds) - @seconds
|
184
|
+
end
|
185
|
+
|
186
|
+
def minutes=(minutes)
|
187
|
+
initialize :seconds => @total - minutes_to_seconds, :minutes => minutes
|
188
|
+
end
|
189
|
+
|
190
|
+
def hours=(hours)
|
191
|
+
initialize :seconds => @total - hours_to_seconds, :hours => hours
|
192
|
+
end
|
193
|
+
|
194
|
+
def days=(days)
|
195
|
+
initialize :seconds => @total - days_to_seconds, :days => days
|
196
|
+
end
|
197
|
+
|
198
|
+
def weeks=(weeks)
|
199
|
+
initialize :seconds => @total - weeks_to_seconds, :weeks => weeks
|
200
|
+
end
|
201
|
+
|
202
|
+
alias_method :to_i, :total
|
203
|
+
alias_method :strftime, :format
|
515
204
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'duration/locale'
|
2
|
+
require 'duration/localizations/english'
|
3
|
+
require 'duration/localizations/korean'
|
4
|
+
|
5
|
+
class Duration
|
6
|
+
# Contains localizations for the time formatters. Standard locales cannot be
|
7
|
+
# used because they don't define time units.
|
8
|
+
module Localizations
|
9
|
+
# Default locale
|
10
|
+
DEFAULT_LOCALE = :english
|
11
|
+
@@locales = {}
|
12
|
+
|
13
|
+
# Load all locales. This is invoked automatically upon loading Duration.
|
14
|
+
def Localizations.load_all
|
15
|
+
locales = []
|
16
|
+
constants.each do |constant|
|
17
|
+
mod = const_get(constant)
|
18
|
+
next unless mod.kind_of?(Module) and mod.const_defined?('LOCALE')
|
19
|
+
|
20
|
+
locale = mod.const_get('LOCALE').to_sym # Locale name
|
21
|
+
plurals = mod.const_defined?('PLURALS') ? mod.const_get('PLURALS') : DEFAULT_LOCALE # Unit plurals
|
22
|
+
singulars = mod.const_defined?('SINGULARS') ? mod.const_get('SINGULARS') : DEFAULT_LOCALE # Unit singulars
|
23
|
+
|
24
|
+
if mod.const_defined? 'FORMAT'
|
25
|
+
format = mod.const_get 'FORMAT'
|
26
|
+
format = format.kind_of?(Proc) ? format : proc { |duration| duration.format(format.to_s) }
|
27
|
+
end
|
28
|
+
|
29
|
+
# Add valid locale to the collection.
|
30
|
+
@@locales[locale] = Locale.new(locale, plurals, singulars, format)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Collection of locales
|
35
|
+
def Localizations.locales
|
36
|
+
@@locales
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class LocaleError < StandardError
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class Duration
|
2
|
+
module Localizations
|
3
|
+
# English localization
|
4
|
+
module English
|
5
|
+
LOCALE = :english
|
6
|
+
PLURALS = %w(seconds minutes hours days weeks)
|
7
|
+
SINGULARS = %w(second minute hour day week)
|
8
|
+
FORMAT = proc do |duration|
|
9
|
+
str = duration.format('%w %~w, %d %~d, %h %~h, %m %~m, %s %~s')
|
10
|
+
str.sub(/^0 [a-z]+,?/i, '').gsub(/ 0 [a-z]+,?/i, '').chomp(',').sub(/, (\d+ [a-z]+)$/i, ' and \1').strip
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class Duration
|
2
|
+
module Localizations
|
3
|
+
# Korean localization
|
4
|
+
module Korean
|
5
|
+
LOCALE = :korean
|
6
|
+
PLURALS = %w(초 분 시간 일 주)
|
7
|
+
SINGULARS = %w(초 분 시간 일 주)
|
8
|
+
FORMAT = proc do |duration|
|
9
|
+
str = duration.format('%w%~w, %d%~d, %h%~h, %m%~m, %s%~s')
|
10
|
+
str.sub(/^0[^\d+,]+,?/i, '').gsub(/ 0[^\d+,]+,?/i, '').chomp(',').strip
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# Additional methods added to numeric objects allow the developer to construct
|
2
|
+
# a calculated number of seconds through human-readable methods.
|
3
|
+
#
|
4
|
+
# 60.seconds #=> 60
|
5
|
+
# 60.minutes #=> 3600
|
6
|
+
# ... and so on
|
7
|
+
#
|
8
|
+
# Singular methods are also defined.
|
9
|
+
#
|
10
|
+
# 1.minute #=> 60
|
11
|
+
# 1.hour #=> 3600
|
12
|
+
# ... and so on
|
13
|
+
class Numeric
|
14
|
+
# Number of seconds (equivalent to Numeric#to_i)
|
15
|
+
def seconds
|
16
|
+
to_i
|
17
|
+
end unless Numeric.instance_methods.include? 'seconds'
|
18
|
+
|
19
|
+
# Number of seconds using the number as the base of minutes.
|
20
|
+
def minutes
|
21
|
+
to_i * 60
|
22
|
+
end unless Numeric.instance_methods.include? 'minutes'
|
23
|
+
|
24
|
+
# Number of seconds using the number as the base of hours.
|
25
|
+
def hours
|
26
|
+
to_i * 3600
|
27
|
+
end unless Numeric.instance_methods.include? 'hours'
|
28
|
+
|
29
|
+
# Number of seconds using the number as the base of days.
|
30
|
+
def days
|
31
|
+
to_i * 86400
|
32
|
+
end unless Numeric.instance_methods.include? 'days'
|
33
|
+
|
34
|
+
# Number of seconds using the number as the base of weeks.
|
35
|
+
def week
|
36
|
+
to_i * 604800
|
37
|
+
end unless Numeric.instance_methods.include? 'weeks'
|
38
|
+
|
39
|
+
# Number of seconds (singular form).
|
40
|
+
def second
|
41
|
+
to_i
|
42
|
+
end unless Numeric.instance_methods.include? 'second'
|
43
|
+
|
44
|
+
# Number of seconds using the number as the base of minutes (singular form).
|
45
|
+
def minute
|
46
|
+
to_i * 60
|
47
|
+
end unless Numeric.instance_methods.include? 'minute'
|
48
|
+
|
49
|
+
# Number of seconds using the number as the base of hours (singular form).
|
50
|
+
def hour
|
51
|
+
to_i * 3600
|
52
|
+
end unless Numeric.instance_methods.include? 'hour'
|
53
|
+
|
54
|
+
# Number of seconds using the number as the base of days (singular form).
|
55
|
+
def day
|
56
|
+
to_i * 86400
|
57
|
+
end unless Numeric.instance_methods.include? 'day'
|
58
|
+
|
59
|
+
# Number of seconds using the number as the base of weeks (singular form).
|
60
|
+
def week
|
61
|
+
to_i * 604800
|
62
|
+
end unless Numeric.instance_methods.include? 'week'
|
63
|
+
end
|
metadata
CHANGED
@@ -1,18 +1,18 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
|
-
rubygems_version: 0.
|
2
|
+
rubygems_version: 0.9.4
|
3
3
|
specification_version: 1
|
4
4
|
name: duration
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.0
|
7
|
-
date:
|
8
|
-
summary: Duration
|
6
|
+
version: 0.1.0
|
7
|
+
date: 2007-12-20 00:00:00 +09:00
|
8
|
+
summary: Duration/timespan manipulation library
|
9
9
|
require_paths:
|
10
10
|
- lib
|
11
|
-
email:
|
11
|
+
email: matt@matthewharris.org
|
12
12
|
homepage: http://duration.rubyforge.org
|
13
13
|
rubyforge_project: duration
|
14
|
-
description: Duration is a
|
15
|
-
autorequire:
|
14
|
+
description: Duration is a library for manipulating timespans. It can give you readable output for a timespan as well as manipulate the timespan itself. With this it is possible to make "countdowns" or "time passed since" type objects.
|
15
|
+
autorequire: duration
|
16
16
|
default_executable:
|
17
17
|
bindir: bin
|
18
18
|
has_rdoc: true
|
@@ -25,16 +25,32 @@ required_ruby_version: !ruby/object:Gem::Version::Requirement
|
|
25
25
|
platform: ruby
|
26
26
|
signing_key:
|
27
27
|
cert_chain:
|
28
|
+
post_install_message:
|
28
29
|
authors:
|
29
30
|
- Matthew Harris
|
30
31
|
files:
|
32
|
+
- lib/duration/holidays.rb
|
33
|
+
- lib/duration/locale.rb
|
34
|
+
- lib/duration/localizations/english.rb
|
35
|
+
- lib/duration/localizations/korean.rb
|
36
|
+
- lib/duration/localizations.rb
|
37
|
+
- lib/duration/numeric.rb
|
38
|
+
- lib/duration/time.rb
|
39
|
+
- lib/duration/version.rb
|
31
40
|
- lib/duration.rb
|
41
|
+
- README
|
42
|
+
- LICENSE
|
32
43
|
test_files: []
|
33
44
|
|
34
|
-
rdoc_options:
|
35
|
-
|
36
|
-
|
37
|
-
|
45
|
+
rdoc_options:
|
46
|
+
- --title
|
47
|
+
- Duration -- The Timespan Library
|
48
|
+
- --main
|
49
|
+
- README
|
50
|
+
- --line-numbers
|
51
|
+
extra_rdoc_files:
|
52
|
+
- README
|
53
|
+
- LICENSE
|
38
54
|
executables: []
|
39
55
|
|
40
56
|
extensions: []
|