Rails time formatting test failing because it's one hour off? - ruby-on-rails

I have the following method in one of my Rails classes:
def human_departure_time
"#{departure_time.strftime("%A, %d %B %Y")} at #{departure_time.strftime("%I:%M %p")}"
end
As you can see, it just takes a datetime attribute of the model and formats it so that it is more human friendly.
Anyway, I have the following test for this method:
describe "human_departure_time" do
it "should output the time in readable format" do
# first I use the Timecop gem to freeze the time
Timecop.freeze(DateTime.now) do
bus_time = DateTime.now + 1.days
# factory a bus with a specific departure time
bus = Factory :bus, departure_time: bus_time
expected = "#{bus_time.strftime("%A, %d %B %Y")} at #{bus_time.strftime("%I:%M %p")}"
# check that the output is as expected
bus.human_departure_time.should == expected
end
end
end
Pretty simple but the test fails by one hour with the following output:
Failures:
1) Bus human_departure_time should output the time in readable format
Failure/Error: bus.human_departure_time.should == expected
expected: "Wednesday, 17 August 2011 at 03:13 AM"
got: "Wednesday, 17 August 2011 at 02:13 AM" (using ==)
# ./spec/models/bus_spec.rb:34:in `block (4 levels) in <top (required)>'
# ./spec/models/bus_spec.rb:30:in `block (3 levels) in <top (required)>'
Here is my bus factory just incase that is important. I'm overwriting the departure time with the frozen time plus one hour in my test.
factory :bus do
origin_name "Drogheda"
event_name "EP"
departure_time { DateTime.now + 14.days }
end
I assume this is something to do with daylight savings time or something? How can I fix this issue?

ActiveRecord could be automatically converting time attributes in your model to local time.
You can try and use the %Z parameter of strftime to see the time zone of the outputted timestamp to see where a possible conversion is sneaking into your time.
Some Googled hints that might be relevant:
default_timezone:
http://apidock.com/rails/ActiveRecord/Base/default_timezone/class
ActiveRecord::Base.time_zone_aware_attributes
config.active_record.time_zone_aware_attributes:
http://tamersalama.com/2011/01/10/rails-disable-timezone-conversions/
https://mirrors.kilnhg.com/Repo/Mirrors/From-Git/Rails/History/70cb470c1ab8
http://www.ruby-forum.com/topic/2096919
http://www.ruby-forum.com/topic/890711

Related

Date.today.to_s(:long) not converting to string

I am working on an assignment and I have written the following method based on our instructions:
def create_todolist(params)
due_date = Date.today.to_s(:long)
TodoList.create(list_name: params[:name],list_due_date: params[:due_date])
end
But when I run the rspec test, I get the following error:
1) Assignment rq03 rq03.2 assignment code has create_todolist method should create_todolist with provided parameters
Failure/Error: expect(testList.list_due_date).to eq due_date
expected: Thu, 07 May 2020
got: "2020-05-07"
(compared using ==)
Diff:
## -1,2 +1,2 ##
-Thu, 07 May 2020
+"2020-05-07"
# ./spec/assignment_spec.rb:177:in `block (4 levels) in <top (required)>'
# ./spec/assignment_spec.rb:14:in `block (2 levels) in <top (required)>'
Here is the rspec test:
context "rq03.2 assignment code has create_todolist method" do
it { is_expected.to respond_to(:create_todolist) }
it "should create_todolist with provided parameters" do
expect(TodoList.find_by list_name: "mylist").to be_nil
due_date=Date.today
assignment.create_todolist(:name=> 'mylist', :due_date=>due_date)
testList = TodoList.find_by list_name: 'mylist'
expect(testList.id).not_to be_nil
expect(testList.list_name).to eq "mylist"
expect(testList.list_due_date).to eq due_date
expect(testList.created_at).not_to be_nil
expect(testList.updated_at).not_to be_nil
end
end
At first I had just due_date = Date.today and was getting the same error and I'm not sure how to fix it. I'm wondering if it is because I am using a different version of ruby/rails than what was used when the course was created ( 5 years ago -_-).
Any help would be greatly appreciated!
Thank you :)
You are trying to compare a Date object:
due_date = Date.today
With a string object you generated while you created your record:
Date.today.to_s(:long)
As you can see, these are different types of objects:
Date.today.to_s(:long)
=> "May 07, 2020"
Date.today.to_s(:long).class
=> String
Date.today
=> 2020-05-07
Date.today.class
=> Date
Date.today.to_s(:long) == Date.today
=> false
I figured it out. When I was creating the TodoLists table, I didn't specify the migration type as :date. so by default, due_date was set to be a string. So I set to type :date and changed due_date to equal:
due_date = Date.today
Thank you for taking the time to help me out :)

Can't set timezone using abbreviation

I can't set timezone on Rails using its abbreviation, for example:
>> Time.zone = 'BRT'
ArgumentError: Invalid Timezone: BRT
from /home/braulio/.rbenv/versions/2.1.5/lib/ruby/gems/2.1.0/gems/activesupport-3.2.21/lib/active_support/core_ext/time/zones.rb:61:in `rescue in find_zone!'
from /home/braulio/.rbenv/versions/2.1.5/lib/ruby/gems/2.1.0/gems/activesupport-3.2.21/lib/active_support/core_ext/time/zones.rb:53:in `find_zone!'
from /home/braulio/.rbenv/versions/2.1.5/lib/ruby/gems/2.1.0/gems/activesupport-3.2.21/lib/active_support/core_ext/time/zones.rb:37:in `zone='
from (irb):14
from /home/braulio/.rbenv/versions/2.1.5/lib/ruby/gems/2.1.0/gems/railties-3.2.21/lib/rails/commands/console.rb:47:in `start'
from /home/braulio/.rbenv/versions/2.1.5/lib/ruby/gems/2.1.0/gems/railties-3.2.21/lib/rails/commands/console.rb:8:in `start'
from /home/braulio/.rbenv/versions/2.1.5/lib/ruby/gems/2.1.0/gems/railties-3.2.21/lib/rails/commands.rb:41:in `<top (required)>'
from script/rails:6:in `require'
from script/rails:6:in `<main>'
This is necessary as some systems (android and some browsers) report timezone using the abbreviation.
The list of abbreviations can be found at http://en.wikipedia.org/wiki/List_of_time_zone_abbreviations
jstimezone was reporting timezone using abbreviations. It is also quite buggy and unmaintained (https://bitbucket.org/pellepim/jstimezonedetect/issues?status=new&status=open). It is simpler to just use standard javascript:
var offset = - new Date().getTimezoneOffset()/60
Then call on document ready:
$.cookie("browser.tzoffset", offset, { expires: 30, path: '/' })
Then in rails use around_filter in ApplicationController:
def set_time_zone
return yield unless (utc_offset = cookies['browser.tzoffset']).present?
utc_offset = utc_offset.to_i
gmt_offset = if utc_offset == 0 then nil elsif utc_offset > 0 then -utc_offset else "+#{-utc_offset}" end
Time.use_zone("Etc/GMT#{gmt_offset}"){ yield }
rescue ArgumentError
yield
end
This localizes all dates for users, independently where he/she is. In Brazil we have multiple timezones, for example.
PS: ActiveSupport::TimeZone[utc_offset.to_i] can't be used as it uses DST, see https://github.com/rails/rails/issues/20504
PS: You can also use moment: moment.parseZone(Date.now()).utcOffset()/60 or moment().format('zz')
You don't have to use around_filter.
Put this in before_action
Time.zone = "Etc/GMT#{gmt_offset}"
(Time.zone is thread local. It's safe to change.)

Random space in Rails DateTime when using RSpec?

I have a method in my Event model:
def when
self.starts_at.strftime("%a %b %e # %-I:%M")
end
which outputs correctly in the rails console or on a webpage.
However, doing an rspec test like this:
it "has simple date display" do
game = FactoryGirl.build(:event, starts_at: DateTime.parse("1/1/2014 3:30pm"))
game.when.should == "Wed Jan 1 # 3:30"
end
fails, because of:
1) Event has simple date display
Failure/Error: event.when.should == "Wed Jan 1 # 3:30"
expected: "Wed Jan 1 # 3:30"
got: "Wed Jan 1 # 3:30" (using ==)
Why is there a random space in my formatted DateTime? Shouldn't the same DateTime code be loaded/running for tests and console?
You're using the %e format directive, which is for the blank-padded day of month. It sounds like you want the unpadded day of the month directive, which is %-d. Here are the docs.
The reason your test is failing is because the %e directives in strftime is:
%e - Day of the month, blank-padded ( 1..31)
So the statement DateTime.parse("1/1/2014 3:30pm").strftime("%a %b %e # %-I:%M") yields Wed Jan 1 # 3:30 (with extra space prepended for days between 1 through 9).
You could use %d directive instead which gives day of month, zero-padded. E.g.
# Event model
def when
self.starts_at.strftime("%a %b %d # %-I:%M")
end
Then in your spec:
it "has simple date display" do
game = FactoryGirl.build(:event, starts_at: DateTime.parse("1/1/2014 3:30pm"))
game.when.should == "Wed Jan 01 # 3:30"
end

rspec - why does this assert_equal comparison test work on a mac and not on Ubuntu? [duplicate]

This question already has an answer here:
Closed 10 years ago.
Possible Duplicate:
rspec - why does this attribute comparison usng assert_equal fail when they are the same, on ubuntu only?
Ruby: 1.9.3-p194
Rails: 3.2.8
Ubuntu: 12.04
The test has a lot of setup and then eventually does:
assert_equal #iep_service.attributes, IepService.first.attributes
Works on a mac but fails on ubuntu with:
2) Iep Service Spreadsheet A typical district With pre-existing students And a pre-existing Iep Service for one of those students And an Iep S[52/427$
SV Prevent importing
Failure/Error: assert_equal #iep_service.attributes, IepService.first.attributes
MiniTest::Assertion:
<{"id"=>212,
"duration"=>30,
"frequency"=>3,
"period"=>"week",
"group_size"=>"group",
"location"=>nil,
"service"=>nil,
"area_of_need"=>"speech",
"created_at"=>Wed, 24 Oct 2012 18:59:47 UTC +00:00,
"updated_at"=>Wed, 24 Oct 2012 18:59:47 UTC +00:00,
"therapist_id"=>nil,
"start_date"=>nil,
"end_date"=>nil,
"student_id"=>233,
"adhoc"=>false}> expected but was
<{"id"=>212,
"duration"=>30,
"frequency"=>3,
"period"=>"week",
"group_size"=>"group",
"location"=>nil,
"service"=>nil,
"area_of_need"=>"speech",
"created_at"=>Wed, 24 Oct 2012 18:59:47 UTC +00:00,
"updated_at"=>Wed, 24 Oct 2012 18:59:47 UTC +00:00,
"therapist_id"=>nil,
"start_date"=>nil,
"end_date"=>nil,
"student_id"=>233,
"adhoc"=>false}>.
# (eval):2:in `assert_equal'
# ./spec/models/iep_service_spreadsheet_spec.rb:71:in `block (6 levels) in <top (required)>'
The full source, if it help, is:
context "A typical district" do
before(:each) { set_up_district }
context "With pre-existing students" do
before(:each) { StudentSpreadsheet.new(#district, open_spec_fixture_file('sample-students.csv')) }
context "And a pre-existing Iep Service for one of those students" do
before(:each) { #iep_service = FactoryGirl.create(:iep_service, :student => #district.students.first) }
context "And an Iep Service CSV" do
before(:each) { #spreadsheet = IepServiceSpreadsheet.new(#district, open_spec_fixture_file('sample-ieps.c sv')) }
specify "Prevent importing" do
# Leave database untouched
assert_equal 1, IepService.count
assert_equal #iep_service.attributes, IepService.first.attributes
# Provide error report
assert #spreadsheet.error_report.any?
end
end
end
end
end
assert_equal uses the operator/method ==.
You can read the documentation for assert_equal here: http://ruby-doc.org/core/classes/Test/Unit/Assertions.html#M006665. It comes directly from Ruby, Rails don't overrides the definition.
So depending on the object type the == bahaves differently, and also as it comes from Ruby there may be a different implementation or slightly difference in the ruby code.
Anyways, it is better for you to compare value by value or live with the == comparison may bring to you.

Comparing time or ditching date in ruby on rails

I have some time defined from my database, and this is how it looks:
ruby-1.9.2-p290 :017 > djel.smjena.pocetak1.to_time
=> 2000-01-01 08:00:00 +0100
and that is ok, it assigned me 2000-1-1
also, I got something that happened in some datetime
ruby-1.9.2-p290 :019 > dog.pocetak
=> Thu, 25 Aug 2011 08:18:00 UTC +00:00
So I was hoping, that .to_time would ditch my date, but that does not
happen
ruby-1.9.2-p290 :020 > dog.pocetak.to_time
=> Thu, 25 Aug 2011 08:18:00 UTC +00:00
so, now, comparing if something happened before 8:00 is useless.
So, how can I compare that? is there a way to set dog.pocetak to
2000-01-01 without touch clock?
thank you
p.s. also, I thought of creating new time variable, only to get from old variable hours and minutes, but this methods dont work?
ruby-1.9.2-p290 :059 > dog.pocetak.hour
=> 8
but
ruby-1.9.2-p290 :060 > dog.pocetak.minute
NoMethodError: undefined method `minute' for 2011-08-25 08:18:00 UTC:Time
from /home/dorijan/.rvm/gems/ruby-1.9.2-p290/gems/activesupport-3.0.10/lib/active_support/time_with_zone.rb:322:in `method_missing'
from (irb):60
from /home/dorijan/.rvm/gems/ruby-1.9.2-p290/gems/railties-3.0.10/lib/rails/commands/console.rb:44:in `start'
from /home/dorijan/.rvm/gems/ruby-1.9.2-p290/gems/railties-3.0.10/lib/rails/commands/console.rb:8:in `start'
from /home/dorijan/.rvm/gems/ruby-1.9.2-p290/gems/railties-3.0.10/lib/rails/commands.rb:23:in `<top (required)>'
from script/rails:6:in `require'
from script/rails:6:in `<main>'
ruby-1.9.2-p290 :061 > dog.pocetak.minutes
NoMethodError: undefined method `minutes' for 2011-08-25 08:18:00 UTC:Time
from /home/dorijan/.rvm/gems/ruby-1.9.2-p290/gems/activesupport-3.0.10/lib/active_support/time_with_zone.rb:322:in `method_missing'
from (irb):61
from /home/dorijan/.rvm/gems/ruby-1.9.2-p290/gems/railties-3.0.10/lib/rails/commands/console.rb:44:in `start'
from /home/dorijan/.rvm/gems/ruby-1.9.2-p290/gems/railties-3.0.10/lib/rails/commands/console.rb:8:in `start'
from /home/dorijan/.rvm/gems/ruby-1.9.2-p290/gems/railties-3.0.10/lib/rails/commands.rb:23:in `<top (required)>'
from script/rails:6:in `require'
from script/rails:6:in `<main>'
really frustrating :)
With ActiveSupport and Time.change you can reset the year, month and day if you like:
> t = Time.now
=> Sun Aug 21 00:46:29 +0000 2011
> t.change(:month => 1, :day => 1, :year => 2000)
=> Sat Jan 01 00:46:29 +0000 2000
This way you could compare the "times" between each other, if they all were reset to the same date. Not sure if this is a good solution though, depends on what you really are looking for.
EDIT:
As per mu's suggestion you could also take a look at the time data type.
To get the minutes from a Time object, you want min not minutes. You can't have a Time instance that's just a "time of day" (i.e. no year, month, ...) but you can use strftime to get a string version that will compare properly:
tod = Time.now.strftime('%H:%M:%S')
# "17:07:23"
if(t1.strftime('%H:%M:%S') == t2.strftime('%H:%M:%S'))
# Same time of day (to one second resolution)
end
Or you could compare the individual hour, min, and sec components:
if(t1.hour == t2.hour && t1.min == t2.min && t1.sec == t2.sec)
# Same time of day (to one second resolution)
end
Which approach you take depends, as usual, on your specific situation and what else is going in in that vicinity.

Resources