简体   繁体   English

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

[英]Ruby: given a date find the next 2nd or 4th Tuesday

I can't seem to find an elegant way to do this... 我似乎无法找到一种优雅的方式来做到这一点......

Given a date how can I find the next Tuesday that is either the 2nd or the 4th Tuesday of the calendar month? 在给定日期的情况下,如何找到下一个星期二,即该日历月的第二个或第四个星期二?

For example: 例如:

Given 2012-10-19 then return 2012-10-23 鉴于2012-10-19然后返回2012-10-23

or 要么

Given 2012-10-31 then return 2012-11-13 鉴于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     

Scroll to the bottom if you just want to see what the end result can look like.. 如果您只想查看最终结果的样子,请滚动到底部。

Using code snippets from some date processing work I've done recently in ruby 1.9.3. 使用我最近在ruby 1.9.3中完成的一些日期处理工作的代码片段。

Some upgrades to DateTime : 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 Tricks: method_missing技巧:

I have also supplemented the class with some method missing tricks to map calls from next_tuesday to next_wday(2) and nth_tuesday(2) to nth_wday(2, 2)`, which makes the next snippet easier on the eyes. 我还补充了一些方法,用一些缺少技巧的方法来映射从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

Example: 例:

Given a date: 给定日期:

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

You could also edit method_missing to handle calls such as :second_tuesday_of_this_month , :fourth_tuesday_of_this_month , etc. I'll post the code here if I decide to write it at a later date. 您还可以编辑method_missing来处理调用,例如:second_tuesday_of_this_month:fourth_tuesday_of_this_month等。如果我决定在以后编写代码,我会在此处发布代码。

Take a look at Chronic or Tickle , both are gems for parsing complex times and dates. 看看ChronicTickle ,两者都是用于解析复杂时间和日期的宝石。 Tickle in particular will parse recurring times (I think it uses Chronic as well). 痒痒特别会解析经常性的时间(我认为它也使用慢性)。

Check out this gem, you might be able to figure out your answer 看看这个宝石,你可能能够找到答案

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

Since you already use Rails, you don't need the includes, but this works in pure Ruby as well for reference. 由于您已经使用了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

I think you should probably use a library if you're needing to branch out into more interesting logic, but if what you've described is all you need, the code below should help 我认为你应该使用一个库,如果你需要扩展到更有趣的逻辑,但如果你所描述的只是你需要的,下面的代码应该有帮助

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

Loop through the last month and next month, grab the tuesdays, group by month, take the 2nd and the 2nd last tuesday of the current month (If you actually do want the 4th tuesday, just change -2 to 3) and the 2nd tuesday of the next month and then choose the first one after the provided date. 循环上个月和下个月,抓住星期二,逐个月,取当月的第二个和第二个星期二(如果你确实想要第4个星期二,只需要改变-2到3)和第二个星期二下个月然后在提供的日期之后选择第一个。

Here's some tests, 4 tuesdays in month, 5 tuesdays in month, random, and your examples: 这里有一些测试,每月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

which produces 哪个产生

[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

I'm sure it could be simplified, and I'd love to hear some suggestions :) (way too late &tired to even bother figuring out what the actual range should be for valid dates ie smaller than -31..62) 我确信它可以简化,我很想听到一些建议:)(太晚了,太累了,甚至懒得弄清楚有效日期的实际范围应该是多少,即小于-31..62)

so here is the code that will resolve a weekday for a given week in a month (what you asked for with little sugar). 所以这里的代码将解决一个月中给定周的工作日(你用少量糖要求)。 You should not have problems if you are running inside rails framework. 如果您在rails框架内运行,那么您应该没有问题。 Otherwise make sure you have active_support gem installed. 否则,请确保已安装active_support gem。 Method name is stupid so feel free to change it :) 方法名称是愚蠢的,所以随时改变它:)

usage: get_next_day_of_week(some_date, :friday, 1) 用法: 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 

I use the following to calculate Microsoft's patch Tuesday date. 我使用以下内容来计算Microsoft的补丁周二日期。 It was adapted from some C# code. 它改编自一些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))

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM