I have an object attribute of the DateTime class.
How would I understand if the saved date is today, tomorrow or else later?
Here are some useful ways to achieve it:
datetime = DateTime.now => Sun, 26 Oct 2014 21:00:00
datetime.today? # => true
datetime.to_date.past? # => false (only based on date)
datetime.to_date.future? # => false (only based on date)
datetime.to_date == Date.tomorrow # => false
datetime.to_date == Date.yesterday # => false
Something like...
datetime = Time.now.to_datetime
=> Sun, 26 Oct 2014 16:24:55 -0600
datetime >= Date.today
=> true
datetime < Date.tomorrow
=> true
datetime += 1.day
=> Mon, 27 Oct 2014 16:25:12 -0600
datetime >= Date.today
=> true
datetime >= Date.tomorrow
=> true
datetime < (Date.tomorrow + 1.day)
=> false
?
yesterday? & tomorrow? (Rails 6.1+)
Rails 6.1 adds new #yesterday? and #tomorrow? methods to Date and Time classes.
As a result, now, your problem can be solved as:
datetime = DateTime.current
# => Mon, 16 Nov 2020 20:50:16 +0000
datetime.today?
# => true
datetime.yesterday?
# => false
datetime.tomorrow?
# => false
It is also worth to mention that #yesterday? and #tomorrow? are aliased to #prev_day? and #next_day?.
Here is a link to the corresponding PR.
Related
How can I make ActiveModel::Dirty ignore milliseconds when comparing datetime in Rails.
news.publish_at
=> Fri, 16 Nov 2018 17:05:37 CET +01:00
news.publish_at = news.publish_at.to_s
=> "2018-11-16 17:05:37 +0100"
news.publish_at_changed?
=> true
But if I add milliseconds to the above datetime, changed? is false.
news.publish_at = "2018-11-16 17:05:37.517 +0100"
=> "2018-11-16 17:05:37.517 +0100"
news.publish_at_changed?
=> false
I am using Rails 5.2.1
I think the simplest workaround might be to just add a custom setter to that model that truncates the milliseconds everytime when assigning a new value.
def publish_at=(value)
if value
time_without_usec = DateTime.parse(value).change(usec: 0)
value = time_without_usec.to_s
end
super(value)
end
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.
I ran into this non-sense problem this morning in Rails 3.2 console. I'm under MacOS 10.10, my timezone is +7.
Loading development environment (Rails 3.2.12)
irb(main):001:0> Date.today
=> Sun, 16 Nov 2014
irb(main):002:0> Date.yesterday
=> Fri, 14 Nov 2014
irb(main):003:0>
Everything is fine with original Ruby Date:
irb(main):006:0> Date.today
=> #<Date: 2014-11-16 ((2456978j,0s,0n),+0s,2299161j)>
irb(main):007:0> Date.today.prev_day
=> #<Date: 2014-11-15 ((2456977j,0s,0n),+0s,2299161j)>
irb(main):008:0>
From the bug report here: https://rails.lighthouseapp.com/projects/8994/tickets/6410#ticket-6410-8
This is a subtle one - Date.yesterday uses Date.current which will use the time zone whereas Date.today doesn't. If you set your time zone to one where it's tomorrow already (e.g. Europe/Berlin as I type this) then you can get Date.today == Date.yesterday:
Time.zone = "Europe/London"
=> "Europe/London"
Date.today == Date.yesterday
=> false
Time.zone = "Europe/Berlin"
=> "Europe/Berlin"
Date.today == Date.yesterday
=> true
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
Im working on a small Rails application. I have set my timezone to IST by changing my config file to
config.time_zone = 'Kolkata'
I have a model which takes a virtual attribute Subscription and converts in into two datetimes valid_from and valid_to, The code is as follows:
def subscription=(value)
case value
when "WEEKLY"
self.valid_from = DateTime.now
self.valid_to = DateTime.now + 7.days
when "MONTHLY"
self.valid_from = DateTime.now
self.valid_to = DateTime.now >> 1
when "HALF YEARLY"
self.valid_from = DateTime.now
self.valid_to = DateTime.now >> 6
when "YEARLY"
self.valid_from = DateTime.now
self.valid_to = DateTime.now >> 12
end
end
It stores the values perfectly, but when I retrieve the number of days its giving me wrong days. i have tried it out in console to show the results, Here are the results:
irb(main):017:0> x = a.valid_from
=> Wed, 16 Apr 2014 14:15:04 IST +05:30
irb(main):018:0> y = a.valid_to
=> Wed, 23 Apr 2014 14:15:04 IST +05:30
irb(main):019:0> z = DateTime.now
=> Wed, 16 Apr 2014 14:19:07 +0530
irb(main):020:0> w = DateTime.now + 7.days
=> Wed, 23 Apr 2014 14:19:28 +0530
irb(main):021:0> p = (y-x).to_i
=> 604800
irb(main):022:0> q = (w-z).to_i
=> 7
irb(main):023:0> Time.zone
=> (GMT+05:30) Mumbai
I can see the difference in times in mine it shows IST + 5:30 and in DateTime.now it just shows +5:30. But I dont know why that is happening. I'm new to rails and if someone could let me know about it I will be very grateful.
Also i tried to decode the value 604800. I guessed that it has to be something x*7 and so I got the value of x to be 86400.
Now 86400 = 24*60*60. This implies I have been getting the value in seconds. Can I know why this is happening? I would like to get it in days.