Overview
Whenever I'm working with an ORM other than ActiveRecord, I miss the relative date locating
functionality like 1.day.ago
and 5.years.ahead
so I mashed up Timetastic to provide
those features to any Ruby script.
Every feature directly depends and deals with Ruby Time
implementation. While I'm unaware of
any gotchas or limitations, it's probably good to know.
Update: there used to be logic that manually wrapped the dates and times, but as I found
out that I could make Ruby do it automatically using Time.at
with the dates converted to epoch
seconds, I dropped the code I wrote because I'm pretty sure Ruby's implementation is more robust.
Locating relative times
The locators are either "past" or "future" ones, all relative to today's date. The available time domains are: hours, days, weeks, months, and years.
-
5.days.ago
or1.day.ago
(singular and plural versions are available for every domain) -
6.months.ahead
or1.year.hence
It properly accomodates wrapping when it's necessary. Knowing that June is 30 days long:
Timetastic.fixate(2012, 6, 30) {
1.day.ahead # => 2012-07-01 00:00:00 +0300
}
Timetastic.fixate(2012, 6, 30, 20) {
4.hours.ahead # => 2012-07-01 00:00:00 +0300
}
The wrapping functionality works for all domains, and in both directions; forward into future dates and backwards to past ones. It's also smart enough to take leap years into account:
# 2012 is not a leap year, February is 29 days long
Timetastic.fixate(2012, 3, 1) {
1.day.ago # => 2012-02-29 00:00:00 +0200
}
# 2011 is a leap year, February is 28 days long
Timetastic.fixate(2011, 3, 1) {
1.day.ago # => 2011-02-28 00:00:00 +0200
}
Some module methods are available that produce anchored dates; the beginning of intervals like months, years, or days. These are useful when, for example, you need to retrieve all records created since a given month, or year, regardless of the current day, or month, respectively.
-
Timetastic.last.month
: points to the start of last month -
Timetastic.last(5).years
: points to the start of the 5th year before the current one -
Timetastic.coming.month
orTimetastic.next.month
: point to the start of the coming month
You can also change the time anchor which is by default set to Time.now
by calling fixate
:
Timetastic.fixate(2012, 6, 1) {
1.month.ahead # => 2012-07-01 00:00:00 +0300
3.days.ago # => 2012-05-29 00:00:00 +0300
}
It's also possible to do it at the time of the selection. All methods accept a relative_to
argument:
1.month.ahead(Time.new(2012, 6, 1)) # => 2012-07-01 00:00:00 +0300
Timetastic.last(1, Time.new(2012, 6, 1)).month # => 2012-05-01 00:00:00 +0300
But as you can see, the interface to the per-method fixating isn't very sexy.
Really far away times
Need to reach a month that's further behind than last year? It's also good:
Timetastic.fixate(2012, 6, 1) {
10.months.ago.should == Time.new(2011, 8, 1)
13.months.ago.should == Time.new(2011, 5, 1)
36.months.ago.should == Time.new(2009, 6, 1)
48.months.ago.should == Time.new(2008, 6, 1)
60.months.ago.should == Time.new(2007, 6, 1)
66.months.ago.should == Time.new(2006, 12, 1)
# or go forward
23.months.ahead.should == Time.new(2014, 5, 1)
24.months.ahead.should == Time.new(2014, 6, 1)
}
Timetastic will handle the proper wrapping of dates, it should never throw an Out of Range argument error or so.
Eww, you're polluting Fixnum!
Yes! And I find it totally sensible to do in this case as it is a truly convenient interface
and makes for a good read. Besides, I tried to keep it to a minimum by moving some of the
less commonly used ops into the Timetastic
module itself.
Legal stuff
Timetastic
the gem is licensed under the MIT terms like PageHub is.
Copyright (c) 2012 Ahmad Amireh
Permission is hereby granted, free of charge, to any person obtaining a copy of this
software and associated documentation files (the "Software"), to deal in the Software
without restriction, including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software, and to permit
persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies
or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
OR OTHER DEALINGS IN THE SOFTWARE.