How to find the months in a year using Ruby - 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"]

Related

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

Step in a Date Interval

I trying to do a each on a Date interval with Rails 3.2.. something like this:
(1.months.ago.to_date..5.months.from_now.to_date).step(1.month).each do |date|
puts date.strftime('%m/%Y')
end
But, the step(1.month) does not work.. seems like it get the first month (ex: today is august, it will return jully) and does not iterate the other months..
Is there a way to do that?
Thanks
You are using Date as your iteration base, and 1.month translates (behind the scenes) into seconds I believe.
When you add to the Date object, it's in days, thus:
Date.today + 1 would be tomorrow
Thus, in your example, you are trying to step 2592000 days.
What you probably want is something more like:
(1.months.ago.to_date..5.months.from_now.to_date).step(30).each { |date| puts date.strftime('%m/%Y') }
If you are looking for the iterator to be smart enough to know how many days are in each month when you are "stepping" that's not going to happen. You will need to roll that on your own.
You can intelligently iterate through months by using the >> operator, so:
date = Date.today
while date < 5.months.from_now.to_date do
puts date.strftime('%m/%Y')
date = date>>1
end
how about this:
current_date, end_date = Date.today, 5.monthes.from_now.to_date
while current_date <= end_date
puts current_date
current_date = current_date.next_month
end

Time-of-day range in Ruby?

I want to know if a time belongs to an schedule or another.
In my case is for calculate if the time is in night schedule or normal schedule.
I have arrived to this solution:
NIGHT = ["21:00", "06:00"]
def night?( date )
date_str = date.strftime( "%H:%M" )
date_str > NIGHT[0] || date_str < NIGHT[1]
end
But I think is not very elegant and also only works for this concrete case and not every time range.
(I've found several similar question is SO but all of them make reference to Date ranges no Time ranges)
Updated
Solution has to work for random time ranges not only for this concrete one. Let's say:
"05:00"-"10:00"
"23:00"-"01:00"
"01:00"-"01:10"
This is actually more or less how I would do it, except maybe a bit more concise:
def night?( date )
!("06:00"..."21:00").include?(date.strftime("%H:%M"))
end
or, if your schedule boundaries can remain on the hour:
def night?(date)
!((6...21).include? date.hour)
end
Note the ... - that means, basically, "day time is hour 6 to hour 21 but not including hour 21".
edit: here is a generic (and sadly much less pithy) solution:
class TimeRange
private
def coerce(time)
time.is_a? String and return time
return time.strftime("%H:%M")
end
public
def initialize(start,finish)
#start = coerce(start)
#finish = coerce(finish)
end
def include?(time)
time = coerce(time)
#start < #finish and return (#start..#finish).include?(time)
return !(#finish..#start).include?(time)
end
end
You can use it almost like a normal Range:
irb(main):013:0> TimeRange.new("02:00","01:00").include?(Time.mktime(2010,04,01,02,30))
=> true
irb(main):014:0> TimeRange.new("02:00","01:00").include?(Time.mktime(2010,04,01,01,30))
=> false
irb(main):015:0> TimeRange.new("01:00","02:00").include?(Time.mktime(2010,04,01,01,30))
=> true
irb(main):016:0> TimeRange.new("01:00","02:00").include?(Time.mktime(2010,04,01,02,30))
=> false
Note, the above class is ignorant about time zones.
In Rails 3.2 it has added Time.all_day and similars as a way of generating date ranges. I think you must see how it works. It may be useful.

Finding the date for a given week number

I am trying to do some date math based on the week number of a given year. For example:
date = Date.today # Monday, March 5, 2012
puts date.cwyear # 2012
puts date.cweek # 10 (10th week of 2012)
Now that I know what the current week is, I want to figure out what the next week and previous week are. I need to take the year (2012) and the week number (10) and turn it back into a date object so I can calculate the value for the next/previous week. How can I do this?
You want Date.commercial:
require 'date'
now = Date.today #=> 2012-03-05
monday_next_week = Date.commercial(now.cwyear,now.cweek+1) #=> 2012-03-12
next_sunday_or_today = monday_next_week - 1 #=> 2012-03-11
Note that weeks start on Monday, so if you are on a Sunday and ask for next monday - 1 you'll get the same day.
Note also that if you don't want Mondays you can also specify the day number in the method:
thursday_next_week = Date.commercial(now.cwyear,now.cweek+1,4) #=> 2012-03-15
Calculating on a day basis is pretty simple with Date objects. If you just want to get the previous / next week from a given Date object use the following:
date = Date.today
previous_week = (date - 7).cweek
next_week = (date + 7).cweek
In ActiveSupport you have helper to convert Fixnum to time http://as.rubyonrails.org/classes/ActiveSupport/CoreExtensions/Numeric/Time.html use:
date = Date.today
week_ago = date - 1.week
next_week = date + 1.week
I have created some methods to get week number of a given date
something like this:
def self.get_week(date)
year = date.year
first_monday_of_the_year = self.get_first_monday_of_the_year(year)
# The first days of January may belong to the previous year!
if date < first_monday_of_the_year
year -= 1
first_monday_of_the_year = self.get_first_monday_of_the_year(year)
end
day_difference = date - first_monday_of_the_year
week = (day_difference / 7).to_i + 1
week
end
def self.get_monday_of_year_week(year, week)
d = self.get_first_monday_of_the_year(year)
d + ((week - 1) * 7).days
end
def self.get_first_monday_of_the_year(year)
d = Date.new(year, 1, 7) # 7 Jan is always in the first week
self.get_monday_of_week(d)
end
def self.get_monday_of_week(date)
wday = (date.wday + 6) % 7
date - wday.days
end
Assuming you mean "a given week number in the current year", you can do the following:
2.weeks.since(Time.gm(Time.now.year))
=> Fri Jan 15 00:00:00 UTC 2010
Substitute (week_number - 1) for the 1 in the above, and you'll get a date in the desired week.

Iterating over days in 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

Resources