lib/fat_period/period.rb in fat_period-1.2.0 vs lib/fat_period/period.rb in fat_period-1.2.1
- old
+ new
@@ -1,7 +1,5 @@
-# -*- coding: utf-8 -*-
-
require 'fat_core/date'
require 'fat_core/range'
require 'fat_core/string'
# The Period class represents a range of Dates and supports a variety of
@@ -87,18 +85,17 @@
#
# @param phrase [String] with 'from <spec> to <spec>'
# @return [Period] translated from phrase
def self.parse_phrase(phrase)
phrase = phrase.clean
- if phrase =~ /\Afrom (.*) to (.*)\z/
+ case phrase
+ when /\Afrom (.*) to (.*)\z/
from_phrase = $1
to_phrase = $2
- elsif phrase =~ /\Afrom (.*)\z/
+ when /\Afrom (.*)\z/, /\Ato (.*)\z/
from_phrase = $1
to_phrase = nil
- elsif phrase =~ /\Ato (.*)\z/
- from_phrase = $1
else
from_phrase = phrase
to_phrase = nil
end
parse(from_phrase, to_phrase)
@@ -340,13 +337,11 @@
def self.chunk_containing(date, chunk)
raise ArgumentError, 'chunk is nil' unless chunk
chunk = chunk.to_sym
- unless CHUNKS.include?(chunk)
- raise ArgumentError, "unknown chunk name: #{chunk}"
- end
+ raise ArgumentError, "unknown chunk name: #{chunk}" unless CHUNKS.include?(chunk)
date = Date.ensure_date(date)
method = "#{chunk}_containing".to_sym
send(method, date)
end
@@ -469,38 +464,38 @@
else
'Period'
end
end
- # Return the chunk symbol represented by the number of days given, but allow a
- # deviation from the minimum and maximum number of days for periods larger
- # than bimonths. The default tolerance is +/-10%, but that can be adjusted. The
- # reason for allowing a bit of tolerance for the larger periods is that
- # financial statements meant to cover a given calendar period are often short
- # or long by a few days due to such things as weekends, holidays, or
- # accounting convenience. For example, a bank might issuer "monthly"
- # statements approximately every 30 days, but issue them earlier or later to
- # avoid having the closing date fall on a weekend or holiday. We still want to
- # be able to recognize them as "monthly", even though the period covered might
- # be a few days shorter or longer than any possible calendar month. You can
- # eliminate this "fudge factor" by setting the `tolerance_pct` to zero. If
- # the number of days corresponds to none of the defined calendar periods,
- # return the symbol `:irregular`.
+ # Return the chunk symbol represented by the number of days given, but allow
+ # a deviation from the minimum and maximum number of days for periods larger
+ # than bimonths. The default tolerance is +/-10%, but that can be
+ # adjusted. The reason for allowing a bit of tolerance for the larger
+ # periods is that financial statements meant to cover a given calendar
+ # period are often short or long by a few days due to such things as
+ # weekends, holidays, or accounting convenience. For example, a bank might
+ # issuer "monthly" statements approximately every 30 days, but issue them
+ # earlier or later to avoid having the closing date fall on a weekend or
+ # holiday. We still want to be able to recognize them as "monthly", even
+ # though the period covered might be a few days shorter or longer than any
+ # possible calendar month. You can eliminate this "fudge factor" by setting
+ # the `tolerance_pct` to zero. If the number of days corresponds to none of
+ # the defined calendar periods, return the symbol `:irregular`.
#
# @example
# Period.days_to_chunk(360) #=> :year
# Period.days_to_chunk(360, 0) #=> :irregular
# Period.days_to_chunk(88) #=> :quarter
# Period.days_to_chunk(88, 0) #=> :irregular
#
# @param days [Integer] the number of days in the period under test
- # @param tolerance_pct [Numberic] the percent deviation allowed, e.g. 10 => 10%
+ # @param tolerance_pct [Numeric] the percent deviation allowed, e.g. 10 => 10%
# @return [Symbol] symbol for the period corresponding to days number of days
def self.days_to_chunk(days, tolerance_pct = 10)
result = :irregular
CHUNK_RANGE.each_pair do |chunk, rng|
- if [:semimonth, :biweek, :week, :day].include?(chunk)
+ if %i[semimonth biweek week day].include?(chunk)
# Be strict for shorter periods.
if rng.cover?(days)
result = chunk
break
end
@@ -587,13 +582,11 @@
# to extend beyond the end of self.
# @return [Array<Period>] periods that subdivide self into chunks of size, `size`
def chunks(size: :month, partial_first: false, partial_last: false,
round_up_last: false)
chunk_size = size.to_sym
- unless CHUNKS.include?(chunk_size)
- raise ArgumentError, "unknown chunk size '#{chunk_size}'"
- end
+ raise ArgumentError, "unknown chunk size '#{chunk_size}'" unless CHUNKS.include?(chunk_size)
containing_period = Period.chunk_containing(first, chunk_size)
return [dup] if self == containing_period
# Period too small for even a single chunk and is wholly-contained by a
@@ -616,17 +609,13 @@
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)
- chunk_start = chunk_end + 1.day
- chunk_end = chunk_start.end_of_chunk(chunk_size)
- else
- # Discard the partial first or move to next whole chunk
- chunk_start = chunk_end + 1.day
- chunk_end = chunk_start.end_of_chunk(chunk_size)
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)
@@ -638,19 +627,19 @@
elsif partial_last
result << Period.new(chunk_start, last)
else
result
end
- elsif partial_last
+ end
+ if partial_last && !partial_first && result.empty?
# Catch the case where the period is too small to make a whole chunk and
# partial_first is false, so it did not get included as the initial
# partial chunk, yet a partial_last is allowed, so include the whole
# period as a partial chunk.
result << Period.new(first, last)
- else
- result
end
+ result
end
# @group Set operations
# Is this period contained wholly within or coincident with `other`?
@@ -690,10 +679,10 @@
# @return [Boolean] self contains or coincident with `other`?
def superset_of?(other)
to_range.superset_of?(other.to_range)
end
- # Does this period wholly contain but not coincident with `other`?
+ # Does this period wholly contain but is not coincident with `other`?
#
# @example
# Period.parse('2015').proper_superset_of?(Period.parse('2015-2Q')) #=> true
# Period.parse('2015-2Q').proper_superset_of?(Period.parse('2015-2Q')) #=> false
# Period.parse('2015-02').proper_superset_of?(Period.parse('2015-2Q')) #=> false