def getTimezone(self, tzid): """ Generate a PyCalendar containing the requested timezone. """ # We will just use our existing TimezoneCache here calendar = Calendar() try: vtz = readVTZ(tzid) calendar.addComponent(vtz.getComponents()[0].duplicate()) except TimezoneException: # Check if an alias exists and create data for that if tzid in self.aliases: try: vtz = readVTZ(self.aliases[tzid]) except TimezoneException: log.error("Failed to find timezone data for alias: %s" % (tzid,)) return None else: vtz = vtz.duplicate() vtz.getComponents()[0].getProperties("TZID")[0].setValue(tzid) addVTZ(tzid, vtz) calendar.addComponent(vtz.getComponents()[0].duplicate()) else: log.error("Failed to find timezone data for: %s" % (tzid,)) return None return calendar
def generateZoneinfoFiles(self, outputdir, minYear, maxYear=2018, links=True, windowsAliases=None, filterzones=None): # Empty current directory try: for root, dirs, files in os.walk(outputdir, topdown=False): for name in files: os.remove(os.path.join(root, name)) for name in dirs: os.rmdir(os.path.join(root, name)) except OSError: pass for zone in self.zones.itervalues(): if filterzones and zone.name not in filterzones: continue cal = Calendar() vtz = zone.vtimezone(cal, self.rules, minYear, maxYear) cal.addComponent(vtz) icsdata = cal.getText() fpath = os.path.join(outputdir, zone.name + ".ics") if not os.path.exists(os.path.dirname(fpath)): os.makedirs(os.path.dirname(fpath)) with open(fpath, "w") as f: f.write(icsdata) if self.verbose: print("Write path: %s" % (fpath,)) if links: if windowsAliases is not None: self.parseWindowsAliases(windowsAliases) link_list = [] for linkTo, linkFrom in sorted(self.links.iteritems(), key=lambda x: x[0]): # Check for existing output file fromPath = os.path.join(outputdir, linkFrom + ".ics") if not os.path.exists(fromPath): print("Missing link from: %s to %s" % (linkFrom, linkTo,)) continue with open(fromPath) as f: icsdata = f.read() icsdata = icsdata.replace(linkFrom, linkTo) toPath = os.path.join(outputdir, linkTo + ".ics") if not os.path.exists(os.path.dirname(toPath)): os.makedirs(os.path.dirname(toPath)) with open(toPath, "w") as f: f.write(icsdata) if self.verbose: print("Write link: %s" % (linkTo,)) link_list.append("%s\t%s" % (linkTo, linkFrom,)) # Generate link mapping file linkPath = os.path.join(outputdir, "links.txt") with open(linkPath, "w") as f: f.write("\n".join(link_list))
def getTimezoneInCalendar(tzid): """ Return a VTIMEZONE inside a valid VCALENDAR """ tz = TimezoneDatabase.getTimezone(tzid) if tz is not None: from pycalendar.icalendar.calendar import Calendar cal = Calendar() cal.addComponent(tz.duplicate(cal)) return cal else: return None
def vtimezones(self, minYear, maxYear=2018, filterzones=None): """ Generate iCalendar data for all VTIMEZONEs or just those specified """ cal = Calendar() for zone in self.zones.itervalues(): if filterzones and zone.name not in filterzones: continue vtz = zone.vtimezone(cal, self.rules, minYear, maxYear) cal.addComponent(vtz) return cal.getText()
class TimezoneDatabase(object): """ On demand timezone database cache. This scans a TZdb directory for .ics files matching a TZID and caches the component data in a calendar from whence the actual component is returned. """ sTimezoneDatabase = None @staticmethod def createTimezoneDatabase(dbpath): TimezoneDatabase.sTimezoneDatabase = TimezoneDatabase() TimezoneDatabase.sTimezoneDatabase.setPath(dbpath) @staticmethod def clearTimezoneDatabase(): if TimezoneDatabase.sTimezoneDatabase is not None: TimezoneDatabase.sTimezoneDatabase.clear() def __init__(self): from pycalendar.icalendar.calendar import Calendar self.dbpath = None self.calendar = Calendar() self.tzcache = {} self.stdtzcache = set() def setPath(self, dbpath): self.dbpath = dbpath def clear(self): from pycalendar.icalendar.calendar import Calendar self.calendar = Calendar() self.tzcache.clear() self.stdtzcache.clear() @staticmethod def getTimezoneDatabase(): if TimezoneDatabase.sTimezoneDatabase is None: TimezoneDatabase.sTimezoneDatabase = TimezoneDatabase() return TimezoneDatabase.sTimezoneDatabase @staticmethod def getTimezone(tzid): return TimezoneDatabase.getTimezoneDatabase()._getTimezone(tzid) @staticmethod def getTimezoneInCalendar(tzid): """ Return a VTIMEZONE inside a valid VCALENDAR """ tz = TimezoneDatabase.getTimezone(tzid) if tz is not None: from pycalendar.icalendar.calendar import Calendar cal = Calendar() cal.addComponent(tz.duplicate(cal)) return cal else: return None @staticmethod def getTimezoneOffsetSeconds(tzid, dt, relative_to_utc=False): # Cache it first tz = TimezoneDatabase.getTimezone(tzid) if tz is not None: return tz.getTimezoneOffsetSeconds(dt, relative_to_utc) else: return 0 @staticmethod def getTimezoneDescriptor(tzid, dt): # Cache it first tz = TimezoneDatabase.getTimezone(tzid) if tz is not None: return tz.getTimezoneDescriptor(dt) else: return "" @staticmethod def isStandardTimezone(tzid): return TimezoneDatabase.getTimezoneDatabase()._isStandardTimezone(tzid) def cacheTimezone(self, tzid): """ Load the specified timezone identifier's timezone data from a file and parse it into the L{Calendar} used to store timezones used by this object. @param tzid: the timezone identifier to load @type tzid: L{str} """ if self.dbpath is None: return tzpath = os.path.join(self.dbpath, "%s.ics" % (tzid,)) tzpath = os.path.normpath(tzpath) if tzpath.startswith(self.dbpath) and os.path.isfile(tzpath): try: with open(tzpath) as f: self.calendar.parseComponent(f) except (IOError, InvalidData): raise NoTimezoneInDatabase(self.dbpath, tzid) else: raise NoTimezoneInDatabase(self.dbpath, tzid) def addTimezone(self, tz): """ Add the specified VTIMEZONE component to this object's L{Calendar} cache. This component is assumed to be a non-standard timezone - i.e., not loaded from the timezone database. @param tz: the VTIMEZONE component to add @type tz: L{Component} """ copy = tz.duplicate(self.calendar) self.calendar.addComponent(copy) self.tzcache[copy.getID()] = copy def _addStandardTimezone(self, tz): """ Same as L{addTimezone} except that the timezone is marked as a standard timezone. This is only meant to be used for testing which happens int he absence of a real standard timezone database. @param tz: the VTIMEZONE component to add @type tz: L{Component} """ if tz.getID() not in self.tzcache: self.addTimezone(tz) self.stdtzcache.add(tz.getID()) def _isStandardTimezone(self, tzid): """ Add the specified VTIMEZONE component to this object's L{Calendar} cache. This component is assumed to be a non-standard timezone - i.e., not loaded from the timezone database. @param tzid: the timezone identifier to lookup @type tzid: L{str} """ return tzid in self.stdtzcache def _getTimezone(self, tzid): """ Get a timezone matching the specified timezone identifier. Use this object's cache - if not in the cache try to load it from a tz database file and store in this object's calendar. @param tzid: the timezone identifier to lookup @type tzid: L{str} """ if tzid not in self.tzcache: tz = self.calendar.getTimezone(tzid) if tz is None: try: self.cacheTimezone(tzid) except NoTimezoneInDatabase: pass tz = self.calendar.getTimezone(tzid) self.tzcache[tzid] = tz if tz is not None and tzid is not None: self.stdtzcache.add(tzid) return self.tzcache[tzid] @staticmethod def mergeTimezones(cal, tzs): """ Merge each timezone from other calendar. """ tzdb = TimezoneDatabase.getTimezoneDatabase() # Not if our own calendar if cal is tzdb.calendar: return # Merge each timezone from other calendar for tz in tzs: tzdb.mergeTimezone(tz) def mergeTimezone(self, tz): """ If the supplied VTIMEZONE is not in our cache then store it in memory. """ if self._getTimezone(tz.getID()) is None: self.addTimezone(tz)
class TimezoneDatabase(object): """ On demand timezone database cache. This scans a TZdb directory for .ics files matching a TZID and caches the component data in a calendar from whence the actual component is returned. """ sTimezoneDatabase = None @staticmethod def createTimezoneDatabase(dbpath): TimezoneDatabase.sTimezoneDatabase = TimezoneDatabase() TimezoneDatabase.sTimezoneDatabase.setPath(dbpath) @staticmethod def clearTimezoneDatabase(): if TimezoneDatabase.sTimezoneDatabase is not None: TimezoneDatabase.sTimezoneDatabase.clear() def __init__(self): from pycalendar.icalendar.calendar import Calendar self.dbpath = None self.calendar = Calendar() self.tzcache = {} self.stdtzcache = set() def setPath(self, dbpath): self.dbpath = dbpath def clear(self): from pycalendar.icalendar.calendar import Calendar self.calendar = Calendar() self.tzcache.clear() self.stdtzcache.clear() @staticmethod def getTimezoneDatabase(): if TimezoneDatabase.sTimezoneDatabase is None: TimezoneDatabase.sTimezoneDatabase = TimezoneDatabase() return TimezoneDatabase.sTimezoneDatabase @staticmethod def getTimezone(tzid): return TimezoneDatabase.getTimezoneDatabase()._getTimezone(tzid) @staticmethod def getTimezoneInCalendar(tzid): """ Return a VTIMEZONE inside a valid VCALENDAR """ tz = TimezoneDatabase.getTimezone(tzid) if tz is not None: from pycalendar.icalendar.calendar import Calendar cal = Calendar() cal.addComponent(tz.duplicate(cal)) return cal else: return None @staticmethod def getTimezoneOffsetSeconds(tzid, dt, relative_to_utc=False): # Cache it first tz = TimezoneDatabase.getTimezone(tzid) if tz is not None: return tz.getTimezoneOffsetSeconds(dt, relative_to_utc) else: return 0 @staticmethod def getTimezoneDescriptor(tzid, dt): # Cache it first tz = TimezoneDatabase.getTimezone(tzid) if tz is not None: return tz.getTimezoneDescriptor(dt) else: return "" @staticmethod def isStandardTimezone(tzid): return TimezoneDatabase.getTimezoneDatabase()._isStandardTimezone(tzid) def cacheTimezone(self, tzid): """ Load the specified timezone identifier's timezone data from a file and parse it into the L{Calendar} used to store timezones used by this object. @param tzid: the timezone identifier to load @type tzid: L{str} """ if self.dbpath is None: return tzpath = os.path.join(self.dbpath, "%s.ics" % (tzid, )) tzpath = os.path.normpath(tzpath) if tzpath.startswith(self.dbpath) and os.path.isfile(tzpath): try: with open(tzpath) as f: self.calendar.parseComponent(f) except (IOError, InvalidData): raise NoTimezoneInDatabase(self.dbpath, tzid) else: raise NoTimezoneInDatabase(self.dbpath, tzid) def addTimezone(self, tz): """ Add the specified VTIMEZONE component to this object's L{Calendar} cache. This component is assumed to be a non-standard timezone - i.e., not loaded from the timezone database. @param tz: the VTIMEZONE component to add @type tz: L{Component} """ copy = tz.duplicate(self.calendar) self.calendar.addComponent(copy) self.tzcache[copy.getID()] = copy def _addStandardTimezone(self, tz): """ Same as L{addTimezone} except that the timezone is marked as a standard timezone. This is only meant to be used for testing which happens int he absence of a real standard timezone database. @param tz: the VTIMEZONE component to add @type tz: L{Component} """ if tz.getID() not in self.tzcache: self.addTimezone(tz) self.stdtzcache.add(tz.getID()) def _isStandardTimezone(self, tzid): """ Add the specified VTIMEZONE component to this object's L{Calendar} cache. This component is assumed to be a non-standard timezone - i.e., not loaded from the timezone database. @param tzid: the timezone identifier to lookup @type tzid: L{str} """ return tzid in self.stdtzcache def _getTimezone(self, tzid): """ Get a timezone matching the specified timezone identifier. Use this object's cache - if not in the cache try to load it from a tz database file and store in this object's calendar. @param tzid: the timezone identifier to lookup @type tzid: L{str} """ if tzid not in self.tzcache: tz = self.calendar.getTimezone(tzid) if tz is None: try: self.cacheTimezone(tzid) except NoTimezoneInDatabase: pass tz = self.calendar.getTimezone(tzid) self.tzcache[tzid] = tz if tz is not None and tzid is not None: self.stdtzcache.add(tzid) return self.tzcache[tzid] @staticmethod def mergeTimezones(cal, tzs): """ Merge each timezone from other calendar. """ tzdb = TimezoneDatabase.getTimezoneDatabase() # Not if our own calendar if cal is tzdb.calendar: return # Merge each timezone from other calendar for tz in tzs: tzdb.mergeTimezone(tz) def mergeTimezone(self, tz): """ If the supplied VTIMEZONE is not in our cache then store it in memory. """ if self._getTimezone(tz.getID()) is None: self.addTimezone(tz)
def generateZoneinfoFiles(self, outputdir, minYear, maxYear=2018, links=True, windowsAliases=None, filterzones=None): # Empty current directory try: for root, dirs, files in os.walk(outputdir, topdown=False): for name in files: os.remove(os.path.join(root, name)) for name in dirs: os.rmdir(os.path.join(root, name)) except OSError: pass for zone in self.zones.itervalues(): if filterzones and zone.name not in filterzones: continue cal = Calendar() vtz = zone.vtimezone(cal, self.rules, minYear, maxYear) cal.addComponent(vtz) icsdata = cal.getText() fpath = os.path.join(outputdir, zone.name + ".ics") if not os.path.exists(os.path.dirname(fpath)): os.makedirs(os.path.dirname(fpath)) with open(fpath, "w") as f: f.write(icsdata) if self.verbose: print("Write path: %s" % (fpath, )) if links: if windowsAliases is not None: self.parseWindowsAliases(windowsAliases) link_list = [] for linkTo, linkFrom in sorted(self.links.iteritems(), key=lambda x: x[0]): # Check for existing output file fromPath = os.path.join(outputdir, linkFrom + ".ics") if not os.path.exists(fromPath): print("Missing link from: %s to %s" % ( linkFrom, linkTo, )) continue with open(fromPath) as f: icsdata = f.read() icsdata = icsdata.replace(linkFrom, linkTo) toPath = os.path.join(outputdir, linkTo + ".ics") if not os.path.exists(os.path.dirname(toPath)): os.makedirs(os.path.dirname(toPath)) with open(toPath, "w") as f: f.write(icsdata) if self.verbose: print("Write link: %s" % (linkTo, )) link_list.append("%s\t%s" % ( linkTo, linkFrom, )) # Generate link mapping file linkPath = os.path.join(outputdir, "links.txt") with open(linkPath, "w") as f: f.write("\n".join(link_list))