Iterate over days, starting from x date through an end date - ruby

I need to start from, for example, January 1 2013, and "do some things" for each date, resulting in a JSON file for each date.
I have the "do some things" part worked out for a single date, but I'm having a hard time starting at a date and looping through to another end date.

You can use ranges :
(Date.new(2012, 01, 01)..Date.new(2012, 01, 30)).each do |date|
# Do stuff with date
end
or (see #awendt answer)
Date.new(2012, 01, 01).upto(Date.new(2012, 01, 30)) do |date|
# Do stuff with date
end

You could use:
first.upto(last) do |date|
where first and last are Date objects.
See what I did here in a project of mine, for example.

Related

Ruby: how to find a date by the year day

How is it possible to find the date based on the current year's day?
For example, you have the year's day 235, which date does it corresponds to?
For example, you can find a year's day as follows:
Date.today.yday
#=> 126 (for May 6, 2021)
How to achieve the opposite?
Date.some_method(126)
#=> 2021-05-06
I tried as follows:
now = Date.today
#=> Thu, 06 May 2021
start_year = now.beginning_of_year
#=> Fri, 01 Jan 2021
start.advance(days: (start.yday + 126))
#=> Sat, 08 May 2021
Why there is a difference of 2 days?
This should work (pure ruby):
def day_of_year(num)
Date.new(Date.today.year) + num - 1
end
day_of_year(1) #=> 01/01/2021
day_of_year(235) #=> 23/08/2021
Another option:
def day_of_year(num)
Date.strptime(num.to_s, "%j")
end
day_of_year(1) #=> 01/01/2021
day_of_year(235) #=> 23/08/2021
Note: #steenslag posted the actually correct answer - please go and upvote.
Date.ordinal gives you the date, given the year and the number. You can even specify a negative number, which would calculate the date counting backwards from the end of the year. doc
Try This.
Date.today - (Date.today.yday - 126)

Get the last Sunday of the Month

I'm using Chronic to get the last Sunday of the month of any given year. It will gladly give me the n‌th Sunday, but not the last.
This works, but is not what I need:
Chronic.parse('4th sunday in march', :now => Time.local(2015,1,1))
This is what I need, but doesn't work:
Chronic.parse('last sunday in march', :now => Time.local(2015,1,1))
Is there any way around this apparent limitation?
UPDATE: I'm upvoting the two answers below because they're both good, but I've already implemented this in "pure Ruby" (in 2 lines of code, besides the require 'date' line), but I'm trying to demonstrate to management that Ruby is the right language to use to replace a Java codebase that is going away (and which had dozens of lines of code to compute this), and I told one manager that I probably could do it in one line of Ruby, and it would be readable and easy to maintain.
I am not sure about Chronic (I haven't heared about it before), but we can implement this in pure ruby :)
##
# returns a Date object being the last sunday of the given month/year
# month: integer between 1 and 12
def last_sunday(month,year)
# get the last day of the month
date = Date.new year, month, -1
#subtract number of days we are ahead of sunday
date -= date.wday
end
The last_sunday method can be used like this:
last_sunday 07, 2013
#=> #<Date: 2013-07-28 ((2456502j,0s,0n),+0s,2299161j)>
Reading the update in your question, I tried to come up with another answer using only a single line of ruby code (without using gems). How about this one?
##
# returns a Date object being the last sunday of the given month/year
# month: integer between 1 and 12
def last_sunday(month,year)
# get the last day of the month, go back until we have a sunday
Date.new(year, month, -1).downto(0).find(&:sunday?)
end
last_sunday 07, 2013
#=> #<Date: 2013-07-28 ((2456502j,0s,0n),+0s,2299161j)>
What about
Chronic.parse('last sunday', now: Chronic.parse('last day of march'))
This works, and is as readable as I can get:
Chronic.parse('last sunday', now: Date.new(year,3,31))
Thanks to Ismael Abreu for the idea to just parse 'last sunday' and control the rest via the :now option.
UPDATE: Please also upvote Ismael's answer.
It's a bit ugly, but you could simply try the 5th or 4th in that order:
d = [5,4].each do |i|
try = Chronic.parse("#{i}th sunday in march", :now => Time.local(2015,1,1))
break try unless try.nil?
end
=> Sun Mar 29 12:30:00 +0100 2015
d = [5,4].each do |i|
try = Chronic.parse("#{i}th sunday in april", :now => Time.local(2015,1,1))
break try unless try.nil?
end
=> Sun Apr 26 12:00:00 +0100 2015

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

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.

How to get the number of days in a given month in Ruby, accounting for year?

I'm sure there's a good simple elegant one-liner in Ruby to give you the number of days in a given month, accounting for year, such as "February 1997". What is it?
If you're working in Rails, chances are you'll get hamstrung eventually if you switch among Time, Date, and DateTime, especially when it comes to dealing with UTC/time zones, daylight savings, and the like. My experience has been it's best to use Time, and stick with it everywhere.
So, assuming you're using Rails's Time class, there are two good options, depending on context:
If you have a month m and year y, use the class method on Time:
days = Time.days_in_month(m, y)
If you have a Time object t, cleaner to ask the day number of the last day of the month:
days = t.end_of_month.day
require 'date'
def days_in_month(year, month)
Date.new(year, month, -1).day
end
# print number of days in February 2012
puts days_in_month(2012, 2)
This is the implementation from ActiveSupport (a little adapted):
COMMON_YEAR_DAYS_IN_MONTH = [nil, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
def days_in_month(month, year = Time.now.year)
return 29 if month == 2 && Date.gregorian_leap?(year)
COMMON_YEAR_DAYS_IN_MONTH[month]
end
How about:
require 'date'
def days_in_month(year, month)
(Date.new(year, 12, 31) << (12-month)).day
end
# print number of days in Feburary 2009
puts days_in_month(2009, 2)
You may also want to look at Time::days_in_month in Ruby on Rails.
In Rails project for current date
Time.days_in_month(Time.now.month, Time.now.year)
For any date t which is instance of Time
Time.days_in_month(t.month, t.year)
or
t.end_of_month.day
.
If you have UTC seconds, you need to get an instance of Time first
Time.at(seconds).end_of_month.day
For a given Date object I feel like the easiest is:
Date.today.all_month.count
Use Time.days_in_month(month) where month = 1 for January, 2 for Feb, etc.
revise the input for other format
def days_in_a_month(date = "2012-02-01")
date.to_date.end_of_month.day
end
as of rails 3.2... there's a built in version:
http://api.rubyonrails.org/classes/Time.html#method-c-days_in_month
(alas, it shows up after this answer, which takes folks on a long hike)
Time.now.end_of_month.day - for current month
Date.parse("2014-07-01").end_of_month.day - use date of first day in month.
Depends on ActiveSupport
A simple way using Date:
def days_of_month(month, year)
Date.new(year, month, -1).day
end
I think it's the simplest way to get it
def get_number_of_days(date = Date.today)
Date.new(date.year, date.month, -1).mday
end
Since the time is irrelevant for this purpose then you just need a date object:
[*Date.today.all_month].size

Resources