Why does Date.yesterday counts as Date.today also? - ruby-on-rails

I have the following model and methods:
class UserPrice < ActiveRecord::Base
attr_accessible :price, :purchase_date,
def self.today
where(:purchase_date => Date.today)
end
def self.yesterday
where(:purchase_date => Date.yesterday)
end
Why on my form if I give my date_select field :purchase_date 1/4/2012(yesterday method) it also counts as today(today method) and if I give it 1/5/2012 (today) it is nil?
P.S. I am using Rails 3.0.10 with PostgreSQL.
Update
This is my console returns:
$ rails console --s
Loading development environment in sandbox (Rails 3.0.10)
Any modifications you make will be rolled back on exit
irb(main):001:0> Date.today
=> Wed, 04 Jan 2012
irb(main):002:0> Time.now
=> 2012-01-04 21:28:07 -0500
irb(main):003:0> Time.zone.now
=> Thu, 05 Jan 2012 02:28:18 UTC +00:00
irb(main):004:0> Date.yesterday
=> Wed, 04 Jan 2012
irb(main):005:0>
Now yesterday is screwed up, makes no sense....

This is happening because calculations.rb is calling the "current" method of the Date class for the configured timezone (Defaults to UTC).
If you open the rails console you can see the date at which "Date.yesterday" is calculating on by doing:
Time.zone.today
This will probably show you tomorrow's date. So Date.yesterday for what rails sees as today, is today. ;)
You can work with the Date library more directly by doing:
Date.today.advance(:days => -1)
This will give you yesterday's date like you expect whereas active_support is returning:
Date.current.advance(:days => -1)

If you use the Time class, you'll have access to UTC (or "zulu") time zone rather than using the environment's time zone in Date class.
class UserPrice < ActiveRecord::Base
attr_accessible :price, :purchase_date,
def self.today
where(purchase_date: today_utc_date)
end
def self.yesterday
where(purchase_date: today_utc_date.yesterday)
end
private
def today_utc_date
#today ||= Time.zone.today
end
end
Also, if you need to process an outside date, for example params[:purchase_date]:
Time.parse(params[:purchase_date]).utc.to_date

Related

Why does an ActiveRecord object return DateTime.now differently than DateTime.now

Say I have a simple ActiveRecord model with this example schema:
create_table "users", id: :serial do |t|
t.datetime "updated_at"
end
When I run the following code in the console:
user = User.new
user.updated_at = DateTime.now
user.updated_at # => Thu, 27 Feb 2020 09:28:51 GMT +00:00
My question is: why this output is different than the normal return value of DateTime.now, which is
DateTime.now # => Thu, 27 Feb 2020 10:28:51 +0100
Is there some kind of serializer involved? How can I find out more about this?
AR updated_at/created_at are time zone aware attributes. You can read more about them here:
https://api.rubyonrails.org/classes/ActiveRecord/Timestamp.html
If you grab a User record for instance, and call time_zone_aware_types on it, you'll get the timezone aware column types
user = User.first
user.time_zone_aware_types
=> [:datetime, :time]
These types of columns are automatically converted to Time.zone when retrieved from the database even though they are stored in UTC.
The difference with DateTime.now is that it's not timezone aware and just returns the time in UTC
add your time zone in your config/application.rb inside the
class Application < Rails::Application
.......
# it will be like this
config.time_zone = "Asia/Riyadh"
.....
end

Changing of difference between created_at and frozen Time.now after reloading of object

I have spec which checks calculation of difference between Time.now and created_at attribute of object. I stubbed Time.now, so this value is constant. Also I've set Time.now to created_at, but this value changes after reloading of object. How is it possible and how can I freeze created_at after object reloading?
This is an example of issue:
time = Time.now
=> 2015-03-19 15:50:13 UTC
Time.stubs :now => time
=> #<Expectation:0x9938830 allowed any number of times...
user = User.last
=> #<User:0x000000097a6e40...
user.update_attribute :created_at, Time.now - 1.minute
=> true
user.created_at
=> Thu, 19 Mar 2015 15:49:13 UTC +00:00
Time.now - user.created_at
=> 60.0
Time.now - user.reload.created_at
=> 60.442063277
I use rails 4.2.0, ruby 2.2.0 and rspec 2.14.1
Just reset nanoseconds:
time = Time.now.change(nsec: 0)
or milliseconds:
time = Time.now.change(usec: 0)
Here details.

Comparing times with time zones in rails

I am using Ruby 2.1.2 and Rails 4.1.4
tl;dr; I am getting a date & time from the user that comes into the controller from a form (via params) like this:
pickuptime = params[:appointment][:pickuptime]
(byebug) pickuptime
"01/06/2015 7:26 PM"
and I need to make sure it is in the future so that customers can't create appointments in the past, and I have the customer's time_zone stored in the database because customers may be in different places.
Detail:
I am getting a time in my controller via a form:
pickuptime = params[:appointment][:pickuptime]
(byebug) pickuptime
"01/06/2015 7:26 PM"
I then convert it to a DateTime so that I can build an appointment record:
pickuptime = DateTime.strptime(pickuptime, "%m/%d/%Y %l:%M %p")
#appointment = #car.appointments.build(garage_id: garage_id, pickuptime: pickuptime)
My Appointment model has a validation that ensures that you cannot create an appointment in the past:
def pickuptime_is_in_future
if pickuptime < Time.current
errors.add(:pickuptime, "Appointment must be in the future")
end
end
I would like to take into account the customer's time_zone (#car.garage.time_zone), which is stored in the database, when building the appointment, e.g.
pry(main)> Garage.first.time_zone
Garage Load (0.6ms) SELECT "garages".* FROM "garages" ORDER BY "garages"."id" ASC LIMIT 1
=> "Eastern Time (US & Canada)"
Right now this is not working. When I create an appointment for 1 minute in the future, I get the following:
(byebug) pickuptime
Tue, 06 Jan 2015 19:22:00 UTC +00:00
(byebug) Time.current
Wed, 07 Jan 2015 00:21:53 UTC +00:00
which causes the validation to fail, despite the fact that the appointment was created in the future and should therefore succeed.
Any guidance on how to implement this correctly would be much appreciated!
Use in_time_zone.
See https://www.reinteractive.net/posts/168-dealing-with-timezones-effectively-in-rails & http://www.elabs.se/blog/36-working-with-time-zones-in-ruby-on-rails for good advice on working with timezones in rails.

How to validate :created_at in model (RAILS 3)

irb(main):044:0> i1.created_at
=> Thu, 24 Apr 2014 02:41:15 UTC +00:00
irb(main):045:0> i2.created_at
=> Thu, 24 Apr 2014 02:41:15 UTC +00:00
irb(main):046:0> i1.created_at == i2.created_at
=> false
irb(main):047:0> i1.created_at.to_time.to_i == i2.created_at.to_time.to_i
=> true
Seems not to work validates_uniqueness_of :created_at
because
irb(main):046:0> i1.created_at == i2.created_at
=> false
How to validate created_at? Don't want to save with the same date.
+++ UPDATE +++
irb(main):048:0> i1.created_at.class
=> ActiveSupport::TimeWithZone
irb(main):049:0> i2.created_at.class
=> ActiveSupport::TimeWithZone
Since they might have different precision milliseconds.
Refer to the post: Testing ActiveSupport::TimeWithZone objects for equality
Chances are the millisecond values would be unequal.
puts i1.created_at.usec
puts i2.created_at.usec
I think, if you are getting concurrent requests, there are chances that you may have multiple entries in the table which are created at same time and will have same time stamps.
As you said, if you don't want to save with the same date, you can put a lock while saving the entries, removing the possibility of creating two entries at same time. In that case validates_uniqueness_of :created_at should also work.
Just in case
class Item < ActiveRecord::Base
attr_accessible :asin, :domain, :formatted_price, :user_id, :created_at
validate :double_dates
private
def double_dates
if Item.where(:user_id => self.user_id, :asin => self.asin, :domain => self.domain).where("DATE(created_at) = ?", Date.today).length >= 1
errors.add(:created_at, "no double dates")
end
end
end

Is Time.zone.now.to_date equivalent to Date.today?

Is Time.zone.now.to_date equivalent to Date.today?
Another way to put it: will Time.zone.now.to_date == Date.today always be true?
If not, what's the best way to get a Date object corresponding to "now" in the application time zone?
They are not always the same. Time.zone.now.to_date will use the applications time zone, while Date.today will use the servers time zone. So if the two lie on different dates then they will be different. An example from my console:
ruby-1.9.2-p290 :036 > Time.zone = "Sydney"
=> "Sydney"
ruby-1.9.2-p290 :037 > Time.zone.now.to_date
=> Wed, 21 Sep 2011
ruby-1.9.2-p290 :038 > Date.today
=> Tue, 20 Sep 2011
Even easier: Time.zone.today
I also wrote a little helper method Date.today_in_zone that makes it really easy to get a "today" Date for a specific time zone without having to change Time.zone:
# Defaults to using Time.zone
> Date.today_in_zone
=> Fri, 26 Oct 2012
# Or specify a zone to use
> Date.today_in_zone('Sydney')
=> Sat, 27 Oct 2012
To use it, just throw this in a file like 'lib/date_extensions.rb' and require 'date_extensions'.
class Date
def self.today_in_zone(zone = ::Time.zone)
::Time.find_zone!(zone).today
end
end
I think the best way is to learn the current time through:
Time.current
This will automatically check to see if you have timezone set then it will call Time.zone.now, but if you've not it will call just Time.now.
Also, don't forget to set your timezone in application.rb
# system timezone
Time.now.to_date == Date.today
# application timezone
Time.zone.now.to_date == Time.current.to_date == Time.zone.today == Date.current
http://edgeapi.rubyonrails.org/classes/Time.html#method-c-current
http://edgeapi.rubyonrails.org/classes/Date.html#method-c-current

Resources