Initial checkin
This commit is contained in:
5
Webcal/README.txt
Normal file
5
Webcal/README.txt
Normal 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
34
Webcal/__init__.py
Normal 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
28
Webcal/config.py
Normal 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
209
Webcal/ical.py
Normal 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
213
Webcal/plugin.py
Normal 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
3
Webcal/test.py
Normal file
@ -0,0 +1,3 @@
|
||||
from supybot.test import *
|
||||
class WebcalTestCase(PluginTestCase):
|
||||
plugins = ('Webcal',)
|
592
Webcal/timezones.html
Normal file
592
Webcal/timezones.html
Normal 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>ℵ</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>ℵ</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>ℵ</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>ℵ</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>ℵ</p>
|
||||
<p>
|
||||
Arctic/Longyearbyen
|
||||
</p>
|
||||
<p>ℵ</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>ℵ</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>ℵ</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>ℵ</p>
|
||||
<p>
|
||||
Brazil/Acre<br />
|
||||
Brazil/DeNoronha<br />
|
||||
Brazil/East<br />
|
||||
Brazil/West
|
||||
</p>
|
||||
<p>ℵ</p>
|
||||
<p>
|
||||
CET<br />
|
||||
CST6CDT
|
||||
</p>
|
||||
<p>ℵ</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>ℵ</p>
|
||||
<p>
|
||||
Cuba<br />
|
||||
EET<br />
|
||||
EST<br />
|
||||
EST5EDT<br />
|
||||
Egypt<br />
|
||||
Eire
|
||||
</p>
|
||||
<p>ℵ</p>
|
||||
<p>
|
||||
Etc/Greenwich<br />
|
||||
Etc/UCT<br />
|
||||
Etc/UTC<br />
|
||||
Etc/Universal<br />
|
||||
Etc/Zulu
|
||||
</p>
|
||||
<p>ℵ</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>ℵ</p>
|
||||
<p>
|
||||
GB<br />
|
||||
GB-Eire<br />
|
||||
Greenwich<br />
|
||||
HST<br />
|
||||
Hongkong<br />
|
||||
Iceland
|
||||
</p>
|
||||
<p>ℵ</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>ℵ</p>
|
||||
<p>
|
||||
Iran<br />
|
||||
Israel<br />
|
||||
Jamaica<br />
|
||||
Japan<br />
|
||||
Kwajalein<br />
|
||||
Libya<br />
|
||||
MET<br />
|
||||
MST<br />
|
||||
MST7MDT
|
||||
</p>
|
||||
<p>ℵ</p>
|
||||
<p>
|
||||
Mexico/BajaNorte<br />
|
||||
Mexico/BajaSur<br />
|
||||
Mexico/General
|
||||
</p>
|
||||
<p>ℵ</p>
|
||||
<p>
|
||||
NZ<br />
|
||||
NZ-CHAT<br />
|
||||
Navajo<br />
|
||||
PRC<br />
|
||||
PST8PDT
|
||||
</p>
|
||||
<p>ℵ</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>ℵ</p>
|
||||
<p>
|
||||
Poland<br />
|
||||
Portugal<br />
|
||||
ROC<br />
|
||||
ROK<br />
|
||||
Singapore<br />
|
||||
Turkey<br />
|
||||
UCT
|
||||
</p>
|
||||
<p>ℵ</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>ℵ</p>
|
||||
<p>
|
||||
UTC<br />
|
||||
Universal<br />
|
||||
W-SU<br />
|
||||
WET<br />
|
||||
Zulu<br />
|
||||
posixrules
|
||||
</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
Reference in New Issue
Block a user