Skip to content

Instantly share code, notes, and snippets.

@brianjlandau
Created January 24, 2010 20:43
Show Gist options
  • Select an option

  • Save brianjlandau/285431 to your computer and use it in GitHub Desktop.

Select an option

Save brianjlandau/285431 to your computer and use it in GitHub Desktop.
A Time Duration object that is meant to primarily be used inside a Rails app (as it requires i18n and ActiveSupport). It aslo uses chronic_duration to parse a time period/duration word phrase..
en:
datetime:
distance_in_words:
x_weeks:
one: '1 week'
other: "{{count}} weeks"
x_hours:
one: "1 hour"
other: "{{count}} hours"
x_years:
one: "1 year"
other: "{{count}} years"
x_seconds:
one: "1 second"
other: "{{count}} seconds"
x_minutes:
one: "1 minute"
other: "{{count}} minutes"
x_days:
one: "1 day"
other: "{{count}} days"
require 'i18n'
require 'active_support/memoizable'
gem 'brianjlandau-chronic_duration'
require 'chronic_duration'
ChronicDuration.raise_exceptions = true
class TimeDuration
include Comparable
extend ActiveSupport::Memoizable
SECOND = 1
MINUTE = 60 * SECOND
HOUR = 60 * MINUTE
DAY = 24 * HOUR
WEEK = 7 * DAY
YEAR = (365.25 * DAY).to_i
attr_reader :total_seconds
def self.parse(duration)
new(ChronicDuration.parse(duration))
end
def self.[](seconds)
new(seconds)
end
def initialize(seconds)
@total_seconds = seconds.to_i
end
def <=>(other)
total_seconds <=> other.to_i
end
def +(other)
self.class.new(total_seconds + other.to_i)
end
def -(other)
self.class.new(total_seconds - other.to_i)
end
def -@
self.class.new(-total_seconds)
end
def +@
self.class.new(+total_seconds)
end
def self.===(other)
other.is_a?(TimeDuration) rescue super
end
def years ; to_h[:years] ; end
memoize :years
def weeks ; to_h[:weeks] ; end
memoize :weeks
def days ; to_h[:days] ; end
memoize :days
def hours ; to_h[:hours] ; end
memoize :hours
def minutes ; to_h[:minutes] ; end
memoize :minutes
def seconds ; to_h[:seconds] ; end
memoize :seconds
def inspect
date_parts = []
%w(years weeks days).each do |date_segment|
if self.send(date_segment) > 0
date_parts << "#{self.send(date_segment)} #{date_segment}"
end
end
date_string = date_parts.join(' ')
time_string = '%02d:%02d:%02d' % [hours, minutes, seconds]
"#<#{self.class.name}: #{[date_string, time_string].join(' ').strip}>"
end
def to_i
total_seconds
end
def to_f
total_seconds.to_f
end
def to_h
h, s = {}, total_seconds
h[:years], s = *s.divmod(YEAR)
h[:weeks], s = *s.divmod(WEEK)
h[:days], s = *s.divmod(DAY)
h[:hours], s = *s.divmod(HOUR)
h[:minutes], s = *s.divmod(MINUTE)
h[:seconds], s = *s.divmod(SECOND)
h
end
memoize :to_h
def strftime(fmt)
hx = {
'y' => to_h[:years] ,
'w' => to_h[:weeks] ,
'd' => to_h[:days] ,
'h' => to_h[:hours] ,
'm' => to_h[:minutes],
's' => to_h[:seconds],
't' => total_seconds
}
fmt.gsub(/%?%(y|w|d|h|m|s|t)/) do |match|
hx[match[1..1]]
end.gsub('%%', '%')
end
def distance_of_time_in_words(options = {})
words = []
I18n.with_options :locale => options[:locale], :scope => :'datetime.distance_in_words' do |locale|
if years > 0
words << locale.t(:x_years, :count => years)
end
if weeks > 0
words << locale.t(:x_weeks, :count => weeks)
end
if days > 0
words << locale.t(:x_days, :count => days)
end
if hours > 0
words << locale.t(:x_hours, :count => hours)
end
if minutes > 0
words << locale.t(:x_minutes, :count => minutes)
end
if seconds > 0
words << locale.t(:x_seconds, :count => seconds)
end
end
return words.to_sentence(:words_connector => ' ', :last_word_connector => ' and ')
end
alias to_s distance_of_time_in_words
private
def method_missing(method, *args, &block)
total_seconds.send(method, *args)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment