1.1 --- a/EventAggregatorSupport.py Sun Mar 14 02:32:51 2010 +0100
1.2 +++ b/EventAggregatorSupport.py Sun Mar 14 21:06:15 2010 +0100
1.3 @@ -45,12 +45,12 @@
1.4
1.5 # Value parsing.
1.6
1.7 -country_code_regexp = re.compile(ur'(?:^|\s)(?P<code>[A-Z]{2})(?:$|\s)', re.UNICODE)
1.8 +country_code_regexp = re.compile(ur'(?:^|\W)(?P<code>[A-Z]{2})(?:$|\W+$)', re.UNICODE)
1.9
1.10 month_regexp_str = ur'(?P<year>[0-9]{4})-(?P<month>[0-9]{2})'
1.11 date_regexp_str = ur'(?P<year>[0-9]{4})-(?P<month>[0-9]{2})-(?P<day>[0-9]{2})'
1.12 time_regexp_str = ur'(?P<hour>[0-2][0-9]):(?P<minute>[0-5][0-9])(?::(?P<second>[0-6][0-9]))?'
1.13 -timezone_regexp_str = ur'(?P<zone>[A-Z]{3,}|[a-zA-Z]+/[-_a-zA-Z]+)'
1.14 +timezone_regexp_str = ur'(?P<zone>[A-Z]{3,}|[a-zA-Z]+/[-_a-zA-Z]+|[-+][0-9]{1,4})'
1.15 datetime_regexp_str = date_regexp_str + ur'(?:\s+' + time_regexp_str + ur'(?:\s+' + timezone_regexp_str + ur')?)?'
1.16
1.17 month_regexp = re.compile(month_regexp_str, re.UNICODE)
1.18 @@ -828,6 +828,9 @@
1.19 def __str__(self):
1.20 return "%04d-%02d" % self.as_tuple()[:2]
1.21
1.22 + def as_datetime(self, day, hour, minute, second, zone):
1.23 + return DateTime(self.as_tuple() + (day, hour, minute, second, zone))
1.24 +
1.25 def as_date(self, day):
1.26 return Date(self.as_tuple() + (day,))
1.27
1.28 @@ -911,6 +914,9 @@
1.29 def __str__(self):
1.30 return "%04d-%02d-%02d" % self.as_tuple()[:3]
1.31
1.32 + def as_datetime(self, hour, minute, second, zone):
1.33 + return DateTime(self.as_tuple() + (hour, minute, second, zone))
1.34 +
1.35 def as_date(self):
1.36 return self
1.37
1.38 @@ -960,7 +966,6 @@
1.39
1.40 def __init__(self, data):
1.41 Date.__init__(self, data)
1.42 - self.utc_offset = None
1.43
1.44 def __str__(self):
1.45 if self.has_time():
1.46 @@ -975,6 +980,9 @@
1.47
1.48 return Date.__str__(self) + time_str
1.49
1.50 + def as_datetime(self):
1.51 + return self
1.52 +
1.53 def as_date(self):
1.54 return Date(self.data[:3])
1.55
1.56 @@ -987,9 +995,8 @@
1.57 def time_zone(self):
1.58 return self.data[6]
1.59
1.60 - def set_time_zone(self, value, utc_offset=None):
1.61 + def set_time_zone(self, value):
1.62 self.data[6] = value
1.63 - self.utc_offset = utc_offset
1.64
1.65 def padded(self):
1.66
1.67 @@ -998,53 +1005,139 @@
1.68 data = map(lambda x: x or 0, self.data[:6]) + self.data[6:]
1.69 return DateTime(data)
1.70
1.71 + def to_utc(self):
1.72 +
1.73 + """
1.74 + Return this object converted to UTC, or None if such a conversion is not
1.75 + defined.
1.76 + """
1.77 +
1.78 + offset = self.utc_offset()
1.79 + if offset:
1.80 + hours, minutes = offset
1.81 +
1.82 + # Invert the offset to get the correction.
1.83 +
1.84 + hours, minutes = -hours, -minutes
1.85 +
1.86 + # Get the components.
1.87 +
1.88 + hour, minute, second, zone = self.as_tuple()[3:]
1.89 + date = self.as_date()
1.90 +
1.91 + # Add the minutes and hours.
1.92 +
1.93 + minute += minutes
1.94 + if minute < 0:
1.95 + hour -= 1
1.96 + minute += 60
1.97 + elif minute > 59:
1.98 + hour += 1
1.99 + minute -= 60
1.100 +
1.101 + hour += hours
1.102 + if hour < 0:
1.103 + date = date.previous_day()
1.104 + hour += 24
1.105 + elif hour > 23:
1.106 + date = date.next_day()
1.107 + hour -= 24
1.108 +
1.109 + return date.as_datetime(hour, minute, second, "UTC")
1.110 + else:
1.111 + return None
1.112 +
1.113 + def utc_offset(self):
1.114 +
1.115 + "Return the UTC offset in hours and minutes."
1.116 +
1.117 + zone = self.time_zone()
1.118 +
1.119 + # Only attempt to return a UTC offset where an explicit offset has been
1.120 + # set.
1.121 +
1.122 + if zone and zone[0] in "-+":
1.123 + digits = zone[1:]
1.124 + if zone[0] == "-":
1.125 + sign = -1
1.126 + else:
1.127 + sign = 1
1.128 +
1.129 + if 1 <= len(digits) <= 2:
1.130 + return int(digits) * sign, 0
1.131 + elif len(digits) == 3:
1.132 + hours = int(digits[:1]) * sign
1.133 + minutes = int(digits[1:]) * sign
1.134 + return hours, minutes
1.135 + elif len(digits) == 4:
1.136 + hours = int(digits[:2]) * sign
1.137 + minutes = int(digits[2:]) * sign
1.138 + return hours, minutes
1.139 +
1.140 + return None
1.141 +
1.142 def apply_location(self, location):
1.143
1.144 """
1.145 - Apply 'location' information, setting the time zone if none is already
1.146 - set.
1.147 + Apply 'location' information, setting the time zone if none has already
1.148 + been set.
1.149 """
1.150
1.151 if not self.time_zone():
1.152 -
1.153 - # Only try and set a time zone if pytz is present and able to
1.154 - # suggest one.
1.155 + zone = getTimeZone(location)
1.156 + if zone:
1.157 + self.set_time_zone(zone)
1.158
1.159 - if pytz is not None:
1.160 +def getTimeZone(location):
1.161
1.162 - # Find a country code in the location.
1.163 + "Find a time zone for the specified 'location'."
1.164 +
1.165 + # Only try and find a time zone if pytz is present and able to suggest one.
1.166
1.167 - match = country_code_regexp.search(location)
1.168 + if pytz is None:
1.169 + return None
1.170
1.171 - if match:
1.172 + code = getCountry(location)
1.173
1.174 - # Attempt to discover zones for that country.
1.175 + if code is None:
1.176 + return None
1.177
1.178 - try:
1.179 - zones = pytz.country_timezones(match.group("code"))
1.180 + try:
1.181 + zones = pytz.country_timezones(code)
1.182 + except KeyError:
1.183 + return None
1.184
1.185 - # Unambiguous choice of zone.
1.186 + # No zones...
1.187
1.188 - if len(zones) == 1:
1.189 - self.set_time_zone(zones[0], pytz.timezone(zones[0]).utcoffset(None))
1.190 + if not zones:
1.191 + return None
1.192 +
1.193 + # Many potential zones.
1.194
1.195 - # Many potential zones.
1.196 + if len(zones) > 1:
1.197 + for zone in zones:
1.198 + continent, city = zone.split("/")
1.199
1.200 - elif len(zones) > 1:
1.201 - for zone in zones:
1.202 - continent, city = zone.split("/")
1.203 + # If the specific city is mentioned, choose the
1.204 + # zone.
1.205 +
1.206 + if location.find(city) != -1:
1.207 + return zone
1.208
1.209 - # If the specific city is mentioned, choose the
1.210 - # zone.
1.211 + # Otherwise choose the first or only zone.
1.212 +
1.213 + return zones[0]
1.214 +
1.215 +def getCountry(s):
1.216
1.217 - if location.find(city) != -1:
1.218 - self.set_time_zone(zone, pytz.timezone(zone).utcoffset(None))
1.219 - break
1.220 - else:
1.221 - self.set_time_zone(zones[0], pytz.timezone(zones[0]).utcoffset(None))
1.222 + "Find a country code in the given string 's'."
1.223 +
1.224 + match = country_code_regexp.search(s)
1.225
1.226 - except KeyError:
1.227 - pass
1.228 + if match:
1.229 + return match.group("code")
1.230 + else:
1.231 + return None
1.232
1.233 def getDate(s):
1.234