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)