Get the day of the last Friday of each month - ruby-on-rails

I am new with ruby and I want to get the day of the last Friday of each month.
For example, the last Friday of March is 29, the Last Friday of April 26.
So, how can I get a solution?
I'm using the rails framework.
The method .cweek returns the week of the year, but does not return the week of the current month.

#!/usr/bin/env ruby
require 'date'
(1..12).each do |month|
d = Date.new(2013, month, -1)
d -= (d.wday - 5) % 7
puts d
end
Source (second/third Google result..)

I would go with Lee's answer, I'm only posting this one because (I thought) it's pretty cool.
Using gem Chronic (https://github.com/mojombo/chronic):
#Last Friday of this coming December
require 'chronic'
last_friday = Chronic.parse("First Friday of next January") - 1.week

Retrieving the last friday of a month can be made in a single line:
def last_fridays_for_every_month_of_year(year)
(1..12).map do |month|
Date.new(year, month, -1).downto(0).find(&:friday?)
end
end
You may use it like this:
last_fridays_for_every_month_of_year 2013
#=> [#<Date: 2013-01-25 ((2456318j,0s,0n),+0s,2299161j)>,
#<Date: 2013-02-22 ((2456346j,0s,0n),+0s,2299161j)>,
#<Date: 2013-03-29 ((2456381j,0s,0n),+0s,2299161j)>,
#<Date: 2013-04-26 ((2456409j,0s,0n),+0s,2299161j)>,
#<Date: 2013-05-31 ((2456444j,0s,0n),+0s,2299161j)>,
#<Date: 2013-06-28 ((2456472j,0s,0n),+0s,2299161j)>,
#<Date: 2013-07-26 ((2456500j,0s,0n),+0s,2299161j)>,
#<Date: 2013-08-30 ((2456535j,0s,0n),+0s,2299161j)>,
#<Date: 2013-09-27 ((2456563j,0s,0n),+0s,2299161j)>,
#<Date: 2013-10-25 ((2456591j,0s,0n),+0s,2299161j)>,
#<Date: 2013-11-29 ((2456626j,0s,0n),+0s,2299161j)>,
#<Date: 2013-12-27 ((2456654j,0s,0n),+0s,2299161j)>]

require "active_support/core_ext"
end_of_month = Date.today.end_of_month
if end_of_month - end_of_month.beginning_of_week >= 4
end_of_month+5.days
else
end_of_month-2.days
end
# => Fri, 29 Mar 2013

Related

Efficient way to get closest pay day to a given date

I'm trying to figure out the most efficient way to find the closest payday from a given date.
Let's say we know payday are every 14 days and that January 6, 2022 was one of them. What is the most efficient way to find the next payday for a given date (the date is the beginning of a week actually) from a known payday?
Here is a current working ugly implementation
def payday_first_date
# TODO: Very reliable, but very inefficient
#payday_first_date ||= begin
date = known_pay_day
date -= 14.days until date < given_date
date += 14.days until date > given_date
date
end
end
def known_pay_day
# Thursday January 6, 2022 was a payday
#known_pay_day ||= Date.new(2022, 2, 17)
end
The given_date could be in the future and also in the past. It can be a date from another year aswell.
Thanks in advance!
First define the known pay date and two helper methods.
require 'date'
def date_str_to_date_obj(date_str)
DateTime.strptime(date_str, "%B %d, %Y").to_date
end
known_date = date_str_to_date_obj("January 6, 2022")
#=> #<Date: 2022-01-06 ((2459586j,0s,0n),+0s,2299161j)>
For presentation only:
def date_obj_to_date_str(date_obj)
date_obj.strftime("%B %d, %Y, %A")
end
date_obj_to_date_str(known_date)
#=> "January 06, 2022, Thursday"
The main method follows.
def closest_pay_day(known_date, target_date)
nbr_pay_dates = (target_date - known_date)/14.0
last_pay_date = known_date + nbr_pay_dates.floor * 14
next_pay_date = last_pay_date + 14
(next_pay_date - target_date < target_date - last_pay_date) ?
next_pay_date : last_pay_date
end
Suppose the target date is after the known pay date.
target_date_str = "March 26, 2022"
Then
target_date = date_str_to_date_obj(target_date_str)
#=> #<Date: 2022-03-26 ((2459665j,0s,0n),+0s,2299161j)>
date_obj_to_date_str(target_date)
#=> "March 26, 2022, Saturday"
The closest pay period to the target date is therefore
closest = closest_pay_day(known_date, target_date)
#=> #<Date: 2022-03-31 ((2459670j,0s,0n),+0s,2299161j)>
date_obj_to_date_str(closest)
#=> "March 31, 2022, Thursday"
which is
(target_date- closest).to_i
#=> -5
days before the target date, meaning 5 days after the target date.
Now suppose the target date is before the known pay date.
target_date_str = "October 19, 2021"
Then
target_date = date_str_to_date_obj(target_date_str)
#=> #<Date: 2021-10-19 ((2459507j,0s,0n),+0s,2299161j)>
date_obj_to_date_str(target_date)
#=> "October 19, 2021, Tuesday"
date_obj_to_date_str(closest_pay_day(known_date, target_date))
#=> "October 14, 2021, Thursday"

Formatting Date in Rails

How can I format this datetime attribute 2017-10-15 or how can I get the 15th of the current month?
#today = Time.now
#mid = time.strftime("%Y-%m-15")
into October 15, 2017? I tried using to_formatted_s(:long), but it gives an error of undefined method.
In Rails 4 or above
> Date.today.beginning_of_month + 14
#=> Sun, 15 Oct 2017
# formatted as per your requirement
> (Date.today.beginning_of_month + 14).strftime("%B %d, %Y")
#=> "October 15, 2017"
Date#beginning_of_month it will return you beginning date of month of specified date (which will be always 1st, add 14 days) so you will get 15th of that month
Mydate = "2017-10-15"
Mydate.to_date.strftime("%B %d, %Y")
=> "October 15, 2017"
Using #GaganGami solution, you can create a Date class method middle_of_current_month
class Date
def self.middle_of_current_month
(today.beginning_of_month + 14).strftime("%B %d, %Y")
end
end
Date.middle_of_current_month
#=> "October 15, 2017"

How to get all 'specific days' within a date range

How can I get let's say All the dates for Saturday and Sunday from X year to Y year and store them as array? Pseudo code would be
(year_today..next_year).get_all_dates_for_saturday_and_sunday
Or perhaps there are gems that cater to this already?
Try this:
(Date.today..Date.today.next_year).select { |date|
date.sunday? or date.saturday?
}
#=> [Sat, 03 Sep 2016,Sun, 04 Sep 2016,Sat, 10 Sep 2016,Sun, 11 Sep 2016...
(Date.today..(Date.today + 1.year)).select do |date|
date.saturday? || date.sunday?
end # => [Sat, 03 Sep 2016, Sun, 04 Sep 2016, Sat, 10 Sep 2016, ...
This will then give you an array of 104 elements containing every date which is a saturday or a sunday between today and today in a year.
The following approach emphasizes efficiency over brevity, by avoiding the need to determine if every day in a range is a given day (or one of two given days) of the week.
Code
require 'date'
def dates_by_years_and_wday(start_year, end_year, wday)
(first_date_by_year_and_wday(start_year, wday)...
first_date_by_year_and_wday(end_year+1, wday)).step(7).to_a
end
def first_date_by_year_and_wday(year, wday)
d = Date.new(year)
d + (wday >= d.wday ? wday - d.wday : 7 + wday - d.wday)
end
Notice that the range is defined with three dots, meaning the first date in end_year is excluded.
Example
SATURDAY = 6
SUNDAY = 0
start_year, end_year = 2015, 2017
dates_by_years_and_wday(start_year, end_year, SATURDAY)
#=> [#<Date: 2015-01-03 ((2457026j,0s,0n),+0s,2299161j)>,
# #<Date: 2015-01-10 ((2457033j,0s,0n),+0s,2299161j)>,
# ...
# #<Date: 2017-12-30 ((2458118j,0s,0n),+0s,2299161j)>]
dates_by_years_and_wday(start_year, end_year, SATURDAY).size
#=> 157
dates_by_years_and_wday(start_year, end_year, SUNDAY)
#=> [#<Date: 2015-01-04 ((2457027j,0s,0n),+0s,2299161j)>,
# #<Date: 2015-01-11 ((2457034j,0s,0n),+0s,2299161j)>,
# ...
# #<Date: 2017-12-31 ((2458119j,0s,0n),+0s,2299161j)>]
dates_by_years_and_wday(start_year, end_year, SUNDAY).size
#=> 157

How can i get the current weekday beginning in ruby?

For example today is 28/07/2011
How do i get the weeks first day the monday which is 25/07/2011 in ruby
>> Date.today.beginning_of_week.strftime('%d/%m/%Y')
#=> 25/07/2011
See the Time and Date classes under Rails for more info, and strftime for information on the formatting options.
Without Rails/ActiveSupport:
phrogz$ irb
> require 'date'
> now = Date.today
#=> #<Date: 2011-07-28 (4911541/2,0,2299161)>
> sunday = now - now.wday
#=> #<Date: 2011-07-24 (4911533/2,0,2299161)>
> monday = now - (now.wday - 1) % 7
#=> #<Date: 2011-07-25 (4911535/2,0,2299161)>
> monday.iso8601
#=> "2011-07-25"
> monday.strftime('%d/%m/%Y')
#=> "25/07/2011"
For more, see the Date class in the Standard Library.
Wrapped up as a method:
require 'date'
# For weekdays to start on Monday use 1 for the offset; for Tuesday use 2, etc.
def week_start( date, offset_from_sunday=0 )
date - (date.wday - offset_from_sunday)%7
end
sun = Date.parse '2011-07-24'
week_start(sun,0).strftime('%a, %b-%d') #=> "Sun, Jul-24"
week_start(sun,1).strftime('%a, %b-%d') #=> "Mon, Jul-18"

Convert Ruby Date to Integer

How can I convert a Ruby Date to an integer?
t = Time.now
# => 2010-12-20 11:20:31 -0700
# Seconds since epoch
t.to_i
#=> 1292869231
require 'date'
d = Date.today
#=> #<Date: 2010-12-20 (4911101/2,0,2299161)>
epoch = Date.new(1970,1,1)
#=> #<Date: 1970-01-01 (4881175/2,0,2299161)>
d - epoch
#=> (14963/1)
# Days since epoch
(d - epoch).to_i
#=> 14963
# Seconds since epoch
d.to_time.to_i
#=> 1292828400
Date cannot directly become an integer. Ex:
$ Date.today
=> #<Date: 2017-12-29 ((2458117j,0s,0n),+0s,2299161j)>
$ Date.today.to_i
=> NoMethodError: undefined method 'to_i' for #<Date: 2017-12-29 ((2458117j,0s,0n),+0s,2299161j)>
Your options are either to turn the Date into a time then an Int which will give you the seconds since epoch:
$ Date.today.to_time.to_i
=> 1514523600
Or come up with some other number you want like days since epoch:
$ Date.today.to_time.to_i / (60 * 60 * 24) ### Number of seconds in a day
=> 17529 ### Number of days since epoch
Time.now.to_i
returns seconds since epoch format
Solution for Ruby 1.8 when you have an arbitrary DateTime object:
1.8.7-p374 :001 > require 'date'
=> true
1.8.7-p374 :002 > DateTime.new(2012, 1, 15).strftime('%s')
=> "1326585600"
I had to do it recently and took some time to figure it out but that is how I came across a solution and it may give you some ideas:
require 'date'
today = Date.today
year = today.year
month = today.mon
day = day.mday
year = year.to_s
month = month.to_s
day = day.to_s
if month.length <2
month = "0" + month
end
if day.length <2
day = "0" + day
end
today = year + month + day
today = today.to_i
puts today
At the date of this post, It will put 20191205.
In case the month or day is less than 2 digits it will add a 0 on the left.
I did like this because I had to compare the current date whit some data that came from a DB in this format and as an integer. I hope it helps you.

Resources