Ruby:给出一个日期,找到下一个第二或第四个星期二

时间:2012-10-19 19:36:59

标签: ruby-on-rails ruby date

我似乎无法找到一种优雅的方式来做到这一点......

给定一个日期,我怎样才能找到下一个星期二,即该日历月的第二个或第四个星期二?

例如:

给定2012-10-19,然后返回2012-10-23

给定2012-10-31,然后返回2012-11-13

      October               November        
Su Mo Tu We Th Fr Sa    Su Mo Tu We Th Fr Sa  
    1  2  3  4  5  6                 1  2  3  
 7  8  9 10 11 12 13     4  5  6  7  8  9 10 
14 15 16 17 18 19 20    11 12 13 14 15 16 17  
21 22 23 24 25 26 27    18 19 20 21 22 23 24 
28 29 30 31             25 26 27 28 29 30     

7 个答案:

答案 0 :(得分:3)

如果您只想查看最终结果的样子,请滚动到底部..

使用我最近在ruby 1.9.3中完成的某些日期处理工作的代码片段。

DateTime的一些升级:

require 'date'

class DateTime

  ALL_DAYS = [ 'sunday', 'monday', 'tuesday',
               'wednesday', 'thursday', 'friday', 'saturday' ]

  def next_week
    self + (7 - self.wday)
  end

  def next_wday (n)
    n > self.wday ? self + (n - self.wday) : self.next_week.next_day(n)
  end

  def nth_wday (n, i)
    current = self.next_wday(n)
    while (i > 0)
      current = current.next_wday(n)
      i = i - 1
    end
    current
  end

  def first_of_month
    self - self.mday + 1
  end

  def last_of_month
    self.first_of_month.next_month - 1
  end

end

method_missing技巧:

我还补充了这个类,其中一些方法缺少技巧来映射从next_tuesdaynext_wday(2) and nth_tuesday(2)to nth_wday(2,2)`的调用,这使得下一个片段上的代码更容易。

class DateTime

  # ...

  def method_missing (sym, *args, &block)
    day = sym.to_s.gsub(/^(next|nth)_(?<day>[a-zA-Z]+)$/i, '\k<day>')
    dindex = ALL_DAYS.include?(day) ? ALL_DAYS.index(day.downcase) : nil
    if (sym =~ /^next_[a-zA-Z]+$/i) && dindex
      self.send(:next_wday, dindex)
    elsif (sym =~ /^nth_[a-zA-Z]+$/i) && dindex
      self.send(:nth_wday, dindex, args[0])
    else
      super(sym, *args, &block)
    end
  end

  def respond_to? (sym)
    day = sym.to_s.gsub(/^(next|nth)_(?<day>[a-zA-Z]+)$/i, '\k<day>')
    (((sym =~ /^next_[a-zA-Z]+$/i) || (sym =~ /^nth_[a-zA-Z]+$/i)) && ALL_DAYS.include?(day)) || super(sym)
  end

end

实施例

给定日期:

today = DateTime.now
second_tuesday = (today.first_of_month - 1).nth_tuesday(2)
fourth_tuesday = (today.first_of_month - 1).nth_tuesday(4)

if today == second_tuesday
  puts "Today is the second tuesday of this month!"
elsif today == fourth_tuesday
  puts "Today is the fourth tuesday of this month!"
else
  puts "Today is not interesting."
end

您还可以修改method_missing来处理:second_tuesday_of_this_month:fourth_tuesday_of_this_month等来电。如果我决定在以后编写代码,我会在此处发布代码。< / p>

答案 1 :(得分:2)

看看ChronicTickle,两者都是解析复杂时间和日期的宝石。痒痒特别会解析经常性的时间(我认为它也使用慢性)。

答案 2 :(得分:1)

看看这个宝石,你或许可以找到答案

https://github.com/mojombo/chronic/

答案 3 :(得分:1)

由于您已经使用了Rails,因此您不需要包含,但这也适用于纯Ruby以供参考。

require 'rubygems'
require 'active_support/core_ext'

d = DateTime.parse('2012-10-19')
result = nil
valid_weeks = [d.beginning_of_month.cweek + 1, d.beginning_of_month.cweek + 3]
if valid_weeks.include?(d.next_week(:tuesday).cweek)
  result = d.next_week(:tuesday)
else
  result = d.next_week.next_week(:tuesday)
end

puts result

答案 4 :(得分:0)

我认为你应该使用一个库,如果你需要扩展到更有趣的逻辑,但如果你所描述的只是你需要的,下面的代码应该有帮助

SECONDS_PER_DAY = 60 * 60 * 24
def find_tuesday_datenight(now)
  tuesdays = [*-31..62].map { |i| now + (SECONDS_PER_DAY * i) }
    .select { |d| d.tuesday? }
    .group_by { |d| d.month }
  [tuesdays[now.month][1], tuesdays[now.month][-2], tuesdays[(now.month + 1) % 12][1]]
    .find {|d| d.yday > now.yday }
end

循环上个月和下个月,抓住星期二,逐个月,取当月的第二个和第二个星期二(如果你真的想要第四个星期二,只需改变-2到3)和下个月的第二个星期二,然后在提供的日期之后选择第一个。

以下是一些测试,每月4个星期二,每月5个星期二,随机,以及您的示例:

[[2013, 5, 1], [2013, 12, 1], [2012, 10, 1], [2012, 10, 19], [2012, 10, 31]].each do |t|
  puts "#{t} => #{find_tuesday_datenight(Time.new *t)}"
end

产生

[2013, 5, 1] => 2013-05-14 00:00:00 +0800
[2013, 12, 1] => 2013-12-10 00:00:00 +0800
[2012, 10, 1] => 2012-10-09 00:00:00 +0800
[2012, 10, 19] => 2012-10-23 00:00:00 +0800
[2012, 10, 31] => 2012-11-13 00:00:00 +0800

我确信它可以简化,我很想听到一些建议:)(太迟了太累了,甚至懒得弄清楚有效日期的实际范围应该是多少,即小于-31。 0.62)

答案 5 :(得分:0)

所以这里的代码将解决一个月内某个特定星期的工作日(你要求的糖含量很少)。如果您在rails框架内运行,那么您应该没有问题。否则,请确保已安装active_support gem。方法名称是愚蠢的,所以随意改变它:)

用法:get_next_day_of_week(some_date, :friday, 1)

require 'rubygems'
require 'active_support/core_ext'

def get_next_day_of_week(date, day_name, count)
  next_date = date + (-date.days_to_week_start(day_name.to_sym) % 7)
  while (next_date.mday / 7) != count - 1 do
    next_date = next_date + 7
  end
  next_date
end 

答案 6 :(得分:0)

我使用以下内容计算Microsoft的补丁周二日期。它改编自一些C#代码。

require 'date'

#find nth iteration of given day (day specified in 'weekday' variable)
findnthday = 2
#Ruby wday number (days are numbered 0-7 beginning with Sunday)
weekday = 2

today = Time.now
todayM = today.month
todayY = today.year
StrtMonth = DateTime.new(todayY,todayM ,1)

while StrtMonth.wday != weekday do

    StrtMonth = StrtMonth + 1;
end

PatchTuesday = StrtMonth + (7 * (findnthday - 1))