Module: DaruLite::DateTimeIndexHelper

Defined in:
lib/daru_lite/date_time/index.rb

Overview

Private module for storing helper functions for DateTimeIndex.

Constant Summary collapse

OFFSETS_HASH =
{
  'S' => DaruLite::Offsets::Second,
  'M' => DaruLite::Offsets::Minute,
  'H' => DaruLite::Offsets::Hour,
  'D' => DaruLite::Offsets::Day,
  'W' => DaruLite::Offsets::Week,
  'MONTH' => DaruLite::Offsets::Month,
  'MB' => DaruLite::Offsets::MonthBegin,
  'ME' => DaruLite::Offsets::MonthEnd,
  'YEAR' => DaruLite::Offsets::Year,
  'YB' => DaruLite::Offsets::YearBegin,
  'YE' => DaruLite::Offsets::YearEnd
}.freeze
TIME_INTERVALS =
{
  Rational(1, 1) => DaruLite::Offsets::Day,
  Rational(1, 24) => DaruLite::Offsets::Hour,
  Rational(1, 1440) => DaruLite::Offsets::Minute,
  Rational(1, 86_400) => DaruLite::Offsets::Second
}.freeze
DOW_REGEXP =
Regexp.new(DaruLite::DAYS_OF_WEEK.keys.join('|'))
FREQUENCY_PATTERN =
/^
(?<multiplier>[0-9]+)?
(
  (?<offset>MONTH|YEAR|S|H|MB|ME|M|D|YB|YE) |
  (?<offset>W)(-(?<weekday>#{DOW_REGEXP}))?
)$/x.freeze
DATE_PRECISION_REGEXP =
/^(\d\d\d\d)(-\d{1,2}(-\d{1,2}( \d{1,2}(:\d{1,2}(:\d{1,2})?)?)?)?)?$/.freeze
DATE_PRECISIONS =
[nil, :year, :month, :day, :hour, :min, :sec].freeze

Class Method Summary collapse

Class Method Details

.begin_from_offset?(offset, start) ⇒ Boolean

Returns:

  • (Boolean)


64
65
66
67
# File 'lib/daru_lite/date_time/index.rb', line 64

def begin_from_offset?(offset, start)
  offset.is_a?(DaruLite::Offsets::Tick) ||
    (offset.respond_to?(:on_offset?) && offset.on_offset?(start))
end

.coerce_date(date) ⇒ Object



58
59
60
61
62
# File 'lib/daru_lite/date_time/index.rb', line 58

def coerce_date(date)
  return date unless date.is_a?(String)

  date_time_from(date, determine_date_precision_of(date))
end

.date_time_from(date_string, date_precision) ⇒ Object



120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/daru_lite/date_time/index.rb', line 120

def date_time_from(date_string, date_precision)
  case date_precision
  when :year
    DateTime.new(date_string.gsub(/[^0-9]/, '').to_i)
  when :month
    DateTime.new(
      date_string.match(/\d\d\d\d/).to_s.to_i,
      date_string.match(/-\d?\d/).to_s.delete('-').to_i
    )
  else
    DateTime.parse date_string
  end
end

.determine_date_precision_of(date_string) ⇒ Object



137
138
139
140
141
# File 'lib/daru_lite/date_time/index.rb', line 137

def determine_date_precision_of(date_string)
  components = date_string.scan(DATE_PRECISION_REGEXP).flatten.compact
  DATE_PRECISIONS[components.count] or
    raise ArgumentError, "Unacceptable date string #{date_string}"
end

.find_date_string_bounds(date_string) ⇒ Object



114
115
116
117
118
# File 'lib/daru_lite/date_time/index.rb', line 114

def find_date_string_bounds(date_string)
  date_precision = determine_date_precision_of date_string
  date_time = date_time_from date_string, date_precision
  generate_bounds date_time, date_precision
end

.find_index_of_date(data, date_time) ⇒ Object

Raises:

  • (ArgumentError)


107
108
109
110
111
112
# File 'lib/daru_lite/date_time/index.rb', line 107

def find_index_of_date(data, date_time)
  searched = data.bsearch { |d| d[0] >= date_time }
  raise(ArgumentError, "Cannot find #{date_time}") if searched.nil? || searched[0] != date_time

  searched[1]
end

.generate_bounds(date_time, date_precision) ⇒ Object

rubocop:disable Metrics/MethodLength



143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
# File 'lib/daru_lite/date_time/index.rb', line 143

def generate_bounds(date_time, date_precision) # rubocop:disable Metrics/MethodLength
  # FIXME: about that ^ disable: I'd like to use my zverok/time_boots here, which will simplify things
  case date_precision
  when :year
    [
      date_time,
      DateTime.new(date_time.year, 12, 31, 23, 59, 59)
    ]
  when :month
    [
      date_time,
      DateTime.new(date_time.year, date_time.month, ((date_time >> 1) - 1).day,
                   23, 59, 59)
    ]
  when :day
    [
      date_time,
      DateTime.new(date_time.year, date_time.month, date_time.day, 23, 59, 59)
    ]
  when :hour
    [
      date_time,
      DateTime.new(date_time.year, date_time.month, date_time.day,
                   date_time.hour, 59, 59)
    ]
  when :min
    [
      date_time,
      DateTime.new(date_time.year, date_time.month, date_time.day,
                   date_time.hour, date_time.min, 59)
    ]
  else # second or when precision is same as offset
    [date_time, date_time]
  end
end

.generate_data(start, en, offset, periods) ⇒ Object



69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/daru_lite/date_time/index.rb', line 69

def generate_data(start, en, offset, periods)
  data = []
  new_date = begin_from_offset?(offset, start) ? start : offset + start

  if periods.nil? # use end
    loop do
      break if new_date > en

      data << new_date
      new_date = offset + new_date
    end
  else
    periods.times do
      data << new_date
      new_date = offset + new_date
    end
  end

  data
end

.infer_offset(data) ⇒ Object



96
97
98
99
100
101
102
103
104
105
# File 'lib/daru_lite/date_time/index.rb', line 96

def infer_offset(data)
  diffs = data.each_cons(2).map { |d1, d2| d2 - d1 }

  return nil unless diffs.uniq.count == 1

  return TIME_INTERVALS[diffs.first].new if TIME_INTERVALS.include?(diffs.first)

  number_of_seconds = diffs.first / DaruLite::Offsets::Second.new.multiplier
  DaruLite::Offsets::Second.new(number_of_seconds.numerator) if number_of_seconds.denominator == 1
end

.key_out_of_bounds?(key, data) ⇒ Boolean

Returns:

  • (Boolean)


187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
# File 'lib/daru_lite/date_time/index.rb', line 187

def key_out_of_bounds?(key, data)
  dates = data.transpose.first

  precision = determine_date_precision_of key
  date_time = date_time_from key, precision

  # FIXME: I'm pretty suspicious about logic here:
  # why only year & month? - zverok 2016-05-16

  case precision
  when :year
    year_out_of_bounds?(date_time, dates)
  when :month
    year_month_out_of_bounds?(date_time, dates)
  end
end

.last_date(data) ⇒ Object



183
184
185
# File 'lib/daru_lite/date_time/index.rb', line 183

def last_date(data)
  data.max_by { |d| d[1] }
end

.offset_from_frequency(frequency) ⇒ Object

Generates a DaruLite::DateOffset object for generic offsets or one of the specialized classed within DaruLite::Offsets depending on the ‘frequency’ string.



38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/daru_lite/date_time/index.rb', line 38

def offset_from_frequency(frequency)
  return frequency if frequency.is_a?(DaruLite::DateOffset)

  frequency ||= 'D'

  matched = FREQUENCY_PATTERN.match(frequency) or
    raise ArgumentError, "Invalid frequency string #{frequency}"

  n             = (matched[:multiplier] || 1).to_i
  offset_string = matched[:offset]
  offset_klass  = OFFSETS_HASH[offset_string] or
    raise ArgumentError, "Cannont interpret offset #{offset_string}"

  if offset_string == 'W'
    offset_klass.new(n, weekday: DaruLite::DAYS_OF_WEEK[matched[:weekday]])
  else
    offset_klass.new(n)
  end
end

.possibly_convert_to_date_time(data) ⇒ Object



179
180
181
# File 'lib/daru_lite/date_time/index.rb', line 179

def possibly_convert_to_date_time(data)
  data[0].is_a?(String) ? data.map! { |e| DateTime.parse(e) } : data
end

.verify_start_and_end(start, en) ⇒ Object

Raises:

  • (ArgumentError)


90
91
92
93
94
# File 'lib/daru_lite/date_time/index.rb', line 90

def verify_start_and_end(start, en)
  raise ArgumentError, 'Start and end cannot be the same' if start == en
  raise ArgumentError, 'Start must be lesser than end'    if start > en
  raise ArgumentError, 'Only same time zones are allowed' if start.zone != en.zone
end