Refactor a convoluted date method - ruby

My users choose some dates from a datepicker, which is saving the dates in this format:
"06/05/2019"
I'd like the date to display back in this format: "Wednesday, June 5"
I'm struggling a bit with Ruby's date methods. This method that I put together is working, but I'm sure if somebody could help me trim this to one line, then I would understand Ruby's date methods better.
def friendly_date(input)
orig_due_date = Date.strptime(input, "%m/%d/%Y")
orig_due_date.strftime("%A, %B %e")
end
Thank you in advance for any insight.

I can't see any improvement except avoiding the local variable
def friendly_date(input)
Date.strptime(input, "%m/%d/%Y").strftime("%A, %B %e")
end

You could write...
def friendly_date(input)
Date.strptime(input, "%m/%d/%Y").strftime("%A, %B %e")
end
...but this would not be an improvement. It's doing two operations, parse and format, so it's easier to understand as two lines. Naming the variable just date is an improvement because inside this function its just a date.
def friendly_date(input)
date = Date.strptime(input, "%m/%d/%Y")
date.strftime("%A, %B %e")
end
A better way to simplify your code is to have one function parsing what the datepicker gives you into a Date, and other to format any Date.
def date_from_datepicker(from_datepicker)
Date.strptime(input, "%m/%d/%Y")
end
def friendly_date(date)
date.strftime("%A, %B %e")
end
Now you have a generic function to transform the datepicker format into a Date. And a generic function to transform any Date or Time into your preferred format.
Now you'd immediately normalize the output from your datepicker into a Date and only transform it into your friendly format as necessary. This will make working with all dates easier by having the be Date objects for as long as possible.
input_date = date_from_datepicker(params['date'])
...work with it like any other Date...
puts friendly_date(input_date)
# And it works on any Date.
puts friendly_date(some_other_date)

Related

rspec - how to compare to an actual DateTime in the expect?

My rspec:
it "can show the current month name" do
expect(Calendar.create_date_using_month(1)).to eq '2000-01-01 00:00:00 -0500'
end
fails with:
expected: "2000-01-01 00:00:00 -0500"
got: 2000-01-01 00:00:00 -0500
For my code:
def self.create_date_using_month(n)
Time.new(2000,n,1)
end
Should/can I change the RSpec so that I am comparing to an actual string not a date?
I tried: Date.strptime("{ 2000, 1, 1 }", "{ %Y, %m, %d }")
but that gives me
expected: #<Date: 2000-01-01 ((2451545j,0s,0n),+0s,2299161j)>
got: 2000-01-01 00:00:00 -0500
I'm a bit confused about what exactly you're testing here. If create_data_using_month creates a Time object, you should compare it with a Time object.
This message:
expected: "2000-01-01 00:00:00 -0500"
got: 2000-01-01 00:00:00 -0500
is telling you it expected the literal string with the date, but got an object whose to_s happens to be the same.
So I guess you could "fix" it, by changing this:
it "can show the current month name" do
expect(Calendar.create_date_using_month(1).to_s).to eq '2000-01-01 00:00:00 -0500'
end
But that seems odd, is that what you want? You'll also likely have issues if you test on a machine with different time zone settings.
I'd just do this:
it "can show the current month name" do
expect(Calendar.create_date_using_month(1)).to eq Time.new(2000, 1, 1)
end
which passes for me just fine.
I think you're having a microseconds issue.
You should use to_i conversion of the date to avoid dealing with microseconds issue (if not relevant).
Time.now().to_i.should == Time.now().to_i
and I do think this work too
Time.now().should.eql?(Time.now())
I've also write a custom matcher:
RSpec::Matchers.define :be_equal_to_time do |another_date|
match do |a_date|
a_date.to_i.should == another_date.to_i
end
end
which can be use like this
Time.now().should be_equal_to_time(Time.now())
DateTime Class http://www.ruby-doc.org/stdlib-1.9.3/libdoc/date/rdoc/DateTime.html
DateTime.parse('2000-01-01 00:00:00 -0500') == DateTime.new(2000,1,1,0,0,0,'-5')
#=> true
You should always try and compare the object and not the it's string value unless you are specifically testing its ability to return a specific string. This is because to_s is simply am method and not a true representation of the object.

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

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.

Ruby: how to get at meridian in Time objects?

If I've got a time object:
t = Time.now
and I want to know if that time is AM or PM, right now the only way I can figure to do this is:
t.strftime("%p") == "PM"
Now, that %p is getting interpolated from something, right? Is there another way to get to it?
I ask because I'm doing some time formatting where I want to display a time range like:
"9:00 AM - 5:00 PM"
"4:00 - 5:30 PM"
"10:15 - 11:45 AM"
Right now I have to do this checking the string value of strftime, but I'd prefer to write something like:
if start_time.am? && end_time.pm? || start_time.pm? && end_time.am?
...instead of the much more verbose strftime string comparisons I'm doing now.
Based on http://www.ruby-doc.org/core-1.9.3/Time.html, I do not believe there is any other way. You could monkey-patch Time to save you some tedious strftime, however:
class Time
def meridian
self.strftime('%p')
end
def am?
self.meridian == 'AM'
end
def pm?
self.meridian == 'PM'
end
end
There isn't anything as nice as time.am? but you can use time.hour < 12 instead.
class Time
def am?
self.hour.in? (0..12)
end
def pm?
self.hour.in? (13..24)
end
end

Ruby: Is Date greater than a particular date

I am trying to do a date comparison like so:
if date > "Sept 22"
#do stuff
else
#do other stuff
end
I just can't think of how to do this in Ruby (well at least looking at a particular date).
Normal comparison operators work with dates too:
require 'date'
date = Date.new(2012,3,3)
if date > Date.new(2011,4,4)
# do whatever
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

Resources