6

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

给定一个日期,我如何才能找到日历月的第二个或第四个星期二的下一个星期二?

例如:

给定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     
4

7 回答 7

4

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

使用我最近在 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) andnth_tuesday(2) tonth_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如果我决定在以后编写代码,我将在此处发布代码。

于 2012-10-19T23:53:19.417 回答
2

看看ChronicTickle,它们都是解析复杂时间和日期的宝石。Tickle 尤其会解析重复出现的时间(我认为它也使用了 Chronic)。

于 2012-10-19T19:56:39.853 回答
1

看看这个宝石,你也许能找出答案

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

于 2012-10-19T19:56:10.467 回答
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
于 2012-10-19T21:06:28.290 回答
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 和第 2 个最后一个星期二(如果你真的想要第 4 个星期二,只需将 -2 更改为 3)和第 2 个星期二下个月,然后选择提供的日期之后的第一个。

这是一些测试,每月 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..62)

于 2012-10-19T21:15:53.253 回答
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 
于 2012-10-19T22:45:37.553 回答
0

我使用以下来计算微软的补丁星期二日期。它改编自一些 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))
于 2014-12-03T23:07:17.530 回答