lib/fat_period/period.rb in fat_period-1.1.0 vs lib/fat_period/period.rb in fat_period-1.1.1

- old
+ new

@@ -267,17 +267,27 @@ # An Array of the valid Symbols for calendar chunks plus the Symbol :irregular # for other periods. CHUNKS = %i[day week biweek semimonth month bimonth quarter half year irregular].freeze + CHUNK_ORDER = {} + CHUNKS.each_with_index do |c, i| + CHUNK_ORDER[c] = i + end + CHUNK_ORDER.freeze + # An Array of Ranges for the number of days that can be covered by each chunk. CHUNK_RANGE = { day: (1..1), week: (7..7), biweek: (14..14), semimonth: (15..16), month: (28..31), bimonth: (59..62), quarter: (90..92), half: (180..183), year: (365..366) }.freeze + def self.chunk_cmp(chunk1, chunk2) + CHUNK_ORDER[chunk1] <=> CHUNK_ORDER[chunk2] + end + # Return a period representing a chunk containing a given Date. def self.day_containing(date) Period.new(date, date) end @@ -311,10 +321,23 @@ def self.year_containing(date) Period.new(date.beginning_of_year, date.end_of_year) end + 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 + + date = Date.ensure_date(date) + method = "#{chunk}_containing".to_sym + send(method, date) + end + # Return a Period representing a chunk containing today. def self.this_day day_containing(Date.current) end @@ -548,32 +571,51 @@ # @param round_up_last [Boolean] allow the last period in the returned array # 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) - size = size.to_sym - unless CHUNKS.include?(size) - raise ArgumentError, "unknown chunk size '#{size}'" + chunk_size = size.to_sym + unless CHUNKS.include?(chunk_size) + raise ArgumentError, "unknown chunk size '#{chunk_size}'" end + 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 + # single chunk. result = [] + if proper_subset_of?(containing_period) + result = + if partial_first || partial_last + if round_up_last + [containing_period] + else + [dup] + end + else + [] + end + return result + end + chunk_start = first.dup - chunk_end = chunk_start.end_of_chunk(size) - if chunk_start.beginning_of_chunk?(size) || partial_first + 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(size) + 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(size) + chunk_end = chunk_start.end_of_chunk(chunk_size) end # 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(size) + chunk_end = chunk_start.end_of_chunk(chunk_size) end # Possibly append the final chunk to result if chunk_start < last if round_up_last result << Period.new(chunk_start, chunk_end)