Rubyを使用して、2つの日付の間ですべての月の名前を順番に取得する必要があります。たとえば、私は取得したい:
['jan','feb','mars']
私が違いを作るとき:
1st january and March 15th
また、日付の差分が1年を超える場合にも機能させたいと思います。
何か案が?
Rubyを使用して、2つの日付の間ですべての月の名前を順番に取得する必要があります。たとえば、私は取得したい:
['jan','feb','mars']
私が違いを作るとき:
1st january and March 15th
また、日付の差分が1年を超える場合にも機能させたいと思います。
何か案が?
私は一緒に行きます:
d1 = Date.parse('jan 1 2011')
d2 = Date.parse('dec 31 2012')
(d1..d2).map{ |m| m.strftime('%Y%m') }.uniq.map{ |m| Date::ABBR_MONTHNAMES[ Date.strptime(m, '%Y%m').mon ] }
=> ["Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec",
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec"]
また:
(d1..d2).map{ |m| m.strftime('%Y%m') }.uniq.map{ |m| Date::ABBR_MONTHNAMES[ m[/\d\d$/ ].to_i ] }
これはおそらく少し速いです。
問題は年の境界です。月だけでなく、年と月を追跡する必要があります。そうしないと、を使用uniq
して日を削除するときに、重複した月のインデックスをすべて削除します。適切な粒度を得るために、YYYYMM
フォーマットを使用しました。
require 'benchmark'
require 'date'
d1 = Date.parse('jan 1 2011')
d2 = Date.parse('dec 31 2012')
n = 100
Benchmark.bm(8) do |x|
x.report('strptime') { n.times { (d1..d2).map{ |m| m.strftime('%Y%m') }.uniq.map{ |m| Date::ABBR_MONTHNAMES[ Date.strptime(m, '%Y%m').mon ] } } }
x.report('regex') { n.times { (d1..d2).map{ |m| m.strftime('%Y%m') }.uniq.map{ |m| Date::ABBR_MONTHNAMES[ m[/\\d\\d$/ ].to_i ] } } }
end
user system total real
strptime 3.060000 0.020000 3.080000 ( 3.076614)
regex 2.820000 0.010000 2.830000 ( 2.829366)
編集:
もっと面白くしましょう。
コードのにおいが気になり続けていました。Date.strftime
私はとを使用するのが好きではなかったDate.strptime
ので、この問題をもう一度実行しました: ベンチマークと共に、はるかに高速に実行されている 2 つのソリューションを次に示します。
require 'benchmark'
require 'date'
def regex_months_between(d1, d2)
d1, d2 = [d1, d2].map{ |d| Date.parse(d) }.minmax
(d1..d2).map{ |m| m.strftime('%Y%m') }.uniq.map{ |m| Date::ABBR_MONTHNAMES[ m[/\d\d$/ ].to_i ] }
end
def months_between1(d1, d2)
d1, d2 = [d1, d2].map{ |d| Date.parse(d) }.minmax
months = (d2.mon - d1.mon) + (d2.year - d1.year) * 12
month_names = []
months.times{ |m|
month_names << Date::ABBR_MONTHNAMES[(d1 >> m).mon]
}
month_names << Date::ABBR_MONTHNAMES[d2.mon]
month_names
end
def months_between2(d1, d2)
d1, d2 = [d1, d2].map{ |d| Date.parse(d) }.minmax
months = (d2.mon - d1.mon) + (d2.year - d1.year) * 12
(d1.mon ... (d1.mon + months)).each_with_object(Date::ABBR_MONTHNAMES[d1.mon, 1]) { |month_offset, month_names_array|
month_names_array << Date::ABBR_MONTHNAMES[(d1 >> month_offset).mon]
}
end
puts regex_months_between('jan 1 2011', 'dec 31 2012').join(', ')
puts months_between1('jan 1 2011', 'dec 31 2012').join(', ')
puts months_between2('jan 1 2011', 'dec 31 2012').join(', ')
n = 100
Benchmark.bm(3) do |b|
b.report('rmb') { n.times { regex_months_between('jan 1 2011', 'dec 31 2012') } }
b.report('mb1') { n.times { months_between1('jan 1 2011', 'dec 31 2012') } }
b.report('mb2') { n.times { months_between2('jan 1 2011', 'dec 31 2012') } }
end
出力は次のようになります。
Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec, Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec
Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec, Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec
Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec, Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec
user system total real
rmb 2.810000 0.010000 2.820000 ( 2.820732)
mb1 0.060000 0.000000 0.060000 ( 0.057763)
mb2 0.060000 0.000000 0.060000 ( 0.057112)
面白い。「rmb
」は現在、かなり遅れています。テストから引き出して、ループを 100 倍に増やします。
n = 10_000
Benchmark.bm(3) do |b|
b.report('mb1') { n.times { months_between1('jan 1 2011', 'dec 31 2012') } }
b.report('mb2') { n.times { months_between2('jan 1 2011', 'dec 31 2012') } }
end
これにより、次のことが得られます。
user system total real
mb1 5.570000 0.060000 5.630000 ( 5.615789)
mb2 5.570000 0.040000 5.610000 ( 5.611323)
これは基本的に、月を取得する 2 つの新しい方法の間の関係です。肛門mb2
なので、これを何百万回も実行すると少し速くなるので、私はそうしますが、あなたのマイレージは異なるかもしれません.
では、2 つの日付があるd1
としd2
ます。
> d1 = 2.years.ago
=> Sun, 19 Sep 2010 15:51:18 CDT -05:00
> d2 = 1.month.ago
=> Sun, 19 Aug 2012 15:51:25 CDT -05:00
(この場合、それらは ActiveSupport::TimeWithZone オブジェクトですが、これは DateTime または what-have-you でも同様に機能します)
出力を含む配列を作成します。
m = []
次に、月を増やしながら( を使用して)月の略語を.strftime
入力します。
while d1 <= d2.at_end_of_month
m << d1.strftime('%b')
d1 = d1 + 1.month
end
そして、それは次のとおりです。
> m
=> ["Sep", "Oct", "Nov", "Dec", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug"]
配列を使用することをお勧めします。
このような何かがそれを行う必要があります:
months = ['jan', 'feb', 'mar', etc...]
次に、下位の月から上位の月へと単純に繰り返して、リストを作成できます。または、オブジェクトのインデックスを次のように使用します。
months[months.index('jan')..months.index('mar')]
(start_date..end_date).map{|m| m.beginning_of_month}.uniq.map{|m| Date::ABBR_MONTHNAMES[m.month]}
自然言語の日付パーサー=ChronicGemを使用して、日付をRuby日付に変換します。次に、2つの日付を比較します。