Skip to content

Instantly share code, notes, and snippets.

@Xowap
Created May 28, 2016 18:58
Show Gist options
  • Select an option

  • Save Xowap/69edfaeec148b7ba54744c2680c863f8 to your computer and use it in GitHub Desktop.

Select an option

Save Xowap/69edfaeec148b7ba54744c2680c863f8 to your computer and use it in GitHub Desktop.
from datetime import tzinfo, timedelta
from math import floor
from django.contrib.gis.db.models.fields import PolygonField
from django.contrib.gis.db.models.manager import GeoManager
from django.contrib.gis.geos.geometry import GEOSGeometry
from django.db import models
from osgeo import ogr
from pytz import timezone
from pytz.exceptions import UnknownTimeZoneError
class TimeZoneManager(GeoManager):
def import_shapefile(self, path):
"""
Imports timezone shapes from the given path.
The expected input file is Eric Muller's tz_world
http://efele.net/maps/tz/world/
:param path:
:return:
"""
source = ogr.Open(path)
if source is None:
raise IOError('The source file {} is invalid or inexistant'.format(path))
layer = source.GetLayer(0)
time_zones = []
for feature in layer:
geometry = GEOSGeometry(str(feature.GetGeometryRef()))
polygons = []
if geometry.geom_type == 'Polygon':
polygons = [geometry]
elif geometry.geom_type == 'MultiPolygon':
polygons = geometry
for polygon in polygons:
time_zones.append(TimeZone(
zone=polygon,
tzid=feature.GetFieldAsString('TZID'),
))
self.all().delete()
self.bulk_create(time_zones)
def tzguess_at(self, geometry):
"""
Returns the time zone name at the given geometry, if known.
:param geometry:
:return:
"""
point = geometry.centroid
if point.srid is not None:
point.transform(4326)
try:
tzid = self.filter(zone__contains=point)[0].tzid
return tzid, point
except IndexError:
return None, point
def tzinfo_name_at(self, geometry):
"""
Calculates the time zone that applies at the given geometry's centroid.
Basically, you sould always pass a point as geometry.
:param geometry:
:return:
"""
tzname, point = self.tzguess_at(geometry)
if tzname is not None:
try:
return timezone(tzname), tzname
except UnknownTimeZoneError:
pass
tz = InternationalWaterTZ(point.x)
return InternationalWaterTZ(point.x), tz.tzname(None)
def tzinfo_at(self, geometry):
return self.tzinfo_name_at(geometry)[0]
class TimeZone(models.Model):
objects = TimeZoneManager()
zone = PolygonField(spatial_index=True)
tzid = models.CharField(max_length=100)
class InternationalWaterTZ(tzinfo):
"""
Generates a tzinfo for a given point in the international waters
"""
def __init__(self, longitude):
super(InternationalWaterTZ, self).__init__()
self.hours_shift = self.calculate_hour_shift(longitude)
self.time_shift = timedelta(
hours=self.hours_shift
)
def __eq__(self, other):
if isinstance(other, InternationalWaterTZ):
return self.hours_shift == other.hours_shift
else:
return False
def __repr__(self):
return "<InternationalWaterTz {}>".format(self.tzname(None))
@staticmethod
def calculate_hour_shift(longitude):
return int(floor(((longitude + 180) % 360 - 180 + 7.5) / 15))
def tzname(self, date_time):
return "GMT{:+d}".format(self.hours_shift)
def utcoffset(self, date_time):
return self.time_shift
def dst(self, date_time):
return self.time_shift
def localize(self, dt, is_dst=False):
"""
Convert naive time to local time
"""
if dt.tzinfo is not None:
raise ValueError('Not naive datetime (tzinfo is already set)')
return dt.replace(tzinfo=self)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment