Iterate every month with date objects

后端 未结 9 2166
轻奢々
轻奢々 2021-02-01 05:47

So I have two ruby Date objects, and I want to iterate them every month. For example if I have Date.new(2008, 12) and Date.new(2009, 3), it would yield me 2008-12, 2009-1, 2009-

相关标签:
9条回答
  • 2021-02-01 06:23
    Date.new(2014,1,1).upto(Date.today).map {|date| "#{date.to_s[0..-4]}"}.uniq
    

    Will give you a string representation of each month including it's year.

    0 讨论(0)
  • 2021-02-01 06:28
    MonthRange.new(date1..date2).each { |month| ... }
    MonthRange.new(date1..date2).map { |month| ... }
    

    You can use all the Enumerable methods if you use this iterator class. I make it handle strings too so that it can take form inputs.

    # Iterate over months in a range
    class MonthRange
      include Enumerable
    
      def initialize(range)
        @start_date = range.first
        @end_date   = range.last
        @start_date = Date.parse(@start_date) unless @start_date.respond_to? :month
        @end_date   = Date.parse(@end_date) unless @end_date.respond_to? :month
      end
    
      def each
        current_month = @start_date.beginning_of_month
        while current_month <= @end_date do
          yield current_month
          current_month = (current_month + 1.month).beginning_of_month
        end
      end
    end
    
    0 讨论(0)
  • 2021-02-01 06:29

    I have added following method to Date class:

    class Date
      def all_months_until to
        from = self
        from, to = to, from if from > to
        m = Date.new from.year, from.month
        result = []
        while m <= to
          result << m
          m >>= 1
        end
    
        result
      end
    end
    

    You use it like:

    >> t = Date.today
    => #<Date: 2009-11-12 (4910295/2,0,2299161)>
    >> t.all_months_until(t+100)   
    => [#<Date: 2009-11-01 (4910273/2,0,2299161)>, #<Date: 2009-12-01 (4910333/2,0,2299161)>, #<Date: 2010-01-01 (4910395/2,0,2299161)>, #<Date: 2010-02-01 (4910457/2,0,2299161)>]
    

    Ok, so, more rubyish approach IMHO would be something along:

    class Month<Date
      def succ
        self >> 1
      end
    end
    

    and

    >> t = Month.today
    => #<Month: 2009-11-13 (4910297/2,0,2299161)>
    >> (t..t+100).to_a
    => [#<Month: 2009-11-13 (4910297/2,0,2299161)>, #<Month: 2009-12-13 (4910357/2,0,2299161)>, #<Month: 2010-01-13 (4910419/2,0,2299161)>, #<Month: 2010-02-13 (4910481/2,0,2299161)>]
    

    But you would need to be careful to use first days of month (or implement such logic in Month)...

    0 讨论(0)
  • 2021-02-01 06:39

    Welp, after lurking 15 years nearly this is my first stack overflow answer, I think.

    start_date = Date.new(2000,12,15) # day is irrelevant and can be omitted
    end_date = Date.new(2001,2,1). #same
    
    (start_date.to_datetime..end_date.to_datetime).map{|d| [d.year, d.month]}.uniq.sort
    
    # returns [[2000,12],[2001,1],[2001,2]]
    
    (start_date.to_datetime..end_date.to_datetime).map{|d| Date.new(d.year, d.month)}.uniq.sort
    
    # returns an array of date objects for the first day of any month in the span
    
    
    0 讨论(0)
  • 2021-02-01 06:40

    Here is something very Ruby:

    first day of each month

    (Date.new(2008, 12)..Date.new(2011, 12)).select {|d| d.day == 1}
    

    It will give you an array of the first day for each month within the range.

    last day of each month

    (Date.new(2008, 12)..Date.new(2012, 01)).select {|d| d.day == 1}.map {|d| d - 1}.drop(1)
    

    Just note that the end date needs to be the month after your end range.

    0 讨论(0)
  • 2021-02-01 06:40
    def each_month(date, end_date)
      ret = []
      (ret << date; date += 1.month) while date <= end_date
      ret
    end
    
    0 讨论(0)
提交回复
热议问题