Class: EDTF::Interval

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Includes:
Comparable, Enumerable
Defined in:
lib/edtf/interval.rb

Overview

An interval behaves like a regular Range but is dedicated to EDTF dates. Most importantly, intervals use the date’s precision when generating the set of contained values and for membership tests. All tests are implemented without iteration and should therefore be considerably faster than if you were to use a regular Range.

For example, the interval “2003/2006” covers the years 2003, 2004, 2005 and 2006. Converting the interval to an array would result in a an array containing exactly four dates with year precision. This is also reflected in membership tests.

Date.edtf('2003/2006').length                           -> 4

Date.edtf('2003/2006').include? Date.edtf('2004')       -> true
Date.edtf('2003/2006').include? Date.edtf('2004-03')    -> false

Date.edtf('2003/2006').cover? Date.edtf('2004-03')      -> true

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(from = Date.today, to = :open) ⇒ Interval

Returns a new instance of Interval.



34
35
36
# File 'lib/edtf/interval.rb', line 34

def initialize(from = Date.today, to = :open)
  self.from, self.to = from, to
end

Instance Attribute Details

#fromObject

Returns the value of attribute from.



32
33
34
# File 'lib/edtf/interval.rb', line 32

def from
  @from
end

#toObject

Returns the value of attribute to.



32
33
34
# File 'lib/edtf/interval.rb', line 32

def to
  @to
end

Instance Method Details

#<=>(other) ⇒ Object



268
269
270
271
272
273
274
275
276
277
# File 'lib/edtf/interval.rb', line 268

def <=>(other)
  case other
  when Interval, Season, Epoch
    [min, max] <=> [other.min, other.max]
  when Date
    cover?(other) ? min <=> other : 0
  else
    nil
  end
end

#===(other) ⇒ Object



279
280
281
282
283
284
285
286
287
288
# File 'lib/edtf/interval.rb', line 279

def ===(other)
  case other
  when Interval
    cover?(other.min) && cover?(other.max)
  when Date
    cover?(other)
  else
    false
  end
end

#beginObject



231
232
233
# File 'lib/edtf/interval.rb', line 231

def begin
  min
end

#cover?(other) ⇒ Boolean

Returns true if other is an element of the Interval, false otherwise. In contrast to #include? and #member? this method does not take into account the date’s precision.

Returns:

  • (Boolean)


163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
# File 'lib/edtf/interval.rb', line 163

def cover?(other)
  return false unless other.is_a?(Date)

  other = other.day_precision

  case
  when unknown_start?
    max.day_precision! == other
  when unknown_end?
    min.day_precision! == other
  when open_end?
    min.day_precision! <= other
  else
    min.day_precision! <= other && other <= max.day_precision!
  end
end

#each(&block) ⇒ Object



94
95
96
# File 'lib/edtf/interval.rb', line 94

def each(&block)
  step(1, &block)
end

#edtfObject Also known as: to_s

Returns the Interval as an EDTF string.



292
293
294
295
296
297
# File 'lib/edtf/interval.rb', line 292

def edtf
  [
    from.send(from.respond_to?(:edtf) ? :edtf : :to_s),
    to.send(to.respond_to?(:edtf) ? :edtf : :to_s)
  ] * '/'
end

#endObject



264
265
266
# File 'lib/edtf/interval.rb', line 264

def end
  max
end

#exclude_end?Boolean

This method always returns false for Range compatibility. EDTF intervals always include the last date.

Returns:

  • (Boolean)


134
135
136
# File 'lib/edtf/interval.rb', line 134

def exclude_end?
  false
end

#first(n = 1) ⇒ Object

call-seq:

interval.first     -> Date or nil
interval.first(n)  -> Array

Returns the first date in the interval, or the first n dates.



185
186
187
188
189
190
191
# File 'lib/edtf/interval.rb', line 185

def first(n = 1)
  if n > 1
    (ds = Array(min)).empty? ? ds : ds.concat(ds[0].next(n - 1))
  else
    min
  end
end

#include?(other) ⇒ Boolean Also known as: member?

Returns true if other is an element of the Interval, false otherwise. Comparision is done according to the Interval’s min/max date and precision.

Returns:

  • (Boolean)


155
156
157
# File 'lib/edtf/interval.rb', line 155

def include?(other)
  cover?(other) && precision == other.precision
end

#last(n = 1) ⇒ Object

call-seq:

interval.last     -> Date or nil
interval.last(n)  -> Array

Returns the last date in the interval, or the last n dates.



198
199
200
201
202
203
204
# File 'lib/edtf/interval.rb', line 198

def last(n = 1)
  if n > 1
    (ds = Array(max)).empty? ? ds : ds.concat(ds[0].prev(n - 1))
  else
    max
  end
end

#max(&block) ⇒ Object

call-seq:

interval.max                  -> Date or nil
interval.max { |a,b| block }  -> Date or nil

Returns the maximum value in the interval. If a block is given, it is used to compare values (slower). Returns nil if the first date of the interval is larger than the last or if the interval has an unknown or open end.

To calculate the dates, precision is taken into account. Thus, the max Date of “2007/2008” would be 2008-12-31, whilst the max Date of “2007-12/2008-10” would be 2009-10-31.



247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
# File 'lib/edtf/interval.rb', line 247

def max(&block)
  if block_given?
    to_a.max(&block)
  else
    case
    when open_end?, unknown_end?, !unknown_start? && to < from
      nil
    when to.day_precision?
      to
    when to.month_precision?
      to.end_of_month
    else
      to.end_of_year
    end
  end
end

#min(&block) ⇒ Object

call-seq:

interval.min                  -> Date or nil
interval.min { |a,b| block }  -> Date or nil

Returns the minimum value in the interval. If a block is given, it is used to compare values (slower). Returns nil if the first date of the interval is larger than the last or if the interval has an unknown or open start.



214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
# File 'lib/edtf/interval.rb', line 214

def min(&block)
  if block_given?
    to_a.min(&block)
  else
    case
    when unknown_start?, !unknown_end? && !open? && to < from
      nil
    when from.day_precision?
      from
    when from.month_precision?
      from.beginning_of_month
    else
      from.beginning_of_year
    end
  end
end

#mixed_precision?Boolean

Returns true if the precisions of start and end date are not the same.

Returns:

  • (Boolean)


90
91
92
# File 'lib/edtf/interval.rb', line 90

def mixed_precision?
  min.precision != max.precision
end

#precisionObject

Returns the intervals precision. Mixed precisions are currently not supported; in that case, the start date’s precision takes precedence.



85
86
87
# File 'lib/edtf/interval.rb', line 85

def precision
  min.precision || max.precision
end

#step(by = 1) ⇒ Object

call-seq:

interval.step(by=1) { |date| block }  -> self
interval.step(by=1)                   -> Enumerator

Iterates over the interval by passing by elements at each step and yielding each date to the passed-in block. Note that the semantics of by are precision dependent: e.g., a value of 2 can mean 2 days, 2 months, or 2 years.

If not block is given, returns an enumerator instead.

Raises:

  • (ArgumentError)


110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/edtf/interval.rb', line 110

def step(by = 1)
  raise ArgumentError unless by.respond_to?(:to_i)

  if block_given?
    f, t, by = min, max, by.to_i

    unless f.nil? || t.nil? || by < 1
      by = { Date::PRECISIONS[precision] => by }

      until f > t do
        yield f
        f = f.advance(by)
      end
    end

    self
  else
    enum_for(:step, by)
  end
end

#to_rangeObject

Returns the Interval as a Range.



143
144
145
146
147
148
149
150
# File 'lib/edtf/interval.rb', line 143

def to_range
  case
  when open?, unknown?
    nil
  else
    Range.new(unknown_start? ? Date.new : @from, max)
  end
end

#unknown?Boolean

Returns:

  • (Boolean)


79
80
81
# File 'lib/edtf/interval.rb', line 79

def unknown?
  unknown_start? || unknown_end?
end

#unknown_start!Object



74
75
76
77
# File 'lib/edtf/interval.rb', line 74

def unknown_start!
  @from = :unknown
  self
end

#unknown_start?Boolean

Returns:

  • (Boolean)


70
71
72
# File 'lib/edtf/interval.rb', line 70

def unknown_start?
  from == :unknown
end