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