Skip to content

Commit

Permalink
Merge pull request #18 from mithro/lint-cleanup
Browse files Browse the repository at this point in the history
Lint cleanup.
  • Loading branch information
mithro committed Jul 28, 2015
2 parents 4efc5ac + cab866e commit be0e036
Show file tree
Hide file tree
Showing 4 changed files with 379 additions and 210 deletions.
68 changes: 51 additions & 17 deletions datetime_tz/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,18 +40,18 @@
import os
import os.path
import re
import sys
import time
import warnings
import dateutil.parser
import dateutil.relativedelta
import dateutil.tz
import pytz
import sys


from . import pytz_abbr # pylint: disable=g-bad-import-order

if sys.platform == "win32":
# pylint: disable=g-import-not-at-top
from .detect_windows import _detect_timezone_windows

try:
Expand Down Expand Up @@ -106,11 +106,20 @@ def _tzinfome(tzinfo):
# Our "local" timezone
_localtz = None


def localize(dt, force_to_local=True):
"""Localize a datetime to the local timezone
"""Localize a datetime to the local timezone.
If dt is naive, returns the same datetime with the local timezone, otherwise
uses astimezone to convert.
If dt is naive, returns the same datetime with the local timezone
Else, uses astimezone to convert"""
Args:
dt: datetime object.
force_to_local: Force all results to be in local time.
Returns:
A datetime_tz object.
"""
if not isinstance(dt, datetime_tz):
if not dt.tzinfo:
return datetime_tz(dt, tzinfo=localtz())
Expand All @@ -119,16 +128,26 @@ def localize(dt, force_to_local=True):
return dt.astimezone(localtz())
return dt


def get_naive(dt):
"""Gets a naive datetime from a datetime.
datetime_tz objects can't just have tzinfo replaced with None - you need to call asdatetime"""
datetime_tz objects can't just have tzinfo replaced with None, you need to
call asdatetime.
Args:
dt: datetime object.
Returns:
datetime object without any timezone information.
"""
if not dt.tzinfo:
return dt
if hasattr(dt, "asdatetime"):
return dt.asdatetime()
return dt.replace(tzinfo=None)


def localtz():
"""Get the local timezone.
Expand All @@ -141,21 +160,25 @@ def localtz():
_localtz = detect_timezone()
return _localtz


def localtz_name():
"""Returns the name of the local timezone"""
"""Returns the name of the local timezone."""
return str(localtz())


def localtz_set(timezone):
"""Set the local timezone."""
# pylint: disable=global-statement
global _localtz
_localtz = _tzinfome(timezone)


def require_timezone(zone):
"""Raises an AssertionError if we are not in the correct timezone"""
assert localtz().zone == zone,\
"Please set your local timezone to %(zone)s (either in the machine,\
or on Linux by exporting TZ=%(zone)s" % {"zone": zone}
"""Raises an AssertionError if we are not in the correct timezone."""
assert localtz().zone == zone, (
"Please set your local timezone to %(zone)s (either in the machine,"
"or on Linux by exporting TZ=%(zone)s") % {"zone": zone}


def detect_timezone():
"""Try and detect the timezone that Python is currently running in.
Expand Down Expand Up @@ -230,7 +253,9 @@ def _detect_timezone_etc_timezone():
except IOError as eo:
warnings.warn("Could not access your /etc/timezone file: %s" % eo)


def _load_local_tzinfo():
"""Load zoneinfo from local disk."""
tzdir = os.environ.get("TZDIR", "/usr/share/zoneinfo/posix")

localtzdata = {}
Expand All @@ -248,6 +273,7 @@ def _load_local_tzinfo():


def _detect_timezone_etc_localtime():
"""Detect timezone based on /etc/localtime file."""
matches = []
if os.path.exists("/etc/localtime"):
f = open("/etc/localtime", "rb")
Expand Down Expand Up @@ -347,7 +373,10 @@ class _default_tzinfos(object):
For more details, please see:
http://labix.org/python-dateutil#head-c0e81a473b647dfa787dc11e8c69557ec2c3ecd2
Usage example:
dateutil.parser.parse("Thu Sep 25 10:36:28 UTC 2003", tzinfos=datetime_tz._default_tzinfos())
>>> dateutil.parser.parse(
... "Thu Sep 25 10:36:28 UTC 2003",
... tzinfos=datetime_tz._default_tzinfos())
"""

_marker = object()
Expand Down Expand Up @@ -516,8 +545,8 @@ def replace(self, **kw):
if kw["tzinfo"] is None:
raise TypeError("Can not remove the timezone use asdatetime()")
else:
tzinfo = kw['tzinfo']
del kw['tzinfo']
tzinfo = kw["tzinfo"]
del kw["tzinfo"]
else:
tzinfo = None

Expand All @@ -531,7 +560,8 @@ def replace(self, **kw):

replaced = self.asdatetime().replace(**kw)

return type(self)(replaced, tzinfo=tzinfo or self.tzinfo.zone, is_dst=is_dst)
return type(self)(
replaced, tzinfo=tzinfo or self.tzinfo.zone, is_dst=is_dst)

# pylint: disable=line-to-long
@classmethod
Expand Down Expand Up @@ -602,7 +632,8 @@ def smartparse(cls, toparse, tzinfo=None):
dt -= datetime.timedelta(days=1)

elif toparselower in ("tomorrow", "tommorrow"):
# tommorrow is spelled wrong, but code out there might be depending on it working
# tommorrow is spelled wrong, but code out there might be depending on it
# working
dt += datetime.timedelta(days=1)

elif "ago" in toparselower:
Expand Down Expand Up @@ -710,12 +741,14 @@ def fromordinal(ordinal):
raise SyntaxError("Not enough information to create a datetime_tz object "
"from an ordinal. Please use datetime.date.fromordinal")


# We can't use datetime's absolute min/max otherwise astimezone will fail.
datetime_tz.min = datetime_tz(
datetime.datetime.min+datetime.timedelta(days=2), pytz.utc)
datetime_tz.max = datetime_tz(
datetime.datetime.max-datetime.timedelta(days=2), pytz.utc)


class iterate(object):
"""Helpful iterators for working with datetime_tz objects."""

Expand Down Expand Up @@ -853,5 +886,6 @@ def wrapper(self, *args, **kw):
"datetime_tz", "detect_timezone", "iterate", "localtz",
"localtz_set", "timedelta", "_detect_timezone_environ",
"_detect_timezone_etc_localtime", "_detect_timezone_etc_timezone",
"_detect_timezone_php", 'localize', 'get_naive', 'localtz_name', 'require_timezone']
"_detect_timezone_php", "localize", "get_naive", "localtz_name",
"require_timezone"]

162 changes: 99 additions & 63 deletions datetime_tz/detect_windows.py
Original file line number Diff line number Diff line change
@@ -1,88 +1,124 @@
__author__ = 'davidm'
# -*- coding: utf-8 -*-
# vim: set ts=2 sw=2 et sts=2 ai:
#
# Copyright 2014 David M
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# pylint: disable=invalid-name,protected-access

"""datetime_tz windows support."""

__author__ = "davidm"

import ctypes
import pytz
import warnings

import pytz

try:
from datetime_tz import win32tz_map
# pylint: disable=g-import-not-at-top
from datetime_tz import win32tz_map
except ImportError:
warnings.warn("win32tz_map is not generated yet - hopefully this only happens in a build")
warnings.warn("win32tz_map has not been generated yet.")

try:
import win32timezone
# pylint: disable=g-import-not-at-top
import win32timezone
except ImportError:
win32timezone = None
win32timezone = None

# The following code is a workaround to
# GetDynamicTimeZoneInformation not being present in win32timezone


class SYSTEMTIME_c(ctypes.Structure):
"""ctypes structure for SYSTEMTIME"""
# pylint: disable=too-few-public-methods
_fields_ = [
('year', ctypes.c_ushort),
('month', ctypes.c_ushort),
('day_of_week', ctypes.c_ushort),
('day', ctypes.c_ushort),
('hour', ctypes.c_ushort),
('minute', ctypes.c_ushort),
('second', ctypes.c_ushort),
('millisecond', ctypes.c_ushort),
]
"""ctypes structure for SYSTEMTIME."""
# pylint: disable=too-few-public-methods
_fields_ = [
("year", ctypes.c_ushort),
("month", ctypes.c_ushort),
("day_of_week", ctypes.c_ushort),
("day", ctypes.c_ushort),
("hour", ctypes.c_ushort),
("minute", ctypes.c_ushort),
("second", ctypes.c_ushort),
("millisecond", ctypes.c_ushort),
]


class TZI_c(ctypes.Structure):
"""ctypes structure for TIME_ZONE_INFORMATION"""
# pylint: disable=too-few-public-methods
_fields_ = [
('bias', ctypes.c_long),
('standard_name', ctypes.c_wchar*32),
('standard_start', SYSTEMTIME_c),
('standard_bias', ctypes.c_long),
('daylight_name', ctypes.c_wchar*32),
('daylight_start', SYSTEMTIME_c),
('daylight_bias', ctypes.c_long),
]
"""ctypes structure for TIME_ZONE_INFORMATION."""
# pylint: disable=too-few-public-methods
_fields_ = [
("bias", ctypes.c_long),
("standard_name", ctypes.c_wchar*32),
("standard_start", SYSTEMTIME_c),
("standard_bias", ctypes.c_long),
("daylight_name", ctypes.c_wchar*32),
("daylight_start", SYSTEMTIME_c),
("daylight_bias", ctypes.c_long),
]


class DTZI_c(ctypes.Structure):
"""ctypes structure for DYNAMIC_TIME_ZONE_INFORMATION"""
# pylint: disable=too-few-public-methods
_fields_ = TZI_c._fields_ + [
('key_name', ctypes.c_wchar*128),
('dynamic_daylight_time_disabled', ctypes.c_bool),
]
"""ctypes structure for DYNAMIC_TIME_ZONE_INFORMATION."""
# pylint: disable=too-few-public-methods
_fields_ = TZI_c._fields_ + [
("key_name", ctypes.c_wchar*128),
("dynamic_daylight_time_disabled", ctypes.c_bool),
]


# Global variable for mapping Window timezone names in the current
# locale to english ones. Initialized when needed
win32timezone_to_en = {}


def _detect_timezone_windows():
# pylint: disable=global-statement
global win32timezone_to_en

# Try and fetch the key_name for the timezone using Get(Dynamic)TimeZoneInformation
tzi = DTZI_c()
kernel32 = ctypes.windll.kernel32
getter = kernel32.GetTimeZoneInformation
getter = getattr(kernel32, 'GetDynamicTimeZoneInformation', getter)
# code is for daylight savings: 0 means disabled/not defined, 1 means enabled but inactive, 2 means enabled and active
code = getter(ctypes.byref(tzi))

win32tz_key_name = tzi.key_name
if not win32tz_key_name:
if win32timezone is None:
return None
# we're on Windows before Vista/Server 2008 - need to look up the standard_name in the registry
# This will not work in some multilingual setups if running in a language
# other than the operating system default
win32tz_name = tzi.standard_name
if not win32timezone_to_en:
win32timezone_to_en = dict(win32timezone.TimeZoneInfo._get_indexed_time_zone_keys("Std"))
win32tz_key_name = win32timezone_to_en.get(win32tz_name, win32tz_name)
olson_name = win32tz_map.win32timezones.get(win32tz_key_name, None)
if not olson_name:
return None
if not isinstance(olson_name, str):
olson_name = olson_name.encode('ascii')
return pytz.timezone(olson_name)
"""Detect timezone on the windows platform."""
# pylint: disable=global-statement
global win32timezone_to_en

# Try and fetch the key_name for the timezone using
# Get(Dynamic)TimeZoneInformation
tzi = DTZI_c()
kernel32 = ctypes.windll.kernel32
getter = kernel32.GetTimeZoneInformation
getter = getattr(kernel32, "GetDynamicTimeZoneInformation", getter)

# code is for daylight savings: 0 means disabled/not defined, 1 means enabled
# but inactive, 2 means enabled and active
_ = getter(ctypes.byref(tzi))

win32tz_key_name = tzi.key_name
if not win32tz_key_name:
if win32timezone is None:
return None
# We're on Windows before Vista/Server 2008 - need to look up the
# standard_name in the registry.
# This will not work in some multilingual setups if running in a language
# other than the operating system default
win32tz_name = tzi.standard_name
if not win32timezone_to_en:
win32timezone_to_en = dict(
win32timezone.TimeZoneInfo._get_indexed_time_zone_keys("Std"))
win32tz_key_name = win32timezone_to_en.get(win32tz_name, win32tz_name)

olson_name = win32tz_map.win32timezones.get(win32tz_key_name, None)
if not olson_name:
return None
if not isinstance(olson_name, str):
olson_name = olson_name.encode("ascii")

return pytz.timezone(olson_name)

0 comments on commit be0e036

Please sign in to comment.