Help us understand the problem. What is going on with this article?

Rubyで年・月・週単位で日付をループさせる

More than 3 years have passed since last update.

通常の日付のループ処理

RubyのDateクラスは先頭の日付と末尾の日付を繰り返し処理出来る仕組みがあります。

head = Date.new(2016, 1)
tail = Date.new(2016, 12)

# 1日ずつループ
(head..tail).each { |date| p date.strftime('%Y%m%d') }
# => '20160101', '20160102', '20160103', ...

# n日の間隔でループ
head.step(tail, 5).each { |date| p date.strftime('%Y%m%d') }
# => '20160101', '20160106', '20160111', ...

これはこれで、とても便利な機能なのですが、ループのstepの幅は日単位でしか指定することが出来ません。

年・月・週単位でループさせる

Enumeratorインスタンスを生成することで、任意の間隔でループさせる処理を書くことが出来るようになります。

e = Enumerator.new do |yielder|
  head = Date.new 2016, 1
  tail = Date.new 2016, 12

  while head <= tail
    yielder << head
    head = head.next_month # ここをhead.next_weekやhead.next_yearに変えることで年毎や週毎のループに出来る
  end
end

e.each { |date| p date.strftime('%Y%m%d') }
# => '20160101', '20160201', '20160301', ...

ほんの少し解説

yielderに対して<< argという形でメソッドを呼び出すとargeachのブロック引数に渡されます。
上記のサンプルコードでは、yielder << headとなっているので、head(whileループの中でカウントアップされている日付)がeachのブロック引数に渡されていることになります。

便利に使う

下記のようなrefinementを利用して、Dateクラスを拡張すると便利です。
active_supportを使うこと前提のコードです)

require 'date'
require 'active_support/all'

module DateExtender
  refine Date do
    def step_by_unit(tail, unit = :day, step = 1)
      head = self.dup

      Enumerator.new do |yielder|
        while head <= tail
          yielder << head
          head += step.send(unit)
        end
      end
    end
  end
end

using DateExtender

# week by
head = Date.new(2016, 1)
tail = Date.new(2016, 1).end_of_month
head.step_by_unit(tail, :week).each { |date| p date.strftime('%Y%m%d') }
# => '20160101', '20160108', '20160115', ...

# month by
head = Date.new(2016, 1)
tail = Date.new(2016, 4)
head.step_by_unit(tail, :month).each { |date| p date.strftime('%Y%m%d') }
# => '20160101', '20160201', '20160301', ...

# year by
head = Date.new(2016)
tail = Date.new(2020)
head.step_by_unit(tail, :year).each { |date| p date.strftime('%Y%m%d') }
# => '20160101', '20170101', '20180101', ...
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away