Get batches of 10, starting at and including a certain number - ruby-on-rails

I have an action in a controller where I want to generate a batch of 10 consecutive days for each request.
I keep track of where we are in the batch cycle by passing a variable page, for each batch.
The cycle of batches should start with today.
How can I express this in the simplest way?
Right now I have the following mess, which doesn't even work (each batch, except for the first one, start one day too early):
#page_number = (params[:page_number] || 0).to_i + 1
today = Date.today
batch_amount = 10
first_day_of_current_batch = today + (batch_amount * (#page_number - 1))
days = first_day_of_current_batch..(first_day_of_current_batch + batch_amount)

As I didn't understand completely all your requirements, so I will make a few assumptions:
Pages start with 1, nil is assumed as 1
Today is 27th of October, assuming that batch size is 10 and page number 1, the batch should be Sun, 27 Oct 2013..Wed, 05 Nov 2013
Today is 27th of October, assuming that batch size is 10 and page number 2, the batch should be Sun, 06 Oct 2013..Wed, 15 Nov 2013
As You have quite a complex logic there, it will be wise to extract it to separate object.
It's considered a common and good practice, as it decouples Your code and tests.
Custom class:
# app/services/batch_of_days.rb
class Services::BatchOfDays
class << self
def create(page_nr = 1, batch_size = 10, start_from = Date.today)
new(page_nr.to_i, batch_size, start_from).create
end
end
def initialize(page_nr, batch_size, start_from)
#page_nr = page_nr
#batch_size = batch_size
#start_from = start_from
end
def create
first_day..last_day
end
private
def first_day
#first_day ||= #start_from + ( #page_nr - 1 ) * #batch_size
end
def last_day
first_day + #batch_size - 1
end
end
Usage:
irb(main):262:0> BatchOfDays.create
# => Sun, 27 Oct 2013..Tue, 05 Nov 2013
irb(main):262:0> BatchOfDays.create(1)
# => Sun, 27 Oct 2013..Tue, 05 Nov 2013
irb(main):263:0> BatchOfDays.create(2)
# => Wed, 06 Nov 2013..Fri, 15 Nov 2013
irb(main):264:0> BatchOfDays.create(3)
# => Sat, 16 Nov 2013..Mon, 25 Nov 2013

I recommend numbering your pages starting at 0 (i.e. page #0, the first one, begins at Date.today).
You could then use the following:
#page_number = (params[:page_number] || 0).to_i
batch_amount = 10
first = (#page_number * batch_amount).days.from_now.to_date
last = ((#page_number + 1) * batch_amount - 1).days.from_now.to_date
days = (first..last).to_a
the to_date let you specify the range as a range of consecutive days, and the to_a converts the range into an Array of Date objects, one for each day.

Related

Ruby(/Rails) dates - bi-weekly and quarterly DateTime ranges

I am trying to do reporting for a specific period, those periods supported being:
"today": start = DateTime.now.beginning_of_day; end = start.end_of_day
"this week": start = DateTime.now.beginning_of_week; end = start.end_of_week
"this bi-week" (first half or second half of the month, relative to the current date of DateTime.now)
it is very important to be sure that the month is evenly divided, and cweek is not used; the start of the first bi-week should be the first of the month, and the end of the second bi-week should be the last of the month. The dividing date between the two should be the total number of days in the month divided by 2, rounded up (i.e. if there are 31 days, the second bi-week would start on the 15th, and not the 14th of the month).
"this month": start = DateTime.now.beginning_of_month; end = start.end_of_month
"last month": start = (DateTime.now - 1.month).beginning_of_month; end = start.end_of_month
"this quarter": one of each of the four 3-month periods in the year, ie quarters. Ex: from 1/1/1999 to 3/31/1999 or from 10/1/2000 to 12/31/2000.
"this year" = start = DateTime.now.beginning_of_year --> end = start.end_of_year
I'm having trouble calculating the bold date ranges based on the current date (assume DateTime.now).
How can I calculate the bi-weekly and quarterly period relative to the current date, DateTime.now?
Expected output
For the date May 7th, 2018 (5/7/2018):
"this bi-week" should be the period of 5/1/2018 to 5/15/2018
"this quarter" should be the period of 4/1/2018 to 6/30/2018
For the date February 29th, 2020 (2/29/2020), a leap year:
"this bi-week" should be the period of 2/15/2018 to 2/29/2018
"this quarter" should be the period of 1/1/2018 to 3/31/2018
What about this:
This bi-week:
def bi_week_limits_for(date)
days_in_month = Time.days_in_month(date.month, date.year)
# this will round down. 30/31-day months will output 15. 28 / 29-day will output 14. Adjust as per your requirements
middle_day = days_in_month / 2
if date.day <= middle_day
[date.beginning_of_month, date.change(day: middle_day)]
else
[date.change(day: middle_day + 1), date.end_of_month]
end
end
In my console:
pry(main)> bi_week_limits_for Date.parse('29/2/2020')
=> [Sat, 15 Feb 2020, Sat, 29 Feb 2020]
pry(main)> bi_week_limits_for Date.parse('7/5/2018')
=> [Tue, 01 May 2018, Tue, 15 May 2018]
This quarter:
def bi_week_limits_for(date)
[date.beginning_of_quarter, date.end_of_quarter]
end
In my console
pry(main)> date = Date.parse('7/5/2018')
=> Mon, 07 May 2018
pry(main)> quarter_limits_for date
=> [Sun, 01 Apr 2018, Sat, 30 Jun 2018]
pry(main)> date = Date.parse '29/2/2020'
=> Sat, 29 Feb 2020
pry(main)> quarter_limits_for date
=> [Wed, 01 Jan 2020, Tue, 31 Mar 2020]
Reference: https://apidock.com/rails/DateAndTime/Calculations/beginning_of_quarter

Select object based on date range

Tech specs: ruby 2.1.5p273, Rails 4.2.3.
I have an array of Days that I want to loop through to pick the right Exits (model) that fall within a date range.
#exits has :start_date and :end_date
#days is an array of dates like:
=> [Sun, 06 Sep 2015, Sat, 12 Sep 2015, Tue, 15 Sep 2015, Fri, 18 Sep 2015, Sat, 19 Sep 2015, Sun, 20 Sep 2015, Wed, 23 Sep 2015]
I thought something like this would work:
#days.each do |day|
#exits.where(:start_date..:end_date).include?(day)
end
but I get an error:
TypeError: Cannot visit Range
What is the best way to query an object that has a date range (between two fields) by comparing it against a single date? Thanks!
You can use the following:
#days.each do |day|
exits = Exit.where('? BETWEEN start_date AND end_date', day)
# etc.
end
If you don't want to loop over them then you can do:
Event.where("start_date IN (:days) AND end_date IN (:days)", { days: #days })
or
Event.where(start_date: #days, end_date: #days)
Exit.where(day: #exit.start_date..#exits.end_date)
or
Exit.where('day >= ? AND day <= ?', #exit.start_date, #exits.end_date)
Doing SQL queries in a loop is probably a bad idea, it could be refactored to be be one call most likely. And this should happen in the controller not in the view.

Finding days between 2 days in Ruby on Rails

I am facing some problem in finding the days between 2 dates.
The scenario is as follow :
time = Time.new
enddate_timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
startdate = #logInfo.updated_at #here updated_at is the column in the db .
What is the best way to find the days ?
Post.where(["date(created_at) BETWEEN ? AND ?", Date.yesterday, Date.tomorrow]
More details: http://api.rubyonrails.org/classes/ActiveRecord/QueryMethods.html#method-i-where
There are several possible solutions. A possibility is to create a Range with the dates, then convert the range into an array
# set the boundaries
today = Time.current
past = 5.days.ago
Note that both boundaries are time instances. We should cast them into dates. I used time(s) because your column is a time.
range = past.to_date..today.to_date
# => Sun, 29 Dec 2013..Fri, 03 Jan 2014
Use to_a to expand the range getting all the days
range.to_a
# => [Sun, 29 Dec 2013, Mon, 30 Dec 2013, Tue, 31 Dec 2013, Wed, 01 Jan 2014, Thu, 02 Jan 2014, Fri, 03 Jan 2014]
range.count
# => 6
You can also enumerate them
range.each { |day| puts day.day }
29
30
31
1
2
3
now = Time.now
future = Time.now + 100 days
while now < future
now = now + 1.day
puts now
end
This will give you the dates, not the days count.
(startdate.beginning_of_day..enddate_timestamp.to_time.beginning_of_day).step(1.day) do |day|
puts day
end
P.S: Performance wise it's not good.

Ruby expression evaluation: whitespace matters?

Imagine it's Jan 19. This will not be hard if you look at this question today.
Date.today
=> Thu, 19 Jan 2012 # as expected
Date.today + 1
=> Fri, 20 Jan 2012 # as expected
Date.today+1
=> Fri, 20 Jan 2012 # as expected
Date.today +1
=> Thu, 19 Jan 2012 # ?!
What am I missing here?
The difference is that:
Date.today + 1
is an addition of two numerical values and
Date.today +1
is a call to the method today with the parameter sg(day of calendar reform) with value +1
The best way to examine this is to monkey patch the original method with debug output included. See this script as example:
require 'date'
class Date
def self.today(sg=ITALY)
puts "ITALY default("+sg.to_s+")" if sg==ITALY
puts sg unless sg==ITALY
jd = civil_to_jd(*(Time.now.to_a[3..5].reverse << sg))
new0(jd_to_ajd(jd, 0, 0), 0, sg)
end
end
puts "- Addition:"
Date.today + 1
puts "- Parameter:"
Date.today +1
This will print the following console output:
- Addition:
ITALY default(2299161)
- Parameter:
1
Yes, whitespace does matter in Ruby, contrary to popular belief. For example, foo bar is not the same as foobar.
In this particular case,
Date.today + 1
is the same as
Date.today().+(1)
Whereas
Date.today +1
is the same as
Date.today(+1)
which is the same as
Date.today(1.+#())

Is it possible to create a list of months between two dates in Rails

I am trying to create a page to display a list of links for each month, grouped into years. The months need to be between two dates, Today, and The date of the first entry.
I am at a brick wall, I have no idea how to create this.
Any help would be massively appriciated
Regards
Adam
Just put what you want inside a range loop and use the Date::MONTHNAMES array like so
(date.year..laterdate.year).each do |y|
mo_start = (date.year == y) ? date.month : 1
mo_end = (laterdate.year == y) ? laterdate.month : 12
(mo_start..mo_end).each do |m|
puts Date::MONTHNAMES[m]
end
end
The following code will add a months_between instance method to the Date class
#!/usr/bin/ruby
require 'date'
class Date
def self.months_between(d1, d2)
months = []
start_date = Date.civil(d1.year, d1.month, 1)
end_date = Date.civil(d2.year, d2.month, 1)
raise ArgumentError unless d1 <= d2
while (start_date < end_date)
months << start_date
start_date = start_date >>1
end
months << end_date
end
end
This is VERY lightly tested, however it returns an Array of dates each date being the 1st day in each affected month.
I don't know if I've completely understood your problem, but some of the following might be useful. I've taken advantage of the extensions to Date provided in ActiveSupport:
d1 = Date.parse("20070617") # => Sun, 17 Jun 2007
d2 = Date.parse("20090529") #=> Fri, 29 May 2009
eom = d1.end_of_month #=> Sat, 30 Jun 2007
mth_ends = [eom] #=> [Sat, 30 Jun 2007]
while eom < d2
eom = eom.advance(:days => 1).end_of_month
mth_ends << eom
end
yrs = mth_ends.group_by{|me| me.year}
The final line uses another handy extension: Array#group_by, which does pretty much exactly what it promises.
d1.year.upto(d2.year) do |yr|
puts "#{yrs[yr].min}, #{yrs[yr].max}"
end
2007-06-30, 2007-12-31
2008-01-31, 2008-12-31
2009-01-31, 2009-05-31
I don't know if the start/end points are as desired, but you should be able to figure out what else you might need.
HTH
Use the date_helper gem which adds the months_between method to the Date class similar to Steve's answer.
xmas = Date.parse("2013-12-25")
hksar_establishment_day = Date.parse("2014-07-01")
Date.months_between(xmas,hksar_establishment_day)
=> [Sun, 01 Dec 2013, Wed, 01 Jan 2014, Sat, 01 Feb 2014, Sat, 01 Mar 2014, Tue, 01 Apr 2014, Thu, 01 May 2014, Sun, 01 Jun 2014, Tue, 01 Jul 2014]

Resources