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)
  @from, @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



250
251
252
253
254
255
256
257
258
259
# File 'lib/edtf/interval.rb', line 250

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



261
262
263
264
265
266
267
268
269
270
# File 'lib/edtf/interval.rb', line 261

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

#beginObject



213
214
215
# File 'lib/edtf/interval.rb', line 213

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)


145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/edtf/interval.rb', line 145

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



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

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

#edtfObject Also known as: to_s

Returns the Interval as an EDTF string.



274
275
276
277
278
279
# File 'lib/edtf/interval.rb', line 274

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

#endObject



246
247
248
# File 'lib/edtf/interval.rb', line 246

def end
  max
end

#exclude_end?Boolean

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

Returns:

  • (Boolean)


116
117
118
# File 'lib/edtf/interval.rb', line 116

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.



167
168
169
170
171
172
173
# File 'lib/edtf/interval.rb', line 167

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)


137
138
139
# File 'lib/edtf/interval.rb', line 137

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.



180
181
182
183
184
185
186
# File 'lib/edtf/interval.rb', line 180

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

#maxObject

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.



229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
# File 'lib/edtf/interval.rb', line 229

def max
  if block_given?
    to_a.max(&Proc.new)
  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

#minObject

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.



196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
# File 'lib/edtf/interval.rb', line 196

def min
  if block_given?
    to_a.min(&Proc.new)
  else
    case
    when unknown_start?, !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)


72
73
74
# File 'lib/edtf/interval.rb', line 72

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

#precisionObject

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



67
68
69
# File 'lib/edtf/interval.rb', line 67

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)


92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/edtf/interval.rb', line 92

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.



125
126
127
128
129
130
131
132
# File 'lib/edtf/interval.rb', line 125

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

#unknown?Boolean

Returns:

  • (Boolean)


61
62
63
# File 'lib/edtf/interval.rb', line 61

def unknown?
  unknown_start? || unknown_end?
end

#unknown_start!Object



56
57
58
59
# File 'lib/edtf/interval.rb', line 56

def unknown_start!
  @from = :unknown
  self
end

#unknown_start?Boolean

Returns:

  • (Boolean)


52
53
54
# File 'lib/edtf/interval.rb', line 52

def unknown_start?
  from == :unknown
end