module DateExtensions def wday_from_monday wday == 0 ? 6 : wday - 1 end def rest_of_days_in_week (0...(7 - wday_from_monday)).map { |i| self + i } end def start_of_next_week(interval = 1) (self + (7 * interval)).start_of_week end def start_of_week self - wday_from_monday end def start_of_next_month(interval = 1) new_month, new_year = month + interval, year until new_month <= 12 do new_month -= 12 new_year += 1 end Date.new(new_year, new_month, 1) end def start_of_next_year(interval = 1) Date.new(year + interval, 1, 1) end def day_occurrence_range(type) case type.to_s when 'first' : 1..7 when 'second': 8..14 when 'third' : 15..21 when 'fourth': 22..28 when 'last' : ((days_in_month - 6)..days_in_month) end end def day_occurrences(type) day_occurrence_range(type).map do |i| renew(:day => i) end end def weekday? (1..5).include?(wday) end def days_in_month self.class.days_in_month(year, month) end def renew(options = {}) self.class.new(options[:year] || year, options[:month] || month, options[:day] || day) end end class Date NORMAL_MONTH_LENGTHS = { 1 => 31, 2 => 28, 3 => 31, 4 => 30, 5 => 31, 6 => 30, 7 => 31, 8 => 31, 9 => 30, 10 => 31, 11 => 30, 12 => 31 } unless const_defined? :NORMAL_MONTH_LENGTHS DAY_OCCURRENCES = %w{first second third fourth last} def self.days_in_month(year, month) days = NORMAL_MONTH_LENGTHS[month] days += 1 if month == 2 && new(year, month, 1).leap? days end include DateExtensions end