Initial checkin

This commit is contained in:
Dennis Kaarsemaker
2006-06-26 19:57:20 +02:00
commit aea38c93ff
43 changed files with 4193 additions and 0 deletions

5
Webcal/README.txt Normal file
View File

@ -0,0 +1,5 @@
This plugin can update a topic given an iCal schedule. It's made to work for
#ubuntu-meeting on Freenode (and iCal from fridge.ubuntu.com) but in theory it
should work with other iCal feeds too. It's not hard to understand.
Requirements: included ical module and pytz

34
Webcal/__init__.py Normal file
View File

@ -0,0 +1,34 @@
###
# Copyright (c) 2005,2006 Dennis Kaarsemaker
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of version 2 of the GNU General Public License as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
###
"""
Update the topic according to an iCal schedule
"""
import supybot
import supybot.world as world
__version__ = "0.2"
__author__ = supybot.Author("Dennis Kaarsemaker","Seveas","dennis@kaarsemaker.net")
__contributors__ = {}
__url__ = 'http://bugbot.ubuntulinux.nl/'
import config
reload(config)
import plugin
reload(plugin)
if world.testing:
import test
Class = plugin.Class
configure = config.configure

28
Webcal/config.py Normal file
View File

@ -0,0 +1,28 @@
###
# Copyright (c) 2005,2006 Dennis Kaarsemaker
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of version 2 of the GNU General Public License as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
###
import supybot.conf as conf
import supybot.registry as registry
def configure(advanced):
from supybot.questions import expect, anything, something, yn
conf.registerPlugin('Webcal', True)
Webcal = conf.registerPlugin('Webcal')
conf.registerChannelValue(conf.supybot.plugins.Webcal, 'url',
registry.String('',"""Webcal URL for the channel"""))
conf.registerChannelValue(conf.supybot.plugins.Webcal, 'topic',
registry.String('',"""Topic template"""))
conf.registerGlobalValue(conf.supybot.plugins.Webcal, 'defaultChannel',
registry.String('',"""Default channel for /msg replies"""))

209
Webcal/ical.py Normal file
View File

@ -0,0 +1,209 @@
#!/usr/bin/python
# Slightly modified version of the iCal module found at
# http://www.devoesquared.com/Software/iCal_Module
import os
import os.path
import re
import datetime
import time
import pytz # pytz can be found on http://pytz.sourceforge.net
SECONDS_PER_DAY=24*60*60
class ICalReader:
def __init__(self, data):
self.events = []
self.raw_data = data
self.readEvents()
def readEvents(self):
self.events = []
lines = self.raw_data.split('\n')
inEvent = False
eventLines = []
stRegex = re.compile("^BEGIN:VEVENT")
enRegex = re.compile("^END:VEVENT")
for line in lines:
if stRegex.match(line):
inEvent = True
eventLines = []
if inEvent:
eventLines.append(line)
if enRegex.match(line):
self.events.append(self.parseEvent(eventLines))
return self.events
def parseEvent(self, lines):
event = ICalEvent()
event.raw_data = "\n".join(lines)
startDate = None
rule = None
endDate = None
for line in lines:
if re.compile("^SUMMARY:(.*)").match(line):
event.summary = re.compile("^SUMMARY:(.*)").match(line).group(1)
elif re.compile("^DTSTART;.*:(.*).*").match(line):
startDate = self.parseDate(re.compile("^DTSTART;.*:(.*).*").match(line).group(1))
elif re.compile("^DTEND;.*:(.*).*").match(line):
endDate = self.parseDate(re.compile("^DTEND;.*:(.*).*").match(line).group(1))
elif re.compile("^EXDATE.*:(.*)").match(line):
event.addExceptionDate(parseDate(re.compile("^EXDATE.*:(.*)").match(line).group(1)))
elif re.compile("^RRULE:(.*)").match(line):
rule = re.compile("^RRULE:(.*)").match(line).group(1)
event.startDate = startDate
event.endDate = endDate
if rule:
event.addRecurrenceRule(rule)
return event
def parseDate(self, dateStr):
year = int(dateStr[0:4])
if year < 1970:
year = 1970
month = int(dateStr[4:4+2])
day = int(dateStr[6:6+2])
try:
hour = int(dateStr[9:9+2])
minute = int(dateStr[11:11+2])
except:
hour = 0
minute = 0
return datetime.datetime(year, month, day, hour, minute, tzinfo=pytz.UTC)
def selectEvents(self, selectFunction):
note = datetime.datetime.today()
self.events.sort()
events = filter(selectFunction, self.events)
return events
def todaysEvents(self, event):
return event.startsToday()
def tomorrowsEvents(self, event):
return event.startsTomorrow()
def eventsFor(self, date):
note = datetime.datetime.today()
self.events.sort()
ret = []
for event in self.events:
if event.startsOn(date):
ret.append(event)
return ret
class ICalEvent:
def __init__(self):
self.exceptionDates = []
self.dateSet = None
def __str__(self):
return self.summary
def __eq__(self, otherEvent):
return self.startDate == otherEvent.startDate
def addExceptionDate(self, date):
self.exceptionDates.append(date)
def addRecurrenceRule(self, rule):
self.dateSet = DateSet(self.startDate, self.endDate, rule)
def startsToday(self):
return self.startsOn(datetime.datetime.today())
def startsTomorrow(self):
tomorrow = datetime.datetime.fromtimestamp(time.time() + SECONDS_PER_DAY)
return self.startsOn(tomorrow)
def startsOn(self, date):
return (self.startDate.year == date.year and
self.startDate.month == date.month and
self.startDate.day == date.day or
(self.dateSet and self.dateSet.includes(date)))
def startTime(self):
return self.startDate
class DateSet:
def __init__(self, startDate, endDate, rule):
self.startDate = startDate
self.endDate = endDate
self.frequency = None
self.count = None
self.untilDate = None
self.byMonth = None
self.byDate = None
self.parseRecurrenceRule(rule)
def parseRecurrenceRule(self, rule):
if re.compile("FREQ=(.*?);").match(rule) :
self.frequency = re.compile("FREQ=(.*?);").match(rule).group(1)
if re.compile("COUNT=(\d*)").match(rule) :
self.count = int(re.compile("COUNT=(\d*)").match(rule).group(1))
if re.compile("UNTIL=(.*?);").match(rule) :
self.untilDate = DateParser.parse(re.compile("UNTIL=(.*?);").match(rule).group(1))
if re.compile("INTERVAL=(\d*)").match(rule) :
self.interval = int(re.compile("INTERVAL=(\d*)").match(rule).group(1))
if re.compile("BYMONTH=(.*?);").match(rule) :
self.byMonth = re.compile("BYMONTH=(.*?);").match(rule).group(1)
if re.compile("BYDAY=(.*?);").match(rule) :
self.byDay = re.compile("BYDAY=(.*?);").match(rule).group(1)
def includes(self, date):
if date == self.startDate:
return True
if self.untilDate and date > self.untilDate:
return False
if self.frequency == 'DAILY':
increment = 1
if self.interval:
increment = self.interval
d = self.startDate
counter = 0
while(d < date):
if self.count:
counter += 1
if counter >= self.count:
return False
d = d.replace(day=d.day+1)
if (d.day == date.day and
d.year == date.year and
d.month == date.month):
return True
elif self.frequency == 'WEEKLY':
if self.startDate.weekday() == date.weekday():
return True
else:
if self.endDate:
for n in range(0, self.endDate.day - self.startDate.day):
newDate = self.startDate.replace(day=self.startDate.day+n)
if newDate.weekday() == date.weekday():
return True
elif self.frequency == 'MONTHLY':
pass
elif self.frequency == 'YEARLY':
pass
return False

213
Webcal/plugin.py Normal file
View File

@ -0,0 +1,213 @@
###
# Copyright (c) 2005,2006 Dennis Kaarsemaker
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of version 2 of the GNU General Public License as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
###
import supybot.utils as utils
from supybot.commands import *
import supybot.plugins as plugins
import supybot.ircutils as ircutils
import supybot.callbacks as callbacks
import supybot.schedule as schedule
import supybot.ircmsgs as ircmsgs
import pytz
import ical
import datetime
reload(ical)
def _event_to_string(event, timezone):
if not timezone:
return "%s UTC: %s" % (event.startDate.strftime("%d %b %H:%M"), event.summary)
return "%s: %s" % (event.startDate.astimezone(pytz.timezone(timezone)).strftime("%d %b %H:%M"), event.summary)
def diff(delta):
s = ''
if delta.days:
if delta.days > 1:
s = 's'
return '%d day%s' % (delta.days, s)
h = ''
if delta.seconds > 7200:
s = 's'
if delta.seconds > 3600:
h = '%d hour%s ' % (int(delta.seconds/3600),s)
s = ''
seconds = delta.seconds % 3600
if seconds > 120:
s = 's'
return '%s%d minute%s' % (h,(seconds/60),s)
class Webcal(callbacks.Plugin):
"""@schedule <timezone>: display the schedule in your timezone"""
threaded = True
def __init__(self, irc):
callbacks.Privmsg.__init__(self, irc)
self.irc = irc
schedule.addPeriodicEvent(self._refresh_cache, 60 * 20, name=self.name())
schedule.addPeriodicEvent(self._autotopics, 60, name=self.name() + 'b')
self.cache = {}
def die(self):
schedule.removeEvent(self.name())
schedule.removeEvent(self.name() + 'b')
self.cache.clear()
def reset(self):
self.cache.clear()
def _filter(self, event, channel, now):
#channel = '#ubuntu-meeting' # Testing hack
if channel.lower() not in event.raw_data.lower():
return False
delta = event.endDate - now
return delta.days >= 0 or (delta.days == -1 and abs(delta).seconds < 30 * 60)
def _gettopic(self, url, channel, do_update=False, only_update=False, timezone = None, no_topic=False):
if do_update or url not in self.cache.keys():
data = utils.web.getUrl(url)
parser = ical.ICalReader(data)
#parser = ical.ICalReader(ical.sample)
self.cache[url] = parser.events
if not only_update:
now = datetime.datetime.now(pytz.UTC)
events = filter(lambda x: self._filter(x,channel,now),self.cache[url])[:6]
preamble = ''
if len(events):
# The standard slack of 30 minutes after the meeting will be an
# error if there are 2 conscutive meetings.
if len(events) > 1 and events[1].startDate < now:
events = events[1:]
ev0 = events[0]
delta = abs(ev0.startDate - now)
if ev0.startDate < now or (delta.days == 0 and delta.seconds < 10 * 60):
preamble = 'Current meeting: %s | ' % ev0.summary.replace('Meeting','').strip()
events = events[1:]
events = map(lambda x: _event_to_string(x,timezone), events)
template = self.registryValue('topic', channel)
newtopic = ' | '.join(events).replace(' Meeting','')
if '%s' in template and not no_topic:
newtopic = template % str(newtopic)
return preamble + newtopic
def _nextmeeting(self, url, channel, timezone):
if url not in self.cache.keys():
data = utils.web.getUrl(url)
parser = ical.ICalReader(data)
#parser = ical.ICalReader(ical.sample)
self.cache[url] = parser.events
now = datetime.datetime.now(pytz.UTC)
events = filter(lambda x: self._filter(x,channel,now),self.cache[url])[:6]
preamble = ''
if len(events):
# The standard slack of 30 minutes after the meeting will be an
# error if there are 2 conscutive meetings.
if len(events) > 1 and events[1].startDate < now:
events = events[1:]
ev0 = events[0]
delta = abs(ev0.startDate - now)
if ev0.startDate < now or (delta.days == 0 and delta.seconds < 10 * 60):
return ' - Current meeting: %s ' % ev0.summary.replace('Meeting','').strip()
return ' - Next meeting: %s in %s' % (ev0.summary.replace('Meeting',''), diff(delta))
return ''
def _autotopics(self):
if not self.irc:
return
for c in self.irc.state.channels:
url = self.registryValue('url', c)
if url:
newtopic = self._gettopic(url, c)
if newtopic and not (newtopic.strip() == self.irc.state.getTopic(c).strip()):
self.irc.queueMsg(ircmsgs.topic(c, newtopic))
def _refresh_cache(self):
if not self.lastIrc:
return
for c in self.lastIrc.state.channels:
url = self.registryValue('url', c)
if url:
self._gettopic(url, c, True, true)
def topic(self, irc, msg, args):
url = self.registryValue('url', msg.args[0])
if not url:
return
newtopic = self._gettopic(url, msg.args[0], True)
if not (newtopic.strip() == irc.state.getTopic(msg.args[0]).strip()):
irc.queueMsg(ircmsgs.topic(msg.args[0], newtopic))
topic = wrap(topic)
def _tzfilter(self, tz, ud):
if tz == ud:
return True
pos = tz.find('/')
while not (pos == -1):
if tz[pos+1:] == ud:
return True
pos = tz.find('/',pos+1)
return False
def schedule(self, irc, msg, args, tz):
""" Retrieve the date/time of scheduled meetings in a specific timezone """
if not tz:
tz = 'utc'
if irc.isChannel(msg.args[0]):
c = msg.args[0]
else:
c = self.registryValue('defaultChannel')
if not c:
return
url = self.registryValue('url', c)
if not url:
return
tzs = filter(lambda x: self._tzfilter(x.lower(),tz.lower()), pytz.all_timezones)
if not tzs or 'gmt' in tz.lower():
irc.error('Unknown timezone: %s - Full list: http://bugbot.ubuntulinux.nl/timezones.html' % tz)
else:
irc.reply('Schedule for %s: %s' % (tzs[0],self._gettopic(url, c, timezone=tzs[0], no_topic=True)))
schedule = wrap(schedule, [additional('text')])
def now(self, irc, msg, args, tz):
""" Display the current time """
now = datetime.datetime.now(pytz.UTC)
if not tz:
tz = 'utc'
#irc.reply('Current time in UTC: %s' % now.strftime("%B %d %Y, %H:%M:%S"))
tzs = filter(lambda x: self._tzfilter(x.lower(),tz.lower()), pytz.all_timezones)
if not tzs or 'gmt' in tz.lower():
irc.error('Unknown timezone: %s - Full list: http://bugbot.ubuntulinux.nl/timezones.html' % tz)
else:
if irc.isChannel(msg.args[0]):
c = msg.args[0]
else:
c = self.registryValue('defaultChannel')
if not c:
return
#c = "#ubuntu-meeting"
url = self.registryValue('url', c)
print c, url
if not url:
meeting = ''
else:
meeting = self._nextmeeting(url, c, tzs[0])
irc.reply('Current time in %s: %s%s' % (tzs[0],now.astimezone(pytz.timezone(tzs[0])).strftime("%B %d %Y, %H:%M:%S"),meeting))
now = wrap(now, [additional('text')])
time = now
def doTopic(self, irc, msg):
url = self.registryValue('url', msg.args[0])
if not url:
return
irc.queueMsg(ircmsgs.privmsg(msg.nick, "The topic of %s is managed by me and filled with the contents of %s - please don't change manually" % (msg.args[0],url)))
Class = Webcal

3
Webcal/test.py Normal file
View File

@ -0,0 +1,3 @@
from supybot.test import *
class WebcalTestCase(PluginTestCase):
plugins = ('Webcal',)

592
Webcal/timezones.html Normal file
View File

@ -0,0 +1,592 @@
<html>
<head>
<title>Ubugtu and Ubotu</title>
<link rel="stylesheet" href="/bot.css" />
</head>
<body>
<div class="home">
<h1>Timezones <img src="xchat.png" alt="Hi!" /></h1>
<p>
These are all timezones known by Ubugtu. You don't need to use the full
zone name, the last part is enough. When this is ambiguous, the
alphabetic first timezone will be used. Timezones are not case sensitive
</p>
<p>&alefsym;</p>
<p>
Examples:<br />
<br />
For Europe/Amsterdam:<br />
@schedule amsterdam<br />
For America/North_Dakota/Center:<br />
@schedule North_Dakota/Center<br />
</p>
<p>&alefsym;</p>
<h1>All timezones</h1>
<p>
Africa/Abidjan<br />
Africa/Accra<br />
Africa/Addis_Ababa<br />
Africa/Algiers<br />
Africa/Asmera<br />
Africa/Bamako<br />
Africa/Bangui<br />
Africa/Banjul<br />
Africa/Bissau<br />
Africa/Blantyre<br />
Africa/Brazzaville<br />
Africa/Bujumbura<br />
Africa/Cairo<br />
Africa/Casablanca<br />
Africa/Ceuta<br />
Africa/Conakry<br />
Africa/Dakar<br />
Africa/Dar_es_Salaam<br />
Africa/Djibouti<br />
Africa/Douala<br />
Africa/El_Aaiun<br />
Africa/Freetown<br />
Africa/Gaborone<br />
Africa/Harare<br />
Africa/Johannesburg<br />
Africa/Kampala<br />
Africa/Khartoum<br />
Africa/Kigali<br />
Africa/Kinshasa<br />
Africa/Lagos<br />
Africa/Libreville<br />
Africa/Lome<br />
Africa/Luanda<br />
Africa/Lubumbashi<br />
Africa/Lusaka<br />
Africa/Malabo<br />
Africa/Maputo<br />
Africa/Maseru<br />
Africa/Mbabane<br />
Africa/Mogadishu<br />
Africa/Monrovia<br />
Africa/Nairobi<br />
Africa/Ndjamena<br />
Africa/Niamey<br />
Africa/Nouakchott<br />
Africa/Ouagadougou<br />
Africa/Porto-Novo<br />
Africa/Sao_Tome<br />
Africa/Timbuktu<br />
Africa/Tripoli<br />
Africa/Tunis<br />
Africa/Windhoek
</p>
<p>&alefsym;</p>
<p>
America/Adak<br />
America/Anchorage<br />
America/Anguilla<br />
America/Antigua<br />
America/Araguaina<br />
America/Argentina/Buenos_Aires<br />
America/Argentina/Catamarca<br />
America/Argentina/ComodRivadavia<br />
America/Argentina/Cordoba<br />
America/Argentina/Jujuy<br />
America/Argentina/La_Rioja<br />
America/Argentina/Mendoza<br />
America/Argentina/Rio_Gallegos<br />
America/Argentina/San_Juan<br />
America/Argentina/Tucuman<br />
America/Argentina/Ushuaia<br />
America/Aruba<br />
America/Asuncion<br />
America/Atka<br />
America/Bahia<br />
America/Barbados<br />
America/Belem<br />
America/Belize<br />
America/Boa_Vista<br />
America/Bogota<br />
America/Boise<br />
America/Buenos_Aires<br />
America/Cambridge_Bay<br />
America/Campo_Grande<br />
America/Cancun<br />
America/Caracas<br />
America/Catamarca<br />
America/Cayenne<br />
America/Cayman<br />
America/Chicago<br />
America/Chihuahua<br />
America/Coral_Harbour<br />
America/Cordoba<br />
America/Costa_Rica<br />
America/Cuiaba<br />
America/Curacao<br />
America/Danmarkshavn<br />
America/Dawson<br />
America/Dawson_Creek<br />
America/Denver<br />
America/Detroit<br />
America/Dominica<br />
America/Edmonton<br />
America/Eirunepe<br />
America/El_Salvador<br />
America/Ensenada<br />
America/Fort_Wayne<br />
America/Fortaleza<br />
America/Glace_Bay<br />
America/Godthab<br />
America/Goose_Bay<br />
America/Grand_Turk<br />
America/Grenada<br />
America/Guadeloupe<br />
America/Guatemala<br />
America/Guayaquil<br />
America/Guyana<br />
America/Halifax<br />
America/Havana<br />
America/Hermosillo<br />
America/Indiana/Indianapolis<br />
America/Indiana/Knox<br />
America/Indiana/Marengo<br />
America/Indiana/Vevay<br />
America/Indianapolis<br />
America/Inuvik<br />
America/Iqaluit<br />
America/Jamaica<br />
America/Jujuy<br />
America/Juneau<br />
America/Kentucky/Louisville<br />
America/Kentucky/Monticello<br />
America/Knox_IN<br />
America/La_Paz<br />
America/Lima<br />
America/Los_Angeles<br />
America/Louisville<br />
America/Maceio<br />
America/Managua<br />
America/Manaus<br />
America/Martinique<br />
America/Mazatlan<br />
America/Mendoza<br />
America/Menominee<br />
America/Merida<br />
America/Mexico_City<br />
America/Miquelon<br />
America/Monterrey<br />
America/Montevideo<br />
America/Montreal<br />
America/Montserrat<br />
America/Nassau<br />
America/New_York<br />
America/Nipigon<br />
America/Nome<br />
America/Noronha<br />
America/North_Dakota/Center<br />
America/Panama<br />
America/Pangnirtung<br />
America/Paramaribo<br />
America/Phoenix<br />
America/Port-au-Prince<br />
America/Port_of_Spain<br />
America/Porto_Acre<br />
America/Porto_Velho<br />
America/Puerto_Rico<br />
America/Rainy_River<br />
America/Rankin_Inlet<br />
America/Recife<br />
America/Regina<br />
America/Rio_Branco<br />
America/Rosario<br />
America/Santiago<br />
America/Santo_Domingo<br />
America/Sao_Paulo<br />
America/Scoresbysund<br />
America/Shiprock<br />
America/St_Johns<br />
America/St_Kitts<br />
America/St_Lucia<br />
America/St_Thomas<br />
America/St_Vincent<br />
America/Swift_Current<br />
America/Tegucigalpa<br />
America/Thule<br />
America/Thunder_Bay<br />
America/Tijuana<br />
America/Toronto<br />
America/Tortola<br />
America/Vancouver<br />
America/Virgin<br />
America/Whitehorse<br />
America/Winnipeg<br />
America/Yakutat<br />
America/Yellowknife
</p>
<p>&alefsym;</p>
<p>
Antarctica/Casey<br />
Antarctica/Davis<br />
Antarctica/DumontDUrville<br />
Antarctica/Mawson<br />
Antarctica/McMurdo<br />
Antarctica/Palmer<br />
Antarctica/Rothera<br />
Antarctica/South_Pole<br />
Antarctica/Syowa<br />
Antarctica/Vostok
</p>
<p>&alefsym;</p>
<p>
Arctic/Longyearbyen
</p>
<p>&alefsym;</p>
</p>
Asia/Aden<br />
Asia/Almaty<br />
Asia/Amman<br />
Asia/Anadyr<br />
Asia/Aqtau<br />
Asia/Aqtobe<br />
Asia/Ashgabat<br />
Asia/Ashkhabad<br />
Asia/Baghdad<br />
Asia/Bahrain<br />
Asia/Baku<br />
Asia/Bangkok<br />
Asia/Beirut<br />
Asia/Bishkek<br />
Asia/Brunei<br />
Asia/Calcutta<br />
Asia/Choibalsan<br />
Asia/Chongqing<br />
Asia/Chungking<br />
Asia/Colombo<br />
Asia/Dacca<br />
Asia/Damascus<br />
Asia/Dhaka<br />
Asia/Dili<br />
Asia/Dubai<br />
Asia/Dushanbe<br />
Asia/Gaza<br />
Asia/Harbin<br />
Asia/Hong_Kong<br />
Asia/Hovd<br />
Asia/Irkutsk<br />
Asia/Istanbul<br />
Asia/Jakarta<br />
Asia/Jayapura<br />
Asia/Jerusalem<br />
Asia/Kabul<br />
Asia/Kamchatka<br />
Asia/Karachi<br />
Asia/Kashgar<br />
Asia/Katmandu<br />
Asia/Krasnoyarsk<br />
Asia/Kuala_Lumpur<br />
Asia/Kuching<br />
Asia/Kuwait<br />
Asia/Macao<br />
Asia/Macau<br />
Asia/Magadan<br />
Asia/Makassar<br />
Asia/Manila<br />
Asia/Muscat<br />
Asia/Nicosia<br />
Asia/Novosibirsk<br />
Asia/Omsk<br />
Asia/Oral<br />
Asia/Phnom_Penh<br />
Asia/Pontianak<br />
Asia/Pyongyang<br />
Asia/Qatar<br />
Asia/Qyzylorda<br />
Asia/Rangoon<br />
Asia/Riyadh<br />
Asia/Saigon<br />
Asia/Sakhalin<br />
Asia/Samarkand<br />
Asia/Seoul<br />
Asia/Shanghai<br />
Asia/Singapore<br />
Asia/Taipei<br />
Asia/Tashkent<br />
Asia/Tbilisi<br />
Asia/Tehran<br />
Asia/Tel_Aviv<br />
Asia/Thimbu<br />
Asia/Thimphu<br />
Asia/Tokyo<br />
Asia/Ujung_Pandang<br />
Asia/Ulaanbaatar<br />
Asia/Ulan_Bator<br />
Asia/Urumqi<br />
Asia/Vientiane<br />
Asia/Vladivostok<br />
Asia/Yakutsk<br />
Asia/Yekaterinburg<br />
Asia/Yerevan
</p>
<p>&alefsym;</p>
<p>
Atlantic/Azores<br />
Atlantic/Bermuda<br />
Atlantic/Canary<br />
Atlantic/Cape_Verde<br />
Atlantic/Faeroe<br />
Atlantic/Jan_Mayen<br />
Atlantic/Madeira<br />
Atlantic/Reykjavik<br />
Atlantic/South_Georgia<br />
Atlantic/St_Helena<br />
Atlantic/Stanley
</p>
<p>&alefsym;</p>
<p>
Australia/ACT<br />
Australia/Adelaide<br />
Australia/Brisbane<br />
Australia/Broken_Hill<br />
Australia/Canberra<br />
Australia/Currie<br />
Australia/Darwin<br />
Australia/Hobart<br />
Australia/LHI<br />
Australia/Lindeman<br />
Australia/Lord_Howe<br />
Australia/Melbourne<br />
Australia/NSW<br />
Australia/North<br />
Australia/Perth<br />
Australia/Queensland<br />
Australia/South<br />
Australia/Sydney<br />
Australia/Tasmania<br />
Australia/Victoria<br />
Australia/West<br />
Australia/Yancowinna
</p>
<p>&alefsym;</p>
<p>
Brazil/Acre<br />
Brazil/DeNoronha<br />
Brazil/East<br />
Brazil/West
</p>
<p>&alefsym;</p>
<p>
CET<br />
CST6CDT
</p>
<p>&alefsym;</p>
<p>
Canada/Atlantic<br />
Canada/Central<br />
Canada/East-Saskatchewan<br />
Canada/Eastern<br />
Canada/Mountain<br />
Canada/Newfoundland<br />
Canada/Pacific<br />
Canada/Saskatchewan<br />
Canada/Yukon<br />
Chile/Continental<br />
Chile/EasterIsland
</p>
<p>&alefsym;</p>
<p>
Cuba<br />
EET<br />
EST<br />
EST5EDT<br />
Egypt<br />
Eire
</p>
<p>&alefsym;</p>
<p>
Etc/Greenwich<br />
Etc/UCT<br />
Etc/UTC<br />
Etc/Universal<br />
Etc/Zulu
</p>
<p>&alefsym;</p>
Europe/Amsterdam<br />
Europe/Andorra<br />
Europe/Athens<br />
Europe/Belfast<br />
Europe/Belgrade<br />
Europe/Berlin<br />
Europe/Bratislava<br />
Europe/Brussels<br />
Europe/Bucharest<br />
Europe/Budapest<br />
Europe/Chisinau<br />
Europe/Copenhagen<br />
Europe/Dublin<br />
Europe/Gibraltar<br />
Europe/Helsinki<br />
Europe/Istanbul<br />
Europe/Kaliningrad<br />
Europe/Kiev<br />
Europe/Lisbon<br />
Europe/Ljubljana<br />
Europe/London<br />
Europe/Luxembourg<br />
Europe/Madrid<br />
Europe/Malta<br />
Europe/Mariehamn<br />
Europe/Minsk<br />
Europe/Monaco<br />
Europe/Moscow<br />
Europe/Nicosia<br />
Europe/Oslo<br />
Europe/Paris<br />
Europe/Prague<br />
Europe/Riga<br />
Europe/Rome<br />
Europe/Samara<br />
Europe/San_Marino<br />
Europe/Sarajevo<br />
Europe/Simferopol<br />
Europe/Skopje<br />
Europe/Sofia<br />
Europe/Stockholm<br />
Europe/Tallinn<br />
Europe/Tirane<br />
Europe/Tiraspol<br />
Europe/Uzhgorod<br />
Europe/Vaduz<br />
Europe/Vatican<br />
Europe/Vienna<br />
Europe/Vilnius<br />
Europe/Warsaw<br />
Europe/Zagreb<br />
Europe/Zaporozhye<br />
Europe/Zurich
</p>
<p>&alefsym;</p>
<p>
GB<br />
GB-Eire<br />
Greenwich<br />
HST<br />
Hongkong<br />
Iceland
</p>
<p>&alefsym;</p>
<p>
Indian/Antananarivo<br />
Indian/Chagos<br />
Indian/Christmas<br />
Indian/Cocos<br />
Indian/Comoro<br />
Indian/Kerguelen<br />
Indian/Mahe<br />
Indian/Maldives<br />
Indian/Mauritius<br />
Indian/Mayotte<br />
Indian/Reunion
</p>
<p>&alefsym;</p>
<p>
Iran<br />
Israel<br />
Jamaica<br />
Japan<br />
Kwajalein<br />
Libya<br />
MET<br />
MST<br />
MST7MDT
</p>
<p>&alefsym;</p>
<p>
Mexico/BajaNorte<br />
Mexico/BajaSur<br />
Mexico/General
</p>
<p>&alefsym;</p>
<p>
NZ<br />
NZ-CHAT<br />
Navajo<br />
PRC<br />
PST8PDT
</p>
<p>&alefsym;</p>
<p>
Pacific/Apia<br />
Pacific/Auckland<br />
Pacific/Chatham<br />
Pacific/Easter<br />
Pacific/Efate<br />
Pacific/Enderbury<br />
Pacific/Fakaofo<br />
Pacific/Fiji<br />
Pacific/Funafuti<br />
Pacific/Galapagos<br />
Pacific/Gambier<br />
Pacific/Guadalcanal<br />
Pacific/Guam<br />
Pacific/Honolulu<br />
Pacific/Johnston<br />
Pacific/Kiritimati<br />
Pacific/Kosrae<br />
Pacific/Kwajalein<br />
Pacific/Majuro<br />
Pacific/Marquesas<br />
Pacific/Midway<br />
Pacific/Nauru<br />
Pacific/Niue<br />
Pacific/Norfolk<br />
Pacific/Noumea<br />
Pacific/Pago_Pago<br />
Pacific/Palau<br />
Pacific/Pitcairn<br />
Pacific/Ponape<br />
Pacific/Port_Moresby<br />
Pacific/Rarotonga<br />
Pacific/Saipan<br />
Pacific/Samoa<br />
Pacific/Tahiti<br />
Pacific/Tarawa<br />
Pacific/Tongatapu<br />
Pacific/Truk<br />
Pacific/Wake<br />
Pacific/Wallis<br />
Pacific/Yap
</p>
<p>&alefsym;</p>
<p>
Poland<br />
Portugal<br />
ROC<br />
ROK<br />
Singapore<br />
Turkey<br />
UCT
</p>
<p>&alefsym;</p>
<p>
US/Alaska<br />
US/Aleutian<br />
US/Arizona<br />
US/Central<br />
US/East-Indiana<br />
US/Eastern<br />
US/Hawaii<br />
US/Indiana-Starke<br />
US/Michigan<br />
US/Mountain<br />
US/Pacific<br />
US/Pacific-New<br />
US/Samoa
</p>
<p>&alefsym;</p>
<p>
UTC<br />
Universal<br />
W-SU<br />
WET<br />
Zulu<br />
posixrules
</p>
</div>
</body>
</html>