~mgorven/ibid/smtp-fixes

« back to all changes in this revision

Viewing changes to ibid/plugins/tools.py

  • Committer: Michael Gorven
  • Date: 2010-01-02 16:01:58 UTC
  • mfrom: (817.1.22 timezone-335551)
  • Revision ID: michael@gorven.za.net-20100102160158-6mmt53pfqolc6m8a
Add timezone plugin.
https://code.launchpad.net/~mgorven/ibid/timezone-335551/+merge/16726

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
import re
2
2
from random import random, randint
3
3
from subprocess import Popen, PIPE
 
4
from datetime import datetime
 
5
from os.path import exists, join
 
6
 
 
7
from dateutil.parser import parse
 
8
from dateutil.tz import gettz, tzutc, tzlocal, tzoffset
4
9
 
5
10
from ibid.plugins import Processor, match
6
11
from ibid.config import Option
7
 
from ibid.utils import file_in_path, unicode_output
 
12
from ibid.utils import file_in_path, unicode_output, human_join, format_date, json_webservice
 
13
from ibid.compat import defaultdict
8
14
 
9
15
help = {}
10
16
 
107
113
            else:
108
114
                event.addresponse(u"I can't do that: %s", result)
109
115
 
 
116
class TimezoneException(Exception):
 
117
    pass
 
118
 
 
119
MONTH_SHORT = ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec')
 
120
MONTH_LONG = ('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December')
 
121
OTHER_STUFF = ('am', 'pm', 'st', 'nd', 'rd', 'th')
 
122
 
 
123
help['timezone'] = "Converts times between timezones."
 
124
class TimeZone(Processor):
 
125
    u"""when is <time> <place|timezone> in <place|timezone>
 
126
    time in <place|timezone>"""
 
127
    feature = 'timezone'
 
128
 
 
129
    zoneinfo = Option('zoneinfo', 'Timezone info directory', '/usr/share/zoneinfo')
 
130
 
 
131
    countries = {}
 
132
    timezones = defaultdict(list)
 
133
 
 
134
    def setup(self):
 
135
        iso3166 = join(self.zoneinfo, 'iso3166.tab')
 
136
        if exists(iso3166):
 
137
            for line in open(iso3166).readlines():
 
138
                if not line.startswith('#'):
 
139
                    code, name = line.strip().split('\t')
 
140
                    self.countries[code] = name
 
141
 
 
142
        zones = join(self.zoneinfo, 'zone.tab')
 
143
        if exists(zones):
 
144
            for line in open(zones).readlines():
 
145
                if not line.startswith('#'):
 
146
                    code, coordinates, zone = line.strip().split('\t', 2)
 
147
                    if '\t' in zone:
 
148
                        zone, comment = zone.split('\t')
 
149
                    self.timezones[code].append(zone)
 
150
 
 
151
    def _find_timezone(self, string):
 
152
        ccode = None
 
153
 
 
154
        if string.upper() in ('GMT', 'UTC', 'UCT', 'ZULU'):
 
155
            string = 'UTC'
 
156
        zone = gettz(string)
 
157
 
 
158
        if not zone:
 
159
            for code, name in self.countries.items():
 
160
                if name.lower() == string.lower():
 
161
                    ccode = code
 
162
            if not ccode:
 
163
                if string.replace('.', '').upper() in self.timezones:
 
164
                    ccode = string.replace('.', '').upper()
 
165
 
 
166
            if ccode:
 
167
                if len(self.timezones[ccode]) == 1:
 
168
                    zone = gettz(self.timezones[ccode][0])
 
169
                else:
 
170
                    raise TimezoneException(u'%s has multiple timezones: %s' % (self.countries[ccode], human_join(self.timezones[ccode])))
 
171
 
 
172
            else:
 
173
                possibles = []
 
174
                for zones in self.timezones.values():
 
175
                    for name in zones:
 
176
                        if string.replace(' ', '_').lower() in name.lower():
 
177
                            possibles.append(name)
 
178
 
 
179
                if len(possibles) == 1:
 
180
                    zone = gettz(possibles[0])
 
181
                elif len(possibles) > 1:
 
182
                    raise TimezoneException(u'Multiple timezones found: %s' % (human_join(possibles)))
 
183
                else:
 
184
                    zone = self._geonames_lookup(string)
 
185
                    if not zone:
 
186
                        raise TimezoneException(u"I don't know about the %s timezone" % (string,))
 
187
 
 
188
        return zone
 
189
 
 
190
    def _geonames_lookup(self, place):
 
191
        search = json_webservice('http://ws.geonames.org/searchJSON', {'q': place, 'maxRows': 1})
 
192
        if search['totalResultsCount'] == 0:
 
193
            return None
 
194
 
 
195
        city = search['geonames'][0]
 
196
        timezone = json_webservice('http://ws.geonames.org/timezoneJSON', {'lat': city['lat'], 'lng': city['lng']})
 
197
 
 
198
        if 'timezoneId' in timezone:
 
199
            return gettz(timezone['timezoneId'])
 
200
 
 
201
        if 'rawOffset' in timezone:
 
202
            offset = timezone['rawOffset']
 
203
            return tzoffset('UTC%s%s' % (offset>=0 and '+' or '', offset), offset*3600)
 
204
 
 
205
    @match(r'^when\s+is\s+((?:[0-9.:/hT -]|%s)+)(?:\s+(.+))?\s+in\s+(.+)$' % '|'.join(MONTH_SHORT+MONTH_LONG+OTHER_STUFF))
 
206
    def convert(self, event, time, from_, to):
 
207
        source = time and parse(time) or datetime.now()
 
208
 
 
209
        try:
 
210
            if from_:
 
211
                from_zone = self._find_timezone(from_)
 
212
            else:
 
213
                from_zone = tzlocal()
 
214
 
 
215
            to_zone = self._find_timezone(to)
 
216
        except TimezoneException, e:
 
217
            event.addresponse(unicode(e))
 
218
            return
 
219
 
 
220
        source = source.replace(tzinfo=from_zone)
 
221
        result = source.astimezone(to_zone)
 
222
 
 
223
        event.addresponse(time and u'%(source)s is %(destination)s' or 'It is %(destination)s', {
 
224
            'source': format_date(source, tolocaltime=False),
 
225
            'destination': format_date(result, tolocaltime=False),
 
226
        })
 
227
 
 
228
    @match(r"^(?:(?:what(?:'?s|\s+is)\s+the\s+)?time\s+in|what\s+time\s+is\s+it\s+in)\s+(.+)$")
 
229
    def time(self, event, place):
 
230
        self.convert(event, None, None, place)
 
231
 
110
232
# vi: set et sta sw=4 ts=4: