6

Rubyを使用して、2つの日付の間ですべての月の名前を順番に取得する必要があります。たとえば、私は取得したい:

['jan','feb','mars']

私が違いを作るとき:

1st january and March 15th

また、日付の差分が1年を超える場合にも機能させたいと思います。

何か案が?

4

6 に答える 6

11

私は一緒に行きます:

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なので、これを何百万回も実行すると少し速くなるので、私はそうしますが、あなたのマイレージは異なるかもしれません.

于 2012-09-19T22:37:21.720 に答える
4

では、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"]
于 2012-09-19T21:05:40.797 に答える
0

配列を使用することをお勧めします。

このような何かがそれを行う必要があります:

months = ['jan', 'feb', 'mar', etc...]

次に、下位の月から上位の月へと単純に繰り返して、リストを作成できます。または、オブジェクトのインデックスを次のように使用します。

months[months.index('jan')..months.index('mar')]
于 2012-09-19T20:27:55.280 に答える
-1
(start_date..end_date).map{|m| m.beginning_of_month}.uniq.map{|m| Date::ABBR_MONTHNAMES[m.month]}
于 2012-09-19T21:11:32.990 に答える
-2

自然言語の日付パーサー=ChronicGemを使用して、日付をRuby日付に変換します。次に、2つの日付を比較します。

于 2012-09-19T22:18:21.887 に答える