]> git.netwichtig.de Git - user/henk/code/ruby/rbot.git/commitdiff
Utils: time parsing routines
authorGiuseppe Bilotta <giuseppe.bilotta@gmail.com>
Wed, 26 Aug 2009 18:52:31 +0000 (20:52 +0200)
committerGiuseppe Bilotta <giuseppe.bilotta@gmail.com>
Wed, 26 Aug 2009 18:52:31 +0000 (20:52 +0200)
Add time parsing routines to Utils, to be used for human-to-computer
conversion of time offsets. Refactored and enhanced from the remind
plugin.

data/rbot/plugins/remind.rb
lib/rbot/core/utils/parse_time.rb [new file with mode: 0644]

index 804e3f0bfd8968003182852d7b8b2f9b8727e6db..1c1a21a93fc356cdf8f42583a98e275d9e387b75 100644 (file)
@@ -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 (file)
index 0000000..bedb2d5
--- /dev/null
@@ -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
+