I've noticed some different behaviour between Rails 2 and Rails 3 when it comes to ActiveSupport date handling.
When I run the following code in a Rails 2.3 application it runs as I expect and outputs the dates one week at a time.
>> first = Date.today
=> Fri, 23 Mar 2012
>> last = Date.today + 2.months
=> Wed, 23 May 2012
>> first.step(last, 1.week) { |date| puts date }
2012-03-23
2012-03-30
2012-04-06
2012-04-13
2012-04-20
2012-04-27
2012-05-04
2012-05-11
2012-05-18
When I try the same code within a Rails 3 application I get the following.
>> first = Date.today
=> Fri, 23 Mar 2012
>> last = Date.today + 2.months
=> Wed, 23 May 2012
>> first.step(last, 1.week) { |date| puts date }
Mar 23, 2012
TypeError: expected numeric
The problems seems to be with how Rails 3 is now handling the .weeks method, Rails 2 outputs the following
>> 1.week
=> 7 days
Where Rails 3 outputs
>> 1.week
=> 604800
Can anyone explain what is going on here and how I can neatly iterate over a date range one week at a time in Rails 3.
No idea why it doesn't work, but this seems to:
(Date.today..(Date.today + 30)).step(7)
Related
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)
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.
Here goes :
Time.zone.now => "Eastern Time (US & Canada)"
Time.zone.now => Wed, 15 Aug 2012 06:05:37 EDT -04:00
Time.zone.now + 39.years => Tue, 15 Aug 2051 06:06:03 EST -05:00
And so you have it, the end of our fabled Eastern Daylight Time has been prophesied by Ruby on Rails to end in the year 2051.
Also works for any other TimeZone changing area.
Time.zone
=> "Pacific Time (US & Canada)"
1.9.2p180 :003 > Time.zone.now
=> Wed, 15 Aug 2012 03:08:57 PDT -07:00
1.9.2p180 :004 > Time.zone.now + 39.years
=> Tue, 15 Aug 2051 03:08:57 PST -08:00
This exists in Rails 3.0 and in Rails 3.2.6
Yes, it looks like a bug. It's not Rails, however, it's the Ruby Time class. It has problems with times after 2038.
For example, with Ruby 1.8.7:
> Time.local(2037,8,16,9,30,15)
=> Sun Aug 16 09:30:15 -0400 2037
>
> Time.local(2038,8,16,9,30,15)
=> Mon Aug 16 09:30:15 -0500 2038
JRuby 1.6.7.2 - for instance - does not have this problem:
> Time.local(2038,8,16,9,30,15)
=> Mon Aug 16 09:30:15 -0400 2038
Note that, on MRI Ruby on 64-bit systems, the ActiveSupport time extension which supports the addition of durations ultimately calls Time.local or Time.utc via this method in active_support/core_ext/time/calculations.rb:
# Returns a new Time if requested year can be accommodated by Ruby's Time class
# (i.e., if year is within either 1970..2038 or 1902..2038, depending on system architecture);
# otherwise returns a DateTime
def time_with_datetime_fallback(utc_or_local, year, month=1, day=1, hour=0, min=0, sec=0, usec=0)
::Time.send(utc_or_local, year, month, day, hour, min, sec, usec)
rescue
offset = utc_or_local.to_sym == :local ? ::DateTime.local_offset : 0
::DateTime.civil(year, month, day, hour, min, sec, offset)
end
I guess the issue is that for years >= 2038, they were expecting an overflow exception and for DateTime to be used instead. On 64-bit systems, this doesn't happen.
UPDATE: This analysis is incorrect for Ruby 1.9.2+. Time.local works as expected, but the original problem still occurs.
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.+#())
In Ruby, how do I do Time.now + 10.hours?
Is there an equivalent for secs and mins? For example:
Time.now + 15.mins
Ruby (the programming language) doesn't have 10.hours, that's ActiveSupport as part of Ruby on Rails (the web framework). And yes, it does have both minutes and seconds methods.
However, Time#+ (the + method on Time instances) returns a new Time instance that is that many seconds in the future. So without any Ruby on Rails sugar, you can simply do:
irb> t = Time.now
#=> 2011-08-03 22:35:01 -0600
irb> t2 = t + 10 # 10 Seconds
#=> 2011-08-03 22:35:11 -0600
irb> t3 = t + 10*60 # 10 minutes
#=> 2011-08-03 22:45:01 -0600
irb> t4 = t + 10*60*60 # 10 hours
#=> 2011-08-04 08:35:01 -0600
If you are using ActiveSupport, what you are looking for is the full .minutes and .seconds.
Time.now + 10.minutes
Time.now + 10.seconds
Also in ActiveSupport you can do:
10.minutes.from_now
10.minutes.ago
I think you're talking about extensions added by Rails. I think you need 15.minutes.
See the Active Support Core Extensions for Date, DateTime and Time for more information.
Time Object
time = Time.now
Adding minutes to a time object:
time + 5.minutes
There is an advance function in Active Support refer.
You can do the following using advance:
d = DateTime.new(2010, 2, 28, 23, 59, 59)
=> Sun, 28 Feb 2010 23:59:59 +0000
d.advance(hours: 1)
=> Mon, 01 Mar 2010 00:59:59 +0000