Ruby: Iterate over days in previous month - ruby

I need to be able to loop through all days of the previous month and do something with the parsed day. This is what I have so far:
def dateTest
d = Date.parse(Time.now.to_s)
from_date = Date.new(d.year, d.month - 1, 1)
to_date = Date.new(d.year, d.month, -1)
from_date..to_date.each do |day|
#Do stuff with day
end
end
My issue is I can't seem to get to_date to equal the last day of the previous month. I've read many similar questions but most of them seem more geared towards Rails, which won't help me in this case.

require 'date'
d = Date.today << 1 # << 1 is one month earlier
(Date.new(d.year, d.month, 1)..Date.new(d.year, d.month,-1)).each{|date| p date}

You also can use ... exclude the end value.
For example:
(1..5).to_a
=> [1, 2, 3, 4, 5]
(1...5).to_a
=> [1, 2, 3, 4]
so you can write like this:
if d.month != 1
from_date = Date.new(d.year, d.month - 1, 1)
else
from_date = Date.new(d.year-1, -1)
end
to_date = Date.new(d.year, d.month)
(from_date...to_date).map { |day| puts day}
output:
2015-04-01
2015-04-02
2015-04-03
......
2015-04-29
2015-04-30

I would do it like this:
require 'date'
d = Date.today << 1
start = Date.new(d.year, d.month)
(start...(start >> 1)).each { |d| p d }
#-> #<Date: 2015-04-01 ((2457114j,0s,0n),+0s,2299161j)>
# #<Date: 2015-04-02 ((2457115j,0s,0n),+0s,2299161j)>
# ...
# #<Date: 2015-04-30 ((2457143j,0s,0n),+0s,2299161j)>

The prev_day function can also be used:
require 'date'
d = Date.today
from_date = Date.new(d.year, d.month - 1, 1)
to_date = Date.new(d.year, d.month, 1).prev_day
from_date..to_date.each do |day|
#Do stuff with day
end

Related

How to get start and end dates from a time/date object created using a month or year in ruby?

I have ISO 8601 compliant date strings like "2016" or "2016-09" representing year or months. How can I get start end dates from this.
for example:
2016 -> ["2016-01-01", "2016-12-31"]
2016-09 -> ["2016-09-01", "2016-09-30"]
Thank you
Try this
require 'date'
def iso8601_range(str)
parts = str.scan(/\d+/).map(&:to_i)
date = Date.new(*parts)
case parts.size
when 1
date .. date.next_year - 1
when 2
date .. date.next_month - 1
else
date .. date
end
end
iso8601_range('2016') # => 2016-01-01..2016-12-31
iso8601_range('2016-09') # => 2016-09-01..2016-09-30
iso8601_range('2016-09-20') # => 2016-09-20..2016-09-20
If you are cool with using send you can replace the case statement with
date .. date.send([:next_year,:next_month,:next_day][parts.size - 1]) - 1
require 'date'
def create_start_end(string)
year, month = string.split('-').map(&:to_i)
if month && !month.zero?
[Date.new(year, month, 1).to_s, Date.new(year, month, -1).to_s]
else
[Date.new(year, 1, 1).to_s, Date.new(year, 12, -1).to_s]
end
end
create_start_end('2016')
#=> ["2016-01-01", "2016-12-31"]
create_start_end('2016-01')
#=> ["2016-01-01", "2016-01-31"]
create_start_end('2016-09')
#=> ["2016-09-01", "2016-09-30"]
One more solution in according to #AndreyDeineko :)
require 'date'
def create_date date
date = date.split('-').map(&:to_i)
[Date.new(*date, 1, 1), Date.new(*date, -1, -1)].map(&:to_s)
end

Convert year and day of year to calender date in Ruby

Need to convert "15307" in to something like "2015-11-03"
I have the following code
def juliantoregular(date1)
temp = "20" + date1[0,2];
year = temp.to_i;
if date1[2,1] == "0"
temp1 = date1[3,2];
else
temp1 = date1[2,3];
end
juliannumber = temp1.to_i;
date1 = Date.ordinal(year,juliannumber)
return date1;
end
Input to the code is String "15307" is there a better way of doing this?
Date.strptime is perfect for this:
str = "15307"
puts Date.strptime(str, "%y%j")
# => 2015-11-03
In the format string "%y%j", %y indicates a two-digit year and %j indicates day of the year.
y, d = "15307".to_i.divmod 1_000 #⇒ [15, 307]
Date.parse("20#{y}-01-01") + (d - 1) #⇒ add days to Jan, 1st
#⇒ #<Date: 2015-11-03 ((2457330j,0s,0n),+0s,2299161j)>

Get an array including every 14th day from a given date

In ruby, how can I get every 14th day of the year, going backwards and forwards from a date.
So consider I'm billed for 2 weeks of recycling on today, 6-16-2015. How can I get an array of every recycling billing day this year based on that date.
Date has a step method:
require 'date'
d = Date.strptime("6-16-2015", '%m-%d-%Y') # strange date format
end_year = Date.new(d.year, -1, -1)
p d.step(end_year, 14).to_a
# =>[#<Date: 2015-06-16 ((2457190j,0s,0n),+0s,2299161j)>, #<Date: 2015-06-30 ((2457204j,0s,0n),+0s,2299161j)>, ...
# Going backward:
begin_year = Date.new(d.year, 1, 1)
p d.step(begin_year,-14).to_a
# =>[#<Date: 2015-06-16 ((2457190j,0s,0n),+0s,2299161j)>, #<Date: 2015-06-02 ((2457176j,0s,0n),+0s,2299161j)>,...
A more descriptive and easy to understand solution:
require 'date'
current_date = Date.parse "16-june-15"
start_date = Date.parse '1-jan-15'
end_date = Date.parse '31-dec-15'
interval = 14
result = current_date.step(start_date, -interval).to_a
result.sort!.pop
result += current_date.step(end_date, interval).to_a
You could do that as follows:
require 'date'
date_str = "6-16-2015"
d = Date.strptime(date_str, '%m-%d-%Y')
f = Date.new(d.year)
((f + (f-d).abs % 14)..Date.new(d.year,-1,-1)).step(14).to_a
#=> [#<Date: 2015-01-13 ((2457036j,0s,0n),+0s,2299161j)>,
# #<Date: 2015-01-27 ((2457050j,0s,0n),+0s,2299161j)>,
# ...
# #<Date: 2015-06-16 ((2457190j,0s,0n),+0s,2299161j)>,
# ...
# #<Date: 2015-12-29 ((2457386j,0s,0n),+0s,2299161j)>]
Based on the second sentence of your question, I assume you simply want an array of all dates in the given year that are two-weeks apart and include the given day.
I attempted a mathy modulus biased approach which turned out unexpectedly confusing.
require 'date'
a_recycle_date_string = "6-17-2015"
interval = 14
a_recycle_date = Date.strptime(a_recycle_date_string, '%m-%d-%Y')
current_year = a_recycle_date.year
end_of_year = Date.new(current_year, -1, -1)
# Find out which index of the first interval's days is the first recycle day
# of the year the (1 indexed)
remainder = (a_recycle_date.yday) % interval
# => 0
# make sure remainder 0 is treated as interval-1 so it doesn't louse
# the equation up
n_days_from_first_recycling_yday_of_year = (remainder - 1) % interval
first_recycle_date_this_year = Date.new(current_year,
1,
1 + n_days_from_first_recycling_yday_of_year)
first_recycle_date_this_year.step(end_of_year, interval).to_a

get no of months, years between two dates in ruby

I'm trying to write web ui tests to choose date from jquery calender based on user input (watir-webdriver), how can find no of months years between two give dates, i searched few solution couldn't get what i want
date1 = Date::strptime("2013-09-19", "%Y-%m-%d")
date2 = Date::strptime("2013-09-25", "%Y-%m-%d")
date3 = Date::strptime("2013-10-01", "%Y-%m-%d")
date4 = Date::strptime("2014-01-20", "%Y-%m-%d")
date5 = Date::strptime("2014-12-01", "%Y-%m-%d")
desired output
diff between date1,date2 -- 0 yrs, 0 month(s)
diff between date1,date3 -- 0 yrs, 1 month(s)
diff between date1,date4 -- 0 yrs, 4 month(s)
diff between date1,date5 -- 1 yrs, 3 month(s)
i checked time_diff gem also
I'd calculate the difference in months (be aware that we ignore day differences here) and then calculate the number of years by dividing that number by 12:
##
# Calculates the difference in years and month between two dates
# Returns an array [year, month]
def date_diff(date1,date2)
month = (date2.year * 12 + date2.month) - (date1.year * 12 + date1.month)
month.divmod(12)
end
date_diff date1, date4 #=> [0, 4]
date_diff date1, date2 #=> [0, 0]
date_diff date1, date3 #=> [0, 1]
date_diff date1, date5 #=> [1, 3]
Here is my attempt. Works in multiple units:
def date_diff(date1, date2, units=:months)
seconds_between = (date2.to_i - date1.to_i).abs
days_between = seconds_between / 60 / 60 / 24
case units
when :days
days_between.floor
when :months
(days_between / 30).floor
when :years
(days_between / 365).floor
else
seconds_between.floor
end
end
Usage:
date_diff(Time.now, 10.years.ago - 77.days, :years) #=> 10
date_diff(10.years.ago - 77.days, Time.now, :months) #=> 124
date_diff(10.years.ago - 77.days, Time.now, :days) #=> 3730
I took this from the TimeDifference gem but it works so nicely that I thought I'd share. If you're using Rails, make a class called TimeDifference with the following code:
class TimeDifference
private_class_method :new
def self.between(start_time:, end_time:)
new(start_time, end_time)
end
def in_seconds
#time_diff
end
def in_minutes
in_component(:minutes)
end
def in_hours
in_component(:hours)
end
def in_days
in_component(:days)
end
def in_weeks
in_component(:weeks)
end
def in_months
(#time_diff / (1.day * 30.42)).round(2)
end
def in_years
in_component(:years)
end
private
def initialize(start_time, end_time)
start_time = time_in_seconds(start_time)
end_time = time_in_seconds(end_time)
#time_diff = (end_time - start_time).abs
end
def time_in_seconds(time)
time.to_time.to_f
end
def in_component(component)
(#time_diff / 1.send(component)).round(2)
end
end
And then simply call:
start_time = DateTime.parse('2 June, 1999 9:00:00')
end_time = DateTime.parse('19 April, 2021 9:00:00')
time_difference = TimeDifference.between(
start_time: start_time,
end_time: end_time
)
time_difference.in_days
=> 7992.0
time_difference.in_months
=> 262.72
time_difference.in_years
=> 21.88
Note: if you're not using Rails you might have to require ActiveSupport.

How to create an infinite enumerable of Times?

I want to be able to have an object extend Enumerable in Ruby to be an infinite list of Mondays (for example).
So it would yield: March 29, April 5, April 12...... etc
How can I implement this in Ruby?
In 1.9 (and probably previous versions using backports), you can easily create enumerator:
require 'date'
def ndays_from(from, step=7)
Enumerator.new {|y|
loop {
y.yield from
from += step
}
}
end
e = ndays_from(Date.today)
p e.take(5)
#=> [#<Date: 2010-03-25 (4910561/2,0,2299161)>, #<Date: 2010-04-01 (4910575/2,0,2299161)>, #<Date: 2010-04-08 (4910589/2,0,2299161)>, #<Date: 2010-04-15 (4910603/2,0,2299161)>, #<Date: 2010-04-22 (4910617/2,0,2299161)>]
Store a Date as instance variable, initialized to a Monday. You would implement an each method which increments the stored date by 7 days using date += 7.
You could do something by extending Date...
#!/usr/bin/ruby
require 'date'
class Date
def current_monday
self - self.wday + 1
end
def next_monday
self.current_monday + 7
end
end
todays_date = Date.today
current_monday = todays_date.current_monday
3.times do |i|
puts current_monday.to_s
current_monday = current_monday.next_monday
end
2010-03-22
2010-03-29
2010-04-05
2010-04-12
...with the usual warnings about extending base classes of course.
You can extend Date class with nw method mondays
class Date
def self.mondays(start_date=Date.today, count=10)
monday = start_date.wday > 1 ? start_date - start_date.wday + 8 : start_date - start_date.wday + 1
mondays = []
count.times { |i| mondays << monday + i*7}
mondays
end
end
Date.mondays will return by default Array of mondays with 10 elements from closest monday to Date.today. You can pass parameters:
Date.mondays(start_date:Date, count:Integer)
start_date - start point to find closest monday
count - number of mondays you are looking
IE:
Date.mondays(Date.parse('11.3.2002'))
Date.mondays(Date.parse('11.3.2002'), 30)
module LazyEnumerable
extend Enumerable
def select(&block)
lazily_enumerate { |enum, value| enum.yield(value) if
block.call(value) }
end
def map(&block)
lazily_enumerate {|enum, value| enum.yield(block.call(value))}
end
def collect(&block)
map(&block)
end
private
def lazily_enumerate(&block)
Enumerator.new do |enum|
self.each do |value|
block.call(enum, value)
end
end
end
end
...........
class LazyInfiniteDays
include LazyEnumerable
attr_reader :day
def self.day_of_week
dow = { :sundays => 0, :mondays => 1, :tuesdays => 2, :wednesdays =>
3, :thursdays => 4, :fridays => 5, :saturdays => 6, :sundays => 7 }
dow.default = -10
dow
end
DAY_OF_WEEK = day_of_week()
def advance_to_midnight_of_next_specified_day(day_sym)
year = DateTime.now.year
month = DateTime.now.month
day_of_month = DateTime.now.day
output_day = DateTime.civil(year, month, day_of_month)
output_day += 1 until output_day.wday == DAY_OF_WEEK[day_sym]
output_day
end
def initialize(day_sym)
#day = advance_to_midnight_of_next_specified_day(day_sym)
end
def each
day = #day.dup
loop {
yield day
day += 7
}
end
def ==(other)
return false unless other.kind_of? LazyInfiniteDays
#day.wday == other.day.wday
end
end
Ruby 2.7 introduced Enumerator#produce for creating an infinite enumerator from any block, which results in a very elegant, very functional way of implementing the original problem:
irb(main):001:0> require 'date'
=> true
irb(main):002:0> puts Date.today
2022-09-23
=> nil
irb(main):003:0> Date.today.friday?
=> true
irb(main):004:0> future_mondays = Enumerator.produce { |date|
date = (date || Date.today).succ
date = date.succ until date.monday?
date
}
=> #<Enumerator: #<Enumerator::Producer:0x00007fa4300b3070>:each>
irb(main):005:0> puts future_mondays.first(5)
2022-09-26
2022-10-03
2022-10-10
2022-10-17
2022-10-24
=> nil
irb(main):006:0> _

Resources