Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

or

Given 2012-10-31 then return 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     
like image 554
Meltemi Avatar asked Oct 19 '12 19:10

Meltemi


People also ask

How to get the today date in Ruby?

To get the today date, use Date.today method. today method exists in Standard Date Library of Ruby. The purpose of this method is to create the date object indicating the present day. today method will return the date object indicating the present day. Add days in the present date.

What is datetime in Ruby?

Introduction to Ruby DateTime Everything in Ruby is class and object, so here DateTime is also available with the help of the date class, here DateTime is a subclass of the date, because DateTime is a subclass of the Date it allow us to easily deal with hour, minute, second and even it allow us to get the offset value.

What is the use of time class in Ruby?

Similarly, the use of Time class will also produce the same result as shown in the example below: In Ruby, you work with dates using Date class. Every exemplary the Date class includes the year, month and date. You can also get a particular year, month or day.

How to convert string date format to time in Ruby?

With the help of parse function we can convert the string date format, it may be nay format (we can read all the available formats for the ruby date) and allow us to get hour, minute, second and even offset value, it is also possible to get the time zone with help of this function.


2 Answers

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.

Some upgrades to 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:

I have also supplemented the class with some method missing tricks to map calls from next_tuesday to next_wday(2) andnth_tuesday(2)tonth_wday(2, 2)`, which makes the next snippet easier on the eyes.

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.

like image 124
Adam Eberlin Avatar answered Sep 20 '22 04:09

Adam Eberlin


Take a look at Chronic or Tickle, both are gems for parsing complex times and dates. Tickle in particular will parse recurring times (I think it uses Chronic as well).

like image 38
Josh Voigts Avatar answered Sep 23 '22 04:09

Josh Voigts