diff --git a/lib/pygeoip/COPYING b/lib/pygeoip/COPYING new file mode 100644 index 0000000..02bbb60 --- /dev/null +++ b/lib/pygeoip/COPYING @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. \ No newline at end of file diff --git a/lib/pygeoip/DEVELOPER b/lib/pygeoip/DEVELOPER new file mode 100644 index 0000000..6f81d45 --- /dev/null +++ b/lib/pygeoip/DEVELOPER @@ -0,0 +1,21 @@ +Bootstrap manual for developers of pygeoip + +Dependencies: tox, nose, epydoc + +For testing we are using tox virtualenv-based Python version testing +and nose as test framwork. + +Tox will create virtualenvs for all Python version pygeoip supports +and installs the current working tree using the setup.py install script. +Running the tests requires a couple of sample databases found on the +link below. + +Maxmind sample databases for testing can be downloaded here: +http://www.defunct.cc/maxmind-geoip-samples.tar.gz (58 MB) + +Extract the tarball in the tests directory and run tox from the root directory. + +Please make sure your code passes all tests before opening pull requests. + +All the best, +William Tisäter diff --git a/lib/pygeoip/__init__.py b/lib/pygeoip/__init__.py index 3e89422..b073088 100644 --- a/lib/pygeoip/__init__.py +++ b/lib/pygeoip/__init__.py @@ -1,17 +1,13 @@ +# -*- coding: utf-8 -*- """ -Pure Python GeoIP API. The API is based off of U{MaxMind's C-based Python API}, -but the code itself is based on the U{pure PHP5 API} -by Jim Winstead and Hans Lellelid. +Pure Python GeoIP API -It is mostly a drop-in replacement, except the -C{new} and C{open} methods are gone. You should instantiate the L{GeoIP} class yourself: +The API is based on MaxMind's C-based Python API, but the code itself is +ported from the Pure PHP GeoIP API by Jim Winstead and Hans Lellelid. -C{gi = GeoIP('/path/to/GeoIP.dat', pygeoip.MEMORY_CACHE)} +@author: Jennifer Ennis -@author: Jennifer Ennis - -@license: -Copyright(C) 2004 MaxMind LLC +@license: Copyright(C) 2004 MaxMind LLC This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -27,39 +23,43 @@ You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . """ -from __future__ import with_statement, absolute_import, division import os import math import socket import mmap -import gzip import codecs -from StringIO import StringIO +from threading import Lock -from . import const -from .util import ip2long -from .timezone import time_zone_by_country_and_region +try: + from StringIO import StringIO +except ImportError: + from io import StringIO, BytesIO -import six +from pygeoip import util, const +from pygeoip.const import PY2, PY3 +from pygeoip.timezone import time_zone_by_country_and_region + +STANDARD = const.STANDARD MMAP_CACHE = const.MMAP_CACHE MEMORY_CACHE = const.MEMORY_CACHE -STANDARD = const.STANDARD + +ENCODING = const.ENCODING + class GeoIPError(Exception): pass -class GeoIPMetaclass(type): +class GeoIPMetaclass(type): def __new__(cls, *args, **kwargs): """ Singleton method to gets an instance without reparsing the db. Unique instances are instantiated based on the filename of the db. Flags are - ignored for this, i.e. if you initialize one with STANDARD flag (default) - and then try later to initialize with MEMORY_CACHE, it will still - return the STANDARD one. + ignored for this, i.e. if you initialize one with STANDARD + flag (default) and then try later to initialize with MEMORY_CACHE, it + will still return the STANDARD one. """ - if not hasattr(cls, '_instances'): cls._instances = {} @@ -68,25 +68,25 @@ class GeoIPMetaclass(type): elif 'filename' in kwargs: filename = kwargs['filename'] - if not filename in cls._instances: + if filename not in cls._instances: cls._instances[filename] = type.__new__(cls, *args, **kwargs) return cls._instances[filename] + GeoIPBase = GeoIPMetaclass('GeoIPBase', (object,), {}) -class GeoIP(GeoIPBase): +class GeoIP(GeoIPBase): def __init__(self, filename, flags=0): """ Initialize the class. - @param filename: path to a geoip database. If MEMORY_CACHE is used, - the file can be gzipped. + @param filename: Path to a geoip database. @type filename: str - @param flags: flags that affect how the database is processed. - Currently the only supported flags are STANDARD (the default), - MEMORY_CACHE (preload the whole file into memory), and + @param flags: Flags that affect how the database is processed. + Currently supported flags are STANDARD (the default), + MEMORY_CACHE (preload the whole file into memory) and MMAP_CACHE (access the file via mmap). @type flags: int """ @@ -94,42 +94,71 @@ class GeoIP(GeoIPBase): self._flags = flags if self._flags & const.MMAP_CACHE: - with open(filename, 'rb') as f: - self._filehandle = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) + f = open(filename, 'rb') + access = mmap.ACCESS_READ + self._filehandle = mmap.mmap(f.fileno(), 0, access=access) + f.close() elif self._flags & const.MEMORY_CACHE: - if filename.endswith('.gz'): - opener = gzip.open - else: - opener = open + f = open(filename, 'rb') + self._memoryBuffer = f.read() + iohandle = BytesIO if PY3 else StringIO + self._filehandle = iohandle(self._memoryBuffer) + f.close() - with opener(filename, 'rb') as f: - self._memoryBuffer = f.read() - self._filehandle = StringIO(self._memoryBuffer) else: - self._filehandle = codecs.open(filename, 'rb','latin_1') + self._filehandle = codecs.open(filename, 'rb', ENCODING) + self._lock = Lock() self._setup_segments() def _setup_segments(self): """ - Parses the database file to determine what kind of database is being used and setup - segment sizes and start points that will be used by the seek*() methods later. + Parses the database file to determine what kind of database is + being used and setup segment sizes and start points that will + be used by the seek*() methods later. + + Supported databases: + + * COUNTRY_EDITION + * COUNTRY_EDITION_V6 + * REGION_EDITION_REV0 + * REGION_EDITION_REV1 + * CITY_EDITION_REV0 + * CITY_EDITION_REV1 + * CITY_EDITION_REV1_V6 + * ORG_EDITION + * ISP_EDITION + * ASNUM_EDITION + * ASNUM_EDITION_V6 + """ self._databaseType = const.COUNTRY_EDITION self._recordLength = const.STANDARD_RECORD_LENGTH + self._databaseSegments = const.COUNTRY_BEGIN + self._lock.acquire() filepos = self._filehandle.tell() self._filehandle.seek(-3, os.SEEK_END) for i in range(const.STRUCTURE_INFO_MAX_SIZE): + chars = chr(255) * 3 delim = self._filehandle.read(3) - if delim == six.u(chr(255) * 3): - self._databaseType = ord(self._filehandle.read(1)) + if PY3 and type(delim) is bytes: + delim = delim.decode(ENCODING) + if PY2: + chars = chars.decode(ENCODING) + if type(delim) is str: + delim = delim.decode(ENCODING) + + if delim == chars: + byte = self._filehandle.read(1) + self._databaseType = ord(byte) + + # Compatibility with databases from April 2003 and earlier if (self._databaseType >= 106): - # backwards compatibility with databases from April 2003 and earlier self._databaseType -= 105 if self._databaseType == const.REGION_EDITION_REV0: @@ -140,51 +169,29 @@ class GeoIP(GeoIPBase): elif self._databaseType in (const.CITY_EDITION_REV0, const.CITY_EDITION_REV1, + const.CITY_EDITION_REV1_V6, const.ORG_EDITION, const.ISP_EDITION, - const.ASNUM_EDITION): + const.ASNUM_EDITION, + const.ASNUM_EDITION_V6): self._databaseSegments = 0 buf = self._filehandle.read(const.SEGMENT_RECORD_LENGTH) + if PY3 and type(buf) is bytes: + buf = buf.decode(ENCODING) + for j in range(const.SEGMENT_RECORD_LENGTH): self._databaseSegments += (ord(buf[j]) << (j * 8)) - if self._databaseType in (const.ORG_EDITION, const.ISP_EDITION): + LONG_RECORDS = (const.ORG_EDITION, const.ISP_EDITION) + if self._databaseType in LONG_RECORDS: self._recordLength = const.ORG_RECORD_LENGTH - break else: self._filehandle.seek(-4, os.SEEK_CUR) - if self._databaseType == const.COUNTRY_EDITION: - self._databaseSegments = const.COUNTRY_BEGIN - self._filehandle.seek(filepos, os.SEEK_SET) - - def _lookup_country_id(self, addr): - """ - Get the country index. - - This method is called by the _lookupCountryCode and _lookupCountryName - methods. It looks up the index ('id') for the country which is the key - for the code and name. - - @param addr: The IP address - @type addr: str - @return: network byte order 32-bit integer - @rtype: int - """ - - ipnum = ip2long(addr) - - if not ipnum: - raise ValueError("Invalid IP address: %s" % addr) - - if self._databaseType != const.COUNTRY_EDITION: - raise GeoIPError('Invalid database type; country_* methods expect '\ - 'Country database') - - return self._seek_country(ipnum) - const.COUNTRY_BEGIN + self._lock.release() def _seek_country(self, ipnum): """ @@ -196,117 +203,119 @@ class GeoIP(GeoIPBase): @return: offset of start of record @rtype: int """ - offset = 0 + try: + offset = 0 + seek_depth = 127 if len(str(ipnum)) > 10 else 31 - for depth in range(31, -1, -1): + for depth in range(seek_depth, -1, -1): + if self._flags & const.MEMORY_CACHE: + startIndex = 2 * self._recordLength * offset + endIndex = startIndex + (2 * self._recordLength) + buf = self._memoryBuffer[startIndex:endIndex] + else: + startIndex = 2 * self._recordLength * offset + readLength = 2 * self._recordLength + self._lock.acquire() + self._filehandle.seek(startIndex, os.SEEK_SET) + buf = self._filehandle.read(readLength) + self._lock.release() - if self._flags & const.MEMORY_CACHE: - startIndex = 2 * self._recordLength * offset - length = 2 * self._recordLength - endIndex = startIndex + length - buf = self._memoryBuffer[startIndex:endIndex] - else: - self._filehandle.seek(2 * self._recordLength * offset, os.SEEK_SET) - buf = self._filehandle.read(2 * self._recordLength) + if PY3 and type(buf) is bytes: + buf = buf.decode(ENCODING) - x = [0,0] + x = [0, 0] + for i in range(2): + for j in range(self._recordLength): + byte = buf[self._recordLength * i + j] + x[i] += ord(byte) << (j * 8) + if ipnum & (1 << depth): + if x[1] >= self._databaseSegments: + return x[1] + offset = x[1] + else: + if x[0] >= self._databaseSegments: + return x[0] + offset = x[0] + except: + pass - for i in range(2): - for j in range(self._recordLength): - x[i] += ord(buf[self._recordLength * i + j]) << (j * 8) - - if ipnum & (1 << depth): - - if x[1] >= self._databaseSegments: - return x[1] - - offset = x[1] - - else: - - if x[0] >= self._databaseSegments: - return x[0] - - offset = x[0] - - - raise Exception('Error traversing database - perhaps it is corrupt?') + raise GeoIPError('Corrupt database') def _get_org(self, ipnum): """ - Seek and return organization (or ISP) name for converted IP addr. + Seek and return organization or ISP name for ipnum. @param ipnum: Converted IP address @type ipnum: int @return: org/isp name @rtype: str """ - seek_org = self._seek_country(ipnum) if seek_org == self._databaseSegments: return None - record_pointer = seek_org + (2 * self._recordLength - 1) * self._databaseSegments + read_length = (2 * self._recordLength - 1) * self._databaseSegments + self._lock.acquire() + self._filehandle.seek(seek_org + read_length, os.SEEK_SET) + buf = self._filehandle.read(const.MAX_ORG_RECORD_LENGTH) + self._lock.release() - self._filehandle.seek(record_pointer, os.SEEK_SET) + if PY3 and type(buf) is bytes: + buf = buf.decode(ENCODING) - org_buf = self._filehandle.read(const.MAX_ORG_RECORD_LENGTH) - - return org_buf[:org_buf.index(chr(0))] + return buf[:buf.index(chr(0))] def _get_region(self, ipnum): """ - Seek and return the region info (dict containing country_code and region_name). + Seek and return the region info (dict containing country_code + and region_name). - @param ipnum: converted IP address + @param ipnum: Converted IP address @type ipnum: int @return: dict containing country_code and region_name @rtype: dict """ - country_code = '' region = '' + country_code = '' + seek_country = self._seek_country(ipnum) + + def get_region_name(offset): + region1 = chr(offset // 26 + 65) + region2 = chr(offset % 26 + 65) + return ''.join([region1, region2]) if self._databaseType == const.REGION_EDITION_REV0: - seek_country = self._seek_country(ipnum) seek_region = seek_country - const.STATE_BEGIN_REV0 if seek_region >= 1000: country_code = 'US' - region = ''.join([chr((seek_region // 1000) // 26 + 65), chr((seek_region // 1000) % 26 + 65)]) + region = get_region_name(seek_region - 1000) else: country_code = const.COUNTRY_CODES[seek_region] - region = '' elif self._databaseType == const.REGION_EDITION_REV1: - seek_country = self._seek_country(ipnum) seek_region = seek_country - const.STATE_BEGIN_REV1 if seek_region < const.US_OFFSET: - country_code = ''; - region = '' + pass elif seek_region < const.CANADA_OFFSET: country_code = 'US' - region = ''.join([chr((seek_region - const.US_OFFSET) // 26 + 65), chr((seek_region - const.US_OFFSET) % 26 + 65)]) - elif seek_region < const.WORLD_OFFSET: + region = get_region_name(seek_region - const.US_OFFSET) + elif seek_region < const.WORLD_OFFSET: country_code = 'CA' - region = ''.join([chr((seek_region - const.CANADA_OFFSET) // 26 + 65), chr((seek_region - const.CANADA_OFFSET) % 26 + 65)]) + region = get_region_name(seek_region - const.CANADA_OFFSET) else: - i = (seek_region - const.WORLD_OFFSET) // const.FIPS_RANGE - if i < len(const.COUNTRY_CODES): - #country_code = const.COUNTRY_CODES[(seek_region - const.WORLD_OFFSET) // const.FIPS_RANGE] - country_code = const.COUNTRY_CODES[i] - else: - country_code = '' - region = '' - - elif self._databaseType in (const.CITY_EDITION_REV0, const.CITY_EDITION_REV1): + index = (seek_region - const.WORLD_OFFSET) // const.FIPS_RANGE + if index in const.COUNTRY_CODES: + country_code = const.COUNTRY_CODES[index] + elif self._databaseType in const.CITY_EDITIONS: rec = self._get_record(ipnum) - country_code = rec['country_code'] if 'country_code' in rec else '' - region = rec['region_name'] if 'region_name' in rec else '' + region = rec.get('region_name', '') + country_code = rec.get('country_code', '') - return {'country_code' : country_code, 'region_name' : region } + return {'country_code': country_code, 'region_name': region} def _get_record(self, ipnum): """ Populate location dict for converted IP. - @param ipnum: converted IP address + @param ipnum: Converted IP address @type ipnum: int @return: dict with country_code, country_code3, country_name, region, city, postal_code, latitude, longitude, @@ -315,107 +324,115 @@ class GeoIP(GeoIPBase): """ seek_country = self._seek_country(ipnum) if seek_country == self._databaseSegments: - return None + return {} - record_pointer = seek_country + (2 * self._recordLength - 1) * self._databaseSegments + read_length = (2 * self._recordLength - 1) * self._databaseSegments + self._lock.acquire() + self._filehandle.seek(seek_country + read_length, os.SEEK_SET) + buf = self._filehandle.read(const.FULL_RECORD_LENGTH) + self._lock.release() - self._filehandle.seek(record_pointer, os.SEEK_SET) - record_buf = self._filehandle.read(const.FULL_RECORD_LENGTH) + if PY3 and type(buf) is bytes: + buf = buf.decode(ENCODING) - record = {} - - record_buf_pos = 0 - char = ord(record_buf[record_buf_pos]) - #char = record_buf[record_buf_pos] if six.PY3 else ord(record_buf[record_buf_pos]) - record['country_code'] = const.COUNTRY_CODES[char] - record['country_code3'] = const.COUNTRY_CODES3[char] - record['country_name'] = const.COUNTRY_NAMES[char] - record_buf_pos += 1 - str_length = 0 - - # get region - char = ord(record_buf[record_buf_pos+str_length]) - while (char != 0): - str_length += 1 - char = ord(record_buf[record_buf_pos+str_length]) - - if str_length > 0: - record['region_name'] = record_buf[record_buf_pos:record_buf_pos+str_length] - - record_buf_pos += str_length + 1 - str_length = 0 - - # get city - char = ord(record_buf[record_buf_pos+str_length]) - while (char != 0): - str_length += 1 - char = ord(record_buf[record_buf_pos+str_length]) - - if str_length > 0: - record['city'] = record_buf[record_buf_pos:record_buf_pos+str_length] - else: - record['city'] = '' - - record_buf_pos += str_length + 1 - str_length = 0 - - # get the postal code - char = ord(record_buf[record_buf_pos+str_length]) - while (char != 0): - str_length += 1 - char = ord(record_buf[record_buf_pos+str_length]) - - if str_length > 0: - record['postal_code'] = record_buf[record_buf_pos:record_buf_pos+str_length] - else: - record['postal_code'] = None - - record_buf_pos += str_length + 1 - str_length = 0 + record = { + 'dma_code': 0, + 'area_code': 0, + 'metro_code': '', + 'postal_code': '' + } latitude = 0 longitude = 0 + buf_pos = 0 + + # Get country + char = ord(buf[buf_pos]) + record['country_code'] = const.COUNTRY_CODES[char] + record['country_code3'] = const.COUNTRY_CODES3[char] + record['country_name'] = const.COUNTRY_NAMES[char] + record['continent'] = const.CONTINENT_NAMES[char] + + buf_pos += 1 + def get_data(buf, buf_pos): + offset = buf_pos + char = ord(buf[offset]) + while (char != 0): + offset += 1 + char = ord(buf[offset]) + if offset > buf_pos: + return (offset, buf[buf_pos:offset]) + return (offset, '') + + offset, record['region_name'] = get_data(buf, buf_pos) + offset, record['city'] = get_data(buf, offset + 1) + offset, record['postal_code'] = get_data(buf, offset + 1) + buf_pos = offset + 1 + for j in range(3): - char = ord(record_buf[record_buf_pos]) - record_buf_pos += 1 + char = ord(buf[buf_pos]) + buf_pos += 1 latitude += (char << (j * 8)) - record['latitude'] = (latitude/10000.0) - 180.0 - for j in range(3): - char = ord(record_buf[record_buf_pos]) - record_buf_pos += 1 + char = ord(buf[buf_pos]) + buf_pos += 1 longitude += (char << (j * 8)) - record['longitude'] = (longitude/10000.0) - 180.0 + record['latitude'] = (latitude / 10000.0) - 180.0 + record['longitude'] = (longitude / 10000.0) - 180.0 - if self._databaseType == const.CITY_EDITION_REV1: + if self._databaseType in (const.CITY_EDITION_REV1, const.CITY_EDITION_REV1_V6): dmaarea_combo = 0 if record['country_code'] == 'US': for j in range(3): - char = ord(record_buf[record_buf_pos]) - record_buf_pos += 1 - dmaarea_combo += (char << (j*8)) + char = ord(buf[buf_pos]) + dmaarea_combo += (char << (j * 8)) + buf_pos += 1 - record['dma_code'] = int(math.floor(dmaarea_combo/1000)) - record['area_code'] = dmaarea_combo%1000 - else: - record['dma_code'] = 0 - record['area_code'] = 0 + record['dma_code'] = int(math.floor(dmaarea_combo / 1000)) + record['area_code'] = dmaarea_combo % 1000 - if 'dma_code' in record and record['dma_code'] in const.DMA_MAP: - record['metro_code'] = const.DMA_MAP[record['dma_code']] - else: - record['metro_code'] = '' - - if 'country_code' in record: - record['time_zone'] = time_zone_by_country_and_region( - record['country_code'], record.get('region_name')) or '' - else: - record['time_zone'] = '' + record['metro_code'] = const.DMA_MAP.get(record['dma_code']) + params = (record['country_code'], record['region_name']) + record['time_zone'] = time_zone_by_country_and_region(*params) return record + def _gethostbyname(self, hostname): + if self._databaseType in const.IPV6_EDITIONS: + try: + response = socket.getaddrinfo(hostname, 0, socket.AF_INET6) + family, socktype, proto, canonname, sockaddr = response[0] + address, port, flow, scope = sockaddr + return address + except socket.gaierror: + return '' + else: + return socket.gethostbyname(hostname) + + def id_by_addr(self, addr): + """ + Get the country index. + Looks up the index for the country which is the key for + the code and name. + + @param addr: The IP address + @type addr: str + @return: network byte order 32-bit integer + @rtype: int + """ + ipnum = util.ip2long(addr) + if not ipnum: + raise ValueError("Invalid IP address: %s" % addr) + + COUNTY_EDITIONS = (const.COUNTRY_EDITION, const.COUNTRY_EDITION_V6) + if self._databaseType not in COUNTY_EDITIONS: + message = 'Invalid database type, expected Country' + raise GeoIPError(message) + + return self._seek_country(ipnum) - const.COUNTRY_BEGIN + def country_code_by_addr(self, addr): """ Returns 2-letter country code (e.g. 'US') for specified IP address. @@ -427,31 +444,38 @@ class GeoIP(GeoIPBase): @rtype: str """ try: - if self._databaseType == const.COUNTRY_EDITION: - country_id = self._lookup_country_id(addr) - return const.COUNTRY_CODES[country_id] - elif self._databaseType in (const.REGION_EDITION_REV0, const.REGION_EDITION_REV1, - const.CITY_EDITION_REV0, const.CITY_EDITION_REV1): - return self.region_by_addr(addr)['country_code'] - else: - raise GeoIPError('Invalid database type; country_* methods expect '\ - 'Country, City, or Region database') + VALID_EDITIONS = (const.COUNTRY_EDITION, const.COUNTRY_EDITION_V6) + if self._databaseType in VALID_EDITIONS: + ipv = 6 if addr.find(':') >= 0 else 4 + if ipv == 4 and self._databaseType != const.COUNTRY_EDITION: + message = 'Invalid database type; expected IPv6 address' + raise ValueError(message) + if ipv == 6 and self._databaseType != const.COUNTRY_EDITION_V6: + message = 'Invalid database type; expected IPv4 address' + raise ValueError(message) + + country_id = self.id_by_addr(addr) + return const.COUNTRY_CODES[country_id] + elif self._databaseType in const.REGION_CITY_EDITIONS: + return self.region_by_addr(addr).get('country_code') + + message = 'Invalid database type, expected Country, City or Region' + raise GeoIPError(message) except ValueError: - raise GeoIPError('*_by_addr methods only accept IP addresses. Use *_by_name for hostnames. (Address: %s)' % addr) + raise GeoIPError('Failed to lookup address %s' % addr) def country_code_by_name(self, hostname): """ Returns 2-letter country code (e.g. 'US') for specified hostname. Use this method if you have a Country, Region, or City database. - @param hostname: host name + @param hostname: Hostname @type hostname: str @return: 2-letter country code @rtype: str """ - addr = socket.gethostbyname(hostname) - + addr = self._gethostbyname(hostname) return self.country_code_by_addr(addr) def country_name_by_addr(self, addr): @@ -465,34 +489,35 @@ class GeoIP(GeoIPBase): @rtype: str """ try: - if self._databaseType == const.COUNTRY_EDITION: - country_id = self._lookup_country_id(addr) + VALID_EDITIONS = (const.COUNTRY_EDITION, const.COUNTRY_EDITION_V6) + if self._databaseType in VALID_EDITIONS: + country_id = self.id_by_addr(addr) return const.COUNTRY_NAMES[country_id] - elif self._databaseType in (const.CITY_EDITION_REV0, const.CITY_EDITION_REV1): - return self.record_by_addr(addr)['country_name'] + elif self._databaseType in const.CITY_EDITIONS: + return self.record_by_addr(addr).get('country_name') else: - raise GeoIPError('Invalid database type; country_* methods expect '\ - 'Country or City database') + message = 'Invalid database type, expected Country or City' + raise GeoIPError(message) except ValueError: - raise GeoIPError('*_by_addr methods only accept IP addresses. Use *_by_name for hostnames. (Address: %s)' % addr) + raise GeoIPError('Failed to lookup address %s' % addr) def country_name_by_name(self, hostname): """ Returns full country name for specified hostname. Use this method if you have a Country database. - @param hostname: host name + @param hostname: Hostname @type hostname: str @return: country name @rtype: str """ - addr = socket.gethostbyname(hostname) + addr = self._gethostbyname(hostname) return self.country_name_by_addr(addr) def org_by_addr(self, addr): """ - Lookup the organization (or ISP) for given IP address. - Use this method if you have an Organization/ISP database. + Lookup Organization, ISP or ASNum for given IP address. + Use this method if you have an Organization, ISP or ASNum database. @param addr: IP address @type addr: str @@ -500,31 +525,30 @@ class GeoIP(GeoIPBase): @rtype: str """ try: - ipnum = ip2long(addr) - + ipnum = util.ip2long(addr) if not ipnum: - raise ValueError("Invalid IP address: %s" % addr) + raise ValueError('Invalid IP address') - if self._databaseType not in (const.ORG_EDITION, const.ISP_EDITION, const.ASNUM_EDITION): - raise GeoIPError('Invalid database type; org_* methods expect '\ - 'Org/ISP database') + valid = (const.ORG_EDITION, const.ISP_EDITION, const.ASNUM_EDITION, const.ASNUM_EDITION_V6) + if self._databaseType not in valid: + message = 'Invalid database type, expected Org, ISP or ASNum' + raise GeoIPError(message) return self._get_org(ipnum) except ValueError: - raise GeoIPError('*_by_addr methods only accept IP addresses. Use *_by_name for hostnames. (Address: %s)' % addr) + raise GeoIPError('Failed to lookup address %s' % addr) def org_by_name(self, hostname): """ Lookup the organization (or ISP) for hostname. Use this method if you have an Organization/ISP database. - @param hostname: host name + @param hostname: Hostname @type hostname: str - @return: organization or ISP name + @return: Organization or ISP name @rtype: str """ - addr = socket.gethostbyname(hostname) - + addr = self._gethostbyname(hostname) return self.org_by_addr(addr) def record_by_addr(self, addr): @@ -534,38 +558,41 @@ class GeoIP(GeoIPBase): @param addr: IP address @type addr: str - @return: dict with country_code, country_code3, country_name, - region, city, postal_code, latitude, longitude, - dma_code, metro_code, area_code, region_name, time_zone + @return: Dictionary with country_code, country_code3, country_name, + region, city, postal_code, latitude, longitude, dma_code, + metro_code, area_code, region_name, time_zone @rtype: dict """ try: - ipnum = ip2long(addr) - + ipnum = util.ip2long(addr) if not ipnum: - raise ValueError("Invalid IP address: %s" % addr) + raise ValueError('Invalid IP address') - if not self._databaseType in (const.CITY_EDITION_REV0, const.CITY_EDITION_REV1): - raise GeoIPError('Invalid database type; record_* methods expect City database') + if self._databaseType not in const.CITY_EDITIONS: + message = 'Invalid database type, expected City' + raise GeoIPError(message) - return self._get_record(ipnum) + rec = self._get_record(ipnum) + if not rec: + return None + + return rec except ValueError: - raise GeoIPError('*_by_addr methods only accept IP addresses. Use *_by_name for hostnames. (Address: %s)' % addr) + raise GeoIPError('Failed to lookup address %s' % addr) def record_by_name(self, hostname): """ Look up the record for a given hostname. Use this method if you have a City database. - @param hostname: host name + @param hostname: Hostname @type hostname: str - @return: dict with country_code, country_code3, country_name, - region, city, postal_code, latitude, longitude, - dma_code, metro_code, area_code, region_name, time_zone + @return: Dictionary with country_code, country_code3, country_name, + region, city, postal_code, latitude, longitude, dma_code, + metro_code, area_code, region_name, time_zone @rtype: dict """ - addr = socket.gethostbyname(hostname) - + addr = self._gethostbyname(hostname) return self.record_by_addr(addr) def region_by_addr(self, addr): @@ -575,37 +602,33 @@ class GeoIP(GeoIPBase): @param addr: IP address @type addr: str - @return: dict containing country_code, region, - and region_name + @return: Dictionary containing country_code, region and region_name @rtype: dict """ try: - ipnum = ip2long(addr) - + ipnum = util.ip2long(addr) if not ipnum: - raise ValueError("Invalid IP address: %s" % addr) + raise ValueError('Invalid IP address') - if not self._databaseType in (const.REGION_EDITION_REV0, const.REGION_EDITION_REV1, - const.CITY_EDITION_REV0, const.CITY_EDITION_REV1): - raise GeoIPError('Invalid database type; region_* methods expect '\ - 'Region or City database') + if self._databaseType not in const.REGION_CITY_EDITIONS: + message = 'Invalid database type, expected Region or City' + raise GeoIPError(message) return self._get_region(ipnum) except ValueError: - raise GeoIPError('*_by_addr methods only accept IP addresses. Use *_by_name for hostnames. (Address: %s)' % addr) + raise GeoIPError('Failed to lookup address %s' % addr) def region_by_name(self, hostname): """ Lookup the region for given hostname. Use this method if you have a Region database. - @param hostname: host name + @param hostname: Hostname @type hostname: str - @return: dict containing country_code, region, - and region_name + @return: Dictionary containing country_code, region, and region_name @rtype: dict """ - addr = socket.gethostbyname(hostname) + addr = self._gethostbyname(hostname) return self.region_by_addr(addr) def time_zone_by_addr(self, addr): @@ -613,35 +636,33 @@ class GeoIP(GeoIPBase): Look up the time zone for a given IP address. Use this method if you have a Region or City database. - @param hostname: IP address - @type hostname: str + @param addr: IP address + @type addr: str @return: Time zone @rtype: str """ try: - ipnum = ip2long(addr) - + ipnum = util.ip2long(addr) if not ipnum: - raise ValueError("Invalid IP address: %s" % addr) + raise ValueError('Invalid IP address') - if not self._databaseType in (const.REGION_EDITION_REV0, const.REGION_EDITION_REV1, - const.CITY_EDITION_REV0, const.CITY_EDITION_REV1): - raise GeoIPError('Invalid database type; region_* methods expect '\ - 'Region or City database') + if self._databaseType not in const.CITY_EDITIONS: + message = 'Invalid database type, expected City' + raise GeoIPError(message) - return self._get_record(ipnum)['time_zone'] + return self._get_record(ipnum).get('time_zone') except ValueError: - raise GeoIPError('*_by_addr methods only accept IP addresses. Use *_by_name for hostnames. (Address: %s)' % addr) + raise GeoIPError('Failed to lookup address %s' % addr) def time_zone_by_name(self, hostname): """ Look up the time zone for a given hostname. Use this method if you have a Region or City database. - @param hostname: host name + @param hostname: Hostname @type hostname: str @return: Time zone @rtype: str """ - addr = socket.gethostbyname(hostname) + addr = self._gethostbyname(hostname) return self.time_zone_by_addr(addr) diff --git a/lib/pygeoip/const.py b/lib/pygeoip/const.py index a215226..b37b045 100644 --- a/lib/pygeoip/const.py +++ b/lib/pygeoip/const.py @@ -1,382 +1,431 @@ -""" -Constants needed for parsing binary GeoIP databases. It is part of the pygeoip -package. - -@author: Jennifer Ennis - -@license: -Copyright(C) 2004 MaxMind LLC - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -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. - -You should have received a copy of the GNU Lesser General Public License -along with this program. If not, see . -""" - -GEOIP_STANDARD = 0 -GEOIP_MEMORY_CACHE = 1 - -DMA_MAP = { - 500 : 'Portland-Auburn, ME', - 501 : 'New York, NY', - 502 : 'Binghamton, NY', - 503 : 'Macon, GA', - 504 : 'Philadelphia, PA', - 505 : 'Detroit, MI', - 506 : 'Boston, MA', - 507 : 'Savannah, GA', - 508 : 'Pittsburgh, PA', - 509 : 'Ft Wayne, IN', - 510 : 'Cleveland, OH', - 511 : 'Washington, DC', - 512 : 'Baltimore, MD', - 513 : 'Flint, MI', - 514 : 'Buffalo, NY', - 515 : 'Cincinnati, OH', - 516 : 'Erie, PA', - 517 : 'Charlotte, NC', - 518 : 'Greensboro, NC', - 519 : 'Charleston, SC', - 520 : 'Augusta, GA', - 521 : 'Providence, RI', - 522 : 'Columbus, GA', - 523 : 'Burlington, VT', - 524 : 'Atlanta, GA', - 525 : 'Albany, GA', - 526 : 'Utica-Rome, NY', - 527 : 'Indianapolis, IN', - 528 : 'Miami, FL', - 529 : 'Louisville, KY', - 530 : 'Tallahassee, FL', - 531 : 'Tri-Cities, TN', - 532 : 'Albany-Schenectady-Troy, NY', - 533 : 'Hartford, CT', - 534 : 'Orlando, FL', - 535 : 'Columbus, OH', - 536 : 'Youngstown-Warren, OH', - 537 : 'Bangor, ME', - 538 : 'Rochester, NY', - 539 : 'Tampa, FL', - 540 : 'Traverse City-Cadillac, MI', - 541 : 'Lexington, KY', - 542 : 'Dayton, OH', - 543 : 'Springfield-Holyoke, MA', - 544 : 'Norfolk-Portsmouth, VA', - 545 : 'Greenville-New Bern-Washington, NC', - 546 : 'Columbia, SC', - 547 : 'Toledo, OH', - 548 : 'West Palm Beach, FL', - 549 : 'Watertown, NY', - 550 : 'Wilmington, NC', - 551 : 'Lansing, MI', - 552 : 'Presque Isle, ME', - 553 : 'Marquette, MI', - 554 : 'Wheeling, WV', - 555 : 'Syracuse, NY', - 556 : 'Richmond-Petersburg, VA', - 557 : 'Knoxville, TN', - 558 : 'Lima, OH', - 559 : 'Bluefield-Beckley-Oak Hill, WV', - 560 : 'Raleigh-Durham, NC', - 561 : 'Jacksonville, FL', - 563 : 'Grand Rapids, MI', - 564 : 'Charleston-Huntington, WV', - 565 : 'Elmira, NY', - 566 : 'Harrisburg-Lancaster-Lebanon-York, PA', - 567 : 'Greenville-Spartenburg, SC', - 569 : 'Harrisonburg, VA', - 570 : 'Florence-Myrtle Beach, SC', - 571 : 'Ft Myers, FL', - 573 : 'Roanoke-Lynchburg, VA', - 574 : 'Johnstown-Altoona, PA', - 575 : 'Chattanooga, TN', - 576 : 'Salisbury, MD', - 577 : 'Wilkes Barre-Scranton, PA', - 581 : 'Terre Haute, IN', - 582 : 'Lafayette, IN', - 583 : 'Alpena, MI', - 584 : 'Charlottesville, VA', - 588 : 'South Bend, IN', - 592 : 'Gainesville, FL', - 596 : 'Zanesville, OH', - 597 : 'Parkersburg, WV', - 598 : 'Clarksburg-Weston, WV', - 600 : 'Corpus Christi, TX', - 602 : 'Chicago, IL', - 603 : 'Joplin-Pittsburg, MO', - 604 : 'Columbia-Jefferson City, MO', - 605 : 'Topeka, KS', - 606 : 'Dothan, AL', - 609 : 'St Louis, MO', - 610 : 'Rockford, IL', - 611 : 'Rochester-Mason City-Austin, MN', - 612 : 'Shreveport, LA', - 613 : 'Minneapolis-St Paul, MN', - 616 : 'Kansas City, MO', - 617 : 'Milwaukee, WI', - 618 : 'Houston, TX', - 619 : 'Springfield, MO', - 620 : 'Tuscaloosa, AL', - 622 : 'New Orleans, LA', - 623 : 'Dallas-Fort Worth, TX', - 624 : 'Sioux City, IA', - 625 : 'Waco-Temple-Bryan, TX', - 626 : 'Victoria, TX', - 627 : 'Wichita Falls, TX', - 628 : 'Monroe, LA', - 630 : 'Birmingham, AL', - 631 : 'Ottumwa-Kirksville, IA', - 632 : 'Paducah, KY', - 633 : 'Odessa-Midland, TX', - 634 : 'Amarillo, TX', - 635 : 'Austin, TX', - 636 : 'Harlingen, TX', - 637 : 'Cedar Rapids-Waterloo, IA', - 638 : 'St Joseph, MO', - 639 : 'Jackson, TN', - 640 : 'Memphis, TN', - 641 : 'San Antonio, TX', - 642 : 'Lafayette, LA', - 643 : 'Lake Charles, LA', - 644 : 'Alexandria, LA', - 646 : 'Anniston, AL', - 647 : 'Greenwood-Greenville, MS', - 648 : 'Champaign-Springfield-Decatur, IL', - 649 : 'Evansville, IN', - 650 : 'Oklahoma City, OK', - 651 : 'Lubbock, TX', - 652 : 'Omaha, NE', - 656 : 'Panama City, FL', - 657 : 'Sherman, TX', - 658 : 'Green Bay-Appleton, WI', - 659 : 'Nashville, TN', - 661 : 'San Angelo, TX', - 662 : 'Abilene-Sweetwater, TX', - 669 : 'Madison, WI', - 670 : 'Ft Smith-Fay-Springfield, AR', - 671 : 'Tulsa, OK', - 673 : 'Columbus-Tupelo-West Point, MS', - 675 : 'Peoria-Bloomington, IL', - 676 : 'Duluth, MN', - 678 : 'Wichita, KS', - 679 : 'Des Moines, IA', - 682 : 'Davenport-Rock Island-Moline, IL', - 686 : 'Mobile, AL', - 687 : 'Minot-Bismarck-Dickinson, ND', - 691 : 'Huntsville, AL', - 692 : 'Beaumont-Port Author, TX', - 693 : 'Little Rock-Pine Bluff, AR', - 698 : 'Montgomery, AL', - 702 : 'La Crosse-Eau Claire, WI', - 705 : 'Wausau-Rhinelander, WI', - 709 : 'Tyler-Longview, TX', - 710 : 'Hattiesburg-Laurel, MS', - 711 : 'Meridian, MS', - 716 : 'Baton Rouge, LA', - 717 : 'Quincy, IL', - 718 : 'Jackson, MS', - 722 : 'Lincoln-Hastings, NE', - 724 : 'Fargo-Valley City, ND', - 725 : 'Sioux Falls, SD', - 734 : 'Jonesboro, AR', - 736 : 'Bowling Green, KY', - 737 : 'Mankato, MN', - 740 : 'North Platte, NE', - 743 : 'Anchorage, AK', - 744 : 'Honolulu, HI', - 745 : 'Fairbanks, AK', - 746 : 'Biloxi-Gulfport, MS', - 747 : 'Juneau, AK', - 749 : 'Laredo, TX', - 751 : 'Denver, CO', - 752 : 'Colorado Springs, CO', - 753 : 'Phoenix, AZ', - 754 : 'Butte-Bozeman, MT', - 755 : 'Great Falls, MT', - 756 : 'Billings, MT', - 757 : 'Boise, ID', - 758 : 'Idaho Falls-Pocatello, ID', - 759 : 'Cheyenne, WY', - 760 : 'Twin Falls, ID', - 762 : 'Missoula, MT', - 764 : 'Rapid City, SD', - 765 : 'El Paso, TX', - 766 : 'Helena, MT', - 767 : 'Casper-Riverton, WY', - 770 : 'Salt Lake City, UT', - 771 : 'Yuma, AZ', - 773 : 'Grand Junction, CO', - 789 : 'Tucson, AZ', - 790 : 'Albuquerque, NM', - 798 : 'Glendive, MT', - 800 : 'Bakersfield, CA', - 801 : 'Eugene, OR', - 802 : 'Eureka, CA', - 803 : 'Los Angeles, CA', - 804 : 'Palm Springs, CA', - 807 : 'San Francisco, CA', - 810 : 'Yakima-Pasco, WA', - 811 : 'Reno, NV', - 813 : 'Medford-Klamath Falls, OR', - 819 : 'Seattle-Tacoma, WA', - 820 : 'Portland, OR', - 821 : 'Bend, OR', - 825 : 'San Diego, CA', - 828 : 'Monterey-Salinas, CA', - 839 : 'Las Vegas, NV', - 855 : 'Santa Barbara, CA', - 862 : 'Sacramento, CA', - 866 : 'Fresno, CA', - 868 : 'Chico-Redding, CA', - 881 : 'Spokane, WA' - } - -COUNTRY_CODES = ( - '', 'AP', 'EU', 'AD', 'AE', 'AF', 'AG', 'AI', 'AL', 'AM', 'AN', 'AO', 'AQ', - 'AR', 'AS', 'AT', 'AU', 'AW', 'AZ', 'BA', 'BB', 'BD', 'BE', 'BF', 'BG', 'BH', - 'BI', 'BJ', 'BM', 'BN', 'BO', 'BR', 'BS', 'BT', 'BV', 'BW', 'BY', 'BZ', 'CA', - 'CC', 'CD', 'CF', 'CG', 'CH', 'CI', 'CK', 'CL', 'CM', 'CN', 'CO', 'CR', 'CU', - 'CV', 'CX', 'CY', 'CZ', 'DE', 'DJ', 'DK', 'DM', 'DO', 'DZ', 'EC', 'EE', 'EG', - 'EH', 'ER', 'ES', 'ET', 'FI', 'FJ', 'FK', 'FM', 'FO', 'FR', 'FX', 'GA', 'GB', - 'GD', 'GE', 'GF', 'GH', 'GI', 'GL', 'GM', 'GN', 'GP', 'GQ', 'GR', 'GS', 'GT', - 'GU', 'GW', 'GY', 'HK', 'HM', 'HN', 'HR', 'HT', 'HU', 'ID', 'IE', 'IL', 'IN', - 'IO', 'IQ', 'IR', 'IS', 'IT', 'JM', 'JO', 'JP', 'KE', 'KG', 'KH', 'KI', 'KM', - 'KN', 'KP', 'KR', 'KW', 'KY', 'KZ', 'LA', 'LB', 'LC', 'LI', 'LK', 'LR', 'LS', - 'LT', 'LU', 'LV', 'LY', 'MA', 'MC', 'MD', 'MG', 'MH', 'MK', 'ML', 'MM', 'MN', - 'MO', 'MP', 'MQ', 'MR', 'MS', 'MT', 'MU', 'MV', 'MW', 'MX', 'MY', 'MZ', 'NA', - 'NC', 'NE', 'NF', 'NG', 'NI', 'NL', 'NO', 'NP', 'NR', 'NU', 'NZ', 'OM', 'PA', - 'PE', 'PF', 'PG', 'PH', 'PK', 'PL', 'PM', 'PN', 'PR', 'PS', 'PT', 'PW', 'PY', - 'QA', 'RE', 'RO', 'RU', 'RW', 'SA', 'SB', 'SC', 'SD', 'SE', 'SG', 'SH', 'SI', - 'SJ', 'SK', 'SL', 'SM', 'SN', 'SO', 'SR', 'ST', 'SV', 'SY', 'SZ', 'TC', 'TD', - 'TF', 'TG', 'TH', 'TJ', 'TK', 'TM', 'TN', 'TO', 'TL', 'TR', 'TT', 'TV', 'TW', - 'TZ', 'UA', 'UG', 'UM', 'US', 'UY', 'UZ', 'VA', 'VC', 'VE', 'VG', 'VI', 'VN', - 'VU', 'WF', 'WS', 'YE', 'YT', 'RS', 'ZA', 'ZM', 'ME', 'ZW', 'A1', 'A2', 'O1', - 'AX', 'GG', 'IM', 'JE', 'BL', 'MF' - ) - -COUNTRY_CODES3 = ( - '','AP','EU','AND','ARE','AFG','ATG','AIA','ALB','ARM','ANT','AGO','AQ','ARG', - 'ASM','AUT','AUS','ABW','AZE','BIH','BRB','BGD','BEL','BFA','BGR','BHR','BDI', - 'BEN','BMU','BRN','BOL','BRA','BHS','BTN','BV','BWA','BLR','BLZ','CAN','CC', - 'COD','CAF','COG','CHE','CIV','COK','CHL','CMR','CHN','COL','CRI','CUB','CPV', - 'CX','CYP','CZE','DEU','DJI','DNK','DMA','DOM','DZA','ECU','EST','EGY','ESH', - 'ERI','ESP','ETH','FIN','FJI','FLK','FSM','FRO','FRA','FX','GAB','GBR','GRD', - 'GEO','GUF','GHA','GIB','GRL','GMB','GIN','GLP','GNQ','GRC','GS','GTM','GUM', - 'GNB','GUY','HKG','HM','HND','HRV','HTI','HUN','IDN','IRL','ISR','IND','IO', - 'IRQ','IRN','ISL','ITA','JAM','JOR','JPN','KEN','KGZ','KHM','KIR','COM','KNA', - 'PRK','KOR','KWT','CYM','KAZ','LAO','LBN','LCA','LIE','LKA','LBR','LSO','LTU', - 'LUX','LVA','LBY','MAR','MCO','MDA','MDG','MHL','MKD','MLI','MMR','MNG','MAC', - 'MNP','MTQ','MRT','MSR','MLT','MUS','MDV','MWI','MEX','MYS','MOZ','NAM','NCL', - 'NER','NFK','NGA','NIC','NLD','NOR','NPL','NRU','NIU','NZL','OMN','PAN','PER', - 'PYF','PNG','PHL','PAK','POL','SPM','PCN','PRI','PSE','PRT','PLW','PRY','QAT', - 'REU','ROU','RUS','RWA','SAU','SLB','SYC','SDN','SWE','SGP','SHN','SVN','SJM', - 'SVK','SLE','SMR','SEN','SOM','SUR','STP','SLV','SYR','SWZ','TCA','TCD','TF', - 'TGO','THA','TJK','TKL','TLS','TKM','TUN','TON','TUR','TTO','TUV','TWN','TZA', - 'UKR','UGA','UM','USA','URY','UZB','VAT','VCT','VEN','VGB','VIR','VNM','VUT', - 'WLF','WSM','YEM','YT','SRB','ZAF','ZMB','MNE','ZWE','A1','A2','O1', - 'ALA','GGY','IMN','JEY','BLM','MAF' - ) - -COUNTRY_NAMES = ( - "", "Asia/Pacific Region", "Europe", "Andorra", "United Arab Emirates", - "Afghanistan", "Antigua and Barbuda", "Anguilla", "Albania", "Armenia", - "Netherlands Antilles", "Angola", "Antarctica", "Argentina", "American Samoa", - "Austria", "Australia", "Aruba", "Azerbaijan", "Bosnia and Herzegovina", - "Barbados", "Bangladesh", "Belgium", "Burkina Faso", "Bulgaria", "Bahrain", - "Burundi", "Benin", "Bermuda", "Brunei Darussalam", "Bolivia", "Brazil", - "Bahamas", "Bhutan", "Bouvet Island", "Botswana", "Belarus", "Belize", - "Canada", "Cocos (Keeling) Islands", "Congo, The Democratic Republic of the", - "Central African Republic", "Congo", "Switzerland", "Cote D'Ivoire", "Cook Islands", - "Chile", "Cameroon", "China", "Colombia", "Costa Rica", "Cuba", "Cape Verde", - "Christmas Island", "Cyprus", "Czech Republic", "Germany", "Djibouti", - "Denmark", "Dominica", "Dominican Republic", "Algeria", "Ecuador", "Estonia", - "Egypt", "Western Sahara", "Eritrea", "Spain", "Ethiopia", "Finland", "Fiji", - "Falkland Islands (Malvinas)", "Micronesia, Federated States of", "Faroe Islands", - "France", "France, Metropolitan", "Gabon", "United Kingdom", - "Grenada", "Georgia", "French Guiana", "Ghana", "Gibraltar", "Greenland", - "Gambia", "Guinea", "Guadeloupe", "Equatorial Guinea", "Greece", - "South Georgia and the South Sandwich Islands", - "Guatemala", "Guam", "Guinea-Bissau", - "Guyana", "Hong Kong", "Heard Island and McDonald Islands", "Honduras", - "Croatia", "Haiti", "Hungary", "Indonesia", "Ireland", "Israel", "India", - "British Indian Ocean Territory", "Iraq", "Iran, Islamic Republic of", - "Iceland", "Italy", "Jamaica", "Jordan", "Japan", "Kenya", "Kyrgyzstan", - "Cambodia", "Kiribati", "Comoros", "Saint Kitts and Nevis", - "Korea, Democratic People's Republic of", - "Korea, Republic of", "Kuwait", "Cayman Islands", - "Kazakstan", "Lao People's Democratic Republic", "Lebanon", "Saint Lucia", - "Liechtenstein", "Sri Lanka", "Liberia", "Lesotho", "Lithuania", "Luxembourg", - "Latvia", "Libyan Arab Jamahiriya", "Morocco", "Monaco", "Moldova, Republic of", - "Madagascar", "Marshall Islands", "Macedonia", - "Mali", "Myanmar", "Mongolia", "Macau", "Northern Mariana Islands", - "Martinique", "Mauritania", "Montserrat", "Malta", "Mauritius", "Maldives", - "Malawi", "Mexico", "Malaysia", "Mozambique", "Namibia", "New Caledonia", - "Niger", "Norfolk Island", "Nigeria", "Nicaragua", "Netherlands", "Norway", - "Nepal", "Nauru", "Niue", "New Zealand", "Oman", "Panama", "Peru", "French Polynesia", - "Papua New Guinea", "Philippines", "Pakistan", "Poland", "Saint Pierre and Miquelon", - "Pitcairn Islands", "Puerto Rico", "Palestinian Territory", - "Portugal", "Palau", "Paraguay", "Qatar", "Reunion", "Romania", - "Russian Federation", "Rwanda", "Saudi Arabia", "Solomon Islands", - "Seychelles", "Sudan", "Sweden", "Singapore", "Saint Helena", "Slovenia", - "Svalbard and Jan Mayen", "Slovakia", "Sierra Leone", "San Marino", "Senegal", - "Somalia", "Suriname", "Sao Tome and Principe", "El Salvador", "Syrian Arab Republic", - "Swaziland", "Turks and Caicos Islands", "Chad", "French Southern Territories", - "Togo", "Thailand", "Tajikistan", "Tokelau", "Turkmenistan", - "Tunisia", "Tonga", "Timor-Leste", "Turkey", "Trinidad and Tobago", "Tuvalu", - "Taiwan", "Tanzania, United Republic of", "Ukraine", - "Uganda", "United States Minor Outlying Islands", "United States", "Uruguay", - "Uzbekistan", "Holy See (Vatican City State)", "Saint Vincent and the Grenadines", - "Venezuela", "Virgin Islands, British", "Virgin Islands, U.S.", - "Vietnam", "Vanuatu", "Wallis and Futuna", "Samoa", "Yemen", "Mayotte", - "Serbia", "South Africa", "Zambia", "Montenegro", "Zimbabwe", - "Anonymous Proxy","Satellite Provider","Other", - "Aland Islands","Guernsey","Isle of Man","Jersey","Saint Barthelemy","Saint Martin" - ) - -# storage / caching flags -STANDARD = 0 -MEMORY_CACHE = 1 -MMAP_CACHE = 8 - -# Database structure constants -COUNTRY_BEGIN = 16776960 -STATE_BEGIN_REV0 = 16700000 -STATE_BEGIN_REV1 = 16000000 - -STRUCTURE_INFO_MAX_SIZE = 20 -DATABASE_INFO_MAX_SIZE = 100 - -# Database editions -COUNTRY_EDITION = 1 -REGION_EDITION_REV0 = 7 -REGION_EDITION_REV1 = 3 -CITY_EDITION_REV0 = 6 -CITY_EDITION_REV1 = 2 -ORG_EDITION = 5 -ISP_EDITION = 4 -PROXY_EDITION = 8 -ASNUM_EDITION = 9 -NETSPEED_EDITION = 11 -COUNTRY_EDITION_V6 = 12 - -SEGMENT_RECORD_LENGTH = 3 -STANDARD_RECORD_LENGTH = 3 -ORG_RECORD_LENGTH = 4 -MAX_RECORD_LENGTH = 4 -MAX_ORG_RECORD_LENGTH = 300 -FULL_RECORD_LENGTH = 50 - -US_OFFSET = 1 -CANADA_OFFSET = 677 -WORLD_OFFSET = 1353 -FIPS_RANGE = 360 - - +# -*- coding: utf-8 -*- +""" +Constants needed for the binary parser. Part of the pygeoip package. + +@author: Jennifer Ennis + +@license: Copyright(C) 2004 MaxMind LLC + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +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. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +""" + +from platform import python_version_tuple + +PY2 = python_version_tuple()[0] == '2' +PY3 = python_version_tuple()[0] == '3' + +GEOIP_STANDARD = 0 +GEOIP_MEMORY_CACHE = 1 + +DMA_MAP = { + 500: 'Portland-Auburn, ME', + 501: 'New York, NY', + 502: 'Binghamton, NY', + 503: 'Macon, GA', + 504: 'Philadelphia, PA', + 505: 'Detroit, MI', + 506: 'Boston, MA', + 507: 'Savannah, GA', + 508: 'Pittsburgh, PA', + 509: 'Ft Wayne, IN', + 510: 'Cleveland, OH', + 511: 'Washington, DC', + 512: 'Baltimore, MD', + 513: 'Flint, MI', + 514: 'Buffalo, NY', + 515: 'Cincinnati, OH', + 516: 'Erie, PA', + 517: 'Charlotte, NC', + 518: 'Greensboro, NC', + 519: 'Charleston, SC', + 520: 'Augusta, GA', + 521: 'Providence, RI', + 522: 'Columbus, GA', + 523: 'Burlington, VT', + 524: 'Atlanta, GA', + 525: 'Albany, GA', + 526: 'Utica-Rome, NY', + 527: 'Indianapolis, IN', + 528: 'Miami, FL', + 529: 'Louisville, KY', + 530: 'Tallahassee, FL', + 531: 'Tri-Cities, TN', + 532: 'Albany-Schenectady-Troy, NY', + 533: 'Hartford, CT', + 534: 'Orlando, FL', + 535: 'Columbus, OH', + 536: 'Youngstown-Warren, OH', + 537: 'Bangor, ME', + 538: 'Rochester, NY', + 539: 'Tampa, FL', + 540: 'Traverse City-Cadillac, MI', + 541: 'Lexington, KY', + 542: 'Dayton, OH', + 543: 'Springfield-Holyoke, MA', + 544: 'Norfolk-Portsmouth, VA', + 545: 'Greenville-New Bern-Washington, NC', + 546: 'Columbia, SC', + 547: 'Toledo, OH', + 548: 'West Palm Beach, FL', + 549: 'Watertown, NY', + 550: 'Wilmington, NC', + 551: 'Lansing, MI', + 552: 'Presque Isle, ME', + 553: 'Marquette, MI', + 554: 'Wheeling, WV', + 555: 'Syracuse, NY', + 556: 'Richmond-Petersburg, VA', + 557: 'Knoxville, TN', + 558: 'Lima, OH', + 559: 'Bluefield-Beckley-Oak Hill, WV', + 560: 'Raleigh-Durham, NC', + 561: 'Jacksonville, FL', + 563: 'Grand Rapids, MI', + 564: 'Charleston-Huntington, WV', + 565: 'Elmira, NY', + 566: 'Harrisburg-Lancaster-Lebanon-York, PA', + 567: 'Greenville-Spartenburg, SC', + 569: 'Harrisonburg, VA', + 570: 'Florence-Myrtle Beach, SC', + 571: 'Ft Myers, FL', + 573: 'Roanoke-Lynchburg, VA', + 574: 'Johnstown-Altoona, PA', + 575: 'Chattanooga, TN', + 576: 'Salisbury, MD', + 577: 'Wilkes Barre-Scranton, PA', + 581: 'Terre Haute, IN', + 582: 'Lafayette, IN', + 583: 'Alpena, MI', + 584: 'Charlottesville, VA', + 588: 'South Bend, IN', + 592: 'Gainesville, FL', + 596: 'Zanesville, OH', + 597: 'Parkersburg, WV', + 598: 'Clarksburg-Weston, WV', + 600: 'Corpus Christi, TX', + 602: 'Chicago, IL', + 603: 'Joplin-Pittsburg, MO', + 604: 'Columbia-Jefferson City, MO', + 605: 'Topeka, KS', + 606: 'Dothan, AL', + 609: 'St Louis, MO', + 610: 'Rockford, IL', + 611: 'Rochester-Mason City-Austin, MN', + 612: 'Shreveport, LA', + 613: 'Minneapolis-St Paul, MN', + 616: 'Kansas City, MO', + 617: 'Milwaukee, WI', + 618: 'Houston, TX', + 619: 'Springfield, MO', + 620: 'Tuscaloosa, AL', + 622: 'New Orleans, LA', + 623: 'Dallas-Fort Worth, TX', + 624: 'Sioux City, IA', + 625: 'Waco-Temple-Bryan, TX', + 626: 'Victoria, TX', + 627: 'Wichita Falls, TX', + 628: 'Monroe, LA', + 630: 'Birmingham, AL', + 631: 'Ottumwa-Kirksville, IA', + 632: 'Paducah, KY', + 633: 'Odessa-Midland, TX', + 634: 'Amarillo, TX', + 635: 'Austin, TX', + 636: 'Harlingen, TX', + 637: 'Cedar Rapids-Waterloo, IA', + 638: 'St Joseph, MO', + 639: 'Jackson, TN', + 640: 'Memphis, TN', + 641: 'San Antonio, TX', + 642: 'Lafayette, LA', + 643: 'Lake Charles, LA', + 644: 'Alexandria, LA', + 646: 'Anniston, AL', + 647: 'Greenwood-Greenville, MS', + 648: 'Champaign-Springfield-Decatur, IL', + 649: 'Evansville, IN', + 650: 'Oklahoma City, OK', + 651: 'Lubbock, TX', + 652: 'Omaha, NE', + 656: 'Panama City, FL', + 657: 'Sherman, TX', + 658: 'Green Bay-Appleton, WI', + 659: 'Nashville, TN', + 661: 'San Angelo, TX', + 662: 'Abilene-Sweetwater, TX', + 669: 'Madison, WI', + 670: 'Ft Smith-Fay-Springfield, AR', + 671: 'Tulsa, OK', + 673: 'Columbus-Tupelo-West Point, MS', + 675: 'Peoria-Bloomington, IL', + 676: 'Duluth, MN', + 678: 'Wichita, KS', + 679: 'Des Moines, IA', + 682: 'Davenport-Rock Island-Moline, IL', + 686: 'Mobile, AL', + 687: 'Minot-Bismarck-Dickinson, ND', + 691: 'Huntsville, AL', + 692: 'Beaumont-Port Author, TX', + 693: 'Little Rock-Pine Bluff, AR', + 698: 'Montgomery, AL', + 702: 'La Crosse-Eau Claire, WI', + 705: 'Wausau-Rhinelander, WI', + 709: 'Tyler-Longview, TX', + 710: 'Hattiesburg-Laurel, MS', + 711: 'Meridian, MS', + 716: 'Baton Rouge, LA', + 717: 'Quincy, IL', + 718: 'Jackson, MS', + 722: 'Lincoln-Hastings, NE', + 724: 'Fargo-Valley City, ND', + 725: 'Sioux Falls, SD', + 734: 'Jonesboro, AR', + 736: 'Bowling Green, KY', + 737: 'Mankato, MN', + 740: 'North Platte, NE', + 743: 'Anchorage, AK', + 744: 'Honolulu, HI', + 745: 'Fairbanks, AK', + 746: 'Biloxi-Gulfport, MS', + 747: 'Juneau, AK', + 749: 'Laredo, TX', + 751: 'Denver, CO', + 752: 'Colorado Springs, CO', + 753: 'Phoenix, AZ', + 754: 'Butte-Bozeman, MT', + 755: 'Great Falls, MT', + 756: 'Billings, MT', + 757: 'Boise, ID', + 758: 'Idaho Falls-Pocatello, ID', + 759: 'Cheyenne, WY', + 760: 'Twin Falls, ID', + 762: 'Missoula, MT', + 764: 'Rapid City, SD', + 765: 'El Paso, TX', + 766: 'Helena, MT', + 767: 'Casper-Riverton, WY', + 770: 'Salt Lake City, UT', + 771: 'Yuma, AZ', + 773: 'Grand Junction, CO', + 789: 'Tucson, AZ', + 790: 'Albuquerque, NM', + 798: 'Glendive, MT', + 800: 'Bakersfield, CA', + 801: 'Eugene, OR', + 802: 'Eureka, CA', + 803: 'Los Angeles, CA', + 804: 'Palm Springs, CA', + 807: 'San Francisco, CA', + 810: 'Yakima-Pasco, WA', + 811: 'Reno, NV', + 813: 'Medford-Klamath Falls, OR', + 819: 'Seattle-Tacoma, WA', + 820: 'Portland, OR', + 821: 'Bend, OR', + 825: 'San Diego, CA', + 828: 'Monterey-Salinas, CA', + 839: 'Las Vegas, NV', + 855: 'Santa Barbara, CA', + 862: 'Sacramento, CA', + 866: 'Fresno, CA', + 868: 'Chico-Redding, CA', + 881: 'Spokane, WA' +} + +COUNTRY_CODES = ( + '', + 'AP', 'EU', 'AD', 'AE', 'AF', 'AG', 'AI', 'AL', 'AM', 'AN', 'AO', 'AQ', + 'AR', 'AS', 'AT', 'AU', 'AW', 'AZ', 'BA', 'BB', 'BD', 'BE', 'BF', 'BG', + 'BH', 'BI', 'BJ', 'BM', 'BN', 'BO', 'BR', 'BS', 'BT', 'BV', 'BW', 'BY', + 'BZ', 'CA', 'CC', 'CD', 'CF', 'CG', 'CH', 'CI', 'CK', 'CL', 'CM', 'CN', + 'CO', 'CR', 'CU', 'CV', 'CX', 'CY', 'CZ', 'DE', 'DJ', 'DK', 'DM', 'DO', + 'DZ', 'EC', 'EE', 'EG', 'EH', 'ER', 'ES', 'ET', 'FI', 'FJ', 'FK', 'FM', + 'FO', 'FR', 'FX', 'GA', 'GB', 'GD', 'GE', 'GF', 'GH', 'GI', 'GL', 'GM', + 'GN', 'GP', 'GQ', 'GR', 'GS', 'GT', 'GU', 'GW', 'GY', 'HK', 'HM', 'HN', + 'HR', 'HT', 'HU', 'ID', 'IE', 'IL', 'IN', 'IO', 'IQ', 'IR', 'IS', 'IT', + 'JM', 'JO', 'JP', 'KE', 'KG', 'KH', 'KI', 'KM', 'KN', 'KP', 'KR', 'KW', + 'KY', 'KZ', 'LA', 'LB', 'LC', 'LI', 'LK', 'LR', 'LS', 'LT', 'LU', 'LV', + 'LY', 'MA', 'MC', 'MD', 'MG', 'MH', 'MK', 'ML', 'MM', 'MN', 'MO', 'MP', + 'MQ', 'MR', 'MS', 'MT', 'MU', 'MV', 'MW', 'MX', 'MY', 'MZ', 'NA', 'NC', + 'NE', 'NF', 'NG', 'NI', 'NL', 'NO', 'NP', 'NR', 'NU', 'NZ', 'OM', 'PA', + 'PE', 'PF', 'PG', 'PH', 'PK', 'PL', 'PM', 'PN', 'PR', 'PS', 'PT', 'PW', + 'PY', 'QA', 'RE', 'RO', 'RU', 'RW', 'SA', 'SB', 'SC', 'SD', 'SE', 'SG', + 'SH', 'SI', 'SJ', 'SK', 'SL', 'SM', 'SN', 'SO', 'SR', 'ST', 'SV', 'SY', + 'SZ', 'TC', 'TD', 'TF', 'TG', 'TH', 'TJ', 'TK', 'TM', 'TN', 'TO', 'TL', + 'TR', 'TT', 'TV', 'TW', 'TZ', 'UA', 'UG', 'UM', 'US', 'UY', 'UZ', 'VA', + 'VC', 'VE', 'VG', 'VI', 'VN', 'VU', 'WF', 'WS', 'YE', 'YT', 'RS', 'ZA', + 'ZM', 'ME', 'ZW', 'A1', 'A2', 'O1', 'AX', 'GG', 'IM', 'JE', 'BL', 'MF', + 'BQ', 'SS' +) + +COUNTRY_CODES3 = ( + '', 'AP', 'EU', 'AND', 'ARE', 'AFG', 'ATG', 'AIA', 'ALB', 'ARM', 'ANT', + 'AGO', 'AQ', 'ARG', 'ASM', 'AUT', 'AUS', 'ABW', 'AZE', 'BIH', 'BRB', 'BGD', + 'BEL', 'BFA', 'BGR', 'BHR', 'BDI', 'BEN', 'BMU', 'BRN', 'BOL', 'BRA', + 'BHS', 'BTN', 'BV', 'BWA', 'BLR', 'BLZ', 'CAN', 'CC', 'COD', 'CAF', 'COG', + 'CHE', 'CIV', 'COK', 'CHL', 'CMR', 'CHN', 'COL', 'CRI', 'CUB', 'CPV', 'CX', + 'CYP', 'CZE', 'DEU', 'DJI', 'DNK', 'DMA', 'DOM', 'DZA', 'ECU', 'EST', + 'EGY', 'ESH', 'ERI', 'ESP', 'ETH', 'FIN', 'FJI', 'FLK', 'FSM', 'FRO', + 'FRA', 'FX', 'GAB', 'GBR', 'GRD', 'GEO', 'GUF', 'GHA', 'GIB', 'GRL', 'GMB', + 'GIN', 'GLP', 'GNQ', 'GRC', 'GS', 'GTM', 'GUM', 'GNB', 'GUY', 'HKG', 'HM', + 'HND', 'HRV', 'HTI', 'HUN', 'IDN', 'IRL', 'ISR', 'IND', 'IO', 'IRQ', 'IRN', + 'ISL', 'ITA', 'JAM', 'JOR', 'JPN', 'KEN', 'KGZ', 'KHM', 'KIR', 'COM', + 'KNA', 'PRK', 'KOR', 'KWT', 'CYM', 'KAZ', 'LAO', 'LBN', 'LCA', 'LIE', + 'LKA', 'LBR', 'LSO', 'LTU', 'LUX', 'LVA', 'LBY', 'MAR', 'MCO', 'MDA', + 'MDG', 'MHL', 'MKD', 'MLI', 'MMR', 'MNG', 'MAC', 'MNP', 'MTQ', 'MRT', + 'MSR', 'MLT', 'MUS', 'MDV', 'MWI', 'MEX', 'MYS', 'MOZ', 'NAM', 'NCL', + 'NER', 'NFK', 'NGA', 'NIC', 'NLD', 'NOR', 'NPL', 'NRU', 'NIU', 'NZL', + 'OMN', 'PAN', 'PER', 'PYF', 'PNG', 'PHL', 'PAK', 'POL', 'SPM', 'PCN', + 'PRI', 'PSE', 'PRT', 'PLW', 'PRY', 'QAT', 'REU', 'ROU', 'RUS', 'RWA', + 'SAU', 'SLB', 'SYC', 'SDN', 'SWE', 'SGP', 'SHN', 'SVN', 'SJM', 'SVK', + 'SLE', 'SMR', 'SEN', 'SOM', 'SUR', 'STP', 'SLV', 'SYR', 'SWZ', 'TCA', + 'TCD', 'TF', 'TGO', 'THA', 'TJK', 'TKL', 'TLS', 'TKM', 'TUN', 'TON', 'TUR', + 'TTO', 'TUV', 'TWN', 'TZA', 'UKR', 'UGA', 'UM', 'USA', 'URY', 'UZB', 'VAT', + 'VCT', 'VEN', 'VGB', 'VIR', 'VNM', 'VUT', 'WLF', 'WSM', 'YEM', 'YT', 'SRB', + 'ZAF', 'ZMB', 'MNE', 'ZWE', 'A1', 'A2', 'O1', 'ALA', 'GGY', 'IMN', 'JEY', + 'BLM', 'MAF', 'BES', 'SSD' +) + +COUNTRY_NAMES = ( + '', 'Asia/Pacific Region', 'Europe', 'Andorra', 'United Arab Emirates', + 'Afghanistan', 'Antigua and Barbuda', 'Anguilla', 'Albania', 'Armenia', + 'Netherlands Antilles', 'Angola', 'Antarctica', 'Argentina', + 'American Samoa', 'Austria', 'Australia', 'Aruba', 'Azerbaijan', + 'Bosnia and Herzegovina', 'Barbados', 'Bangladesh', 'Belgium', + 'Burkina Faso', 'Bulgaria', 'Bahrain', 'Burundi', 'Benin', 'Bermuda', + 'Brunei Darussalam', 'Bolivia', 'Brazil', 'Bahamas', 'Bhutan', + 'Bouvet Island', 'Botswana', 'Belarus', 'Belize', 'Canada', + 'Cocos (Keeling) Islands', 'Congo, The Democratic Republic of the', + 'Central African Republic', 'Congo', 'Switzerland', 'Cote D\'Ivoire', + 'Cook Islands', 'Chile', 'Cameroon', 'China', 'Colombia', 'Costa Rica', + 'Cuba', 'Cape Verde', 'Christmas Island', 'Cyprus', 'Czech Republic', + 'Germany', 'Djibouti', 'Denmark', 'Dominica', 'Dominican Republic', + 'Algeria', 'Ecuador', 'Estonia', 'Egypt', 'Western Sahara', 'Eritrea', + 'Spain', 'Ethiopia', 'Finland', 'Fiji', 'Falkland Islands (Malvinas)', + 'Micronesia, Federated States of', 'Faroe Islands', 'France', + 'France, Metropolitan', 'Gabon', 'United Kingdom', 'Grenada', 'Georgia', + 'French Guiana', 'Ghana', 'Gibraltar', 'Greenland', 'Gambia', 'Guinea', + 'Guadeloupe', 'Equatorial Guinea', 'Greece', + 'South Georgia and the South Sandwich Islands', 'Guatemala', 'Guam', + 'Guinea-Bissau', 'Guyana', 'Hong Kong', + 'Heard Island and McDonald Islands', 'Honduras', 'Croatia', 'Haiti', + 'Hungary', 'Indonesia', 'Ireland', 'Israel', 'India', + 'British Indian Ocean Territory', 'Iraq', 'Iran, Islamic Republic of', + 'Iceland', 'Italy', 'Jamaica', 'Jordan', 'Japan', 'Kenya', 'Kyrgyzstan', + 'Cambodia', 'Kiribati', 'Comoros', 'Saint Kitts and Nevis', + 'Korea, Democratic People\'s Republic of', 'Korea, Republic of', 'Kuwait', + 'Cayman Islands', 'Kazakhstan', 'Lao People\'s Democratic Republic', + 'Lebanon', 'Saint Lucia', 'Liechtenstein', 'Sri Lanka', 'Liberia', + 'Lesotho', 'Lithuania', 'Luxembourg', 'Latvia', 'Libya', 'Morocco', + 'Monaco', 'Moldova, Republic of', 'Madagascar', 'Marshall Islands', + 'Macedonia', 'Mali', 'Myanmar', 'Mongolia', 'Macau', + 'Northern Mariana Islands', 'Martinique', 'Mauritania', 'Montserrat', + 'Malta', 'Mauritius', 'Maldives', 'Malawi', 'Mexico', 'Malaysia', + 'Mozambique', 'Namibia', 'New Caledonia', 'Niger', 'Norfolk Island', + 'Nigeria', 'Nicaragua', 'Netherlands', 'Norway', 'Nepal', 'Nauru', 'Niue', + 'New Zealand', 'Oman', 'Panama', 'Peru', 'French Polynesia', + 'Papua New Guinea', 'Philippines', 'Pakistan', 'Poland', + 'Saint Pierre and Miquelon', 'Pitcairn Islands', 'Puerto Rico', + 'Palestinian Territory', 'Portugal', 'Palau', 'Paraguay', 'Qatar', + 'Reunion', 'Romania', 'Russian Federation', 'Rwanda', 'Saudi Arabia', + 'Solomon Islands', 'Seychelles', 'Sudan', 'Sweden', 'Singapore', + 'Saint Helena', 'Slovenia', 'Svalbard and Jan Mayen', 'Slovakia', + 'Sierra Leone', 'San Marino', 'Senegal', 'Somalia', 'Suriname', + 'Sao Tome and Principe', 'El Salvador', 'Syrian Arab Republic', + 'Swaziland', 'Turks and Caicos Islands', 'Chad', + 'French Southern Territories', 'Togo', 'Thailand', 'Tajikistan', 'Tokelau', + 'Turkmenistan', 'Tunisia', 'Tonga', 'Timor-Leste', 'Turkey', + 'Trinidad and Tobago', 'Tuvalu', 'Taiwan', 'Tanzania, United Republic of', + 'Ukraine', 'Uganda', 'United States Minor Outlying Islands', + 'United States', 'Uruguay', 'Uzbekistan', 'Holy See (Vatican City State)', + 'Saint Vincent and the Grenadines', 'Venezuela', 'Virgin Islands, British', + 'Virgin Islands, U.S.', 'Vietnam', 'Vanuatu', 'Wallis and Futuna', 'Samoa', + 'Yemen', 'Mayotte', 'Serbia', 'South Africa', 'Zambia', 'Montenegro', + 'Zimbabwe', 'Anonymous Proxy', 'Satellite Provider', 'Other', + 'Aland Islands', 'Guernsey', 'Isle of Man', 'Jersey', 'Saint Barthelemy', + 'Saint Martin', 'Bonaire, Sint Eustatius and Saba', 'South Sudan' +) + +CONTINENT_NAMES = ( + '--', 'AS', 'EU', 'EU', 'AS', 'AS', 'NA', 'NA', 'EU', 'AS', 'NA', 'AF', + 'AN', 'SA', 'OC', 'EU', 'OC', 'NA', 'AS', 'EU', 'NA', 'AS', 'EU', 'AF', + 'EU', 'AS', 'AF', 'AF', 'NA', 'AS', 'SA', 'SA', 'NA', 'AS', 'AN', 'AF', + 'EU', 'NA', 'NA', 'AS', 'AF', 'AF', 'AF', 'EU', 'AF', 'OC', 'SA', 'AF', + 'AS', 'SA', 'NA', 'NA', 'AF', 'AS', 'AS', 'EU', 'EU', 'AF', 'EU', 'NA', + 'NA', 'AF', 'SA', 'EU', 'AF', 'AF', 'AF', 'EU', 'AF', 'EU', 'OC', 'SA', + 'OC', 'EU', 'EU', 'NA', 'AF', 'EU', 'NA', 'AS', 'SA', 'AF', 'EU', 'NA', + 'AF', 'AF', 'NA', 'AF', 'EU', 'AN', 'NA', 'OC', 'AF', 'SA', 'AS', 'AN', + 'NA', 'EU', 'NA', 'EU', 'AS', 'EU', 'AS', 'AS', 'AS', 'AS', 'AS', 'EU', + 'EU', 'NA', 'AS', 'AS', 'AF', 'AS', 'AS', 'OC', 'AF', 'NA', 'AS', 'AS', + 'AS', 'NA', 'AS', 'AS', 'AS', 'NA', 'EU', 'AS', 'AF', 'AF', 'EU', 'EU', + 'EU', 'AF', 'AF', 'EU', 'EU', 'AF', 'OC', 'EU', 'AF', 'AS', 'AS', 'AS', + 'OC', 'NA', 'AF', 'NA', 'EU', 'AF', 'AS', 'AF', 'NA', 'AS', 'AF', 'AF', + 'OC', 'AF', 'OC', 'AF', 'NA', 'EU', 'EU', 'AS', 'OC', 'OC', 'OC', 'AS', + 'NA', 'SA', 'OC', 'OC', 'AS', 'AS', 'EU', 'NA', 'OC', 'NA', 'AS', 'EU', + 'OC', 'SA', 'AS', 'AF', 'EU', 'EU', 'AF', 'AS', 'OC', 'AF', 'AF', 'EU', + 'AS', 'AF', 'EU', 'EU', 'EU', 'AF', 'EU', 'AF', 'AF', 'SA', 'AF', 'NA', + 'AS', 'AF', 'NA', 'AF', 'AN', 'AF', 'AS', 'AS', 'OC', 'AS', 'AF', 'OC', + 'AS', 'EU', 'NA', 'OC', 'AS', 'AF', 'EU', 'AF', 'OC', 'NA', 'SA', 'AS', + 'EU', 'NA', 'SA', 'NA', 'NA', 'AS', 'OC', 'OC', 'OC', 'AS', 'AF', 'EU', + 'AF', 'AF', 'EU', 'AF', '--', '--', '--', 'EU', 'EU', 'EU', 'EU', 'NA', + 'NA', 'NA', 'AF' +) + +# storage / caching flags +STANDARD = 0 +MEMORY_CACHE = 1 +MMAP_CACHE = 8 + +# Database structure constants +COUNTRY_BEGIN = 16776960 +STATE_BEGIN_REV0 = 16700000 +STATE_BEGIN_REV1 = 16000000 + +STRUCTURE_INFO_MAX_SIZE = 20 +DATABASE_INFO_MAX_SIZE = 100 + +# Database editions +COUNTRY_EDITION = 1 +COUNTRY_EDITION_V6 = 12 +REGION_EDITION_REV0 = 7 +REGION_EDITION_REV1 = 3 +CITY_EDITION_REV0 = 6 +CITY_EDITION_REV1 = 2 +CITY_EDITION_REV1_V6 = 30 +ORG_EDITION = 5 +ISP_EDITION = 4 +ASNUM_EDITION = 9 +ASNUM_EDITION_V6 = 21 +# Not yet supported databases +PROXY_EDITION = 8 +NETSPEED_EDITION = 11 + +# Collection of databases +IPV6_EDITIONS = (COUNTRY_EDITION_V6, ASNUM_EDITION_V6, CITY_EDITION_REV1_V6) +CITY_EDITIONS = (CITY_EDITION_REV0, CITY_EDITION_REV1, CITY_EDITION_REV1_V6) +REGION_EDITIONS = (REGION_EDITION_REV0, REGION_EDITION_REV1) +REGION_CITY_EDITIONS = REGION_EDITIONS + CITY_EDITIONS + +SEGMENT_RECORD_LENGTH = 3 +STANDARD_RECORD_LENGTH = 3 +ORG_RECORD_LENGTH = 4 +MAX_RECORD_LENGTH = 4 +MAX_ORG_RECORD_LENGTH = 300 +FULL_RECORD_LENGTH = 50 + +US_OFFSET = 1 +CANADA_OFFSET = 677 +WORLD_OFFSET = 1353 +FIPS_RANGE = 360 +ENCODING = 'iso-8859-1' diff --git a/lib/pygeoip/timezone.py b/lib/pygeoip/timezone.py index 033c8d6..6346709 100644 --- a/lib/pygeoip/timezone.py +++ b/lib/pygeoip/timezone.py @@ -1,714 +1,760 @@ +# -*- coding: utf-8 -*- +""" +Time zone functions. Part of the pygeoip package. + +@author: Jennifer Ennis + +@license: Copyright(C) 2004 MaxMind LLC + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +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. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +""" + __all__ = ['time_zone_by_country_and_region'] -_country = {} -_country["AD"] = "Europe/Andorra" -_country["AE"] = "Asia/Dubai" -_country["AF"] = "Asia/Kabul" -_country["AG"] = "America/Antigua" -_country["AI"] = "America/Anguilla" -_country["AL"] = "Europe/Tirane" -_country["AM"] = "Asia/Yerevan" -_country["AO"] = "Africa/Luanda" -_country["AR"] = {} -_country["AR"]["01"] = "America/Argentina/Buenos_Aires" -_country["AR"]["02"] = "America/Argentina/Catamarca" -_country["AR"]["03"] = "America/Argentina/Tucuman" -_country["AR"]["04"] = "America/Argentina/Rio_Gallegos" -_country["AR"]["05"] = "America/Argentina/Cordoba" -_country["AR"]["06"] = "America/Argentina/Tucuman" -_country["AR"]["07"] = "America/Argentina/Buenos_Aires" -_country["AR"]["08"] = "America/Argentina/Buenos_Aires" -_country["AR"]["09"] = "America/Argentina/Tucuman" -_country["AR"]["10"] = "America/Argentina/Jujuy" -_country["AR"]["11"] = "America/Argentina/San_Luis" -_country["AR"]["12"] = "America/Argentina/La_Rioja" -_country["AR"]["13"] = "America/Argentina/Mendoza" -_country["AR"]["14"] = "America/Argentina/Buenos_Aires" -_country["AR"]["15"] = "America/Argentina/San_Luis" -_country["AR"]["16"] = "America/Argentina/Buenos_Aires" -_country["AR"]["17"] = "America/Argentina/Salta" -_country["AR"]["18"] = "America/Argentina/San_Juan" -_country["AR"]["19"] = "America/Argentina/San_Luis" -_country["AR"]["20"] = "America/Argentina/Rio_Gallegos" -_country["AR"]["21"] = "America/Argentina/Buenos_Aires" -_country["AR"]["22"] = "America/Argentina/Catamarca" -_country["AR"]["23"] = "America/Argentina/Ushuaia" -_country["AR"]["24"] = "America/Argentina/Tucuman" -_country["AS"] = "US/Samoa" -_country["AT"] = "Europe/Vienna" -_country["AU"] = {} -_country["AU"]["01"] = "Australia/Canberra" -_country["AU"]["02"] = "Australia/NSW" -_country["AU"]["03"] = "Australia/North" -_country["AU"]["04"] = "Australia/Queensland" -_country["AU"]["05"] = "Australia/South" -_country["AU"]["06"] = "Australia/Tasmania" -_country["AU"]["07"] = "Australia/Victoria" -_country["AU"]["08"] = "Australia/West" -_country["AW"] = "America/Aruba" -_country["AX"] = "Europe/Mariehamn" -_country["AZ"] = "Asia/Baku" -_country["BA"] = "Europe/Sarajevo" -_country["BB"] = "America/Barbados" -_country["BD"] = "Asia/Dhaka" -_country["BE"] = "Europe/Brussels" -_country["BF"] = "Africa/Ouagadougou" -_country["BG"] = "Europe/Sofia" -_country["BH"] = "Asia/Bahrain" -_country["BI"] = "Africa/Bujumbura" -_country["BJ"] = "Africa/Porto-Novo" -_country["BL"] = "America/St_Barthelemy" -_country["BM"] = "Atlantic/Bermuda" -_country["BN"] = "Asia/Brunei" -_country["BO"] = "America/La_Paz" -_country["BQ"] = "America/Curacao" -_country["BR"] = {} -_country["BR"]["01"] = "America/Rio_Branco" -_country["BR"]["02"] = "America/Maceio" -_country["BR"]["03"] = "America/Sao_Paulo" -_country["BR"]["04"] = "America/Manaus" -_country["BR"]["05"] = "America/Bahia" -_country["BR"]["06"] = "America/Fortaleza" -_country["BR"]["07"] = "America/Sao_Paulo" -_country["BR"]["08"] = "America/Sao_Paulo" -_country["BR"]["11"] = "America/Campo_Grande" -_country["BR"]["13"] = "America/Belem" -_country["BR"]["14"] = "America/Cuiaba" -_country["BR"]["15"] = "America/Sao_Paulo" -_country["BR"]["16"] = "America/Belem" -_country["BR"]["17"] = "America/Recife" -_country["BR"]["18"] = "America/Sao_Paulo" -_country["BR"]["20"] = "America/Fortaleza" -_country["BR"]["21"] = "America/Sao_Paulo" -_country["BR"]["22"] = "America/Recife" -_country["BR"]["23"] = "America/Sao_Paulo" -_country["BR"]["24"] = "America/Porto_Velho" -_country["BR"]["25"] = "America/Boa_Vista" -_country["BR"]["26"] = "America/Sao_Paulo" -_country["BR"]["27"] = "America/Sao_Paulo" -_country["BR"]["28"] = "America/Maceio" -_country["BR"]["29"] = "America/Sao_Paulo" -_country["BR"]["30"] = "America/Recife" -_country["BR"]["31"] = "America/Araguaina" -_country["BS"] = "America/Nassau" -_country["BT"] = "Asia/Thimphu" -_country["BW"] = "Africa/Gaborone" -_country["BY"] = "Europe/Minsk" -_country["BZ"] = "America/Belize" -_country["CA"] = {} -_country["CA"]["AB"] = "America/Edmonton" -_country["CA"]["BC"] = "America/Vancouver" -_country["CA"]["MB"] = "America/Winnipeg" -_country["CA"]["NB"] = "America/Halifax" -_country["CA"]["NL"] = "America/St_Johns" -_country["CA"]["NS"] = "America/Halifax" -_country["CA"]["NT"] = "America/Yellowknife" -_country["CA"]["NU"] = "America/Rankin_Inlet" -_country["CA"]["ON"] = "America/Rainy_River" -_country["CA"]["PE"] = "America/Halifax" -_country["CA"]["QC"] = "America/Montreal" -_country["CA"]["SK"] = "America/Regina" -_country["CA"]["YT"] = "America/Whitehorse" -_country["CC"] = "Indian/Cocos" -_country["CD"] = {} -_country["CD"]["02"] = "Africa/Kinshasa" -_country["CD"]["05"] = "Africa/Lubumbashi" -_country["CD"]["06"] = "Africa/Kinshasa" -_country["CD"]["08"] = "Africa/Kinshasa" -_country["CD"]["10"] = "Africa/Lubumbashi" -_country["CD"]["11"] = "Africa/Lubumbashi" -_country["CD"]["12"] = "Africa/Lubumbashi" -_country["CF"] = "Africa/Bangui" -_country["CG"] = "Africa/Brazzaville" -_country["CH"] = "Europe/Zurich" -_country["CI"] = "Africa/Abidjan" -_country["CK"] = "Pacific/Rarotonga" -_country["CL"] = "Chile/Continental" -_country["CM"] = "Africa/Lagos" -_country["CN"] = {} -_country["CN"]["01"] = "Asia/Shanghai" -_country["CN"]["02"] = "Asia/Shanghai" -_country["CN"]["03"] = "Asia/Shanghai" -_country["CN"]["04"] = "Asia/Shanghai" -_country["CN"]["05"] = "Asia/Harbin" -_country["CN"]["06"] = "Asia/Chongqing" -_country["CN"]["07"] = "Asia/Shanghai" -_country["CN"]["08"] = "Asia/Harbin" -_country["CN"]["09"] = "Asia/Shanghai" -_country["CN"]["10"] = "Asia/Shanghai" -_country["CN"]["11"] = "Asia/Chongqing" -_country["CN"]["12"] = "Asia/Shanghai" -_country["CN"]["13"] = "Asia/Urumqi" -_country["CN"]["14"] = "Asia/Chongqing" -_country["CN"]["15"] = "Asia/Chongqing" -_country["CN"]["16"] = "Asia/Chongqing" -_country["CN"]["18"] = "Asia/Chongqing" -_country["CN"]["19"] = "Asia/Harbin" -_country["CN"]["20"] = "Asia/Harbin" -_country["CN"]["21"] = "Asia/Chongqing" -_country["CN"]["22"] = "Asia/Harbin" -_country["CN"]["23"] = "Asia/Shanghai" -_country["CN"]["24"] = "Asia/Chongqing" -_country["CN"]["25"] = "Asia/Shanghai" -_country["CN"]["26"] = "Asia/Chongqing" -_country["CN"]["28"] = "Asia/Shanghai" -_country["CN"]["29"] = "Asia/Chongqing" -_country["CN"]["30"] = "Asia/Chongqing" -_country["CN"]["31"] = "Asia/Chongqing" -_country["CN"]["32"] = "Asia/Chongqing" -_country["CN"]["33"] = "Asia/Chongqing" -_country["CO"] = "America/Bogota" -_country["CR"] = "America/Costa_Rica" -_country["CU"] = "America/Havana" -_country["CV"] = "Atlantic/Cape_Verde" -_country["CW"] = "America/Curacao" -_country["CX"] = "Indian/Christmas" -_country["CY"] = "Asia/Nicosia" -_country["CZ"] = "Europe/Prague" -_country["DE"] = "Europe/Berlin" -_country["DJ"] = "Africa/Djibouti" -_country["DK"] = "Europe/Copenhagen" -_country["DM"] = "America/Dominica" -_country["DO"] = "America/Santo_Domingo" -_country["DZ"] = "Africa/Algiers" -_country["EC"] = {} -_country["EC"]["01"] = "Pacific/Galapagos" -_country["EC"]["02"] = "America/Guayaquil" -_country["EC"]["03"] = "America/Guayaquil" -_country["EC"]["04"] = "America/Guayaquil" -_country["EC"]["05"] = "America/Guayaquil" -_country["EC"]["06"] = "America/Guayaquil" -_country["EC"]["07"] = "America/Guayaquil" -_country["EC"]["08"] = "America/Guayaquil" -_country["EC"]["09"] = "America/Guayaquil" -_country["EC"]["10"] = "America/Guayaquil" -_country["EC"]["11"] = "America/Guayaquil" -_country["EC"]["12"] = "America/Guayaquil" -_country["EC"]["13"] = "America/Guayaquil" -_country["EC"]["14"] = "America/Guayaquil" -_country["EC"]["15"] = "America/Guayaquil" -_country["EC"]["17"] = "America/Guayaquil" -_country["EC"]["18"] = "America/Guayaquil" -_country["EC"]["19"] = "America/Guayaquil" -_country["EC"]["20"] = "America/Guayaquil" -_country["EC"]["22"] = "America/Guayaquil" -_country["EE"] = "Europe/Tallinn" -_country["EG"] = "Africa/Cairo" -_country["EH"] = "Africa/El_Aaiun" -_country["ER"] = "Africa/Asmera" -_country["ES"] = {} -_country["ES"]["07"] = "Europe/Madrid" -_country["ES"]["27"] = "Europe/Madrid" -_country["ES"]["29"] = "Europe/Madrid" -_country["ES"]["31"] = "Europe/Madrid" -_country["ES"]["32"] = "Europe/Madrid" -_country["ES"]["34"] = "Europe/Madrid" -_country["ES"]["39"] = "Europe/Madrid" -_country["ES"]["51"] = "Africa/Ceuta" -_country["ES"]["52"] = "Europe/Madrid" -_country["ES"]["53"] = "Atlantic/Canary" -_country["ES"]["54"] = "Europe/Madrid" -_country["ES"]["55"] = "Europe/Madrid" -_country["ES"]["56"] = "Europe/Madrid" -_country["ES"]["57"] = "Europe/Madrid" -_country["ES"]["58"] = "Europe/Madrid" -_country["ES"]["59"] = "Europe/Madrid" -_country["ES"]["60"] = "Europe/Madrid" -_country["ET"] = "Africa/Addis_Ababa" -_country["FI"] = "Europe/Helsinki" -_country["FJ"] = "Pacific/Fiji" -_country["FK"] = "Atlantic/Stanley" -_country["FO"] = "Atlantic/Faeroe" -_country["FR"] = "Europe/Paris" -_country["GA"] = "Africa/Libreville" -_country["GB"] = "Europe/London" -_country["GD"] = "America/Grenada" -_country["GE"] = "Asia/Tbilisi" -_country["GF"] = "America/Cayenne" -_country["GG"] = "Europe/Guernsey" -_country["GH"] = "Africa/Accra" -_country["GI"] = "Europe/Gibraltar" -_country["GL"] = {} -_country["GL"]["01"] = "America/Thule" -_country["GL"]["02"] = "America/Godthab" -_country["GL"]["03"] = "America/Godthab" -_country["GM"] = "Africa/Banjul" -_country["GN"] = "Africa/Conakry" -_country["GP"] = "America/Guadeloupe" -_country["GQ"] = "Africa/Malabo" -_country["GR"] = "Europe/Athens" -_country["GS"] = "Atlantic/South_Georgia" -_country["GT"] = "America/Guatemala" -_country["GU"] = "Pacific/Guam" -_country["GW"] = "Africa/Bissau" -_country["GY"] = "America/Guyana" -_country["HK"] = "Asia/Hong_Kong" -_country["HN"] = "America/Tegucigalpa" -_country["HR"] = "Europe/Zagreb" -_country["HT"] = "America/Port-au-Prince" -_country["HU"] = "Europe/Budapest" -_country["ID"] = {} -_country["ID"]["01"] = "Asia/Pontianak" -_country["ID"]["02"] = "Asia/Makassar" -_country["ID"]["03"] = "Asia/Jakarta" -_country["ID"]["04"] = "Asia/Jakarta" -_country["ID"]["05"] = "Asia/Jakarta" -_country["ID"]["06"] = "Asia/Jakarta" -_country["ID"]["07"] = "Asia/Jakarta" -_country["ID"]["08"] = "Asia/Jakarta" -_country["ID"]["09"] = "Asia/Jayapura" -_country["ID"]["10"] = "Asia/Jakarta" -_country["ID"]["11"] = "Asia/Pontianak" -_country["ID"]["12"] = "Asia/Makassar" -_country["ID"]["13"] = "Asia/Makassar" -_country["ID"]["14"] = "Asia/Makassar" -_country["ID"]["15"] = "Asia/Jakarta" -_country["ID"]["16"] = "Asia/Makassar" -_country["ID"]["17"] = "Asia/Makassar" -_country["ID"]["18"] = "Asia/Makassar" -_country["ID"]["19"] = "Asia/Pontianak" -_country["ID"]["20"] = "Asia/Makassar" -_country["ID"]["21"] = "Asia/Makassar" -_country["ID"]["22"] = "Asia/Makassar" -_country["ID"]["23"] = "Asia/Makassar" -_country["ID"]["24"] = "Asia/Jakarta" -_country["ID"]["25"] = "Asia/Pontianak" -_country["ID"]["26"] = "Asia/Pontianak" -_country["ID"]["30"] = "Asia/Jakarta" -_country["ID"]["31"] = "Asia/Makassar" -_country["ID"]["33"] = "Asia/Jakarta" -_country["IE"] = "Europe/Dublin" -_country["IL"] = "Asia/Jerusalem" -_country["IM"] = "Europe/Isle_of_Man" -_country["IN"] = "Asia/Calcutta" -_country["IO"] = "Indian/Chagos" -_country["IQ"] = "Asia/Baghdad" -_country["IR"] = "Asia/Tehran" -_country["IS"] = "Atlantic/Reykjavik" -_country["IT"] = "Europe/Rome" -_country["JE"] = "Europe/Jersey" -_country["JM"] = "America/Jamaica" -_country["JO"] = "Asia/Amman" -_country["JP"] = "Asia/Tokyo" -_country["KE"] = "Africa/Nairobi" -_country["KG"] = "Asia/Bishkek" -_country["KH"] = "Asia/Phnom_Penh" -_country["KI"] = "Pacific/Tarawa" -_country["KM"] = "Indian/Comoro" -_country["KN"] = "America/St_Kitts" -_country["KP"] = "Asia/Pyongyang" -_country["KR"] = "Asia/Seoul" -_country["KW"] = "Asia/Kuwait" -_country["KY"] = "America/Cayman" -_country["KZ"] = {} -_country["KZ"]["01"] = "Asia/Almaty" -_country["KZ"]["02"] = "Asia/Almaty" -_country["KZ"]["03"] = "Asia/Qyzylorda" -_country["KZ"]["04"] = "Asia/Aqtobe" -_country["KZ"]["05"] = "Asia/Qyzylorda" -_country["KZ"]["06"] = "Asia/Aqtau" -_country["KZ"]["07"] = "Asia/Oral" -_country["KZ"]["08"] = "Asia/Qyzylorda" -_country["KZ"]["09"] = "Asia/Aqtau" -_country["KZ"]["10"] = "Asia/Qyzylorda" -_country["KZ"]["11"] = "Asia/Almaty" -_country["KZ"]["12"] = "Asia/Qyzylorda" -_country["KZ"]["13"] = "Asia/Aqtobe" -_country["KZ"]["14"] = "Asia/Qyzylorda" -_country["KZ"]["15"] = "Asia/Almaty" -_country["KZ"]["16"] = "Asia/Aqtobe" -_country["KZ"]["17"] = "Asia/Almaty" -_country["LA"] = "Asia/Vientiane" -_country["LB"] = "Asia/Beirut" -_country["LC"] = "America/St_Lucia" -_country["LI"] = "Europe/Vaduz" -_country["LK"] = "Asia/Colombo" -_country["LR"] = "Africa/Monrovia" -_country["LS"] = "Africa/Maseru" -_country["LT"] = "Europe/Vilnius" -_country["LU"] = "Europe/Luxembourg" -_country["LV"] = "Europe/Riga" -_country["LY"] = "Africa/Tripoli" -_country["MA"] = "Africa/Casablanca" -_country["MC"] = "Europe/Monaco" -_country["MD"] = "Europe/Chisinau" -_country["ME"] = "Europe/Podgorica" -_country["MF"] = "America/Marigot" -_country["MG"] = "Indian/Antananarivo" -_country["MK"] = "Europe/Skopje" -_country["ML"] = "Africa/Bamako" -_country["MM"] = "Asia/Rangoon" -_country["MN"] = "Asia/Choibalsan" -_country["MO"] = "Asia/Macao" -_country["MP"] = "Pacific/Saipan" -_country["MQ"] = "America/Martinique" -_country["MR"] = "Africa/Nouakchott" -_country["MS"] = "America/Montserrat" -_country["MT"] = "Europe/Malta" -_country["MU"] = "Indian/Mauritius" -_country["MV"] = "Indian/Maldives" -_country["MW"] = "Africa/Blantyre" -_country["MX"] = {} -_country["MX"]["01"] = "America/Mexico_City" -_country["MX"]["02"] = "America/Tijuana" -_country["MX"]["03"] = "America/Hermosillo" -_country["MX"]["04"] = "America/Merida" -_country["MX"]["05"] = "America/Mexico_City" -_country["MX"]["06"] = "America/Chihuahua" -_country["MX"]["07"] = "America/Monterrey" -_country["MX"]["08"] = "America/Mexico_City" -_country["MX"]["09"] = "America/Mexico_City" -_country["MX"]["10"] = "America/Mazatlan" -_country["MX"]["11"] = "America/Mexico_City" -_country["MX"]["12"] = "America/Mexico_City" -_country["MX"]["13"] = "America/Mexico_City" -_country["MX"]["14"] = "America/Mazatlan" -_country["MX"]["15"] = "America/Chihuahua" -_country["MX"]["16"] = "America/Mexico_City" -_country["MX"]["17"] = "America/Mexico_City" -_country["MX"]["18"] = "America/Mazatlan" -_country["MX"]["19"] = "America/Monterrey" -_country["MX"]["20"] = "America/Mexico_City" -_country["MX"]["21"] = "America/Mexico_City" -_country["MX"]["22"] = "America/Mexico_City" -_country["MX"]["23"] = "America/Cancun" -_country["MX"]["24"] = "America/Mexico_City" -_country["MX"]["25"] = "America/Mazatlan" -_country["MX"]["26"] = "America/Hermosillo" -_country["MX"]["27"] = "America/Merida" -_country["MX"]["28"] = "America/Monterrey" -_country["MX"]["29"] = "America/Mexico_City" -_country["MX"]["30"] = "America/Mexico_City" -_country["MX"]["31"] = "America/Merida" -_country["MX"]["32"] = "America/Monterrey" -_country["MY"] = {} -_country["MY"]["01"] = "Asia/Kuala_Lumpur" -_country["MY"]["02"] = "Asia/Kuala_Lumpur" -_country["MY"]["03"] = "Asia/Kuala_Lumpur" -_country["MY"]["04"] = "Asia/Kuala_Lumpur" -_country["MY"]["05"] = "Asia/Kuala_Lumpur" -_country["MY"]["06"] = "Asia/Kuala_Lumpur" -_country["MY"]["07"] = "Asia/Kuala_Lumpur" -_country["MY"]["08"] = "Asia/Kuala_Lumpur" -_country["MY"]["09"] = "Asia/Kuala_Lumpur" -_country["MY"]["11"] = "Asia/Kuching" -_country["MY"]["12"] = "Asia/Kuala_Lumpur" -_country["MY"]["13"] = "Asia/Kuala_Lumpur" -_country["MY"]["14"] = "Asia/Kuala_Lumpur" -_country["MY"]["15"] = "Asia/Kuching" -_country["MY"]["16"] = "Asia/Kuching" -_country["MZ"] = "Africa/Maputo" -_country["NA"] = "Africa/Windhoek" -_country["NC"] = "Pacific/Noumea" -_country["NE"] = "Africa/Niamey" -_country["NF"] = "Pacific/Norfolk" -_country["NG"] = "Africa/Lagos" -_country["NI"] = "America/Managua" -_country["NL"] = "Europe/Amsterdam" -_country["NO"] = "Europe/Oslo" -_country["NP"] = "Asia/Katmandu" -_country["NR"] = "Pacific/Nauru" -_country["NU"] = "Pacific/Niue" -_country["NZ"] = {} -_country["NZ"]["85"] = "Pacific/Auckland" -_country["NZ"]["E7"] = "Pacific/Auckland" -_country["NZ"]["E8"] = "Pacific/Auckland" -_country["NZ"]["E9"] = "Pacific/Auckland" -_country["NZ"]["F1"] = "Pacific/Auckland" -_country["NZ"]["F2"] = "Pacific/Auckland" -_country["NZ"]["F3"] = "Pacific/Auckland" -_country["NZ"]["F4"] = "Pacific/Auckland" -_country["NZ"]["F5"] = "Pacific/Auckland" -_country["NZ"]["F7"] = "Pacific/Chatham" -_country["NZ"]["F8"] = "Pacific/Auckland" -_country["NZ"]["F9"] = "Pacific/Auckland" -_country["NZ"]["G1"] = "Pacific/Auckland" -_country["NZ"]["G2"] = "Pacific/Auckland" -_country["NZ"]["G3"] = "Pacific/Auckland" -_country["OM"] = "Asia/Muscat" -_country["PA"] = "America/Panama" -_country["PE"] = "America/Lima" -_country["PF"] = "Pacific/Marquesas" -_country["PG"] = "Pacific/Port_Moresby" -_country["PH"] = "Asia/Manila" -_country["PK"] = "Asia/Karachi" -_country["PL"] = "Europe/Warsaw" -_country["PM"] = "America/Miquelon" -_country["PN"] = "Pacific/Pitcairn" -_country["PR"] = "America/Puerto_Rico" -_country["PS"] = "Asia/Gaza" -_country["PT"] = {} -_country["PT"]["02"] = "Europe/Lisbon" -_country["PT"]["03"] = "Europe/Lisbon" -_country["PT"]["04"] = "Europe/Lisbon" -_country["PT"]["05"] = "Europe/Lisbon" -_country["PT"]["06"] = "Europe/Lisbon" -_country["PT"]["07"] = "Europe/Lisbon" -_country["PT"]["08"] = "Europe/Lisbon" -_country["PT"]["09"] = "Europe/Lisbon" -_country["PT"]["10"] = "Atlantic/Madeira" -_country["PT"]["11"] = "Europe/Lisbon" -_country["PT"]["13"] = "Europe/Lisbon" -_country["PT"]["14"] = "Europe/Lisbon" -_country["PT"]["16"] = "Europe/Lisbon" -_country["PT"]["17"] = "Europe/Lisbon" -_country["PT"]["18"] = "Europe/Lisbon" -_country["PT"]["19"] = "Europe/Lisbon" -_country["PT"]["20"] = "Europe/Lisbon" -_country["PT"]["21"] = "Europe/Lisbon" -_country["PT"]["22"] = "Europe/Lisbon" -_country["PW"] = "Pacific/Palau" -_country["PY"] = "America/Asuncion" -_country["QA"] = "Asia/Qatar" -_country["RE"] = "Indian/Reunion" -_country["RO"] = "Europe/Bucharest" -_country["RS"] = "Europe/Belgrade" -_country["RU"] = {} -_country["RU"]["01"] = "Europe/Volgograd" -_country["RU"]["02"] = "Asia/Irkutsk" -_country["RU"]["03"] = "Asia/Novokuznetsk" -_country["RU"]["04"] = "Asia/Novosibirsk" -_country["RU"]["05"] = "Asia/Vladivostok" -_country["RU"]["06"] = "Europe/Moscow" -_country["RU"]["07"] = "Europe/Volgograd" -_country["RU"]["08"] = "Europe/Samara" -_country["RU"]["09"] = "Europe/Moscow" -_country["RU"]["10"] = "Europe/Moscow" -_country["RU"]["11"] = "Asia/Irkutsk" -_country["RU"]["13"] = "Asia/Yekaterinburg" -_country["RU"]["14"] = "Asia/Irkutsk" -_country["RU"]["15"] = "Asia/Anadyr" -_country["RU"]["16"] = "Europe/Samara" -_country["RU"]["17"] = "Europe/Volgograd" -_country["RU"]["18"] = "Asia/Krasnoyarsk" -_country["RU"]["20"] = "Asia/Irkutsk" -_country["RU"]["21"] = "Europe/Moscow" -_country["RU"]["22"] = "Europe/Volgograd" -_country["RU"]["23"] = "Europe/Kaliningrad" -_country["RU"]["24"] = "Europe/Volgograd" -_country["RU"]["25"] = "Europe/Moscow" -_country["RU"]["26"] = "Asia/Kamchatka" -_country["RU"]["27"] = "Europe/Volgograd" -_country["RU"]["28"] = "Europe/Moscow" -_country["RU"]["29"] = "Asia/Novokuznetsk" -_country["RU"]["30"] = "Asia/Vladivostok" -_country["RU"]["31"] = "Asia/Krasnoyarsk" -_country["RU"]["32"] = "Asia/Omsk" -_country["RU"]["33"] = "Asia/Yekaterinburg" -_country["RU"]["34"] = "Asia/Yekaterinburg" -_country["RU"]["35"] = "Asia/Yekaterinburg" -_country["RU"]["36"] = "Asia/Anadyr" -_country["RU"]["37"] = "Europe/Moscow" -_country["RU"]["38"] = "Europe/Volgograd" -_country["RU"]["39"] = "Asia/Krasnoyarsk" -_country["RU"]["40"] = "Asia/Yekaterinburg" -_country["RU"]["41"] = "Europe/Moscow" -_country["RU"]["42"] = "Europe/Moscow" -_country["RU"]["43"] = "Europe/Moscow" -_country["RU"]["44"] = "Asia/Magadan" -_country["RU"]["45"] = "Europe/Samara" -_country["RU"]["46"] = "Europe/Samara" -_country["RU"]["47"] = "Europe/Moscow" -_country["RU"]["48"] = "Europe/Moscow" -_country["RU"]["49"] = "Europe/Moscow" -_country["RU"]["50"] = "Asia/Yekaterinburg" -_country["RU"]["51"] = "Europe/Moscow" -_country["RU"]["52"] = "Europe/Moscow" -_country["RU"]["53"] = "Asia/Novosibirsk" -_country["RU"]["54"] = "Asia/Omsk" -_country["RU"]["55"] = "Europe/Samara" -_country["RU"]["56"] = "Europe/Moscow" -_country["RU"]["57"] = "Europe/Samara" -_country["RU"]["58"] = "Asia/Yekaterinburg" -_country["RU"]["59"] = "Asia/Vladivostok" -_country["RU"]["60"] = "Europe/Kaliningrad" -_country["RU"]["61"] = "Europe/Volgograd" -_country["RU"]["62"] = "Europe/Moscow" -_country["RU"]["63"] = "Asia/Yakutsk" -_country["RU"]["64"] = "Asia/Sakhalin" -_country["RU"]["65"] = "Europe/Samara" -_country["RU"]["66"] = "Europe/Moscow" -_country["RU"]["67"] = "Europe/Samara" -_country["RU"]["68"] = "Europe/Volgograd" -_country["RU"]["69"] = "Europe/Moscow" -_country["RU"]["70"] = "Europe/Volgograd" -_country["RU"]["71"] = "Asia/Yekaterinburg" -_country["RU"]["72"] = "Europe/Moscow" -_country["RU"]["73"] = "Europe/Samara" -_country["RU"]["74"] = "Asia/Krasnoyarsk" -_country["RU"]["75"] = "Asia/Novosibirsk" -_country["RU"]["76"] = "Europe/Moscow" -_country["RU"]["77"] = "Europe/Moscow" -_country["RU"]["78"] = "Asia/Yekaterinburg" -_country["RU"]["79"] = "Asia/Irkutsk" -_country["RU"]["80"] = "Asia/Yekaterinburg" -_country["RU"]["81"] = "Europe/Samara" -_country["RU"]["82"] = "Asia/Irkutsk" -_country["RU"]["83"] = "Europe/Moscow" -_country["RU"]["84"] = "Europe/Volgograd" -_country["RU"]["85"] = "Europe/Moscow" -_country["RU"]["86"] = "Europe/Moscow" -_country["RU"]["87"] = "Asia/Novosibirsk" -_country["RU"]["88"] = "Europe/Moscow" -_country["RU"]["89"] = "Asia/Vladivostok" -_country["RW"] = "Africa/Kigali" -_country["SA"] = "Asia/Riyadh" -_country["SB"] = "Pacific/Guadalcanal" -_country["SC"] = "Indian/Mahe" -_country["SD"] = "Africa/Khartoum" -_country["SE"] = "Europe/Stockholm" -_country["SG"] = "Asia/Singapore" -_country["SH"] = "Atlantic/St_Helena" -_country["SI"] = "Europe/Ljubljana" -_country["SJ"] = "Arctic/Longyearbyen" -_country["SK"] = "Europe/Bratislava" -_country["SL"] = "Africa/Freetown" -_country["SM"] = "Europe/San_Marino" -_country["SN"] = "Africa/Dakar" -_country["SO"] = "Africa/Mogadishu" -_country["SR"] = "America/Paramaribo" -_country["ST"] = "Africa/Sao_Tome" -_country["SV"] = "America/El_Salvador" -_country["SX"] = "America/Curacao" -_country["SY"] = "Asia/Damascus" -_country["SZ"] = "Africa/Mbabane" -_country["TC"] = "America/Grand_Turk" -_country["TD"] = "Africa/Ndjamena" -_country["TF"] = "Indian/Kerguelen" -_country["TG"] = "Africa/Lome" -_country["TH"] = "Asia/Bangkok" -_country["TJ"] = "Asia/Dushanbe" -_country["TK"] = "Pacific/Fakaofo" -_country["TL"] = "Asia/Dili" -_country["TM"] = "Asia/Ashgabat" -_country["TN"] = "Africa/Tunis" -_country["TO"] = "Pacific/Tongatapu" -_country["TR"] = "Asia/Istanbul" -_country["TT"] = "America/Port_of_Spain" -_country["TV"] = "Pacific/Funafuti" -_country["TW"] = "Asia/Taipei" -_country["TZ"] = "Africa/Dar_es_Salaam" -_country["UA"] = {} -_country["UA"]["01"] = "Europe/Kiev" -_country["UA"]["02"] = "Europe/Kiev" -_country["UA"]["03"] = "Europe/Uzhgorod" -_country["UA"]["04"] = "Europe/Zaporozhye" -_country["UA"]["05"] = "Europe/Zaporozhye" -_country["UA"]["06"] = "Europe/Uzhgorod" -_country["UA"]["07"] = "Europe/Zaporozhye" -_country["UA"]["08"] = "Europe/Simferopol" -_country["UA"]["09"] = "Europe/Kiev" -_country["UA"]["10"] = "Europe/Zaporozhye" -_country["UA"]["11"] = "Europe/Simferopol" -_country["UA"]["13"] = "Europe/Kiev" -_country["UA"]["14"] = "Europe/Zaporozhye" -_country["UA"]["15"] = "Europe/Uzhgorod" -_country["UA"]["16"] = "Europe/Zaporozhye" -_country["UA"]["17"] = "Europe/Simferopol" -_country["UA"]["18"] = "Europe/Zaporozhye" -_country["UA"]["19"] = "Europe/Kiev" -_country["UA"]["20"] = "Europe/Simferopol" -_country["UA"]["21"] = "Europe/Kiev" -_country["UA"]["22"] = "Europe/Uzhgorod" -_country["UA"]["23"] = "Europe/Kiev" -_country["UA"]["24"] = "Europe/Uzhgorod" -_country["UA"]["25"] = "Europe/Uzhgorod" -_country["UA"]["26"] = "Europe/Zaporozhye" -_country["UA"]["27"] = "Europe/Kiev" -_country["UG"] = "Africa/Kampala" -_country["US"] = {} -_country["US"]["AK"] = "America/Anchorage" -_country["US"]["AL"] = "America/Chicago" -_country["US"]["AR"] = "America/Chicago" -_country["US"]["AZ"] = "America/Phoenix" -_country["US"]["CA"] = "America/Los_Angeles" -_country["US"]["CO"] = "America/Denver" -_country["US"]["CT"] = "America/New_York" -_country["US"]["DC"] = "America/New_York" -_country["US"]["DE"] = "America/New_York" -_country["US"]["FL"] = "America/New_York" -_country["US"]["GA"] = "America/New_York" -_country["US"]["HI"] = "Pacific/Honolulu" -_country["US"]["IA"] = "America/Chicago" -_country["US"]["ID"] = "America/Denver" -_country["US"]["IL"] = "America/Chicago" -_country["US"]["IN"] = "America/Indianapolis" -_country["US"]["KS"] = "America/Chicago" -_country["US"]["KY"] = "America/New_York" -_country["US"]["LA"] = "America/Chicago" -_country["US"]["MA"] = "America/New_York" -_country["US"]["MD"] = "America/New_York" -_country["US"]["ME"] = "America/New_York" -_country["US"]["MI"] = "America/New_York" -_country["US"]["MN"] = "America/Chicago" -_country["US"]["MO"] = "America/Chicago" -_country["US"]["MS"] = "America/Chicago" -_country["US"]["MT"] = "America/Denver" -_country["US"]["NC"] = "America/New_York" -_country["US"]["ND"] = "America/Chicago" -_country["US"]["NE"] = "America/Chicago" -_country["US"]["NH"] = "America/New_York" -_country["US"]["NJ"] = "America/New_York" -_country["US"]["NM"] = "America/Denver" -_country["US"]["NV"] = "America/Los_Angeles" -_country["US"]["NY"] = "America/New_York" -_country["US"]["OH"] = "America/New_York" -_country["US"]["OK"] = "America/Chicago" -_country["US"]["OR"] = "America/Los_Angeles" -_country["US"]["PA"] = "America/New_York" -_country["US"]["RI"] = "America/New_York" -_country["US"]["SC"] = "America/New_York" -_country["US"]["SD"] = "America/Chicago" -_country["US"]["TN"] = "America/Chicago" -_country["US"]["TX"] = "America/Chicago" -_country["US"]["UT"] = "America/Denver" -_country["US"]["VA"] = "America/New_York" -_country["US"]["VT"] = "America/New_York" -_country["US"]["WA"] = "America/Los_Angeles" -_country["US"]["WI"] = "America/Chicago" -_country["US"]["WV"] = "America/New_York" -_country["US"]["WY"] = "America/Denver" -_country["UY"] = "America/Montevideo" -_country["UZ"] = {} -_country["UZ"]["01"] = "Asia/Tashkent" -_country["UZ"]["02"] = "Asia/Samarkand" -_country["UZ"]["03"] = "Asia/Tashkent" -_country["UZ"]["06"] = "Asia/Tashkent" -_country["UZ"]["07"] = "Asia/Samarkand" -_country["UZ"]["08"] = "Asia/Samarkand" -_country["UZ"]["09"] = "Asia/Samarkand" -_country["UZ"]["10"] = "Asia/Samarkand" -_country["UZ"]["12"] = "Asia/Samarkand" -_country["UZ"]["13"] = "Asia/Tashkent" -_country["UZ"]["14"] = "Asia/Tashkent" -_country["VA"] = "Europe/Vatican" -_country["VC"] = "America/St_Vincent" -_country["VE"] = "America/Caracas" -_country["VG"] = "America/Tortola" -_country["VI"] = "America/St_Thomas" -_country["VN"] = "Asia/Phnom_Penh" -_country["VU"] = "Pacific/Efate" -_country["WF"] = "Pacific/Wallis" -_country["WS"] = "Pacific/Samoa" -_country["YE"] = "Asia/Aden" -_country["YT"] = "Indian/Mayotte" -_country["YU"] = "Europe/Belgrade" -_country["ZA"] = "Africa/Johannesburg" -_country["ZM"] = "Africa/Lusaka" -_country["ZW"] = "Africa/Harare" +_country = { + 'AD': 'Europe/Andorra', + 'AE': 'Asia/Dubai', + 'AF': 'Asia/Kabul', + 'AG': 'America/Antigua', + 'AI': 'America/Anguilla', + 'AL': 'Europe/Tirane', + 'AM': 'Asia/Yerevan', + 'AN': 'America/Curacao', + 'AO': 'Africa/Luanda', + 'AR': { + '01': 'America/Argentina/Buenos_Aires', + '02': 'America/Argentina/Catamarca', + '03': 'America/Argentina/Tucuman', + '04': 'America/Argentina/Rio_Gallegos', + '05': 'America/Argentina/Cordoba', + '06': 'America/Argentina/Tucuman', + '07': 'America/Argentina/Buenos_Aires', + '08': 'America/Argentina/Buenos_Aires', + '09': 'America/Argentina/Tucuman', + '10': 'America/Argentina/Jujuy', + '11': 'America/Argentina/San_Luis', + '12': 'America/Argentina/La_Rioja', + '13': 'America/Argentina/Mendoza', + '14': 'America/Argentina/Buenos_Aires', + '15': 'America/Argentina/San_Luis', + '16': 'America/Argentina/Buenos_Aires', + '17': 'America/Argentina/Salta', + '18': 'America/Argentina/San_Juan', + '19': 'America/Argentina/San_Luis', + '20': 'America/Argentina/Rio_Gallegos', + '21': 'America/Argentina/Buenos_Aires', + '22': 'America/Argentina/Catamarca', + '23': 'America/Argentina/Ushuaia', + '24': 'America/Argentina/Tucuman' + }, + 'AS': 'US/Samoa', + 'AT': 'Europe/Vienna', + 'AU': { + '01': 'Australia/Canberra', + '02': 'Australia/NSW', + '03': 'Australia/North', + '04': 'Australia/Queensland', + '05': 'Australia/South', + '06': 'Australia/Tasmania', + '07': 'Australia/Victoria', + '08': 'Australia/West' + }, + 'AW': 'America/Aruba', + 'AX': 'Europe/Mariehamn', + 'AZ': 'Asia/Baku', + 'BA': 'Europe/Sarajevo', + 'BB': 'America/Barbados', + 'BD': 'Asia/Dhaka', + 'BE': 'Europe/Brussels', + 'BF': 'Africa/Ouagadougou', + 'BG': 'Europe/Sofia', + 'BH': 'Asia/Bahrain', + 'BI': 'Africa/Bujumbura', + 'BJ': 'Africa/Porto-Novo', + 'BL': 'America/St_Barthelemy', + 'BM': 'Atlantic/Bermuda', + 'BN': 'Asia/Brunei', + 'BO': 'America/La_Paz', + 'BQ': 'America/Curacao', + 'BR': { + '01': 'America/Rio_Branco', + '02': 'America/Maceio', + '03': 'America/Sao_Paulo', + '04': 'America/Manaus', + '05': 'America/Bahia', + '06': 'America/Fortaleza', + '07': 'America/Sao_Paulo', + '08': 'America/Sao_Paulo', + '11': 'America/Campo_Grande', + '13': 'America/Belem', + '14': 'America/Cuiaba', + '15': 'America/Sao_Paulo', + '16': 'America/Belem', + '17': 'America/Recife', + '18': 'America/Sao_Paulo', + '20': 'America/Fortaleza', + '21': 'America/Sao_Paulo', + '22': 'America/Recife', + '23': 'America/Sao_Paulo', + '24': 'America/Porto_Velho', + '25': 'America/Boa_Vista', + '26': 'America/Sao_Paulo', + '27': 'America/Sao_Paulo', + '28': 'America/Maceio', + '29': 'America/Sao_Paulo', + '30': 'America/Recife', + '31': 'America/Araguaina' + }, + 'BS': 'America/Nassau', + 'BT': 'Asia/Thimphu', + 'BW': 'Africa/Gaborone', + 'BY': 'Europe/Minsk', + 'BZ': 'America/Belize', + 'CA': { + 'AB': 'America/Edmonton', + 'BC': 'America/Vancouver', + 'MB': 'America/Winnipeg', + 'NB': 'America/Halifax', + 'NL': 'America/St_Johns', + 'NS': 'America/Halifax', + 'NT': 'America/Yellowknife', + 'NU': 'America/Rankin_Inlet', + 'ON': 'America/Toronto', + 'PE': 'America/Halifax', + 'QC': 'America/Montreal', + 'SK': 'America/Regina', + 'YT': 'America/Whitehorse' + }, + 'CC': 'Indian/Cocos', + 'CD': { + '02': 'Africa/Kinshasa', + '05': 'Africa/Lubumbashi', + '06': 'Africa/Kinshasa', + '08': 'Africa/Kinshasa', + '10': 'Africa/Lubumbashi', + '11': 'Africa/Lubumbashi', + '12': 'Africa/Lubumbashi' + }, + 'CF': 'Africa/Bangui', + 'CG': 'Africa/Brazzaville', + 'CH': 'Europe/Zurich', + 'CI': 'Africa/Abidjan', + 'CK': 'Pacific/Rarotonga', + 'CL': 'Chile/Continental', + 'CM': 'Africa/Lagos', + 'CN': { + '01': 'Asia/Shanghai', + '02': 'Asia/Shanghai', + '03': 'Asia/Shanghai', + '04': 'Asia/Shanghai', + '05': 'Asia/Harbin', + '06': 'Asia/Chongqing', + '07': 'Asia/Shanghai', + '08': 'Asia/Harbin', + '09': 'Asia/Shanghai', + '10': 'Asia/Shanghai', + '11': 'Asia/Chongqing', + '12': 'Asia/Shanghai', + '13': 'Asia/Urumqi', + '14': 'Asia/Chongqing', + '15': 'Asia/Chongqing', + '16': 'Asia/Chongqing', + '18': 'Asia/Chongqing', + '19': 'Asia/Harbin', + '20': 'Asia/Harbin', + '21': 'Asia/Chongqing', + '22': 'Asia/Harbin', + '23': 'Asia/Shanghai', + '24': 'Asia/Chongqing', + '25': 'Asia/Shanghai', + '26': 'Asia/Chongqing', + '28': 'Asia/Shanghai', + '29': 'Asia/Chongqing', + '30': 'Asia/Chongqing', + '31': 'Asia/Chongqing', + '32': 'Asia/Chongqing', + '33': 'Asia/Chongqing' + }, + 'CO': 'America/Bogota', + 'CR': 'America/Costa_Rica', + 'CU': 'America/Havana', + 'CV': 'Atlantic/Cape_Verde', + 'CW': 'America/Curacao', + 'CX': 'Indian/Christmas', + 'CY': 'Asia/Nicosia', + 'CZ': 'Europe/Prague', + 'DE': 'Europe/Berlin', + 'DJ': 'Africa/Djibouti', + 'DK': 'Europe/Copenhagen', + 'DM': 'America/Dominica', + 'DO': 'America/Santo_Domingo', + 'DZ': 'Africa/Algiers', + 'EC': { + '01': 'Pacific/Galapagos', + '02': 'America/Guayaquil', + '03': 'America/Guayaquil', + '04': 'America/Guayaquil', + '05': 'America/Guayaquil', + '06': 'America/Guayaquil', + '07': 'America/Guayaquil', + '08': 'America/Guayaquil', + '09': 'America/Guayaquil', + '10': 'America/Guayaquil', + '11': 'America/Guayaquil', + '12': 'America/Guayaquil', + '13': 'America/Guayaquil', + '14': 'America/Guayaquil', + '15': 'America/Guayaquil', + '17': 'America/Guayaquil', + '18': 'America/Guayaquil', + '19': 'America/Guayaquil', + '20': 'America/Guayaquil', + '22': 'America/Guayaquil' + }, + 'EE': 'Europe/Tallinn', + 'EG': 'Africa/Cairo', + 'EH': 'Africa/El_Aaiun', + 'ER': 'Africa/Asmera', + 'ES': { + '07': 'Europe/Madrid', + '27': 'Europe/Madrid', + '29': 'Europe/Madrid', + '31': 'Europe/Madrid', + '32': 'Europe/Madrid', + '34': 'Europe/Madrid', + '39': 'Europe/Madrid', + '51': 'Africa/Ceuta', + '52': 'Europe/Madrid', + '53': 'Atlantic/Canary', + '54': 'Europe/Madrid', + '55': 'Europe/Madrid', + '56': 'Europe/Madrid', + '57': 'Europe/Madrid', + '58': 'Europe/Madrid', + '59': 'Europe/Madrid', + '60': 'Europe/Madrid' + }, + 'ET': 'Africa/Addis_Ababa', + 'FI': 'Europe/Helsinki', + 'FJ': 'Pacific/Fiji', + 'FK': 'Atlantic/Stanley', + 'FO': 'Atlantic/Faeroe', + 'FR': 'Europe/Paris', + 'FX': 'Europe/Paris', + 'GA': 'Africa/Libreville', + 'GB': 'Europe/London', + 'GD': 'America/Grenada', + 'GE': 'Asia/Tbilisi', + 'GF': 'America/Cayenne', + 'GG': 'Europe/Guernsey', + 'GH': 'Africa/Accra', + 'GI': 'Europe/Gibraltar', + 'GL': { + '01': 'America/Thule', + '02': 'America/Godthab', + '03': 'America/Godthab' + }, + 'GM': 'Africa/Banjul', + 'GN': 'Africa/Conakry', + 'GP': 'America/Guadeloupe', + 'GQ': 'Africa/Malabo', + 'GR': 'Europe/Athens', + 'GS': 'Atlantic/South_Georgia', + 'GT': 'America/Guatemala', + 'GU': 'Pacific/Guam', + 'GW': 'Africa/Bissau', + 'GY': 'America/Guyana', + 'HK': 'Asia/Hong_Kong', + 'HN': 'America/Tegucigalpa', + 'HR': 'Europe/Zagreb', + 'HT': 'America/Port-au-Prince', + 'HU': 'Europe/Budapest', + 'ID': { + '01': 'Asia/Pontianak', + '02': 'Asia/Makassar', + '03': 'Asia/Jakarta', + '04': 'Asia/Jakarta', + '05': 'Asia/Jakarta', + '06': 'Asia/Jakarta', + '07': 'Asia/Jakarta', + '08': 'Asia/Jakarta', + '09': 'Asia/Jayapura', + '10': 'Asia/Jakarta', + '11': 'Asia/Pontianak', + '12': 'Asia/Makassar', + '13': 'Asia/Makassar', + '14': 'Asia/Makassar', + '15': 'Asia/Jakarta', + '16': 'Asia/Makassar', + '17': 'Asia/Makassar', + '18': 'Asia/Makassar', + '19': 'Asia/Pontianak', + '20': 'Asia/Makassar', + '21': 'Asia/Makassar', + '22': 'Asia/Makassar', + '23': 'Asia/Makassar', + '24': 'Asia/Jakarta', + '25': 'Asia/Pontianak', + '26': 'Asia/Pontianak', + '30': 'Asia/Jakarta', + '31': 'Asia/Makassar', + '33': 'Asia/Jakarta' + }, + 'IE': 'Europe/Dublin', + 'IL': 'Asia/Jerusalem', + 'IM': 'Europe/Isle_of_Man', + 'IN': 'Asia/Calcutta', + 'IO': 'Indian/Chagos', + 'IQ': 'Asia/Baghdad', + 'IR': 'Asia/Tehran', + 'IS': 'Atlantic/Reykjavik', + 'IT': 'Europe/Rome', + 'JE': 'Europe/Jersey', + 'JM': 'America/Jamaica', + 'JO': 'Asia/Amman', + 'JP': 'Asia/Tokyo', + 'KE': 'Africa/Nairobi', + 'KG': 'Asia/Bishkek', + 'KH': 'Asia/Phnom_Penh', + 'KI': 'Pacific/Tarawa', + 'KM': 'Indian/Comoro', + 'KN': 'America/St_Kitts', + 'KP': 'Asia/Pyongyang', + 'KR': 'Asia/Seoul', + 'KW': 'Asia/Kuwait', + 'KY': 'America/Cayman', + 'KZ': { + '01': 'Asia/Almaty', + '02': 'Asia/Almaty', + '03': 'Asia/Qyzylorda', + '04': 'Asia/Aqtobe', + '05': 'Asia/Qyzylorda', + '06': 'Asia/Aqtau', + '07': 'Asia/Oral', + '08': 'Asia/Qyzylorda', + '09': 'Asia/Aqtau', + '10': 'Asia/Qyzylorda', + '11': 'Asia/Almaty', + '12': 'Asia/Qyzylorda', + '13': 'Asia/Aqtobe', + '14': 'Asia/Qyzylorda', + '15': 'Asia/Almaty', + '16': 'Asia/Aqtobe', + '17': 'Asia/Almaty' + }, + 'LA': 'Asia/Vientiane', + 'LB': 'Asia/Beirut', + 'LC': 'America/St_Lucia', + 'LI': 'Europe/Vaduz', + 'LK': 'Asia/Colombo', + 'LR': 'Africa/Monrovia', + 'LS': 'Africa/Maseru', + 'LT': 'Europe/Vilnius', + 'LU': 'Europe/Luxembourg', + 'LV': 'Europe/Riga', + 'LY': 'Africa/Tripoli', + 'MA': 'Africa/Casablanca', + 'MC': 'Europe/Monaco', + 'MD': 'Europe/Chisinau', + 'ME': 'Europe/Podgorica', + 'MF': 'America/Marigot', + 'MG': 'Indian/Antananarivo', + 'MK': 'Europe/Skopje', + 'ML': 'Africa/Bamako', + 'MM': 'Asia/Rangoon', + 'MN': 'Asia/Choibalsan', + 'MO': 'Asia/Macao', + 'MP': 'Pacific/Saipan', + 'MQ': 'America/Martinique', + 'MR': 'Africa/Nouakchott', + 'MS': 'America/Montserrat', + 'MT': 'Europe/Malta', + 'MU': 'Indian/Mauritius', + 'MV': 'Indian/Maldives', + 'MW': 'Africa/Blantyre', + 'MX': { + '01': 'America/Mexico_City', + '02': 'America/Tijuana', + '03': 'America/Hermosillo', + '04': 'America/Merida', + '05': 'America/Mexico_City', + '06': 'America/Chihuahua', + '07': 'America/Monterrey', + '08': 'America/Mexico_City', + '09': 'America/Mexico_City', + '10': 'America/Mazatlan', + '11': 'America/Mexico_City', + '12': 'America/Mexico_City', + '13': 'America/Mexico_City', + '14': 'America/Mazatlan', + '15': 'America/Chihuahua', + '16': 'America/Mexico_City', + '17': 'America/Mexico_City', + '18': 'America/Mazatlan', + '19': 'America/Monterrey', + '20': 'America/Mexico_City', + '21': 'America/Mexico_City', + '22': 'America/Mexico_City', + '23': 'America/Cancun', + '24': 'America/Mexico_City', + '25': 'America/Mazatlan', + '26': 'America/Hermosillo', + '27': 'America/Merida', + '28': 'America/Monterrey', + '29': 'America/Mexico_City', + '30': 'America/Mexico_City', + '31': 'America/Merida', + '32': 'America/Monterrey' + }, + 'MY': { + '01': 'Asia/Kuala_Lumpur', + '02': 'Asia/Kuala_Lumpur', + '03': 'Asia/Kuala_Lumpur', + '04': 'Asia/Kuala_Lumpur', + '05': 'Asia/Kuala_Lumpur', + '06': 'Asia/Kuala_Lumpur', + '07': 'Asia/Kuala_Lumpur', + '08': 'Asia/Kuala_Lumpur', + '09': 'Asia/Kuala_Lumpur', + '11': 'Asia/Kuching', + '12': 'Asia/Kuala_Lumpur', + '13': 'Asia/Kuala_Lumpur', + '14': 'Asia/Kuala_Lumpur', + '15': 'Asia/Kuching', + '16': 'Asia/Kuching' + }, + 'MZ': 'Africa/Maputo', + 'NA': 'Africa/Windhoek', + 'NC': 'Pacific/Noumea', + 'NE': 'Africa/Niamey', + 'NF': 'Pacific/Norfolk', + 'NG': 'Africa/Lagos', + 'NI': 'America/Managua', + 'NL': 'Europe/Amsterdam', + 'NO': 'Europe/Oslo', + 'NP': 'Asia/Katmandu', + 'NR': 'Pacific/Nauru', + 'NU': 'Pacific/Niue', + 'NZ': { + '85': 'Pacific/Auckland', + 'E7': 'Pacific/Auckland', + 'E8': 'Pacific/Auckland', + 'E9': 'Pacific/Auckland', + 'F1': 'Pacific/Auckland', + 'F2': 'Pacific/Auckland', + 'F3': 'Pacific/Auckland', + 'F4': 'Pacific/Auckland', + 'F5': 'Pacific/Auckland', + 'F7': 'Pacific/Chatham', + 'F8': 'Pacific/Auckland', + 'F9': 'Pacific/Auckland', + 'G1': 'Pacific/Auckland', + 'G2': 'Pacific/Auckland', + 'G3': 'Pacific/Auckland' + }, + 'OM': 'Asia/Muscat', + 'PA': 'America/Panama', + 'PE': 'America/Lima', + 'PF': 'Pacific/Marquesas', + 'PG': 'Pacific/Port_Moresby', + 'PH': 'Asia/Manila', + 'PK': 'Asia/Karachi', + 'PL': 'Europe/Warsaw', + 'PM': 'America/Miquelon', + 'PN': 'Pacific/Pitcairn', + 'PR': 'America/Puerto_Rico', + 'PS': 'Asia/Gaza', + 'PT': { + '02': 'Europe/Lisbon', + '03': 'Europe/Lisbon', + '04': 'Europe/Lisbon', + '05': 'Europe/Lisbon', + '06': 'Europe/Lisbon', + '07': 'Europe/Lisbon', + '08': 'Europe/Lisbon', + '09': 'Europe/Lisbon', + '10': 'Atlantic/Madeira', + '11': 'Europe/Lisbon', + '13': 'Europe/Lisbon', + '14': 'Europe/Lisbon', + '16': 'Europe/Lisbon', + '17': 'Europe/Lisbon', + '18': 'Europe/Lisbon', + '19': 'Europe/Lisbon', + '20': 'Europe/Lisbon', + '21': 'Europe/Lisbon', + '22': 'Europe/Lisbon' + }, + 'PW': 'Pacific/Palau', + 'PY': 'America/Asuncion', + 'QA': 'Asia/Qatar', + 'RE': 'Indian/Reunion', + 'RO': 'Europe/Bucharest', + 'RS': 'Europe/Belgrade', + 'RU': { + '01': 'Europe/Volgograd', + '02': 'Asia/Irkutsk', + '03': 'Asia/Novokuznetsk', + '04': 'Asia/Novosibirsk', + '05': 'Asia/Vladivostok', + '06': 'Europe/Moscow', + '07': 'Europe/Volgograd', + '08': 'Europe/Samara', + '09': 'Europe/Moscow', + '10': 'Europe/Moscow', + '11': 'Asia/Irkutsk', + '13': 'Asia/Yekaterinburg', + '14': 'Asia/Irkutsk', + '15': 'Asia/Anadyr', + '16': 'Europe/Samara', + '17': 'Europe/Volgograd', + '18': 'Asia/Krasnoyarsk', + '20': 'Asia/Irkutsk', + '21': 'Europe/Moscow', + '22': 'Europe/Volgograd', + '23': 'Europe/Kaliningrad', + '24': 'Europe/Volgograd', + '25': 'Europe/Moscow', + '26': 'Asia/Kamchatka', + '27': 'Europe/Volgograd', + '28': 'Europe/Moscow', + '29': 'Asia/Novokuznetsk', + '30': 'Asia/Vladivostok', + '31': 'Asia/Krasnoyarsk', + '32': 'Asia/Omsk', + '33': 'Asia/Yekaterinburg', + '34': 'Asia/Yekaterinburg', + '35': 'Asia/Yekaterinburg', + '36': 'Asia/Anadyr', + '37': 'Europe/Moscow', + '38': 'Europe/Volgograd', + '39': 'Asia/Krasnoyarsk', + '40': 'Asia/Yekaterinburg', + '41': 'Europe/Moscow', + '42': 'Europe/Moscow', + '43': 'Europe/Moscow', + '44': 'Asia/Magadan', + '45': 'Europe/Samara', + '46': 'Europe/Samara', + '47': 'Europe/Moscow', + '48': 'Europe/Moscow', + '49': 'Europe/Moscow', + '50': 'Asia/Yekaterinburg', + '51': 'Europe/Moscow', + '52': 'Europe/Moscow', + '53': 'Asia/Novosibirsk', + '54': 'Asia/Omsk', + '55': 'Europe/Samara', + '56': 'Europe/Moscow', + '57': 'Europe/Samara', + '58': 'Asia/Yekaterinburg', + '59': 'Asia/Vladivostok', + '60': 'Europe/Kaliningrad', + '61': 'Europe/Volgograd', + '62': 'Europe/Moscow', + '63': 'Asia/Yakutsk', + '64': 'Asia/Sakhalin', + '65': 'Europe/Samara', + '66': 'Europe/Moscow', + '67': 'Europe/Samara', + '68': 'Europe/Volgograd', + '69': 'Europe/Moscow', + '70': 'Europe/Volgograd', + '71': 'Asia/Yekaterinburg', + '72': 'Europe/Moscow', + '73': 'Europe/Samara', + '74': 'Asia/Krasnoyarsk', + '75': 'Asia/Novosibirsk', + '76': 'Europe/Moscow', + '77': 'Europe/Moscow', + '78': 'Asia/Yekaterinburg', + '79': 'Asia/Irkutsk', + '80': 'Asia/Yekaterinburg', + '81': 'Europe/Samara', + '82': 'Asia/Irkutsk', + '83': 'Europe/Moscow', + '84': 'Europe/Volgograd', + '85': 'Europe/Moscow', + '86': 'Europe/Moscow', + '87': 'Asia/Novosibirsk', + '88': 'Europe/Moscow', + '89': 'Asia/Vladivostok' + }, + 'RW': 'Africa/Kigali', + 'SA': 'Asia/Riyadh', + 'SB': 'Pacific/Guadalcanal', + 'SC': 'Indian/Mahe', + 'SD': 'Africa/Khartoum', + 'SE': 'Europe/Stockholm', + 'SG': 'Asia/Singapore', + 'SH': 'Atlantic/St_Helena', + 'SI': 'Europe/Ljubljana', + 'SJ': 'Arctic/Longyearbyen', + 'SK': 'Europe/Bratislava', + 'SL': 'Africa/Freetown', + 'SM': 'Europe/San_Marino', + 'SN': 'Africa/Dakar', + 'SO': 'Africa/Mogadishu', + 'SR': 'America/Paramaribo', + 'SS': 'Africa/Juba', + 'ST': 'Africa/Sao_Tome', + 'SV': 'America/El_Salvador', + 'SX': 'America/Curacao', + 'SY': 'Asia/Damascus', + 'SZ': 'Africa/Mbabane', + 'TC': 'America/Grand_Turk', + 'TD': 'Africa/Ndjamena', + 'TF': 'Indian/Kerguelen', + 'TG': 'Africa/Lome', + 'TH': 'Asia/Bangkok', + 'TJ': 'Asia/Dushanbe', + 'TK': 'Pacific/Fakaofo', + 'TL': 'Asia/Dili', + 'TM': 'Asia/Ashgabat', + 'TN': 'Africa/Tunis', + 'TO': 'Pacific/Tongatapu', + 'TR': 'Asia/Istanbul', + 'TT': 'America/Port_of_Spain', + 'TV': 'Pacific/Funafuti', + 'TW': 'Asia/Taipei', + 'TZ': 'Africa/Dar_es_Salaam', + 'UA': { + '01': 'Europe/Kiev', + '02': 'Europe/Kiev', + '03': 'Europe/Uzhgorod', + '04': 'Europe/Zaporozhye', + '05': 'Europe/Zaporozhye', + '06': 'Europe/Uzhgorod', + '07': 'Europe/Zaporozhye', + '08': 'Europe/Simferopol', + '09': 'Europe/Kiev', + '10': 'Europe/Zaporozhye', + '11': 'Europe/Simferopol', + '13': 'Europe/Kiev', + '14': 'Europe/Zaporozhye', + '15': 'Europe/Uzhgorod', + '16': 'Europe/Zaporozhye', + '17': 'Europe/Simferopol', + '18': 'Europe/Zaporozhye', + '19': 'Europe/Kiev', + '20': 'Europe/Simferopol', + '21': 'Europe/Kiev', + '22': 'Europe/Uzhgorod', + '23': 'Europe/Kiev', + '24': 'Europe/Uzhgorod', + '25': 'Europe/Uzhgorod', + '26': 'Europe/Zaporozhye', + '27': 'Europe/Kiev' + }, + 'UG': 'Africa/Kampala', + 'US': { + 'AK': 'America/Anchorage', + 'AL': 'America/Chicago', + 'AR': 'America/Chicago', + 'AZ': 'America/Phoenix', + 'CA': 'America/Los_Angeles', + 'CO': 'America/Denver', + 'CT': 'America/New_York', + 'DC': 'America/New_York', + 'DE': 'America/New_York', + 'FL': 'America/New_York', + 'GA': 'America/New_York', + 'HI': 'Pacific/Honolulu', + 'IA': 'America/Chicago', + 'ID': 'America/Denver', + 'IL': 'America/Chicago', + 'IN': 'America/Indianapolis', + 'KS': 'America/Chicago', + 'KY': 'America/New_York', + 'LA': 'America/Chicago', + 'MA': 'America/New_York', + 'MD': 'America/New_York', + 'ME': 'America/New_York', + 'MI': 'America/New_York', + 'MN': 'America/Chicago', + 'MO': 'America/Chicago', + 'MS': 'America/Chicago', + 'MT': 'America/Denver', + 'NC': 'America/New_York', + 'ND': 'America/Chicago', + 'NE': 'America/Chicago', + 'NH': 'America/New_York', + 'NJ': 'America/New_York', + 'NM': 'America/Denver', + 'NV': 'America/Los_Angeles', + 'NY': 'America/New_York', + 'OH': 'America/New_York', + 'OK': 'America/Chicago', + 'OR': 'America/Los_Angeles', + 'PA': 'America/New_York', + 'RI': 'America/New_York', + 'SC': 'America/New_York', + 'SD': 'America/Chicago', + 'TN': 'America/Chicago', + 'TX': 'America/Chicago', + 'UT': 'America/Denver', + 'VA': 'America/New_York', + 'VT': 'America/New_York', + 'WA': 'America/Los_Angeles', + 'WI': 'America/Chicago', + 'WV': 'America/New_York', + 'WY': 'America/Denver' + }, + 'UY': 'America/Montevideo', + 'UZ': { + '01': 'Asia/Tashkent', + '02': 'Asia/Samarkand', + '03': 'Asia/Tashkent', + '06': 'Asia/Tashkent', + '07': 'Asia/Samarkand', + '08': 'Asia/Samarkand', + '09': 'Asia/Samarkand', + '10': 'Asia/Samarkand', + '12': 'Asia/Samarkand', + '13': 'Asia/Tashkent', + '14': 'Asia/Tashkent' + }, + 'VA': 'Europe/Vatican', + 'VC': 'America/St_Vincent', + 'VE': 'America/Caracas', + 'VG': 'America/Tortola', + 'VI': 'America/St_Thomas', + 'VN': 'Asia/Phnom_Penh', + 'VU': 'Pacific/Efate', + 'WF': 'Pacific/Wallis', + 'WS': 'Pacific/Samoa', + 'YE': 'Asia/Aden', + 'YT': 'Indian/Mayotte', + 'YU': 'Europe/Belgrade', + 'ZA': 'Africa/Johannesburg', + 'ZM': 'Africa/Lusaka', + 'ZW': 'Africa/Harare' + } + def time_zone_by_country_and_region(country_code, region_name=None): - if country_code not in _country: - return None + return '' if not region_name or region_name == '00': region_name = None timezones = _country[country_code] - if isinstance(timezones, str): return timezones - if region_name: - return timezones.get(region_name) + if not region_name: + return '' + + return timezones.get(region_name) diff --git a/lib/pygeoip/util.py b/lib/pygeoip/util.py index f2873f3..cca3fec 100644 --- a/lib/pygeoip/util.py +++ b/lib/pygeoip/util.py @@ -1,42 +1,36 @@ -""" -Misc. utility functions. It is part of the pygeoip package. - -@author: Jennifer Ennis - -@license: -Copyright(C) 2004 MaxMind LLC - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -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. - -You should have received a copy of the GNU Lesser General Public License -along with this program. If not, see . -""" - -import six - -def ip2long(ip): - """ - Convert a IPv4 address into a 32-bit integer. - - @param ip: quad-dotted IPv4 address - @type ip: str - @return: network byte order 32-bit integer - @rtype: int - """ - ip_array = ip.split('.') - - if six.PY3: - # int and long are unified in py3 - ip_long = int(ip_array[0]) * 16777216 + int(ip_array[1]) * 65536 + int(ip_array[2]) * 256 + int(ip_array[3]) - else: - ip_long = long(ip_array[0]) * 16777216 + long(ip_array[1]) * 65536 + long(ip_array[2]) * 256 + long(ip_array[3]) - return ip_long - +# -*- coding: utf-8 -*- +""" +Utility functions. Part of the pygeoip package. + +@author: Jennifer Ennis + +@license: Copyright(C) 2004 MaxMind LLC + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +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. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +""" + +import socket +import binascii + + +def ip2long(ip): + """ + Wrapper function for IPv4 and IPv6 converters + @param ip: IPv4 or IPv6 address + @type ip: str + """ + try: + return int(binascii.hexlify(socket.inet_aton(ip)), 16) + except socket.error: + return int(binascii.hexlify(socket.inet_pton(socket.AF_INET6, ip)), 16)