[tor-commits] [metrics-tasks/master] Check diversity of a consensus in the face of a national level adversary

karsten at torproject.org karsten at torproject.org
Sat Jul 14 15:31:31 UTC 2012


commit e3e89c3ea085c700a86ab971e31efc90882d7f44
Author: Sathyanarayanan Gunasekaran <gsathya.ceg at gmail.com>
Date:   Fri Jul 13 23:24:51 2012 +0530

    Check diversity of a consensus in the face of a national level adversary
---
 task-6232/.gitignore          |    2 +-
 task-6232/GeoIP.dat           |  Bin 0 -> 1343110 bytes
 task-6232/pyentropy.py        |   42 ++-
 task-6232/pygeoip/__init__.py |  647 +++++++++++++++++++++++++++++++++++++
 task-6232/pygeoip/const.py    |  382 ++++++++++++++++++++++
 task-6232/pygeoip/six.py      |  353 ++++++++++++++++++++
 task-6232/pygeoip/timezone.py |  714 +++++++++++++++++++++++++++++++++++++++++
 task-6232/pygeoip/util.py     |   35 ++
 8 files changed, 2160 insertions(+), 15 deletions(-)

diff --git a/task-6232/.gitignore b/task-6232/.gitignore
index 8a0c627..0f63a8b 100644
--- a/task-6232/.gitignore
+++ b/task-6232/.gitignore
@@ -1,3 +1,3 @@
 in/
 entropy.csv
-
+*.pyc
diff --git a/task-6232/GeoIP.dat b/task-6232/GeoIP.dat
new file mode 100644
index 0000000..00510d5
Binary files /dev/null and b/task-6232/GeoIP.dat differ
diff --git a/task-6232/pyentropy.py b/task-6232/pyentropy.py
index ec3365f..b064339 100644
--- a/task-6232/pyentropy.py
+++ b/task-6232/pyentropy.py
@@ -1,15 +1,15 @@
 """
 Usage - python pyentropy.py <consensus-dir> <output-file>
-Output - A CSV file of the format <valid-after>,<entropy for all nodes>,<entropy for exitnodes>,<entropy for guardnodes>
+Output - A CSV file of the format <valid-after>,<entropy for all nodes>,<entropy for exitnodes>,<entropy for guardnodes>,<entropy for countries>
 rsync -arz --delete metrics.torproject.org::metrics-recent/relay-descriptors/consensuses in
 """
 
 import sys
 import math
 import os
-from decimal import *
+import pygeoip
+import getopt
 
-RESULTS = []
 KEYS = ['r','s','v','w','p','m']
 
 class Router:
@@ -19,12 +19,16 @@ class Router:
         self.bandwidth = None
         self.flags = None
         self.probability = None
+        self.ip = None
+        self.country = None
         self.is_exit = None
         self.is_guard = None
 
     def add(self, key, values):
         if key == 'r':
            self.nick = values[0]
+           self.ip = values[5]
+           self.country = gi.country_name_by_addr(self.ip)
         if key == 'w':
            self.bandwidth = int(values[0].split('=')[1])
         if key == 's':
@@ -36,7 +40,7 @@ class Router:
 
 def run(file_name):
     routers = []
-        # parse consensus
+    # parse consensus
     with open(file_name, 'r') as f:
         for line in f.readlines():
             key = line.split()[0]
@@ -53,33 +57,41 @@ def run(file_name):
                 router.add(key, values)
 
     totalBW, totalExitBW, totalGuardBW = 0, 0, 0
+    bw_countries = {}
     for router in routers:
         totalBW += router.bandwidth
         if router.is_guard:
             totalGuardBW += router.bandwidth
         if router.is_exit:
             totalExitBW += router.bandwidth
+        if bw_countries.has_key(router.country):
+            bw_countries[router.country] += router.bandwidth
+        else:
+            bw_countries[router.country] = router.bandwidth
 
     if len(routers) <= 0:
         return
 
-    entropy, entropy_exit, entropy_guard = 0.0, 0.0, 0.0
+    entropy, entropy_exit, entropy_guard, entropy_country = 0.0, 0.0, 0.0, 0.0
     for router in routers:
         p = float(router.bandwidth) / float(totalBW)
         if p != 0:
             entropy += -(p * math.log(p, 2))
-
         if router.is_guard:
             p = float(router.bandwidth) / float(totalGuardBW)
             if p != 0:
                 entropy_guard += -(p * math.log(p, 2))
-
         if router.is_exit:
             p = float(router.bandwidth) / float(totalExitBW)
             if p != 0:
                 entropy_exit += -(p * math.log(p, 2))
 
-    return ",".join([valid_after, str(entropy), str(entropy_exit), str(entropy_guard)])
+    for country in bw_countries.iterkeys():
+        p = float(bw_countries[country]) / float(totalBW)
+        if p != 0:
+            entropy_country += -(p * math.log(p, 2))
+    
+    return ",".join([valid_after, str(entropy), str(entropy_exit), str(entropy_guard), str(entropy_country)])
 
 def usage():
     print "Usage - python pyentropy.py <consensus-dir> <output-file>"
@@ -87,9 +99,11 @@ def usage():
 if __name__ == "__main__":
     if len(sys.argv) != 3:
         usage()
-    else:
-        with open(sys.argv[2], 'w') as f:
-            for file_name in os.listdir(sys.argv[1]):
-                string = run(os.path.join(sys.argv[1], file_name))
-                if string:
-                    f.write("%s\n" % (string))
+        sys.exit()
+
+    gi = pygeoip.GeoIP(os.path.join(os.path.dirname(__file__), 'GeoIP.dat'))
+    with open(sys.argv[2], 'w') as f:
+        for file_name in os.listdir(sys.argv[1]):
+            string = run(os.path.join(sys.argv[1], file_name))
+            if string:
+                f.write("%s\n" % (string))
diff --git a/task-6232/pygeoip/__init__.py b/task-6232/pygeoip/__init__.py
new file mode 100644
index 0000000..759c9e1
--- /dev/null
+++ b/task-6232/pygeoip/__init__.py
@@ -0,0 +1,647 @@
+"""
+Pure Python GeoIP API. The API is based off of U{MaxMind's C-based Python API<http://www.maxmind.com/app/python>},
+but the code itself is based on the U{pure PHP5 API<http://pear.php.net/package/Net_GeoIP/>}
+by Jim Winstead and Hans Lellelid.
+
+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:
+
+C{gi = GeoIP('/path/to/GeoIP.dat', pygeoip.MEMORY_CACHE)}
+
+ at author: Jennifer Ennis <zaylea at gmail dot com>
+
+ at 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 <http://www.gnu.org/licenses/lgpl.txt>.
+"""
+
+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 . import const
+from .util import ip2long
+from .timezone import time_zone_by_country_and_region
+
+from . import six
+
+MMAP_CACHE = const.MMAP_CACHE
+MEMORY_CACHE = const.MEMORY_CACHE
+STANDARD = const.STANDARD
+
+class GeoIPError(Exception):
+    pass
+
+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.
+        """
+
+        if not hasattr(cls, '_instances'):
+            cls._instances = {}
+
+        if len(args) > 0:
+            filename = args[0]
+        elif 'filename' in kwargs:
+            filename = kwargs['filename']
+
+        if not filename in cls._instances:
+            cls._instances[filename] = type.__new__(cls, *args, **kwargs)
+
+        return cls._instances[filename]
+
+GeoIPBase = GeoIPMetaclass('GeoIPBase', (object,), {})
+
+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.
+        @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
+            MMAP_CACHE (access the file via mmap).
+        @type flags: int
+        """
+        self._filename = filename
+        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)
+
+        elif self._flags & const.MEMORY_CACHE:
+            if filename.endswith('.gz'):
+                opener = gzip.open
+            else:
+                opener = open
+
+            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._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.
+        """
+        self._databaseType = const.COUNTRY_EDITION
+        self._recordLength = const.STANDARD_RECORD_LENGTH
+
+        filepos = self._filehandle.tell()
+        self._filehandle.seek(-3, os.SEEK_END)
+
+        for i in range(const.STRUCTURE_INFO_MAX_SIZE):
+            delim = self._filehandle.read(3)
+
+            if delim == six.u(chr(255) * 3):
+                self._databaseType = ord(self._filehandle.read(1))
+
+                if (self._databaseType >= 106):
+                    # backwards compatibility with databases from April 2003 and earlier
+                    self._databaseType -= 105
+
+                if self._databaseType == const.REGION_EDITION_REV0:
+                    self._databaseSegments = const.STATE_BEGIN_REV0
+
+                elif self._databaseType == const.REGION_EDITION_REV1:
+                    self._databaseSegments = const.STATE_BEGIN_REV1
+
+                elif self._databaseType in (const.CITY_EDITION_REV0,
+                                            const.CITY_EDITION_REV1,
+                                            const.ORG_EDITION,
+                                            const.ISP_EDITION,
+                                            const.ASNUM_EDITION):
+                    self._databaseSegments = 0
+                    buf = self._filehandle.read(const.SEGMENT_RECORD_LENGTH)
+
+                    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):
+                        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
+
+    def _seek_country(self, ipnum):
+        """
+        Using the record length and appropriate start points, seek to the
+        country that corresponds to the converted IP address integer.
+
+        @param ipnum: result of ip2long conversion
+        @type ipnum: int
+        @return: offset of start of record
+        @rtype: int
+        """
+        offset = 0
+
+        for depth in range(31, -1, -1):
+
+            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)
+
+            x = [0,0]
+
+            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?')
+
+    def _get_org(self, ipnum):
+        """
+        Seek and return organization (or ISP) name for converted IP addr.
+        @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
+
+        self._filehandle.seek(record_pointer, os.SEEK_SET)
+
+        org_buf = self._filehandle.read(const.MAX_ORG_RECORD_LENGTH)
+
+        return org_buf[:org_buf.index(chr(0))]
+
+    def _get_region(self, ipnum):
+        """
+        Seek and return the region info (dict containing country_code and region_name).
+
+        @param ipnum: converted IP address
+        @type ipnum: int
+        @return: dict containing country_code and region_name
+        @rtype: dict
+        """
+        country_code = ''
+        region = ''
+
+        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)])
+            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 = ''
+            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:
+                country_code = 'CA'
+                region = ''.join([chr((seek_region - const.CANADA_OFFSET) // 26 + 65), chr((seek_region - const.CANADA_OFFSET) % 26 + 65)])
+            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):
+            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 ''
+
+        return {'country_code' : country_code, 'region_name' : region }
+
+    def _get_record(self, ipnum):
+        """
+        Populate location dict for converted IP.
+
+        @param ipnum: converted IP address
+        @type ipnum: int
+        @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
+        @rtype: dict
+        """
+        seek_country = self._seek_country(ipnum)
+        if seek_country == self._databaseSegments:
+            return None
+
+        record_pointer = seek_country + (2 * self._recordLength - 1) * self._databaseSegments
+
+        self._filehandle.seek(record_pointer, os.SEEK_SET)
+        record_buf = self._filehandle.read(const.FULL_RECORD_LENGTH)
+
+        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
+
+        latitude = 0
+        longitude = 0
+        for j in range(3):
+            char = ord(record_buf[record_buf_pos])
+            record_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
+            longitude += (char << (j * 8))
+
+        record['longitude'] = (longitude/10000.0) - 180.0
+
+        if self._databaseType == const.CITY_EDITION_REV1:
+            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))
+
+                record['dma_code'] = int(math.floor(dmaarea_combo/1000))
+                record['area_code'] = dmaarea_combo%1000
+        else:
+            record['dma_code'] = 0
+            record['area_code'] = 0
+
+        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'] = ''
+
+        return record
+
+    def country_code_by_addr(self, addr):
+        """
+        Returns 2-letter country code (e.g. 'US') for specified IP address.
+        Use this method if you have a Country, Region, or City database.
+
+        @param addr: IP address
+        @type addr: str
+        @return: 2-letter country code
+        @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')
+
+        except ValueError:
+            raise GeoIPError('*_by_addr methods only accept IP addresses. Use *_by_name for hostnames. (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
+        @type hostname: str
+        @return: 2-letter country code
+        @rtype: str
+        """
+        addr = socket.gethostbyname(hostname)
+
+        return self.country_code_by_addr(addr)
+
+    def country_name_by_addr(self, addr):
+        """
+        Returns full country name for specified IP address.
+        Use this method if you have a Country or City database.
+
+        @param addr: IP address
+        @type addr: str
+        @return: country name
+        @rtype: str
+        """
+        try:
+            if self._databaseType == const.COUNTRY_EDITION:
+                country_id = self._lookup_country_id(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']
+            else:
+                raise GeoIPError('Invalid database type; country_* methods expect '\
+                                 'Country or City database')
+        except ValueError:
+            raise GeoIPError('*_by_addr methods only accept IP addresses. Use *_by_name for hostnames. (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
+        @type hostname: str
+        @return: country name
+        @rtype: str
+        """
+        addr = socket.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.
+
+        @param addr: IP address
+        @type addr: str
+        @return: organization or ISP name
+        @rtype: str
+        """
+        try:
+            ipnum = ip2long(addr)
+
+            if not ipnum:
+                raise ValueError("Invalid IP address: %s" % addr)
+
+            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')
+
+            return self._get_org(ipnum)
+        except ValueError:
+            raise GeoIPError('*_by_addr methods only accept IP addresses. Use *_by_name for hostnames. (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
+        @type hostname: str
+        @return: organization or ISP name
+        @rtype: str
+        """
+        addr = socket.gethostbyname(hostname)
+
+        return self.org_by_addr(addr)
+
+    def record_by_addr(self, addr):
+        """
+        Look up the record for a given IP address.
+        Use this method if you have a City database.
+
+        @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
+        @rtype: dict
+        """
+        try:
+            ipnum = ip2long(addr)
+
+            if not ipnum:
+                raise ValueError("Invalid IP address: %s" % addr)
+
+            if not self._databaseType in (const.CITY_EDITION_REV0, const.CITY_EDITION_REV1):
+                raise GeoIPError('Invalid database type; record_* methods expect City database')
+
+            return self._get_record(ipnum)
+        except ValueError:
+            raise GeoIPError('*_by_addr methods only accept IP addresses. Use *_by_name for hostnames. (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
+        @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
+        @rtype: dict
+        """
+        addr = socket.gethostbyname(hostname)
+
+        return self.record_by_addr(addr)
+
+    def region_by_addr(self, addr):
+        """
+        Lookup the region for given IP address.
+        Use this method if you have a Region database.
+
+        @param addr: IP address
+        @type addr: str
+        @return: dict containing country_code, region,
+            and region_name
+        @rtype: dict
+        """
+        try:
+            ipnum = ip2long(addr)
+
+            if not ipnum:
+                raise ValueError("Invalid IP address: %s" % addr)
+
+            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')
+
+            return self._get_region(ipnum)
+        except ValueError:
+            raise GeoIPError('*_by_addr methods only accept IP addresses. Use *_by_name for hostnames. (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
+        @type hostname: str
+        @return: dict containing country_code, region,
+            and region_name
+        @rtype: dict
+        """
+        addr = socket.gethostbyname(hostname)
+        return self.region_by_addr(addr)
+
+    def time_zone_by_addr(self, addr):
+        """
+        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
+        @return: Time zone
+        @rtype: str
+        """
+        try:
+            ipnum = ip2long(addr)
+
+            if not ipnum:
+                raise ValueError("Invalid IP address: %s" % addr)
+
+            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')
+
+            return self._get_record(ipnum)['time_zone']
+        except ValueError:
+            raise GeoIPError('*_by_addr methods only accept IP addresses. Use *_by_name for hostnames. (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
+        @type hostname: str
+        @return: Time zone
+        @rtype: str
+        """
+        addr = socket.gethostbyname(hostname)
+        return self.time_zone_by_addr(addr)
diff --git a/task-6232/pygeoip/const.py b/task-6232/pygeoip/const.py
new file mode 100644
index 0000000..a215226
--- /dev/null
+++ b/task-6232/pygeoip/const.py
@@ -0,0 +1,382 @@
+"""
+Constants needed for parsing binary GeoIP databases. It is part of the pygeoip
+package.
+
+ at author: Jennifer Ennis <zaylea at gmail dot com>
+
+ at 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 <http://www.gnu.org/licenses/lgpl.txt>.
+"""
+
+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
+
+
diff --git a/task-6232/pygeoip/six.py b/task-6232/pygeoip/six.py
new file mode 100644
index 0000000..6526d76
--- /dev/null
+++ b/task-6232/pygeoip/six.py
@@ -0,0 +1,353 @@
+"""Utilities for writing code that runs on Python 2 and 3"""
+
+import operator
+import sys
+import types
+
+__author__ = "Benjamin Peterson <benjamin at python.org>"
+__version__ = "1.1.0"
+
+
+# True if we are running on Python 3.
+PY3 = sys.version_info[0] == 3
+
+if PY3:
+    string_types = str,
+    integer_types = int,
+    class_types = type,
+    text_type = str
+    binary_type = bytes
+
+    MAXSIZE = sys.maxsize
+else:
+    string_types = basestring,
+    integer_types = (int, long)
+    class_types = (type, types.ClassType)
+    text_type = unicode
+    binary_type = str
+
+    # It's possible to have sizeof(long) != sizeof(Py_ssize_t).
+    class X(object):
+        def __len__(self):
+            return 1 << 31
+    try:
+        len(X())
+    except OverflowError:
+        # 32-bit
+        MAXSIZE = int((1 << 31) - 1)
+    else:
+        # 64-bit
+        MAXSIZE = int((1 << 63) - 1)
+    del X
+
+
+def _add_doc(func, doc):
+    """Add documentation to a function."""
+    func.__doc__ = doc
+
+
+def _import_module(name):
+    """Import module, returning the module after the last dot."""
+    __import__(name)
+    return sys.modules[name]
+
+
+class _LazyDescr(object):
+
+    def __init__(self, name):
+        self.name = name
+
+    def __get__(self, obj, tp):
+        result = self._resolve()
+        setattr(obj, self.name, result)
+        # This is a bit ugly, but it avoids running this again.
+        delattr(tp, self.name)
+        return result
+
+
+class MovedModule(_LazyDescr):
+
+    def __init__(self, name, old, new=None):
+        super(MovedModule, self).__init__(name)
+        if PY3:
+            if new is None:
+                new = name
+            self.mod = new
+        else:
+            self.mod = old
+
+    def _resolve(self):
+        return _import_module(self.mod)
+
+
+class MovedAttribute(_LazyDescr):
+
+    def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None):
+        super(MovedAttribute, self).__init__(name)
+        if PY3:
+            if new_mod is None:
+                new_mod = name
+            self.mod = new_mod
+            if new_attr is None:
+                if old_attr is None:
+                    new_attr = name
+                else:
+                    new_attr = old_attr
+            self.attr = new_attr
+        else:
+            self.mod = old_mod
+            if old_attr is None:
+                old_attr = name
+            self.attr = old_attr
+
+    def _resolve(self):
+        module = _import_module(self.mod)
+        return getattr(module, self.attr)
+
+
+
+class _MovedItems(types.ModuleType):
+    """Lazy loading of moved objects"""
+
+
+_moved_attributes = [
+    MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"),
+    MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"),
+    MovedAttribute("map", "itertools", "builtins", "imap", "map"),
+    MovedAttribute("reload_module", "__builtin__", "imp", "reload"),
+    MovedAttribute("reduce", "__builtin__", "functools"),
+    MovedAttribute("StringIO", "StringIO", "io"),
+    MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"),
+    MovedAttribute("zip", "itertools", "builtins", "izip", "zip"),
+
+    MovedModule("builtins", "__builtin__"),
+    MovedModule("configparser", "ConfigParser"),
+    MovedModule("copyreg", "copy_reg"),
+    MovedModule("http_cookiejar", "cookielib", "http.cookiejar"),
+    MovedModule("http_cookies", "Cookie", "http.cookies"),
+    MovedModule("html_entities", "htmlentitydefs", "html.entities"),
+    MovedModule("html_parser", "HTMLParser", "html.parser"),
+    MovedModule("http_client", "httplib", "http.client"),
+    MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"),
+    MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"),
+    MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"),
+    MovedModule("cPickle", "cPickle", "pickle"),
+    MovedModule("queue", "Queue"),
+    MovedModule("reprlib", "repr"),
+    MovedModule("socketserver", "SocketServer"),
+    MovedModule("tkinter", "Tkinter"),
+    MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"),
+    MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"),
+    MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"),
+    MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"),
+    MovedModule("tkinter_tix", "Tix", "tkinter.tix"),
+    MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"),
+    MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"),
+    MovedModule("tkinter_colorchooser", "tkColorChooser",
+                "tkinter.colorchooser"),
+    MovedModule("tkinter_commondialog", "tkCommonDialog",
+                "tkinter.commondialog"),
+    MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"),
+    MovedModule("tkinter_font", "tkFont", "tkinter.font"),
+    MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"),
+    MovedModule("tkinter_tksimpledialog", "tkSimpleDialog",
+                "tkinter.simpledialog"),
+    MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"),
+    MovedModule("winreg", "_winreg"),
+]
+for attr in _moved_attributes:
+    setattr(_MovedItems, attr.name, attr)
+del attr
+
+moves = sys.modules["six.moves"] = _MovedItems("moves")
+
+
+def add_move(move):
+    """Add an item to six.moves."""
+    setattr(_MovedItems, move.name, move)
+
+
+def remove_move(name):
+    """Remove item from six.moves."""
+    try:
+        delattr(_MovedItems, name)
+    except AttributeError:
+        try:
+            del moves.__dict__[name]
+        except KeyError:
+            raise AttributeError("no such move, %r" % (name,))
+
+
+if PY3:
+    _meth_func = "__func__"
+    _meth_self = "__self__"
+
+    _func_code = "__code__"
+    _func_defaults = "__defaults__"
+
+    _iterkeys = "keys"
+    _itervalues = "values"
+    _iteritems = "items"
+else:
+    _meth_func = "im_func"
+    _meth_self = "im_self"
+
+    _func_code = "func_code"
+    _func_defaults = "func_defaults"
+
+    _iterkeys = "iterkeys"
+    _itervalues = "itervalues"
+    _iteritems = "iteritems"
+
+
+if PY3:
+    def get_unbound_function(unbound):
+        return unbound
+
+
+    advance_iterator = next
+
+    def callable(obj):
+        return any("__call__" in klass.__dict__ for klass in type(obj).__mro__)
+else:
+    def get_unbound_function(unbound):
+        return unbound.im_func
+
+
+    def advance_iterator(it):
+        return it.next()
+
+    callable = callable
+_add_doc(get_unbound_function,
+         """Get the function out of a possibly unbound function""")
+
+
+get_method_function = operator.attrgetter(_meth_func)
+get_method_self = operator.attrgetter(_meth_self)
+get_function_code = operator.attrgetter(_func_code)
+get_function_defaults = operator.attrgetter(_func_defaults)
+
+
+def iterkeys(d):
+    """Return an iterator over the keys of a dictionary."""
+    return getattr(d, _iterkeys)()
+
+def itervalues(d):
+    """Return an iterator over the values of a dictionary."""
+    return getattr(d, _itervalues)()
+
+def iteritems(d):
+    """Return an iterator over the (key, value) pairs of a dictionary."""
+    return getattr(d, _iteritems)()
+
+
+if PY3:
+    def b(s):
+        return s.encode("latin-1")
+    def u(s):
+        return s
+    if sys.version_info[1] <= 1:
+        def int2byte(i):
+            return bytes((i,))
+    else:
+        # This is about 2x faster than the implementation above on 3.2+
+        int2byte = operator.methodcaller("to_bytes", 1, "big")
+    import io
+    StringIO = io.StringIO
+    BytesIO = io.BytesIO
+else:
+    def b(s):
+        return s
+    def u(s):
+        return unicode(s, "unicode_escape")
+    int2byte = chr
+    import StringIO
+    StringIO = BytesIO = StringIO.StringIO
+_add_doc(b, """Byte literal""")
+_add_doc(u, """Text literal""")
+
+
+if PY3:
+    import builtins
+    exec_ = getattr(builtins, "exec")
+
+
+    def reraise(tp, value, tb=None):
+        if value.__traceback__ is not tb:
+            raise value.with_traceback(tb)
+        raise value
+
+
+    print_ = getattr(builtins, "print")
+    del builtins
+
+else:
+    def exec_(code, globs=None, locs=None):
+        """Execute code in a namespace."""
+        if globs is None:
+            frame = sys._getframe(1)
+            globs = frame.f_globals
+            if locs is None:
+                locs = frame.f_locals
+            del frame
+        elif locs is None:
+            locs = globs
+        exec("""exec code in globs, locs""")
+
+
+    exec_("""def reraise(tp, value, tb=None):
+    raise tp, value, tb
+""")
+
+
+    def print_(*args, **kwargs):
+        """The new-style print function."""
+        fp = kwargs.pop("file", sys.stdout)
+        if fp is None:
+            return
+        def write(data):
+            if not isinstance(data, basestring):
+                data = str(data)
+            fp.write(data)
+        want_unicode = False
+        sep = kwargs.pop("sep", None)
+        if sep is not None:
+            if isinstance(sep, unicode):
+                want_unicode = True
+            elif not isinstance(sep, str):
+                raise TypeError("sep must be None or a string")
+        end = kwargs.pop("end", None)
+        if end is not None:
+            if isinstance(end, unicode):
+                want_unicode = True
+            elif not isinstance(end, str):
+                raise TypeError("end must be None or a string")
+        if kwargs:
+            raise TypeError("invalid keyword arguments to print()")
+        if not want_unicode:
+            for arg in args:
+                if isinstance(arg, unicode):
+                    want_unicode = True
+                    break
+        if want_unicode:
+            newline = unicode("\n")
+            space = unicode(" ")
+        else:
+            newline = "\n"
+            space = " "
+        if sep is None:
+            sep = space
+        if end is None:
+            end = newline
+        for i, arg in enumerate(args):
+            if i:
+                write(sep)
+            write(arg)
+        write(end)
+
+_add_doc(reraise, """Reraise an exception.""")
+
+
+def with_metaclass(meta, base=object):
+    """Create a base class with a metaclass."""
+    return meta("NewBase", (base,), {})
diff --git a/task-6232/pygeoip/timezone.py b/task-6232/pygeoip/timezone.py
new file mode 100644
index 0000000..033c8d6
--- /dev/null
+++ b/task-6232/pygeoip/timezone.py
@@ -0,0 +1,714 @@
+__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"
+
+def time_zone_by_country_and_region(country_code, region_name=None):
+
+    if country_code not in _country:
+        return None
+
+    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)
diff --git a/task-6232/pygeoip/util.py b/task-6232/pygeoip/util.py
new file mode 100644
index 0000000..2ea6985
--- /dev/null
+++ b/task-6232/pygeoip/util.py
@@ -0,0 +1,35 @@
+"""
+Misc. utility functions. It is part of the pygeoip package.
+
+ at author: Jennifer Ennis <zaylea at gmail dot com>
+
+ at 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 <http://www.gnu.org/licenses/lgpl.txt>.
+"""
+
+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('.')
+    ip_long = long(ip_array[0]) * 16777216 + long(ip_array[1]) * 65536 + long(ip_array[2]) * 256 + long(ip_array[3])
+    return ip_long  
+



More information about the tor-commits mailing list