timesteps 0.9.3

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.
@@ -0,0 +1,251 @@
1
+ #
2
+ # format.rb is modified version of date/format.rb distributed in Ruby 1.8.7.
3
+ #
4
+ # format.rb: Written by Tadayoshi Funaba 1999-2008
5
+ # $Id: format.rb,v 2.43 2008-01-17 20:16:31+09 tadf Exp $
6
+
7
+
8
+ #
9
+ #
10
+ #
11
+ class DateTimeLike
12
+
13
+ # @private
14
+ MILLISECONDS_IN_DAY = Rational(1, 86400*10**3)
15
+
16
+ # @private
17
+ MILLISECONDS_IN_SECOND = Rational(1, 10**3)
18
+
19
+ # @private
20
+ NANOSECONDS_IN_DAY = Rational(1, 86400*10**9)
21
+
22
+ # @private
23
+ NANOSECONDS_IN_SECOND = Rational(1, 10**9)
24
+
25
+ # @private
26
+ SECONDS_IN_DAY = Rational(1, 86400)
27
+
28
+ # @private
29
+ # Full month names, in English. Months count from 1 to 12; a
30
+ # month's numerical representation indexed into this array
31
+ # gives the name of that month (hence the first element is nil).
32
+ MONTHNAMES = [nil] + %w(January February March April May June July
33
+ August September October November December)
34
+
35
+ # @private
36
+ # Full names of days of the week, in English. Days of the week
37
+ # count from 0 to 6 (except in the commercial week); a day's numerical
38
+ # representation indexed into this array gives the name of that day.
39
+ DAYNAMES = %w(Sunday Monday Tuesday Wednesday Thursday Friday Saturday)
40
+
41
+ # @private
42
+ # Abbreviated month names, in English.
43
+ ABBR_MONTHNAMES = [nil] + %w(Jan Feb Mar Apr May Jun
44
+ Jul Aug Sep Oct Nov Dec)
45
+
46
+ # @private
47
+ # Abbreviated day names, in English.
48
+ ABBR_DAYNAMES = %w(Sun Mon Tue Wed Thu Fri Sat)
49
+
50
+ [MONTHNAMES, DAYNAMES, ABBR_MONTHNAMES, ABBR_DAYNAMES].each do |xs|
51
+ xs.each{|x| x.freeze unless x.nil?}.freeze
52
+ end
53
+
54
+ def emit(e, f) # :nodoc:
55
+ case e
56
+ when Numeric
57
+ sign = %w(+ + -)[e <=> 0]
58
+ e = e.abs
59
+ end
60
+
61
+ s = e.to_s
62
+
63
+ if f[:s] && f[:p] == '0'
64
+ f[:w] -= 1
65
+ end
66
+
67
+ if f[:s] && f[:p] == "\s"
68
+ s[0,0] = sign
69
+ end
70
+
71
+ if f[:p] != '-'
72
+ s = s.rjust(f[:w], f[:p])
73
+ end
74
+
75
+ if f[:s] && f[:p] != "\s"
76
+ s[0,0] = sign
77
+ end
78
+
79
+ s = s.upcase if f[:u]
80
+ s = s.downcase if f[:d]
81
+ s
82
+ end
83
+
84
+ def emit_w(e, w, f) # :nodoc:
85
+ f[:w] = [f[:w], w].compact.max
86
+ emit(e, f)
87
+ end
88
+
89
+ def emit_n(e, w, f) # :nodoc:
90
+ f[:p] ||= '0'
91
+ emit_w(e, w, f)
92
+ end
93
+
94
+ def emit_sn(e, w, f) # :nodoc:
95
+ if e < 0
96
+ w += 1
97
+ f[:s] = true
98
+ end
99
+ emit_n(e, w, f)
100
+ end
101
+
102
+ def emit_z(e, w, f) # :nodoc:
103
+ w += 1
104
+ f[:s] = true
105
+ emit_n(e, w, f)
106
+ end
107
+
108
+ def emit_a(e, w, f) # :nodoc:
109
+ f[:p] ||= "\s"
110
+ emit_w(e, w, f)
111
+ end
112
+
113
+ def emit_ad(e, w, f) # :nodoc:
114
+ if f[:x]
115
+ f[:u] = true
116
+ f[:d] = false
117
+ end
118
+ emit_a(e, w, f)
119
+ end
120
+
121
+ def emit_au(e, w, f) # :nodoc:
122
+ if f[:x]
123
+ f[:u] = false
124
+ f[:d] = true
125
+ end
126
+ emit_a(e, w, f)
127
+ end
128
+
129
+ private :emit, :emit_w, :emit_n, :emit_sn, :emit_z,
130
+ :emit_a, :emit_ad, :emit_au
131
+
132
+ def strftime(fmt='%F')
133
+
134
+ fmt.gsub(/%([-_0^#]+)?(\d+)?([EO]?(?::{1,3}z|.))/m) do |m|
135
+ f = {}
136
+ a = $&
137
+ s, w, c = $1, $2, $3
138
+ if s
139
+ s.scan(/./) do |k|
140
+ case k
141
+ when '-'; f[:p] = '-'
142
+ when '_'; f[:p] = "\s"
143
+ when '0'; f[:p] = '0'
144
+ when '^'; f[:u] = true
145
+ when '#'; f[:x] = true
146
+ end
147
+ end
148
+ end
149
+ if w
150
+ f[:w] = w.to_i
151
+ end
152
+ case c
153
+ when 'A'; emit_ad(DAYNAMES[wday], 0, f)
154
+ when 'a'; emit_ad(ABBR_DAYNAMES[wday], 0, f)
155
+ when 'B'; emit_ad(MONTHNAMES[month], 0, f)
156
+ when 'b'; emit_ad(ABBR_MONTHNAMES[month], 0, f)
157
+ when 'C', 'EC'; emit_sn((year / 100).floor, 2, f)
158
+ when 'c', 'Ec'; emit_a(strftime('%a %b %e %H:%M:%S %Y'), 0, f)
159
+ when 'D'; emit_a(strftime('%m/%d/%y'), 0, f)
160
+ when 'd', 'Od'; emit_n(day, 2, f)
161
+ when 'e', 'Oe'; emit_a(day, 2, f)
162
+ when 'F'
163
+ if m == '%F'
164
+ format('%.4d-%02d-%02d', year, month, day) # 4p
165
+ else
166
+ emit_a(strftime('%Y-%m-%d'), 0, f)
167
+ end
168
+ when 'G'; emit_sn(cwyear, 4, f)
169
+ when 'g'; emit_n(cwyear % 100, 2, f)
170
+ when 'H', 'OH'; emit_n(hour, 2, f)
171
+ when 'h'; emit_ad(strftime('%b'), 0, f)
172
+ when 'I', 'OI'; emit_n((hour % 12).nonzero? || 12, 2, f)
173
+ when 'j'; emit_n(yday, 3, f)
174
+ when 'k'; emit_a(hour, 2, f)
175
+ when 'L'
176
+ emit_n((sec_fraction / MILLISECONDS_IN_SECOND).round, 3, f)
177
+ when 'l'; emit_a((hour % 12).nonzero? || 12, 2, f)
178
+ when 'M', 'OM'; emit_n(minute, 2, f)
179
+ when 'm', 'Om'; emit_n(month, 2, f)
180
+ when 'N'
181
+ emit_n((sec_fraction / NANOSECONDS_IN_SECOND).round, 9, f)
182
+ when 'n'; "\n"
183
+ when 'P'; emit_ad(strftime('%p').downcase, 0, f)
184
+ when 'p'; emit_au(if hour < 12 then 'AM' else 'PM' end, 0, f)
185
+ when 'Q'
186
+ s = ((ajd - self::class::UNIX_EPOCH_IN_AJD) / MILLISECONDS_IN_DAY).round
187
+ emit_sn(s, 1, f)
188
+ when 'R'; emit_a(strftime('%H:%M'), 0, f)
189
+ when 'r'; emit_a(strftime('%I:%M:%S %p'), 0, f)
190
+ when 'S', 'OS'; emit_n(second.floor, 2, f)
191
+ when 's'
192
+ s = ((ajd - self::class::UNIX_EPOCH_IN_AJD) / SECONDS_IN_DAY).round
193
+ emit_sn(s, 1, f)
194
+ when 'T'
195
+ if m == '%T'
196
+ format('%02d:%02d:%02d', hour, minute, second) # 4p
197
+ else
198
+ emit_a(strftime('%H:%M:%S'), 0, f)
199
+ end
200
+ when 't'; "\t"
201
+ when 'U', 'W', 'OU', 'OW'
202
+ emit_n(if c[-1,1] == 'U' then wnum0 else wnum1 end, 2, f)
203
+ when 'u', 'Ou'; emit_n(cwday, 1, f)
204
+ when 'V', 'OV'; emit_n(cweek, 2, f)
205
+ when 'v'; emit_a(strftime('%e-%b-%Y'), 0, f)
206
+ when 'w', 'Ow'; emit_n(wday, 1, f)
207
+ when 'X', 'EX'; emit_a(strftime('%H:%M:%S'), 0, f)
208
+ when 'x', 'Ex'; emit_a(strftime('%m/%d/%y'), 0, f)
209
+ when 'Y', 'EY'; emit_sn(year, 4, f)
210
+ when 'y', 'Ey', 'Oy'; emit_n(year % 100, 2, f)
211
+ when 'Z'; emit_au(strftime('%:z'), 0, f)
212
+ when /\A(:{0,3})z/
213
+ t = $1.size
214
+ sign = if offset < 0 then -1 else +1 end
215
+ fr = offset.abs
216
+ ss = fr.div(SECONDS_IN_DAY) # 4p
217
+ hh, ss = ss.divmod(3600)
218
+ mm, ss = ss.divmod(60)
219
+ if t == 3
220
+ if ss.nonzero? then t = 2
221
+ elsif mm.nonzero? then t = 1
222
+ else t = -1
223
+ end
224
+ end
225
+ case t
226
+ when -1
227
+ tail = []
228
+ sep = ''
229
+ when 0
230
+ f[:w] -= 2 if f[:w]
231
+ tail = ['%02d' % mm]
232
+ sep = ''
233
+ when 1
234
+ f[:w] -= 3 if f[:w]
235
+ tail = ['%02d' % mm]
236
+ sep = ':'
237
+ when 2
238
+ f[:w] -= 6 if f[:w]
239
+ tail = ['%02d' % mm, '%02d' % ss]
240
+ sep = ':'
241
+ end
242
+ ([emit_z(sign * hh, 2, f)] + tail).join(sep)
243
+ when '%'; emit_a('%', 0, f)
244
+ when '+'; emit_a(strftime('%a %b %e %H:%M:%S %Z %Y'), 0, f)
245
+ else
246
+ a
247
+ end
248
+ end
249
+ end
250
+
251
+ end
@@ -0,0 +1,43 @@
1
+ require_relative "../timesteps"
2
+
3
+ class TimeStep
4
+
5
+ # @private
6
+ REGEXP_GRADS_TDEF = /\A(?:TDEF\s+)?(\d+)\s+LINEAR\s+([^\s]*?)\s+([^\s]*?)\s*\z/i
7
+
8
+ # @private
9
+ GRADS_INCREMENT = {
10
+ "mn" => "minutes",
11
+ "hr" => "hours",
12
+ "dy" => "days",
13
+ "mo" => "months",
14
+ "yr" => "years",
15
+ }
16
+
17
+ def self.parse_grads_tdef (tdef_string)
18
+ if tdef_string.strip =~ REGEXP_GRADS_TDEF
19
+ count = $1.to_i
20
+ time = $2
21
+ increment = $3
22
+ else
23
+ raise "invalid grads tdef string"
24
+ end
25
+ if time =~ /(((\d{2})?(:(\d{2}))?Z)?(\d{2}))?(\w{3})(\d{4})/i
26
+ hour = $3 || "00"
27
+ min = $5 || "00"
28
+ day = $6 || "01"
29
+ mon = $7
30
+ year = $8
31
+ origin = DateTime.parse("#{year}#{mon}#{day} #{hour}:#{min}:00")
32
+ else
33
+ raise "invalid time format"
34
+ end
35
+
36
+ increment = increment.sub(/(mn|hr|dy|mo|yr)/i) {|s| GRADS_INCREMENT[s.downcase]}
37
+
38
+ spec = format("%s since %s", increment, origin.strftime("%F %T"))
39
+
40
+ return TimeStep.new(spec, count: count)
41
+ end
42
+
43
+ end
@@ -0,0 +1,48 @@
1
+ class DateTime
2
+
3
+ module ParseTimeStampExtension
4
+
5
+ # Parses the given datetime expression and creates an instance.
6
+ # `DateTime._parse()` is called internally.
7
+ #
8
+ # @param spec [String]
9
+ # @option bc [Boolean]
10
+ #
11
+ # @return [DateTimeFixedDPY]
12
+
13
+ def parse_timestamp (spec, start=Date::ITALY, bc: false)
14
+ hash = DateTime._parse(spec)
15
+ year, month, day, hour, minute, second, sec_fraction, offset =
16
+ hash.values_at(:year, :mon, :mday, :hour, :min, :sec, :sec_fraction, :offset)
17
+ if bc and year < 0
18
+ year = year + 1
19
+ end
20
+ hour ||= 0
21
+ minute ||= 0
22
+ second ||= 0.0
23
+ sec_fraction ||= 0.0
24
+ offset ||= 0
25
+ if hour == 24 && minute == 0 && second == 0.0
26
+ self.new(year, month, day, 23, minute, second + sec_fraction, offset.quo(86400), start) + 1.quo(24)
27
+ else
28
+ self.new(year, month, day, hour, minute, second + sec_fraction, offset.quo(86400), start)
29
+ end
30
+ end
31
+
32
+ end
33
+
34
+ extend ParseTimeStampExtension
35
+
36
+ class NoLeap < DateTimeLike
37
+ extend ParseTimeStampExtension
38
+ end
39
+
40
+ class AllLeap < DateTimeLike
41
+ extend ParseTimeStampExtension
42
+ end
43
+
44
+ class Fixed360Day < DateTimeLike
45
+ extend ParseTimeStampExtension
46
+ end
47
+
48
+ end
@@ -0,0 +1,362 @@
1
+
2
+ class TimeStep
3
+
4
+ using DateTimeExt
5
+
6
+ # Constructs the object.
7
+ #
8
+ # The argument `spec` specifies the time step definition, which has the form,
9
+ # "<INTERVAL> since <TIME>"
10
+ # For example,
11
+ # * "second since 1970-01-01 00:00:00 +00:00"
12
+ # * "hour since 2001-01-01 00:00:00 JST"
13
+ # * "3 days since 2001-01-01 00:00:00 +00:00"
14
+ # * "10 years since 1901-01-01 00:00:00 +00:00"
15
+ # The symbol for time unit symbols should be one of
16
+ # * years, year
17
+ # * months, month
18
+ # * days, day, d
19
+ # * hours, hr, h
20
+ # * minutes, minute, min
21
+ # * seconds, second, sec, s
22
+ # The option `calendar` specifies the calendar for datetime calculation,
23
+ # * standard, gregorian -> DateTime with Date::ITALY as start
24
+ # * proleptic_gregorian -> DateTime with Date::GREGORIAN as start
25
+ # * proleptic_julian, julian -> DateTime with Date::JULIAN as start
26
+ # * noleap, 365_day -> DateTimeNoLeap
27
+ # * allleap, 366_day -> DateTimeAllLeap
28
+ # * 360_day -> DateTimeFixed360Day
29
+ # The option `bc` is a flag whether AD/BC or EC when parsing timestamp for
30
+ # negative year.
31
+ # The option count specifies the number of time steps, which is hint for
32
+ # some methods (#each etc).
33
+ #
34
+ # @param spec [String]
35
+ # @option calendar [String, TimeStep::Calendar]
36
+ # @option bc [Boolean]
37
+ # @option count [Integer] number of time steps (as hint for some methods)
38
+ #
39
+ def initialize (spec, calendar: "standard", bc: false, count: nil)
40
+ case calendar
41
+ when String
42
+ @calendar = Calendar.new(calendar, bc: bc)
43
+ else
44
+ @calendar = calendar
45
+ end
46
+ intervalspec, timespec = spec.split(/\s+since\s+/)
47
+ parse_interval(intervalspec)
48
+ @origin = @calendar.parse(timespec)
49
+ @intervalspec = format("%g %s", @numeric, symbol)
50
+ @timespec = @origin.strftime("%Y-%m-%d %H:%M:%S.%N %:z")
51
+ @definition = format("%s since %s", @intervalspec, @timespec)
52
+ @count = count
53
+ end
54
+
55
+ # @private
56
+ PATTERN_NUMERIC = '[\+\-]?\d*(?:\.\d+)?(?:[eE][\+\-]?\d+)?'
57
+ # @private
58
+ PATTERN_UNITS = 'years?|months?|days?|hours?|minutes?|seconds?|d|hr|h|min|sec|s'
59
+
60
+ def parse_interval (spec)
61
+ if spec.strip =~ /(#{PATTERN_NUMERIC}|)\s*(#{PATTERN_UNITS})/
62
+ if $1 == ""
63
+ @numeric = 1.to_r
64
+ else
65
+ @numeric = Float($1).to_r
66
+ end
67
+ symbol = $2
68
+ else
69
+ raise "invalid interval specification"
70
+ end
71
+ @interval = @numeric
72
+ case symbol
73
+ when "years", "year"
74
+ unless numeric.denominator == 1
75
+ raise "numeric factor for year should be an integer"
76
+ end
77
+ @symbol = :years
78
+ @interval *= 356.242*86400
79
+ when "months", "month"
80
+ unless numeric.denominator == 1
81
+ raise "numeric factor for month should be an integer"
82
+ end
83
+ @symbol = :months
84
+ @interval *= 30.4368*86400
85
+ when "days", "day", "d"
86
+ @symbol = :days
87
+ @interval *= 86400
88
+ when "hours", "hour", "hr", "h"
89
+ @symbol = :hours
90
+ @interval *= 3600
91
+ when "minutes", "minute", "min"
92
+ @symbol = :minutes
93
+ @interval *= 60
94
+ when "seconds", "second", "sec", "s"
95
+ @symbol = :seconds
96
+ end
97
+ end
98
+
99
+ private :parse_interval
100
+
101
+ attr_reader :definition,
102
+ :intervalspec,
103
+ :timespec,
104
+ :numeric,
105
+ :symbol,
106
+ :interval,
107
+ :origin,
108
+ :calendar
109
+
110
+ attr_accessor :count
111
+
112
+ # Returns limit time if the object has `count`, otherwise returns nil.
113
+ #
114
+ # @return [DateTime, nil]
115
+ def limit
116
+ if @count
117
+ return time_at(@count - 1)
118
+ else
119
+ return nil
120
+ end
121
+ end
122
+
123
+ # Sets count by giving maximum time.
124
+ #
125
+ # @return [DateTime, nil]
126
+ def limit= (time)
127
+ if time
128
+ @count = (prev_index_of(time) + 1).to_i
129
+ else
130
+ @count = nil
131
+ end
132
+ return limit
133
+ end
134
+
135
+ def valid? (*indices)
136
+ if @count
137
+ if indices.size == 1
138
+ index = indices.first
139
+ if index >= 0 and index < @count
140
+ return true
141
+ else
142
+ return false
143
+ end
144
+ else
145
+ return indices.map{|index| valid?(index) }
146
+ end
147
+ else
148
+ if indices.size == 1
149
+ return true
150
+ else
151
+ return indices.map{|index| true }
152
+ end
153
+ end
154
+ end
155
+
156
+ def info
157
+ return {
158
+ "definition" => @definition.clone,
159
+ "intervalspec" => @intervalspec.clone,
160
+ "timespec" => @timespec.clone,
161
+ "calendar" => @calendar.name,
162
+ "numeric" => @numeric,
163
+ "symbol" => @symbol.to_s,
164
+ "interval" => @interval,
165
+ "origin" => @origin.clone,
166
+ "count" => @count,
167
+ "bc" => @calendar.bc?,
168
+ }
169
+ end
170
+
171
+ def inspect
172
+ "#<TimeStep definition='#{definition}' calendar='#{calendar.name}'>"
173
+ end
174
+
175
+ # Returns true if other has same contents of `definition` and `calendar`
176
+ # as self has.
177
+ #
178
+ # @return [Boolean]
179
+ def == (other)
180
+ return @definition == other.definition && @calendar == other.calendar
181
+ end
182
+
183
+ def user_to_days (index)
184
+ return ( @interval * index.to_r ).quo(86400)
185
+ end
186
+
187
+ def days_to_user (index)
188
+ return ( 86400 * index.to_r ).quo(@interval)
189
+ end
190
+
191
+ # Returns the time represented by the given amount as DateTime object
192
+ #
193
+ # @param indices [Numeric,Array<Numeric>]
194
+ #
195
+ # @return [DateTime]
196
+ def time_at (*indices)
197
+ if indices.size == 1
198
+ index = indices.first
199
+ raise ArgumentError, "index should be numeric" unless index.is_a?(Numeric)
200
+ index = index.to_r
201
+ case @symbol
202
+ when :years
203
+ unless index.denominator == 1
204
+ raise "index for years should be an integer"
205
+ end
206
+ return @origin.next_year(index*@numeric)
207
+ when :months
208
+ unless index.denominator == 1
209
+ raise "index for years should be an integer"
210
+ end
211
+ return @origin.next_month(index*@numeric)
212
+ else
213
+ days = user_to_days(index) + @origin.jd + @origin.fraction - @origin.offset
214
+ jday = days.floor
215
+ fday = days - days.floor
216
+ return (@calendar.jday2date(jday) + fday).new_offset(@origin.offset)
217
+ end
218
+ else
219
+ return indices.map{|index| time_at(index) }
220
+ end
221
+ end
222
+
223
+ # Returns the index for the given time in the unit represented by the object
224
+ #
225
+ # @param times [DateTime] time object
226
+ #
227
+ # @return [Numeric]
228
+ def index_at (*times)
229
+ if times.size == 1
230
+ time = times.first
231
+ time = @calendar.parse(time) if time.is_a?(String)
232
+ case @symbol
233
+ when :years
234
+ return time.difference_in_years(@origin).quo(@numeric.to_i)
235
+ when :months
236
+ return time.difference_in_months(@origin).quo(@numeric.to_i)
237
+ else
238
+ jday = @calendar.date2jday(time.year, time.month, time.day)
239
+ fday = time.fraction
240
+ udays = days_to_user(jday - @origin.jd)
241
+ utime = days_to_user(fday - @origin.fraction)
242
+ return udays + utime
243
+ end
244
+ else
245
+ return times.map{|time| index_at(time) }
246
+ end
247
+ end
248
+
249
+ # Calculate the time difference in days from origin at the index.
250
+ #
251
+ # @param indices [Array]
252
+ # @return [DateTime, Array<DateTime>]
253
+ def days_at (*indices)
254
+ if indices.size == 1
255
+ index = indices.first
256
+ case @symbol
257
+ when :years
258
+ unless index.denominator == 1
259
+ raise "index for years should be an integer"
260
+ end
261
+ return @origin.next_year(@numeric*index) - @origin
262
+ when :months
263
+ unless index.denominator == 1
264
+ raise "index for years should be an integer"
265
+ end
266
+ return @origin.next_month(@numeric*index) - @origin
267
+ else
268
+ return user_to_days(index)
269
+ end
270
+ else
271
+ return indices.map{ |index| days_at(index) }
272
+ end
273
+ end
274
+
275
+ # Returns TimeStep object which has origin time determined
276
+ # by the given `index`.
277
+ #
278
+ # @param index [Numeric]
279
+ #
280
+ # @return [TimeStep]
281
+ def shift_origin (index)
282
+ time = time_at(index)
283
+ return TimeStep.new(@intervalspec + " since " + time.strftime("%Y-%m-%d %H:%M:%S.%N %:z"), calendar: @calendar)
284
+ end
285
+
286
+ # Returns TimeStep object which has origin time specified
287
+ # by the given `time`.
288
+ #
289
+ # @param time [DateTime]
290
+ #
291
+ # @return [TimeStep]
292
+ def new_origin (time)
293
+ case time
294
+ when String
295
+ time = @calendar.parse(time)
296
+ end
297
+ return TimeStep.new(@intervalspec + " since " + time.strftime("%Y-%m-%d %H:%M:%S.%N %:z"), calendar: @calendar)
298
+ end
299
+
300
+ include Enumerable
301
+
302
+ def each (limit = nil, incr = 1, &block)
303
+ if limit.nil?
304
+ raise "step method require count" unless @count
305
+ limit = @count - 1
306
+ end
307
+ if block
308
+ 0.step(limit, incr) do |k|
309
+ block.call(time_at(k))
310
+ end
311
+ else
312
+ return Enumerator.new {|y|
313
+ 0.step(limit, incr) do |k|
314
+ y << time_at(k)
315
+ end
316
+ }
317
+ end
318
+ end
319
+
320
+ def times (&block)
321
+ raise "step method require count" unless @count
322
+ (0...@count).each(&block)
323
+ end
324
+
325
+ # Creates TimeStep::Pair
326
+ #
327
+ #
328
+ def in (unit)
329
+ return TimeStep::Pair.new(self, format("%s since %s", unit, @timespec), calendar: @calendar)
330
+ end
331
+
332
+ def next_index_of (time)
333
+ case time
334
+ when String
335
+ time = @calendar.parse(time)
336
+ end
337
+ index = index_at(time).to_r
338
+ if index.denominator == 1
339
+ return index + 1
340
+ else
341
+ return index.ceil
342
+ end
343
+ end
344
+
345
+ def prev_index_of (time)
346
+ return next_index_of(time) - 1
347
+ end
348
+
349
+ def next_time_of (time)
350
+ return time_at(next_index_of(time))
351
+ end
352
+
353
+ def prev_time_of (time)
354
+ return time_at(prev_index_of(time))
355
+ end
356
+
357
+ def parse (time)
358
+ return @calendar.parse(time)
359
+ end
360
+
361
+ end
362
+