Rails TimeWithZone and leap years - ruby-on-rails

Today is Feb 29th: the day our tests broke.
Our tests are failing because these tests do things that, in the end, can be compared to a command like this: Time.zone.now - 1.year + 1.year. And it happens to be NOT equal to Time.zone.now.
Why does this happen? Why isn't ActiveSupport able to handle leap years in calculations like that? Doesn't it work with timestamps, that would prevent this sort of problem from happening?

You could use 4 years instead of 1 for the same effect. (Note: - 1.day because it is 1st March right now)
(Time.zone.now - 1.day) - 4.year + 4.year
=> Mon, 29 Feb 2016 15:12:58 UTC +00:00
It may be worth noting that (below), regardless of date.
Time.zone.now
=> Tue, 01 Mar 2016 15:11:51 UTC +00:00
Time.zone.now == Time.zone.now - 1.year + 1.year
=> false
Unless you use beginning_of_day:
(Time.zone.now - 1.day).beginning_of_day == ((Time.zone.now - 1.day) - 4.year + 4.year).beginning_of_day
=> true
Also, it depends what you are actually trying to do... Why would you want to test Time.now - 1.year + 1.year ?
Also...
1.year == 365.days
=> false
1.year == 365.25.days
=> true

Related

Get the difference in days between two dates Rails "Date"

What is the proper way to subtract or add dates in Rails?
I tried the intuitive way but got Rational:
irb(main):089:0> Date.today.increase_by("3 days")
=> Sun, 19 May 2019
irb(main):090:0> Date.today
=> Thu, 16 May 2019
irb(main):091:0> Date.today.increase_by("3 days") - Date.today
=> (3/1)
Disclaimer: Please note that I am new to Ruby and Rails as well. 2 months of experience so far :)
You can use
Date.today # Thu, 16 May 2019
Date.today + 3 # Thu, 19 May 2019
Date.today - 3 # Thu, 13 May 2019
For Difference
Date.today + 3 # Thu, 19 May 2019
Date.today - 3 # Thu, 13 May 2019
(d1 - d2).to_i # 6 (Days)

Storing time in rails

I want to store a date 3 months from the current date and put it into current_user.expiry_date.
current_user.expiry_date = Date.today + 3.months
I'm new to doing dates but is this sufficient? expiry_date is of data type date. This would give an expiry date 3 months after this attribute is set. Anything wrong with this?
That looks fine to me. It should give you the date 3 months from now.
irb(main):001:0> require 'active_support/all'
=> true
irb(main):002:0> Date.today + 3.months
=> Mon, 21 Nov 2016
irb(main):003:0> (Date.today + 3.months).class
=> Date

Ruby expression evaluation: whitespace matters?

Imagine it's Jan 19. This will not be hard if you look at this question today.
Date.today
=> Thu, 19 Jan 2012 # as expected
Date.today + 1
=> Fri, 20 Jan 2012 # as expected
Date.today+1
=> Fri, 20 Jan 2012 # as expected
Date.today +1
=> Thu, 19 Jan 2012 # ?!
What am I missing here?
The difference is that:
Date.today + 1
is an addition of two numerical values and
Date.today +1
is a call to the method today with the parameter sg(day of calendar reform) with value +1
The best way to examine this is to monkey patch the original method with debug output included. See this script as example:
require 'date'
class Date
def self.today(sg=ITALY)
puts "ITALY default("+sg.to_s+")" if sg==ITALY
puts sg unless sg==ITALY
jd = civil_to_jd(*(Time.now.to_a[3..5].reverse << sg))
new0(jd_to_ajd(jd, 0, 0), 0, sg)
end
end
puts "- Addition:"
Date.today + 1
puts "- Parameter:"
Date.today +1
This will print the following console output:
- Addition:
ITALY default(2299161)
- Parameter:
1
Yes, whitespace does matter in Ruby, contrary to popular belief. For example, foo bar is not the same as foobar.
In this particular case,
Date.today + 1
is the same as
Date.today().+(1)
Whereas
Date.today +1
is the same as
Date.today(+1)
which is the same as
Date.today(1.+#())

"Ago" date/time functions in Ruby/Rails

I was wondering if there's a way in Rails to calculate time stamp like - half a minute ago, 2 minute ago, 1 day ago etc. Something like twitter real time date stamp.
I want to know if Ruby/Rails has a built-in function for such date-time conversion?
You can use:
10.minutes.ago
2.days.since
Or in your views you have the helpers:
distance_of_time_in_words(from_time, to_time)
time_ago_in_words(from_time)
Check the API for details and more options.
You can use available methods to get the time in past or future using ago, since alias for from_now and many available methods
Time.current
#=> Tue, 20 Sep 2016 15:03:30 UTC +00:00
2.minutes.ago
#=> Tue, 20 Sep 2016 15:01:30 UTC +00:00
2.minutes.since
#=> Tue, 20 Sep 2016 15:05:30 UTC +00:00
1.month.ago
#=> Sat, 20 Aug 2016 15:03:30 UTC +00:00
1.year.since
#=> Wed, 20 Sep 2017 15:03:30 UTC +00:00
Check all the available methods in Time class
distance_of_time_in_words:
from_time = Time.now
distance_of_time_in_words(from_time, from_time + 50.minutes) # => about 1 hour
distance_of_time_in_words(from_time, 50.minutes.from_now) # => about 1 hour
distance_of_time_in_words(from_time, from_time + 15.seconds) # => less than a minute
distance_of_time_in_words(from_time, from_time + 15.seconds, include_seconds: true) # => less than 20 seconds
time_ago_in_words:
time_ago_in_words(3.minutes.from_now) # => 3 minutes
time_ago_in_words(3.minutes.ago) # => 3 minutes
time_ago_in_words(Time.now - 15.hours) # => about 15 hours

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