Use Timespans in Ruby :)
Will calculate time diff between two dates, then allow you to get the time difference in some time unit as a number.
t = Timespan.new(:start => Date.today, :duration => 3.days)
t.to_days # => 3
t.to_weeks # => 0
t.to_secs # => 259200
t.to_hours = 10800
t = Timespan.new("2 days") # from today
t = Timespan.new(2.days) # from today
t = Timespan.new(200) # 200 secs from today
t = Timespan.new(duration: 2.days) # specific use of :duration option
t = Timespan.new("3 hrs").from(2.days.from_now)
t = Timespan.new(:from => 2.days.ago)
t = Timespan.new(:end_date => 4.days.from_now)
t = Timespan.new(:from => Date.today, :to => "6 weeks from now")
t = Timespan.new(:from => Date.today, :duration => "7 weeks 3 days")
t = Timespan.new(:from => 2.days.ago, :duration => "5 months and 2 weeks")
See specs for more examples of usage
gem 'mongoid'
gem 'origin-selectable_ext'
gem 'timespan'
timespan.between?(2.days.ago, 1.minute.from_now)
timespan.between?(1.days, 3.days)
timespan < 3.days
3_days_more = timespan + 3.days
day_less = timespan -1 3.day
Internally Timespan uses Spanner to parse duration strings.
`Spanner.parse('23 hours 12 minutes')
The type Duration
is supported via the xduration gem, which extends ruby-duration
with additional macros, specific time unit classes and supports longer duration time periods such as weeks, months and years.
Duration.new(100) => #<Duration: minutes=1, seconds=40, total=100>
Duration.new(:hours => 5, :minutes => 70) => #<Duration: hours=6, minutes=10, total=22200>
Duration.new(:weeks => 3, :days => 1).format("%w %~w and %d %~d") => "3 weeks and 1 day"
Duration.new(:weeks => 1, :days => 20).format("%w %~w and %d %~d") => "3 weeks and 6 days"
Duration locale file
da:
ruby_duration:
second: sekond
seconds: sekonder
minute: minut
minutes: minutter
hour: time
hours: timer
day: dag
days: dage
week: uge
weeks: uges
month: måned
months: måneder
year: år
years: år
Duration datatype for Mongoid
require 'duration/mongoid'
class MyModel
include Mongoid::Document
field :duration, type => ::Duration
end
Timespan locale file
da:
timespan:
from: fra
to: til
lasting: der varer ialt
Tested and works with Mongoid 2.4 and 3.0+ (also tested with Mongoid 4)
Custom Timespan datatype:
Mongoid::Timespanned
adds the following class level macros:
timespan_methods target, *names
timespan_delegates target, *names
timespan_delegate name, target = :period
timespan_setters target, *names
timespan_setter target, name
timespan_container_delegates container, timespan_field, *names
timespan_container_delegate container, timespan_field, name
Note that all the macros, take an options
Hash as the last argument, where you can set override: true
in order to override any existing methods. Otherwise an ArgumentError
exception will be raised, to warn you of a method name conflict!
require 'timespan/mongoid'
class Account
include Mongoid::Document
include Mongoid::Timespanned
field :period, :type => Timespan
timespan_methods :period
embeds_one :time_period
timespan_container_delegates :time_period, :dates, :start, :end
end
class TimePeriod
include Mongoid::Document
include Mongoid::Timespanned
field :dates, :type => ::Timespan, :between => true
embedded_in :account
timespan_methods :dates
end
Note: See mongoid_timespan_spec.rb
for more examples of usage, and also see the ClassMethods
module in timespanned.rb
:)
Usage example:
account = Account.create :period => {:duration => '2 days', :from => Date.today }
account.period.start_date
account.period.end_date
account.period.days
account.period.duration # => Duration
# using timespan setters defined by timespan_methods
account.period_start = tomorrow
account.period_end = 5.days.from_now
# using timespan delegates defined by timespan_methods
account.start_date == tomorrow
account.end_date == tomorrow
# using timespan_container_delegates on time_period
account.start_date = tomorrow
account.end_date = tomorrow + 5.days
Timespan now has the class level factory methods #from
and #untill
.
account = Account.create :period => Timespan.from :tomorrow, 7.days
# today
Timespan.from :today, 7.days
# now
Timespan.from :asap, 7.days
Timespan.from :now, 7.days
# now
Timespan.from :today, 7.days
# starting one week from today
Timespan.from :next_week, 7.days
# starting first day next week
Timespan.from :next_week, 7.days, start: true
# starting first day next month
Timespan.from :next_month, 7.days, start: true
Creates timespan from Time.now
until the time specified.
Timespan.untill :tomorrow
Timespan.untill :next_week
Timespan.untill :next_month, start: true
Timespan.untill 2.days.from_now
Account.where(:'period.from'.lt => 6.days.ago.to_i)
Account.where(:'period.from'.gt => 3.days.ago.to_i)
# in range
Account.where(:'period.from'.gt => 3.days.ago.to_i, :'period.to'.lt => Time.now.utc.to_i)
Make it easier by introducing a class helper:
class Account
include Mongoid::Document
field :period, :type => Timespan
def self.between from, to
Account.where(:'period.from'.gt => from.to_i, :'period.to'.lte => to.to_i)
end
end
Account.between(6.days.ago, 1.day.ago)
Alternatively auto-generate a #between
helper for the field:
class Account
include Mongoid::Document
field :period, :type => TimeSpan, :between => true
Account.period_between(6.days.ago, 1.day.ago)
See the mongoid_search_spec.rb
for examples:
This gem can be used in combination with Time lord, which has a lot of functionality for time periods, conversions, calculations and outputting to strings etc.
Some initial support for time-lord
has been added, but not yet tested.
Please help out ;)
Since integration with time-lord is not yet complete and tested, to use the experimental features, use:
require 'timespan/extensions/time_lord'
There are known issues with time_lord so that:
4.days.from_now
returns a date far into the future!
I hope this gets fixed soon!
Is used to parse duration strings if Spanner can't be handle it
`ChronicDuration.parse('4 minutes and 30 seconds')
Use the 'endure' gem based on the old "days_and_times".
See: days_and_times
Currently it also uses Duration, which conflicts with the 'ruby-duration' gem.
1.day #=> A duration of 1 day
7.days #=> A duration of 7 days
1.week #=> A duration of 1 week
1.week - 2.days #=> A duration of 5 days
1.week.from(Now()) #=> The time of 1 week from this moment
1.week.from(Today()) #=> The time of 1 week from the beginning of today
3.minutes.ago.until(7.minutes.from(Now())) #=> duration 3 minutes ago to 7 minutes from now
3.minutes.ago.until(7.minutes.from(Now())) - 2.minutes #=> duration 3 minutes ago to 5 minutes from now
4.weeks.from(2.days.from(Now())).until(8.weeks.from(Yesterday())) #=> A duration, starting in 4 weeks and 2 days, and ending 8 weeks from yesterday
1.week - 1.second #=> A duration of 6 days, 23 hours, 59 minutes, and 59 seconds
4.weeks / 2 #=> A duration of 2 weeks
4.weeks / 2.weeks #=> The integer 2
8.weeks.each {|week| ...} #=> Runs code for each week contained in the duration (of 8 weeks)
8.weeks.starting(Now()).each {|week| ...} #=> Runs code for each week in the duration, but each week is also anchored to a starting time, in sequence through the duration.
1.week.each {|week| ...} #=> Automatically chooses week as its iterator
7.days.each {|day| ...} #=> Automatically chooses day as its iterator
1.week.each_day {|day| ...} #=> Forcing the week to iterate through days
1.week.each(10.hours) {|ten_hour_segment| ...} #=> Using a custom iterator of 10 hours. There would be 17 of them, but notice that the last iteration will only be 8 hours.
``
## Configuration and overrides
Timespan by default uses `Time.now.utc` to set the current time, fx used when either `end_date` or `start_date` otherwise would be nil. This is used in order to work with Mongoid (see [issue #400](https://github.com/mongoid/mongoid/issues/400))
You can customize `now` to return fx `Time.now`, `Date.today` or whatever suits you.
```ruby
class Timespan
def now
Time.now # or Date.today
end
end
By default the TimeSpan
is stored using :from
and :to
for the start and end times. This can be customized as follows:
TimeSpan.start_field = :start
TimeSpan.end_field = :end
A Range can be converted into either a Timespan
or a DurationRange
dr = (1..5).days # => DurationRange 1..5, :days
ts =(1..5).days(:timespan) # => Timespan start_date: 1.day.from_now, end_date: 5.days.from_now
dr.between?(4.days) # => true
You can also use Range#intersect from sugar-high gem to test intersection of time ranges ;)
See https://github.com/kristianmandrup/sugar-high/blob/master/spec/sugar-high/range_spec.rb
date_range.intersect(other_date_range).should == intersecting_range
subject.intersect(other).should == nil # if no intersection
The duration range by default supports the following units: [seconds, minutes, hours, days, weeks, months, years]
You can subclass the DurationRange to supply your own list of time units to fit your particular scenario.
This gem comes with two subclasses:
ShortDurationRange
and LongDurationRange
They both override the method #allowed_units
to provide a particular list:
ShortDurationRange
supports time ranges less than a day, whereas LongDurationRange
only supports time ranges of a day or more.
To create a LongDurationRange
or ShortDurationRange
from a range, you can use the macros:
long_time_range = (1..5).days(:long)
# or simply
long_time_range = (1..5).days!
# and for short time range
long_time_range = (1..5).minutes(:short)
short_time_range = (1..5).hours!
This gem now includes som javascript assets to assist in performing date and timespan (duration) calculations on the client side also:
moment.js
(1.7.2)date_ext.js
DP_DateExtensionstimespan.js
Date.timeleft("12/10/2012", "07/05/2013");
Date.timeleft(Date.create("12/10/2012"), "07/05/2013");
Aliases for timeleft are: duration
and timespan
.
See the javascript source for the full API or check out http://momentjs.com/docs/ and http://depressedpress.com/javascript-extensions/dp_dateextensions/.
Note: timeleft was extracted from (http://www.proglogic.com/code/javascript/time/timeleft.php)
To use these assets with the Asset pipeline, simply add this to your application.js
or similar manifest file :) (after jquery which is required!)
//= require moment.js
//= require date_ext.js
//= require timespan.js
Copyright (c) 2012 Kristian Mandrup. See LICENSE.txt for further details.