Iterating over days in Ruby - ruby

I've tried the following:
def next_seven_days
today = Date.today
(today .. today + 7).each { |date| puts date }
end
But this just gives me the first and last date. I can't figure out how to get all the ones in between.
I was trying to follow the example here: http://www.whynotwiki.com/Ruby_/_Dates_and_times

I think you want something more like this:
def next_seven_days
today = Date.today
(today .. today + 7).inject { |init, date| "#{init} #{date}" }
end
In this case, the return value is a concatenated string containing all the dates.
Alternatively, if it's not a concatenated string you want, you could change the "#{init} #{date}" part of it.
As a side note, using puts in ruby on rails won't print to the web page. When you use <%= next_seven_days %>, the return value of that function is what will be printed to the page. The each function returns the range in parentheses.

Your code will definitely print all eight days to stdout. Your problem is that you're looking at the return value (which since each returns self will be (today .. today + 7)).
Note that if you print to stdout inside an erb template, that won't cause the output to show up in the rendered template.

Your function RETURNS an enumeration designated by 2011-07-07..2011-07-14 which is displayed in your view, but your puts prints to STDOUT which is not going to be your view, but the console screen your server is running in =)
If you want your view to show a list of the seven days, you need to actually create the string that does that and RETURN that :)
def next_seven_days
outputstr = ""
today = Date.today
(today..(today+7.days)).each { |date| outputstr += date.to_s }
return outputstr
end

def next_seven_days
today = Date.today
(today..(today+7.days)).each { |date| puts date }
end

Use the "next" method
def next_seven_days
today= Date.today
7.times do
puts today
today=today.next
end
end

Related

How to find the months in a year using Ruby

How to find the months in a year till this month.
months_of_year should return (2013-01-01, 2013-02-01, 2013-03-01, 2013-04-01, 2013-05-01) (assuming current month is 2013-05-01)
You can create helper method:
def months_of_year(till = Date.today)
(1..till.month).map { |m| Date.new(till.year, m) }
end
This will return array of dates of each 1st days from the beginning of year.
This will give you an array of beginning of month dates for this year until today.
i=Date.today.beginning_of_year
a = []
while i <= Date.today
a << i.beginning_of_month
i=i + 1.month
end
require "date"
require "time"
(1..DateTime.now.month).map{|i| Date.new(DateTime.now.year,i,1).to_s }
#=> ["2013-01-01", "2013-02-01", "2013-03-01", "2013-04-01", "2013-05-01"]

Ruby, Recursion Method

I have a method that is basically a loop and it calls itself at the end each time. What is the best way for the method to not call itself when the date reaches a certain point? Each iteration through adds 1 day and basically processes stats for that day. It looks like the below:
def loop(start_day)
date = start_day
#do a bunch of stuff
date = date +1.day
if date > Time.now
puts "loop should be over"
end
loop(date)
end
Each iteration through adds 1 day
That's not true for the code you've posted. In your code you add 1 day to the start date once and then you keep processing the same date over and over again because you recurse on the old date (start_date), not the incremented date (date).
What is the best way for the method to not call itself when the date reaches a certain point?
Just put the recursive call inside an if or, in this case, inside of the else of the if that you already have.
Since you set date to start_date immediately, it seems there's no point in having both. Here's the more canonical form of doing recursion:
def loop(date)
return if date > Time.now
#do a bunch of stuff
loop(date + 1.day)
end
Update: If it's not obvious to you that recursion isn't necessary here, in real life, it would make more sense to do something like this:
def process_from(date)
while date <= Time.now
# Process date
date += 1.day
end
end
What about this?
def loop(start_day)
return "loop should be over" if start_day >= Time.now
#...
loop(modified_date)
end
or...
def loop(start_day)
date = start_day.dup
time = Time.now
date += 1.day while date <= time
'loop should be over'
end
It seems like you want to iterate over all days from starting date to today. Then maybe this is even more simple:
def map_date(date)
(date.to_date..Date.today).map do |d|
d.strftime("Today is %A")
end
end
You must have base case (stop case) for recursive function;
example:
def loop(date)
if date == certain_point
return [something_for_stop]
end
loop(date - 1)
end

How to reverse Date.parse()

My code:
require 'Date'
s = "I'm going away on Oct 2, 2012th"
puts Date.parse(s)
=> 2012-10-02
I want to delete the date from my strings, that I found with Date.parse(s). The problem is, I know that there is a date, but not how it was written in the string. I know that Date.parse found it and converted "2012-10-02" to a new format.
Here is a quick and dirty solution. The function date_string
returns just the portion of the string containing the date
found by parse.
require 'date'
DATE_ERROR = -1
# If the string doesn't contain a date, it raises an
# exception. This little helper routine catches the
# exception.
def get_date(s)
date = 0
begin
date = Date.parse(s)
rescue
date = DATE_ERROR
end
date
end
# Returns just the part of the string containing the date
def date_string(s)
# First, find the date contained in the string
date = get_date(s)
return "" if date == DATE_ERROR
# Repeatedly chop off characters from the front to find the
# start of the date
first = 1
while date == get_date(s[first..-1])
first += 1
end
# Repeatedly chop off characters from the end to find the
# end of the date
last = s.length - 2
while date == get_date(s[0..last])
last -= 1
end
#Return just the date
s[first - 1..last + 1]
end
puts date_string("I'm going away on Oct 2, 2012th")
puts date_string("I'm going away on 10/2/12 and not coming back")
puts date_string("10 Nov 1999")
puts date_string("I see no date here")
This outputs:
Oct 2, 2012
10/2/12
10 Nov 1999
So you could do something like:
s = "I'm going away on Oct 2, 2012th"
datestr = date_string(s)
s.gsub!(datestr, "")
puts s
Date doesn't appear to be able to tell you where it found the date. You're probably going to have to write your own custom date finder.

Ruby: day selection program (select next available day from a list)

I'm tryind to build a helper to select the next available day from a list.
I have a list of days as a reference (those are the day where I want something to happen)
class_list = ["Monday","Saturday","Sunday"]
I need to check the current day and match it in the list.
If it's part of the list I select it, if it isn't I select the next one from the list.
this is what i have so far:
#select current day, get its name value and weekday number value
today = Time.now
today_name = today.strftime("%A")
#not sure which of the 2 following line is better
#today_index = DateTime.parse(today_name).wday
today_index = today.strftime("%u").to_i
Then I do the matching
if class_list.include? today_name
#victory!!!
puts today_name
else
puts "find next day"
class_list.each do |x|
if DateTime.parse(x).wday > today_index
puts "result #{x}"
break
end
end
end
When I run it seems to work fine, but as i'm just learning Ruby i'm always wondering if i'm not overcomplicating things.
Does this code looks alright to you Ruby masters?
require 'date'
def next_date_from(ar)
cur_day = Date.today
cur_day += 1 until ar.include?(cur_day.strftime('%A'))
cur_day
end
puts next_date_from(%w(Monday Saturday Sunday))
#=>2011-10-01
I would better have a map linking a given day to the following one and a default value if the day is not found:
days = {:Monday => :Tuesday, :Tuesday => :Wednesday ...}
days.default = :Monday
When you do days[:Monday] you get :Tuesday
when you try to get a non existing entry, you get the default.
For the part:
if class_list.include? today_name
#victory!!!
puts today_name
else
puts "find next day"
class_list.each do |x|
if DateTime.parse(x).wday > today_index
puts "result #{x}"
break
end
end
end
You could write it like this:
if class_list.include? today_name
#victory!!!
puts today_name
else
puts "find next day"
result = class_list.find {|e| DateTime.parse(e).wday > today_index }
puts "result = #{result}"
end

How to get strptime to raise ArgumentError with garbage trailing characters

We have to handle user specified date formats in our application. We decided to go with Date.strptime for parsing and validation, which works great, except for how it just ignores any garbage data entered. Here is an irb session demonstrating the issue
ree-1.8.7-2010.01 > require 'date'
=> true
ree-1.8.7-2010.01 > d = Date.strptime '2001-01-01failfailfail', '%Y-%m-%d'
=> #<Date: 4903821/2,0,2299161>
ree-1.8.7-2010.01 > d.to_s
=> "2001-01-01"
what we would like, is behavior more like this
ree-1.8.7-2010.01 > d = Date.strptime '2001failfailfail-01-01', '%Y-%m-%d'
ArgumentError: invalid date
Any suggestions would be appreciated
One possibility is to pass the resulting date through strftime using the same format and compare to the original string.
i.e. valid = Date.strptime(date_string, format).strftime(format) == date_string
One limitation of this is that it wouldn't handle leading 0s in parts of the date e.g. if you wanted to accept 2010-6-1 but strftime returned 2010-06-01 you wouldn't have a match.
Also, I'm not sure if this is what you meant or not, but your second example Date.strptime '2001failfailfail-01-01', '%Y-%m-%d' does raise an ArgumentError. It seems like only trailing junk is ignored.
one way is to define a new class which does the validation first.
require 'date'
class Mydate
def self.strptime(string)
raise ArgumentError 'Fail' if string !~ /^\d\d\d\d-\d\d-\d\d$/
Date.strptime(string)
end
end
d = Mydate.strptime '2001-01-01'
puts d
d2 = Mydate.strptime '2001-01-02xxxx'
puts d2
Another way would be to open up the Date class, alias the strptime method, write a new one to do the validation you require and then call the aliased one.
require 'date'
class Date
class << self
alias :orig_strptime :strptime
end
def self.strptime(string)
puts "in new strptime"
raise ArgumentError 'Fail' if string !~ /^\d\d\d\d-\d\d-\d\d$/
Date.orig_strptime(string)
end
end
d = Date.strptime '2001-01-01'
puts d
d2 = Date.strptime '2001-01-02xxxx'
puts d2

Resources