diff options
-rw-r--r-- | data/rbot/plugins/remind.rb | 96 | ||||
-rw-r--r-- | lib/rbot/core/utils/parse_time.rb | 185 |
2 files changed, 186 insertions, 95 deletions
diff --git a/data/rbot/plugins/remind.rb b/data/rbot/plugins/remind.rb index 804e3f0b..1c1a21a9 100644 --- a/data/rbot/plugins/remind.rb +++ b/data/rbot/plugins/remind.rb @@ -5,101 +5,7 @@ class RemindPlugin < Plugin # # Throws:: RunTimeError "invalid time string" on parse failure def timestr_offset(timestr) - case timestr - when (/^(\S+)\s+(\S+)$/) - mult = $1 - unit = $2 - if(mult =~ /^([\d.]+)$/) - num = $1.to_f - raise "invalid time string" unless num - else - case mult - when(/^(one|an|a)$/) - num = 1 - when(/^two$/) - num = 2 - when(/^three$/) - num = 3 - when(/^four$/) - num = 4 - when(/^five$/) - num = 5 - when(/^six$/) - num = 6 - when(/^seven$/) - num = 7 - when(/^eight$/) - num = 8 - when(/^nine$/) - num = 9 - when(/^ten$/) - num = 10 - when(/^fifteen$/) - num = 15 - when(/^twenty$/) - num = 20 - when(/^thirty$/) - num = 30 - when(/^sixty$/) - num = 60 - else - raise "invalid time string" - end - end - case unit - when (/^(s|sec(ond)?s?)$/) - return num - when (/^(m|min(ute)?s?)$/) - return num * 60 - when (/^(h|h(ou)?rs?)$/) - return num * 60 * 60 - when (/^(d|days?)$/) - return num * 60 * 60 * 24 - else - raise "invalid time string" - end - when (/^(\d+):(\d+)(?:\:(\d+))?$/) - hour = $1.to_i - min = $2.to_i - sec = $3.to_i - now = Time.now - later = Time.mktime(now.year, now.month, now.day, hour, min, sec) - - # if the given hour is earlier than current hour, given timestr - # must have been meant to be in the future - if hour < now.hour || hour <= now.hour && min < now.min - later += 60*60*24 - end - - return later - now - when (/^(\d+):(\d+)(am|pm)$/) - hour = $1.to_i - min = $2.to_i - ampm = $3 - if ampm == "pm" - hour += 12 - end - now = Time.now - later = Time.mktime(now.year, now.month, now.day, hour, min, now.sec) - return later - now - when (/^(\S+)$/) - num = 1 - unit = $1 - case unit - when (/^(s|sec(ond)?s?)$/) - return num - when (/^(m|min(ute)?s?)$/) - return num * 60 - when (/^(h|h(ou)?rs?)$/) - return num * 60 * 60 - when (/^(d|days?)$/) - return num * 60 * 60 * 24 - else - raise "invalid time string" - end - else - raise "invalid time string" - end + Utils.parse_time_offset(timestr) end def initialize diff --git a/lib/rbot/core/utils/parse_time.rb b/lib/rbot/core/utils/parse_time.rb new file mode 100644 index 00000000..bedb2d5f --- /dev/null +++ b/lib/rbot/core/utils/parse_time.rb @@ -0,0 +1,185 @@ +#-- vim:sw=2:et +#++ +# +# :title: rbot time parsing utilities +# +# Author:: Giuseppe "Oblomov" Bilotta <giuseppe.bilotta@gmail.com> +# +# These routines read a string and return the number of seconds they +# represent. + +module ::Irc + module Utils + module Time + FLOAT_RX = /((?:\d*\.)?\d+)/ + + ONE_TO_NINE = { + :one => 1, + :two => 2, + :three => 3, + :four => 4, + :five => 5, + :six => 6, + :seven => 7, + :eight => 8, + :nine => 9, + } + + ONE_TO_NINE_RX = Regexp.new ONE_TO_NINE.keys.join('|') + + TEENS_ETC = { + :an => 1, + :a => 1, + :ten => 10, + :eleven => 11, + :twelve => 12, + :thirteen => 13, + :fourteen => 14, + :fifteen => 15, + :sixteen => 16, + :seventeen => 17, + :eighteen => 18, + :nineteen => 19, + } + + TEENS_ETC_RX = Regexp.new TEENS_ETC.keys.join('|') + + ENTIES = { + :twenty => 20, + :thirty => 30, + :forty => 40, + :fifty => 50, + :sixty => 60, + } + + ENTIES_RX = Regexp.new ENTIES.keys.join('|') + + LITNUM_RX = /(#{ONE_TO_NINE_RX})|(#{TEENS_ETC_RX})|(#{ENTIES_RX})\s*(#{ONE_TO_NINE_RX})?/ + + FRACTIONS = { + :"half" => 0.5, + :"half a" => 0.5, + :"half an" => 0.5, + :"a half" => 0.5, + :"a quarter" => 0.25, + :"a quarter of" => 0.25, + :"a quarter of a" => 0.25, + :"a quarter of an" => 0.25, + :"three quarter" => 0.75, + :"three quarters" => 0.75, + :"three quarter of" => 0.75, + :"three quarters of" => 0.75, + :"three quarter of a" => 0.75, + :"three quarters of a" => 0.75, + :"three quarter of an" => 0.75, + :"three quarters of an" => 0.75, + } + + FRACTION_RX = Regexp.new FRACTIONS.keys.join('|') + + UNITSPEC_RX = /(s(?:ec(?:ond)?s?)?|m(?:in(?:ute)?s?)?|h(?:(?:ou)?rs?)?|d(?:ays?)?|weeks?)/ + + # str must much UNITSPEC_RX + def Time.time_unit(str) + case str[0,1].intern + when :s + 1 + when :m + 60 + when :h + 3600 + when :d + 3600*24 + when :w + 3600*24*7 + end + end + + # example: half an hour, two and a half weeks, 5 seconds, an hour and 5 minutes + def Time.parse_period(str) + clean = str.gsub(/\s+/, ' ').strip + + sofar = 0 + until clean.empty? + if clean.sub!(/^(#{FRACTION_RX})\s+#{UNITSPEC_RX}/, '') + # fraction followed by unit + num = FRACTIONS[$1.intern] + unit = Time.time_unit($2) + elsif clean.sub!(/^#{FLOAT_RX}\s*(?:\s+and\s+(#{FRACTION_RX})\s+)?#{UNITSPEC_RX}/, '') + # float plus optional fraction followed by unit + num = $1.to_f + frac = $2 + unit = Time.time_unit($3) + clean.strip! + if frac.nil? and clean.sub!(/^and\s+(#{FRACTION_RX})/, '') + frac = $1 + end + if frac + num += FRACTIONS[frac.intern] + end + elsif clean.sub!(/^(?:#{LITNUM_RX})\s+(?:and\s+(#{FRACTION_RX})\s+)?#{UNITSPEC_RX}/, '') + if $1 + num = ONE_TO_NINE[$1.intern] + elsif $2 + num = TEENS_ETC[$2.intern] + elsif $3 + num = ENTIES[$3.intern] + if $4 + num += ONE_TO_NINE[$4.intern] + end + end + frac = $5 + unit = Time.time_unit($6) + clean.strip! + if frac.nil? and clean.sub!(/^and\s+(#{FRACTION_RX})/, '') + frac = $1 + end + if frac + num += FRACTIONS[frac.intern] + end + else + raise "invalid time string: #{clean} (parsed #{sofar} so far)" + end + sofar += num * unit + clean.sub!(/^and\s+/, '') + end + return sofar + end + + # TODO 'at hh:mm:ss', 'next week, 'tomorrow', 'saturday' etc + end + + def Utils.parse_time_offset(str) + case str + when /^(\d+):(\d+)(?:\:(\d+))?$/ # TODO refactor + hour = $1.to_i + min = $2.to_i + sec = $3.to_i + now = ::Time.now + later = ::Time.mktime(now.year, now.month, now.day, hour, min, sec) + + # if the given hour is earlier than current hour, given timestr + # must have been meant to be in the future + if hour < now.hour || hour <= now.hour && min < now.min + later += 60*60*24 + end + + return later - now + when /^(\d+):(\d+)(am|pm)$/ # TODO refactor + hour = $1.to_i + min = $2.to_i + ampm = $3 + if ampm == "pm" + hour += 12 + end + now = ::Time.now + later = ::Time.mktime(now.year, now.month, now.day, hour, min, now.sec) + return later - now + else + Time.parse_period(str) + end + end + + end +end + |