Although result of Fixnum#day, Fixnum#days, Fixnum#week, etc looks
like a new Fixnum with seconds in given period it's actually not a Fixnum.
Let's take a quick look that might lead us toward pretty wrong direction:
1.week.is_a? Fixnum # => true
1.week.instance_of? Fixnum # => true
1.week.class # => Fixnum
1.week.to_i # => 604800
1.week.to_s # => "604800"
puts 1.week # Prints out: 604800Looks like a Fixnum, right? No, it's fucking not! Let's see:
604800.inspect # => "604800"
1.week.inspect # => "7 days"Quiet interesting, huh? Anyway, this rant is not about interesting things at all. It's an attempt to help you to not cut shoot your own foot while playing with Rails...
Let's talk about Time first. It provides us with following API:
time = Time.now # => 2016-04-18 17:49:31 +0200
time.class # => Time
time.to_i # => 1460994571
# `time:Time + seconds:Numeric # => new_time:Time`
# Get new Time instance with 1 hour (in seconds) ahead:
time + 60 * 60 # => 2016-04-18 18:49:31 +0200
# `time:Time - seconds:Numeric # => new_time:Time`
# Get new Time instance with 1 day (in seconds) behind:
time - 24 * 60 * 60 # => 2016-04-17 17:49:31 +0200
# `time:Time - other_time:Time # => duration:Float`
# Get time duration in seconds between two times (one day in example below):
Time.new(2016, 2, 1) - Time.new(2016, 1, 31) # => 86400.0As you can see, when we add/remove Numeric (Float or Integer) from Time
it assumes value to be some duration in seconds. Thus it's absolutely safe and
natural to call:
time - 1.weekThe tricky part comes when you start working with Date:
date = Date.today # => Mon, 18 Apr 2016
date.class # => Date
date.to_i # => fails with NoMethodErrorMost important thing to know about Date is that it operates with (!) days:
# date:Date + days:Integer # => new_date:Date
# Get new date instance with 7 days ahead:
date + 7 # => Mon, 25 Apr 2016
# date:Date - days:Integer # => new_date:Date
# Get new date instance with 3 days behind:
date - 3 # => Fri, 15 Apr 2016
# date:Date - other_date:Date # => duration:Rational
# Get amount of days between to dates:
Date.new(2016, 2, 1) - Date.new(2016, 1, 31) # => (1/1)That brings us to a pretty interesting code:
date - 1.weekMost likely without any background like given in the beginning your guess will
be right, it will return new date with 7 days (1 week) behind. But as soon as
you will call #to_i on result of 1.week it will become real Integer and
it will return new date with 604_800 days behind:
date - 1.week # => Mon, 11 Apr 2016
date - 1.week.to_i # => Mon, 29 May 0360So, every time you work with Time or Date, make sure to:
- ensure that your variable is either
TimeorDate - if you are working with
Datedon't use Rails extensions - if you are working with
Timefeel free to use Rails extensions
Why you need to use Rails extensions when you work with Time?
Because it provides decent and predictable API:
1.week.ago # returns Time.now - 1.week
1.month.from_now # returns Time.now + 1.month
time - 1.month # => 2016-03-18 15:49:31 +0000
time + 2.months # => 2016-06-18 15:49:31 +0000See, how month(s) helper helps you avoid lots of calculations...
What if you want to add/remove month from Date. In addition to -/+
which receive amount of days, Date provides << and >> to remove/add
months:
date << 3 # => Mon, 18 Jan 2016
date >> 3 # => Mon, 18 Jul 2016