lib/fat_period/period.rb in fat_period-1.2.1 vs lib/fat_period/period.rb in fat_period-1.3.0
- old
+ new
@@ -68,39 +68,63 @@
first = Date.parse_spec(from, :from)
second = Date.parse_spec(to, :to)
Period.new(first, second) if first && second
end
- # Return a period as in `Period.parse` from a String phrase in which the from
- # spec is introduced with 'from' and, optionally, the to spec is introduced
- # with 'to'. A phrase with only a to spec is treated the same as one with
- # only a from spec. If neither 'from' nor 'to' appear in phrase, treat the
- # whole string as a from spec.
+ PHRASE_RE = %r{\A
+ # One or both of from and to parts
+ ((from\s+(?<from_part>[-_a-z0-9]+)\s*)?
+ (to\s+(?<to_part>[-_a-z0-9]+))?)
+ # Wholly optional chunk part
+ (\s+per\s+(?<chunk_part>\w+))?\z}xi
+
+ # Return an array of periods, either a single period as in `Period.parse`
+ # from a String phrase in which a `from spec` is introduced with 'from' and,
+ # optionally, a `to spec` is introduced with 'to', or a number of periods if
+ # there is a 'per <chunk>' modifier. A phrase with only a `to spec` is
+ # treated the same as one with only a from spec. If neither 'from' nor 'to'
+ # appear in phrase, treat the whole string as a from spec.
#
# @example
- # Period.parse_phrase('from 2014-11 to 2015-3Q') #=> Period('2014-11-01..2015-09-30')
- # Period.parse_phrase('from 2014-11') #=> Period('2014-11-01..2014-11-30')
- # Period.parse_phrase('from 2015-3Q') #=> Period('2015-09-01..2015-12-31')
- # Period.parse_phrase('to 2015-3Q') #=> Period('2015-09-01..2015-12-31')
- # Period.parse_phrase('2015-3Q') #=> Period('2015-09-01..2015-12-31')
+ # Period.parse_phrase('from 2014-11 to 2015-3Q') #=> [Period('2014-11-01..2015-09-30')]
+ # Period.parse_phrase('from 2014-11') #=> [Period('2014-11-01..2014-11-30')]
+ # Period.parse_phrase('from 2015-3Q') #=> [Period('2015-09-01..2015-12-31')]
+ # Period.parse_phrase('to 2015-3Q') #=> [Period('2015-09-01..2015-12-31')]
+ # Period.parse_phrase('from 2015-3Q') #=> [Period('2015-09-01..2015-12-31')]
+ # Period.parse_phrase('from 2015 per month') #=> [
+ # Period('2015-01-01..2015-01-31'),
+ # Period('2015-02-01..2015-02-28'),
+ # ...
+ # Period('2015-12-01..2015-12-31')
+ # ]
#
- # @param phrase [String] with 'from <spec> to <spec>'
- # @return [Period] translated from phrase
- def self.parse_phrase(phrase)
- phrase = phrase.clean
- case phrase
- when /\Afrom (.*) to (.*)\z/
- from_phrase = $1
- to_phrase = $2
- when /\Afrom (.*)\z/, /\Ato (.*)\z/
- from_phrase = $1
- to_phrase = nil
+ # @param phrase [String] with 'from <spec> to <spec> [per <chunk>]'
+ # @return [Array<Period>] translated from phrase
+ def self.parse_phrase(phrase,
+ partial_first: false, partial_last: false, round_up_last: false)
+ phrase = phrase.downcase.clean
+ mm = phrase.match(PHRASE_RE)
+ raise ArgumentError, "invalid period phrase: `#{phrase}`" unless mm
+
+ if mm[:from_part] && mm[:to_part].nil?
+ from_part = mm[:from_part]
+ to_part = nil
+ elsif mm[:from_part].nil? && mm[:to_part]
+ from_part = mm[:to_part]
+ to_part = nil
else
- from_phrase = phrase
- to_phrase = nil
+ from_part = mm[:from_part]
+ to_part = mm[:to_part]
end
- parse(from_phrase, to_phrase)
+
+ whole_period = parse(from_part, to_part)
+ if mm[:chunk_part].nil?
+ [whole_period]
+ else
+ whole_period.chunks(size: mm[:chunk_part], partial_first: partial_first,
+ partial_last: partial_last, round_up_last: round_up_last)
+ end
end
# @group Conversion
# Convert this Period to a Range.
@@ -604,25 +628,28 @@
[]
end
return result
end
+ # The first chunk
chunk_start = first.dup
chunk_end = chunk_start.end_of_chunk(chunk_size)
if chunk_start.beginning_of_chunk?(chunk_size) || partial_first
# Keep the first chunk if it's whole or partials allowed
result << Period.new(chunk_start, chunk_end)
end
chunk_start = chunk_end + 1.day
chunk_end = chunk_start.end_of_chunk(chunk_size)
+
# Add Whole chunks
while chunk_end <= last
result << Period.new(chunk_start, chunk_end)
chunk_start = chunk_end + 1.day
chunk_end = chunk_start.end_of_chunk(chunk_size)
end
+
# Possibly append the final chunk to result
- if chunk_start < last
+ if chunk_start <= last
if round_up_last
result << Period.new(chunk_start, chunk_end)
elsif partial_last
result << Period.new(chunk_start, last)
else