]> git.netwichtig.de Git - user/henk/code/ruby/rbot.git/blob - lib/rbot/core/utils/parse_time.rb
fix: TCPSocked.gethostbyname is deprecated
[user/henk/code/ruby/rbot.git] / lib / rbot / core / utils / parse_time.rb
1 #-- vim:sw=2:et
2 #++
3 #
4 # :title: rbot time parsing utilities
5 #
6 # Author:: Giuseppe "Oblomov" Bilotta <giuseppe.bilotta@gmail.com>
7 #
8 # These routines read a string and return the number of seconds they
9 # represent.
10
11 module ::Irc
12   module Utils
13     module ParseTime
14       FLOAT_RX = /((?:\d*\.)?\d+)/
15
16       ONE_TO_NINE = {
17         :one => 1,
18         :two => 2,
19         :three => 3,
20         :four => 4,
21         :five => 5,
22         :six => 6,
23         :seven => 7,
24         :eight => 8,
25         :nine => 9,
26       }
27
28       ONE_TO_NINE_RX = Regexp.new ONE_TO_NINE.keys.join('|')
29
30       TEENS_ETC = {
31         :an => 1,
32         :a => 1,
33         :ten => 10,
34         :eleven => 11,
35         :twelve => 12,
36         :thirteen => 13,
37         :fourteen => 14,
38         :fifteen => 15,
39         :sixteen => 16,
40         :seventeen => 17,
41         :eighteen => 18,
42         :nineteen => 19,
43       }
44
45       TEENS_ETC_RX = Regexp.new TEENS_ETC.keys.join('|')
46
47       ENTIES = {
48         :twenty => 20,
49         :thirty => 30,
50         :forty => 40,
51         :fifty => 50,
52         :sixty => 60,
53       }
54
55       ENTIES_RX = Regexp.new ENTIES.keys.join('|')
56
57       LITNUM_RX = /(#{ONE_TO_NINE_RX})|(#{TEENS_ETC_RX})|(#{ENTIES_RX})\s*(#{ONE_TO_NINE_RX})?/
58
59         FRACTIONS = {
60         :"half" => 0.5,
61         :"half a" => 0.5,
62         :"half an" => 0.5,
63         :"a half" => 0.5,
64         :"a quarter" => 0.25,
65         :"a quarter of" => 0.25,
66         :"a quarter of a" => 0.25,
67         :"a quarter of an" => 0.25,
68         :"three quarter" => 0.75,
69         :"three quarters" => 0.75,
70         :"three quarter of" => 0.75,
71         :"three quarters of" => 0.75,
72         :"three quarter of a" => 0.75,
73         :"three quarters of a" => 0.75,
74         :"three quarter of an" => 0.75,
75         :"three quarters of an" => 0.75,
76       }
77
78       FRACTION_RX = Regexp.new FRACTIONS.keys.join('|')
79
80       UNITSPEC_RX = /(years?|months?|s(?:ec(?:ond)?s?)?|m(?:in(?:ute)?s?)?|h(?:(?:ou)?rs?)?|d(?:ays?)?|weeks?)/
81
82       # str must much UNITSPEC_RX
83       def ParseTime.time_unit(str)
84         case str[0,1].intern
85         when :s
86           1
87         when :m
88           if str[1,1] == 'o'
89             # months
90             3600*24*30
91           else
92             #minutes
93             60
94           end
95         when :h
96           3600
97         when :d
98           3600*24
99         when :w
100           3600*24*7
101         when :y
102           3600*24*365
103         end
104       end
105
106       # example: half an hour, two and a half weeks, 5 seconds, an hour and 5 minutes
107       def ParseTime.parse_period(str)
108         clean = str.gsub(/\s+/, ' ').strip
109
110         sofar = 0
111         until clean.empty?
112           if clean.sub!(/^(#{FRACTION_RX})\s+#{UNITSPEC_RX}/, '')
113             # fraction followed by unit
114             num = FRACTIONS[$1.intern]
115             unit = ParseTime.time_unit($2)
116           elsif clean.sub!(/^#{FLOAT_RX}\s*(?:\s+and\s+(#{FRACTION_RX})\s+)?#{UNITSPEC_RX}/, '')
117             # float plus optional fraction followed by unit
118             num = $1.to_f
119             frac = $2
120             unit = ParseTime.time_unit($3)
121             clean.strip!
122             if frac.nil? and clean.sub!(/^and\s+(#{FRACTION_RX})/, '')
123               frac = $1
124             end
125             if frac
126               num += FRACTIONS[frac.intern]
127             end
128           elsif clean.sub!(/^(?:#{LITNUM_RX})\s+(?:and\s+(#{FRACTION_RX})\s+)?#{UNITSPEC_RX}/, '')
129             if $1
130               num = ONE_TO_NINE[$1.intern]
131             elsif $2
132               num = TEENS_ETC[$2.intern]
133             elsif $3
134               num = ENTIES[$3.intern]
135               if $4
136                 num += ONE_TO_NINE[$4.intern]
137               end
138             end
139             frac = $5
140             unit = ParseTime.time_unit($6)
141             clean.strip!
142             if frac.nil? and clean.sub!(/^and\s+(#{FRACTION_RX})/, '')
143               frac = $1
144             end
145             if frac
146               num += FRACTIONS[frac.intern]
147             end
148           else
149             raise "invalid time string: #{clean} (parsed #{sofar} so far)"
150           end
151           sofar += num * unit
152           clean.sub!(/^and\s+/, '')
153         end
154         return sofar
155       end
156
157       # TODO 'at hh:mm:ss', 'next week, 'tomorrow', 'saturday' etc
158     end
159
160     def Utils.parse_time_offset(str)
161       case str
162       when /^(\d+):(\d+)(?:\:(\d+))?$/ # TODO refactor
163         hour = $1.to_i
164         min = $2.to_i
165         sec = $3.to_i
166         now = Time.now
167         later = Time.mktime(now.year, now.month, now.day, hour, min, sec)
168
169         # if the given hour is earlier than current hour, given timestr
170         # must have been meant to be in the future
171         if hour < now.hour || hour <= now.hour && min < now.min
172           later += 60*60*24
173         end
174
175         return later - now
176       when /^(\d+):(\d+)(am|pm)$/ # TODO refactor
177         hour = $1.to_i
178         min = $2.to_i
179         ampm = $3
180         if ampm == "pm"
181           hour += 12
182         end
183         now = Time.now
184         later = Time.mktime(now.year, now.month, now.day, hour, min, now.sec)
185         return later - now
186       else
187         ParseTime.parse_period(str)
188       end
189     end
190
191   end
192 end
193