diff options
Diffstat (limited to 'django/utils/timezone.py')
| -rw-r--r-- | django/utils/timezone.py | 266 |
1 files changed, 266 insertions, 0 deletions
diff --git a/django/utils/timezone.py b/django/utils/timezone.py new file mode 100644 index 0000000000..22860eb8cc --- /dev/null +++ b/django/utils/timezone.py @@ -0,0 +1,266 @@ +"""Timezone helper functions. + +This module uses pytz when it's available and fallbacks when it isn't. +""" + +from datetime import datetime, timedelta, tzinfo +from threading import local +import time as _time + +try: + import pytz +except ImportError: + pytz = None + +from django.conf import settings + +__all__ = [ + 'utc', 'get_default_timezone', 'get_current_timezone', + 'activate', 'deactivate', 'override', + 'aslocaltime', 'isnaive', +] + + +# UTC and local time zones + +ZERO = timedelta(0) + +class UTC(tzinfo): + """ + UTC implementation taken from Python's docs. + + Used only when pytz isn't available. + """ + + def utcoffset(self, dt): + return ZERO + + def tzname(self, dt): + return "UTC" + + def dst(self, dt): + return ZERO + +class LocalTimezone(tzinfo): + """ + Local time implementation taken from Python's docs. + + Used only when pytz isn't available, and most likely inaccurate. If you're + having trouble with this class, don't waste your time, just install pytz. + """ + + def __init__(self): + # This code is moved in __init__ to execute it as late as possible + # See get_default_timezone(). + self.STDOFFSET = timedelta(seconds=-_time.timezone) + if _time.daylight: + self.DSTOFFSET = timedelta(seconds=-_time.altzone) + else: + self.DSTOFFSET = self.STDOFFSET + self.DSTDIFF = self.DSTOFFSET - self.STDOFFSET + tzinfo.__init__(self) + + def utcoffset(self, dt): + if self._isdst(dt): + return self.DSTOFFSET + else: + return self.STDOFFSET + + def dst(self, dt): + if self._isdst(dt): + return self.DSTDIFF + else: + return ZERO + + def tzname(self, dt): + return _time.tzname[self._isdst(dt)] + + def _isdst(self, dt): + tt = (dt.year, dt.month, dt.day, + dt.hour, dt.minute, dt.second, + dt.weekday(), 0, 0) + stamp = _time.mktime(tt) + tt = _time.localtime(stamp) + return tt.tm_isdst > 0 + + +utc = pytz.utc if pytz else UTC() +"""UTC time zone as a tzinfo instance.""" + +# In order to avoid accessing the settings at compile time, +# wrap the expression in a function and cache the result. +# If you change settings.TIME_ZONE in tests, reset _localtime to None. +_localtime = None + +def get_default_timezone(): + """ + Returns the default time zone as a tzinfo instance. + + This is the time zone defined by settings.TIME_ZONE. + + See also :func:`get_current_timezone`. + """ + global _localtime + if _localtime is None: + tz = settings.TIME_ZONE + _localtime = pytz.timezone(tz) if pytz else LocalTimezone() + return _localtime + +# This function exists for consistency with get_current_timezone_name +def get_default_timezone_name(): + """ + Returns the name of the default time zone. + """ + return _get_timezone_name(get_default_timezone()) + +_active = local() + +def get_current_timezone(): + """ + Returns the currently active time zone as a tzinfo instance. + """ + return getattr(_active, "value", get_default_timezone()) + +def get_current_timezone_name(): + """ + Returns the name of the currently active time zone. + """ + return _get_timezone_name(get_current_timezone()) + +def _get_timezone_name(timezone): + """ + Returns the name of ``timezone``. + """ + try: + # for pytz timezones + return timezone.zone + except AttributeError: + # for regular tzinfo objects + local_now = datetime.now(timezone) + return timezone.tzname(local_now) + +# Timezone selection functions. + +# These functions don't change os.environ['TZ'] and call time.tzset() +# because it isn't thread safe. + +def activate(timezone): + """ + Sets the time zone for the current thread. + + The ``timezone`` argument must be an instance of a tzinfo subclass or a + time zone name. If it is a time zone name, pytz is required. + """ + if isinstance(timezone, tzinfo): + _active.value = timezone + elif isinstance(timezone, basestring) and pytz is not None: + _active.value = pytz.timezone(timezone) + else: + raise ValueError("Invalid timezone: %r" % timezone) + +def deactivate(): + """ + Unsets the time zone for the current thread. + + Django will then use the time zone defined by settings.TIME_ZONE. + """ + if hasattr(_active, "value"): + del _active.value + +class override(object): + """ + Temporarily set the time zone for the current thread. + + This is a context manager that uses ``~django.utils.timezone.activate()`` + to set the timezone on entry, and restores the previously active timezone + on exit. + + The ``timezone`` argument must be an instance of a ``tzinfo`` subclass, a + time zone name, or ``None``. If is it a time zone name, pytz is required. + If it is ``None``, Django enables the default time zone. + """ + def __init__(self, timezone): + self.timezone = timezone + self.old_timezone = getattr(_active, 'value', None) + + def __enter__(self): + if self.timezone is None: + deactivate() + else: + activate(self.timezone) + + def __exit__(self, exc_type, exc_value, traceback): + if self.old_timezone is not None: + _active.value = self.old_timezone + else: + del _active.value + + +# Utilities + +def aslocaltime(value, use_tz=None): + """ + Checks if value is a datetime and converts it to local time if necessary. + + If use_tz is provided and is not None, that will force the value to + be converted (or not), overriding the value of settings.USE_TZ. + """ + if (isinstance(value, datetime) + and (settings.USE_TZ if use_tz is None else use_tz) + and not is_naive(value) + and getattr(value, 'convert_to_local_time', True)): + timezone = get_current_timezone() + value = value.astimezone(timezone) + if hasattr(timezone, 'normalize'): + # available for pytz time zones + value = timezone.normalize(value) + return value + +def now(): + """ + Returns an aware or naive datetime.datetime, depending on settings.USE_TZ. + """ + if settings.USE_TZ: + # timeit shows that datetime.now(tz=utc) is 24% slower + return datetime.utcnow().replace(tzinfo=utc) + else: + return datetime.now() + +def is_aware(value): + """ + Determines if a given datetime.datetime is aware. + + The logic is described in Python's docs: + http://docs.python.org/library/datetime.html#datetime.tzinfo + """ + return value.tzinfo is not None and value.tzinfo.utcoffset(value) is not None + +def is_naive(value): + """ + Determines if a given datetime.datetime is naive. + + The logic is described in Python's docs: + http://docs.python.org/library/datetime.html#datetime.tzinfo + """ + return value.tzinfo is None or value.tzinfo.utcoffset(value) is None + +def make_aware(value, timezone): + """ + Makes a naive datetime.datetime in a given time zone aware. + """ + if hasattr(timezone, 'localize'): + # available for pytz time zones + return timezone.localize(value, is_dst=None) + else: + # may be wrong around DST changes + return value.replace(tzinfo=timezone) + +def make_naive(value, timezone): + """ + Makes an aware datetime.datetime naive in a given time zone. + """ + value = value.astimezone(timezone) + if hasattr(timezone, 'normalize'): + # available for pytz time zones + return timezone.normalize(value) + return value.replace(tzinfo=None) |
