I can't get the 15th weekday in ruby - ruby

I cant get the next 15th day but not the working day.
DateTime.now.next_day(+15).strftime('%d %^B %Y')
how can i get the next 15th weekday?

You're just adding 15 days to the current date. What you want is to adjust the date:
date = DateTime.now
if (date.mday > 15)
date = date.next_month
end
date = date.next_day(15 - date.mday)
Where that adjusts to be the 15th of the next month if it's already past the 15th of the current month.
Now this can be extended to be an Enumerator:
def each_mday(mday, from: nil)
from ||= DateTime.now
Enumerator.new do |y|
loop do
if (from.mday > mday)
from = from.next_month
end
from = from.next_day(mday - from.mday)
y << from
from += 1
end
end
end
Which makes it possible to find the first day matching particular criteria, like being a weekday:
each_mday(15, from: Date.parse('2019-06-14')).find { |d| (1..5).include?(d.wday) }
Where that returns July 15th, as June 15th is a weekend.
The from argument is optional but useful for testing cases like this to ensure it's working correctly.

15.times.reduce(Date.civil 2019, 03, 24) do |acc, _|
begin
acc += 1
end while [0, 6].include? acc.wday
acc
end
#⇒ #<Date: 2019-04-12 ((2458586j,0s,0n),+0s,2299161j)>

So you want to add 15 business days from the current date. You can go with iGian or Aleksei vanilla ruby answers or use business_time gem:
15.business_days.from_now

If I understood correctly, you want to get next Monday if you hit Saturday or Sunday. Since wday gives you 0 for Sun and 6 for Sat, you can use it to as a conditional to add days towards Monday.
def date_add_next_week_day(days)
date = (Date.today + days)
date += 1 if date.wday == 6
date += 1 if date.wday == 0
date
end
date_add_next_week_day(15).strftime('%d %^B %Y')

If I get the point you need to find the 15th day after a specified date, skipping weekends.
One possible option is to define the skipping_weekend hash like this, considering Date.html#wday:
skip_weekend = { 6 => 2, 0 => 1}
skip_weekend.default = 0
Then:
next15 = DateTime.now.next_day(15)
next15_working = next15.next_day(skip_weekend[next15.wday]).strftime('%d %B %Y')
Now if next15 falls on a working day, next15_working is the same day (hash defaults to 0), otherwise it skips 2 days if Saturday (6th week day, hash maps to 2) or 1 day if Sunday (0th week day, hash maps to 1)

I assume that, given a starting date, ds (a Date object), and a positive integer n, the problem is determine a later date, dt, such that between ds+1 and dt, inclusive, there n weekdays.
require 'date'
def given_date_plus_week_days(dt, week_days)
wday = dt.wday
weeks, days = (week_days + {0=>4, 6=>4}.fetch(wday, wday-1)).divmod(5)
dt - (wday.zero? ? 6 : (wday - 1)) + 7*weeks + days
end
The variable wday is assigned to the day of week for the start date, dt. The start date is moved back to the previous Monday, unless it falls on a Monday, in which case it is not changed. That is reflected in the expression
wday.zero? ? 6 : (wday - 1)
which is subtracted from dt. The number of week days is correspondingly adjusted to
week_days + { 0=>4, 6=>4 }.fetch(wday, wday-1)
The remaining calculations are straightforward.
def display(start_str, week_days)
start = Date.parse(start_str)
7.times.map { |i| start + i }.each do |ds|
de = given_date_plus_week_days(ds, week_days)
puts "#{ds.strftime("%a, %b %d, %Y")} + #{week_days} -> #{de.strftime("%a, %b %d, %Y")}"
end
end
display("April 8", 15)
Mon, Apr 08, 2019 + 15 -> Mon, Apr 29, 2019
Tue, Apr 09, 2019 + 15 -> Tue, Apr 30, 2019
Wed, Apr 10, 2019 + 15 -> Wed, May 01, 2019
Thu, Apr 11, 2019 + 15 -> Thu, May 02, 2019
Fri, Apr 12, 2019 + 15 -> Fri, May 03, 2019
Sat, Apr 13, 2019 + 15 -> Fri, May 03, 2019
Sun, Apr 14, 2019 + 15 -> Fri, May 03, 2019
display("April 8", 17)
Mon, Apr 08, 2019 + 17 -> Wed, May 01, 2019
Tue, Apr 09, 2019 + 17 -> Thu, May 02, 2019
Wed, Apr 10, 2019 + 17 -> Fri, May 03, 2019
Thu, Apr 11, 2019 + 17 -> Mon, May 06, 2019
Fri, Apr 12, 2019 + 17 -> Tue, May 07, 2019
Sat, Apr 13, 2019 + 17 -> Tue, May 07, 2019
Sun, Apr 14, 2019 + 17 -> Tue, May 07, 2019

Related

I am trying to subtract 3 hours from a method that I have created but I am getting an error

I have successfully created a DST method called dst_datechange that takes a date, is parsed out using Time.parse. It looks like this:
require 'time'
def dst_datechange(date)
date = Time.parse(date.to_s) # if date.class.eql?(String)
case
when (date > Time.parse('March 11, 2018 2:00am')) && (date <
Time.parse('November 4, 2018 2:00am'))
date = Time.parse('November 4, 2018 2:00am')
puts "the date rounded to november 4, 2018"
when (date > Time.parse('November 4 2018, 2:00am')) && (date <
Time.parse('March 10, 2:00am'))
date = Time.parse('March 10, 2019 2:00am')
puts "the date rounded to march 10 2019"
when (date > Time.parse('March 10, 2019 2:00am')) && (date <
Time.parse('November 3, 2019 2:00am'))
date = Time.parse('November 3, 2019 2:00am')
when (date > Time.parse('November 3, 2019 2:00am')) && (date <
Time.parse('March 8, 2020 2:00am'))
date = Time.parse('March 8, 2020 2:00am')
when (date > Time.parse('March 8, 2020 2:00am')) && (date <
Time.parse('November 1, 2020 2:00am'))
date = Time.parse ('November 1, 2020 2:00am')
else
raise "The date #{date} does not match any dst date parameter"
end
date
puts "the new DST date is #{date}"
end
and my "puts" displays this...
the date rounded to: november 4, 2018
the new DST date is now: 2018-11-04 02:00:00 -0600
Now that I am receiving the correct date, I have a step that takes that dst_datechange and performs a subtraction, however, I am getting an error that says:
TypeError: no implicit conversion of Integer into Array
I am not sure what I am doing wrong but I know its most likely a formatting issue where I am trying to subtract a date time object with just a time object. here is my step below where the stacktrace is pointing the failure at:
date = (dst_datechange(Time.now) - (60*60*3))
puts "the date is now adjusted 3 hours back from 2:00am: #{date} "
end
I am unsure how to format that (60*60*3) to subtract 3 hours from that new November 2018-11-04 02:00:00 -0600 date and basically roll it back to 2018-11-03 23:00:00 -0600
Your method def dst_datechange(date) doesn't return the date you want it to, but instead puts your string.
When you call that in your second part, dst_datechange(Time.now), that doesn't return the date, but the return value for the last puts.
Try calling 'date' again after your final puts in your dst_datechange method:
when (date > Time.parse('March 8, 2020 2:00am')) && (date < Time.parse('November 1, 2020 2:00am'))
date = Time.parse ('November 1, 2020 2:00am')
else
raise "The date #{date} does not match any dst date parameter"
end
puts "the new DST date is #{date}"
date
end
You need to specify that you want to subtract hours. Try:
(Time.now - 3.hours).to_datetime
Or, if using ActiveSupport you can specify that you want three hours ago:
3.hours.ago
Since 2007, in most jurisdictions in the United States and Canada, Daylight Saving Time ("DST") has begun at 2:00am on the second Sunday of March, and ended at 2:00am on the first Sunday in November. In Mexico, DST begins at 2:00am on the first Sunday in April and ends at 2:00am on the last Sunday in October. The experience varies in other parts of the world.1
Given a Time object (possibly computed from a string representation of a time) your code returns a second Time object equal to the time at which the next time change occurs, from DST to STD or vice-versa (i.e., in those parts of the United States and Canada that have adopted DST). There is no need to hard-wire those dates. The calculation can be done as follows.
require 'time'
def dst_datechange(time)
year = time.year
if time.dst?
start_std(year)
else
year += 1 if time >= Time.new(year, 11)
start_dst(year)
end
end
def start_std(year)
date = Date.new(year, 11)
date += (7-date.wday) % 7
Time.new(date.year, date.month, date.day, 2)
end
def start_dst(year)
date = Date.new(year, 3, 8)
date += (7-date.wday) % 7
Time.new(date.year, date.month, date.day, 2)
end
['June 17, 2018 1:00pm', 'November 4, 2018 12:59am', 'November 4, 2018 1:00am',
'March 10, 2019 1:59am', 'March 10, 2019 2:00am'].
each do |s|
t = DateTime.strptime(s, '%B %e, %Y %l:%M%P').to_time
t = Time.new(t.year, t.month, t.day, t.hour, t.min, t.sec)
puts "#{s}: #{ dst_datechange(t) }"
end
June 17, 2018 1:00pm: 2018-11-04 02:00:00 -0800
November 4, 2018 12:59am: 2018-11-04 02:00:00 -0800
November 4, 2018 1:00am: 2019-03-10 03:00:00 -0700
March 10, 2019 1:59am: 2019-03-10 03:00:00 -0700
March 10, 2019 2:00am: 2019-11-03 02:00:00 -0800
Notice that the first, second and last examples return a time whose hour is 2. This is in fact one hour after the time changed. The other two examples return an hour of 3, which is an instant after the time changed. Note:
Time.new(2019, 3, 10, 2)
#=> 2019-03-10 03:00:00 -0700
Time.parse('March 10, 2019 2:00am')
#=> 2019-03-10 03:00:00 -0700
In future, DST date ranges could change (as they did in 2007) or DST could be eliminated altogether. In production code, therefore, it would be prudent to address these possibilities. Assuming the method Time#dst? would continue to exist, Time.new(year,6).dst? should tell us if we still have DST, and if so, one could search day-by-day to see when the time changes occur.
1. Source

Sort a hash with key as month date with many values for the same month

I create a hash with months as keys and timelaps as values
biens_delai[bien_date.mon] = b.delai
I get this result without month parsing
{Wed, 18 Jan 2017=>3.0, Sat, 25 Feb 2017=>2.0, Fri, 17 Mar 2017=>3.0, Sat, 25 Mar 2017=>5.0, Tue, 18 Apr 2017=>2.0, Thu, 29 Jun 2017=>2.0}
In March i have 2 values but when i parse by month i get the most high value and i want a addition of 2 values for March not the most high
{1=>3.0, 2=>2.0, 3=>5.0, 4=>2.0, 6=>2.0}
That's not the high value which you are getting, the values are getting overwritten, try the following
biens_delai[bien_date.mon] = biens_delai[bien_date.mon].to_f + b.delai

Setting future dates in Ruby [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 years ago.
Improve this question
Update for clarity: I have a starting date of Wed or Sat for one lottery ticket, and Tue or Fri for another. Given the starting date, I need an indefinite number of future drawing dates for the tickets, alternating between the given days for the respective tickets. The starting dates have already been validated to land on these days of the week for their games.
I want to check that the Powerball drawings are scheduled consecutively on the proper dates of Wed or Sat:
# Increment zero is the first subsequent drawing
if increment.zero?
if starting_date.saturday? or starting_date.friday?
starting_date + 4.days
else
starting_date + 3.days
end
elsif increment == 1
starting_date + 1.week
else
if starting_date.saturday? or starting_date.friday?
starting_date + (increment / 2.0).ceil.weeks + 4.days
else
starting_date + (increment / 2.0).ceil.weeks + 3.days
end
end
Some results I'm getting...
t.lines[0].draw_date #=> Wed, 09 Mar 2016
t.lines[1].draw_date #=> Sat, 12 Mar 2016
t.lines[2].draw_date #=> Wed, 16 Mar 2016
t.lines[3].draw_date #=> Sat, 19 Mar 2016
t.lines[4].draw_date #=> Sat, 26 Mar 2016
t.lines[5].draw_date #=> Sat, 26 Mar 2016
t.lines[6].draw_date #=> Sat, 02 Apr 2016
t.lines[7].draw_date #=> Sat, 02 Apr 2016
It seems like there should be a simple solution.
loop do
break d if d.wednesday? || d.saturday?
d += 1.day
end
I found the solution...
if increment.even?
if starting_date.saturday? or starting_date.friday?
starting_date + (increment / 2).weeks + 4.days
else
starting_date + (increment / 2).weeks + 3.days
end
elsif increment.odd?
if starting_date.saturday? or starting_date.friday?
starting_date + (increment / 2.0).ceil.weeks
else
starting_date + (increment / 2.0).ceil.weeks
end
end

How to get current day name from Date.today?

How does someone get the name of the day from Date.today?
The below should be equal too "Fri" or "Friday":
Date.today #=> Fri, 20 Nov 2015
Use the following:
Date.today.strftime("%A")

Rails 3.2.8 - How do I get the week number from Rails?

I would like to know how to get the current week number from Rails and how do I manipulate it:
Translate the week number into date.
Make an interval based on week number.
Thanks.
Use strftime:
%U - Week number of the year. The week starts with Sunday. (00..53)
%W - Week number of the year. The week starts with Monday. (00..53)
Time.now.strftime("%U").to_i # 43
# Or...
Date.today.strftime("%U").to_i # 43
If you want to add 43 weeks (or days,years,minutes, etc...) to a date, you can use 43.weeks, provided by ActiveSupport:
irb(main):001:0> 43.weeks
=> 301 days
irb(main):002:0> Date.today + 43.weeks
=> Thu, 22 Aug 2013
irb(main):003:0> Date.today + 10.days
=> Sun, 04 Nov 2012
irb(main):004:0> Date.today + 1.years # or 1.year
=> Fri, 25 Oct 2013
irb(main):005:0> Date.today + 5.months
=> Mon, 25 Mar 2013
You are going to want to stay away from strftime("%U") and "%W".
Instead, use Date.cweek.
The problem is, if you ever want to take a week number and convert it to a date, strftime won't give you a value that you can pass back to Date.commercial.
Date.commercial expects a range of values that are 1 based.
Date.strftime("%U|%W") returns a value that is 0 based. You would think you could just +1 it and it would be fine. The problem will hit you at the end of a year when there are 53 weeks. (Like what just happened...)
For example, let's look at the end of Dec 2015 and the results from your two options for getting a week number:
Date.parse("2015-12-31").strftime("%W") = 52
Date.parse("2015-12-31").cweek = 53
Now, let's look at converting that week number to a date...
Date.commercial(2015, 52, 1) = Mon, 21 Dec 2015
Date.commercial(2015, 53, 1) = Mon, 28 Dec 2015
If you blindly just +1 the value you pass to Date.commercial, you'll end up with an invalid date in other situations:
For example, December 2014:
Date.commercial(2014, 53, 1) = ArgumentError: invalid date
If you ever have to convert that week number back to a date, the only surefire way is to use Date.cweek.
date.commercial([cwyear=-4712[, cweek=1[, cwday=1[, start=Date::ITALY]]]]) → date
Creates a date object denoting the given week date.
The week and the day of week should be a negative
or a positive number (as a relative week/day from the end of year/week when negative).
They should not be zero.
For the interval
require 'date'
def week_dates( week_num )
year = Time.now.year
week_start = Date.commercial( year, week_num, 1 )
week_end = Date.commercial( year, week_num, 7 )
week_start.strftime( "%m/%d/%y" ) + ' - ' + week_end.strftime("%m/%d/%y" )
end
puts week_dates(22)
EG: Input (Week Number): 22
Output: 06/12/08 - 06/19/08
credit: Siep Korteling http://www.ruby-forum.com/topic/125140
Date#cweek seems to get the ISO-8601 week number (a Monday-based week) like %V in strftime (mentioned by #Robban in a comment).
For example, the Monday and the Sunday of the week I'm writing this:
[ Date.new(2015, 7, 13), Date.new(2015, 7, 19) ].map { |date|
date.strftime("U: %U - W: %W - V: %V - cweek: #{date.cweek}")
}
# => ["U: 28 - W: 28 - V: 29 - cweek: 29", "U: 29 - W: 28 - V: 29 - cweek: 29"]

Resources