Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
_common.py420 linesDownload Raw Back to tz
1from six import PY22 3from functools import wraps4 5from datetime import datetime, timedelta, tzinfo6 7 8ZERO = timedelta(0)9 10__all__ = ['tzname_in_python2', 'enfold']11 12 13def tzname_in_python2(namefunc):14    """Change unicode output into bytestrings in Python 215 16    tzname() API changed in Python 3. It used to return bytes, but was changed17    to unicode strings18    """19    if PY2:20        @wraps(namefunc)21        def adjust_encoding(*args, **kwargs):22            name = namefunc(*args, **kwargs)23            if name is not None:24                name = name.encode()25 26            return name27 28        return adjust_encoding29    else:30        return namefunc31 32 33# The following is adapted from Alexander Belopolsky's tz library34# https://github.com/abalkin/tz35if hasattr(datetime, 'fold'):36    # This is the pre-python 3.6 fold situation37    def enfold(dt, fold=1):38        """39        Provides a unified interface for assigning the ``fold`` attribute to40        datetimes both before and after the implementation of PEP-495.41 42        :param fold:43            The value for the ``fold`` attribute in the returned datetime. This44            should be either 0 or 1.45 46        :return:47            Returns an object for which ``getattr(dt, 'fold', 0)`` returns48            ``fold`` for all versions of Python. In versions prior to49            Python 3.6, this is a ``_DatetimeWithFold`` object, which is a50            subclass of :py:class:`datetime.datetime` with the ``fold``51            attribute added, if ``fold`` is 1.52 53        .. versionadded:: 2.6.054        """55        return dt.replace(fold=fold)56 57else:58    class _DatetimeWithFold(datetime):59        """60        This is a class designed to provide a PEP 495-compliant interface for61        Python versions before 3.6. It is used only for dates in a fold, so62        the ``fold`` attribute is fixed at ``1``.63 64        .. versionadded:: 2.6.065        """66        __slots__ = ()67 68        def replace(self, *args, **kwargs):69            """70            Return a datetime with the same attributes, except for those71            attributes given new values by whichever keyword arguments are72            specified. Note that tzinfo=None can be specified to create a naive73            datetime from an aware datetime with no conversion of date and time74            data.75 76            This is reimplemented in ``_DatetimeWithFold`` because pypy3 will77            return a ``datetime.datetime`` even if ``fold`` is unchanged.78            """79            argnames = (80                'year', 'month', 'day', 'hour', 'minute', 'second',81                'microsecond', 'tzinfo'82            )83 84            for arg, argname in zip(args, argnames):85                if argname in kwargs:86                    raise TypeError('Duplicate argument: {}'.format(argname))87 88                kwargs[argname] = arg89 90            for argname in argnames:91                if argname not in kwargs:92                    kwargs[argname] = getattr(self, argname)93 94            dt_class = self.__class__ if kwargs.get('fold', 1) else datetime95 96            return dt_class(**kwargs)97 98        @property99        def fold(self):100            return 1101 102    def enfold(dt, fold=1):103        """104        Provides a unified interface for assigning the ``fold`` attribute to105        datetimes both before and after the implementation of PEP-495.106 107        :param fold:108            The value for the ``fold`` attribute in the returned datetime. This109            should be either 0 or 1.110 111        :return:112            Returns an object for which ``getattr(dt, 'fold', 0)`` returns113            ``fold`` for all versions of Python. In versions prior to114            Python 3.6, this is a ``_DatetimeWithFold`` object, which is a115            subclass of :py:class:`datetime.datetime` with the ``fold``116            attribute added, if ``fold`` is 1.117 118        .. versionadded:: 2.6.0119        """120        if getattr(dt, 'fold', 0) == fold:121            return dt122 123        args = dt.timetuple()[:6]124        args += (dt.microsecond, dt.tzinfo)125 126        if fold:127            return _DatetimeWithFold(*args)128        else:129            return datetime(*args)130 131 132def _validate_fromutc_inputs(f):133    """134    The CPython version of ``fromutc`` checks that the input is a ``datetime``135    object and that ``self`` is attached as its ``tzinfo``.136    """137    @wraps(f)138    def fromutc(self, dt):139        if not isinstance(dt, datetime):140            raise TypeError("fromutc() requires a datetime argument")141        if dt.tzinfo is not self:142            raise ValueError("dt.tzinfo is not self")143 144        return f(self, dt)145 146    return fromutc147 148 149class _tzinfo(tzinfo):150    """151    Base class for all ``dateutil`` ``tzinfo`` objects.152    """153 154    def is_ambiguous(self, dt):155        """156        Whether or not the "wall time" of a given datetime is ambiguous in this157        zone.158 159        :param dt:160            A :py:class:`datetime.datetime`, naive or time zone aware.161 162 163        :return:164            Returns ``True`` if ambiguous, ``False`` otherwise.165 166        .. versionadded:: 2.6.0167        """168 169        dt = dt.replace(tzinfo=self)170 171        wall_0 = enfold(dt, fold=0)172        wall_1 = enfold(dt, fold=1)173 174        same_offset = wall_0.utcoffset() == wall_1.utcoffset()175        same_dt = wall_0.replace(tzinfo=None) == wall_1.replace(tzinfo=None)176 177        return same_dt and not same_offset178 179    def _fold_status(self, dt_utc, dt_wall):180        """181        Determine the fold status of a "wall" datetime, given a representation182        of the same datetime as a (naive) UTC datetime. This is calculated based183        on the assumption that ``dt.utcoffset() - dt.dst()`` is constant for all184        datetimes, and that this offset is the actual number of hours separating185        ``dt_utc`` and ``dt_wall``.186 187        :param dt_utc:188            Representation of the datetime as UTC189 190        :param dt_wall:191            Representation of the datetime as "wall time". This parameter must192            either have a `fold` attribute or have a fold-naive193            :class:`datetime.tzinfo` attached, otherwise the calculation may194            fail.195        """196        if self.is_ambiguous(dt_wall):197            delta_wall = dt_wall - dt_utc198            _fold = int(delta_wall == (dt_utc.utcoffset() - dt_utc.dst()))199        else:200            _fold = 0201 202        return _fold203 204    def _fold(self, dt):205        return getattr(dt, 'fold', 0)206 207    def _fromutc(self, dt):208        """209        Given a timezone-aware datetime in a given timezone, calculates a210        timezone-aware datetime in a new timezone.211 212        Since this is the one time that we *know* we have an unambiguous213        datetime object, we take this opportunity to determine whether the214        datetime is ambiguous and in a "fold" state (e.g. if it's the first215        occurrence, chronologically, of the ambiguous datetime).216 217        :param dt:218            A timezone-aware :class:`datetime.datetime` object.219        """220 221        # Re-implement the algorithm from Python's datetime.py222        dtoff = dt.utcoffset()223        if dtoff is None:224            raise ValueError("fromutc() requires a non-None utcoffset() "225                             "result")226 227        # The original datetime.py code assumes that `dst()` defaults to228        # zero during ambiguous times. PEP 495 inverts this presumption, so229        # for pre-PEP 495 versions of python, we need to tweak the algorithm.230        dtdst = dt.dst()231        if dtdst is None:232            raise ValueError("fromutc() requires a non-None dst() result")233        delta = dtoff - dtdst234 235        dt += delta236        # Set fold=1 so we can default to being in the fold for237        # ambiguous dates.238        dtdst = enfold(dt, fold=1).dst()239        if dtdst is None:240            raise ValueError("fromutc(): dt.dst gave inconsistent "241                             "results; cannot convert")242        return dt + dtdst243 244    @_validate_fromutc_inputs245    def fromutc(self, dt):246        """247        Given a timezone-aware datetime in a given timezone, calculates a248        timezone-aware datetime in a new timezone.249 250        Since this is the one time that we *know* we have an unambiguous251        datetime object, we take this opportunity to determine whether the252        datetime is ambiguous and in a "fold" state (e.g. if it's the first253        occurrence, chronologically, of the ambiguous datetime).254 255        :param dt:256            A timezone-aware :class:`datetime.datetime` object.257        """258        dt_wall = self._fromutc(dt)259 260        # Calculate the fold status given the two datetimes.261        _fold = self._fold_status(dt, dt_wall)262 263        # Set the default fold value for ambiguous dates264        return enfold(dt_wall, fold=_fold)265 266 267class tzrangebase(_tzinfo):268    """269    This is an abstract base class for time zones represented by an annual270    transition into and out of DST. Child classes should implement the following271    methods:272 273        * ``__init__(self, *args, **kwargs)``274        * ``transitions(self, year)`` - this is expected to return a tuple of275          datetimes representing the DST on and off transitions in standard276          time.277 278    A fully initialized ``tzrangebase`` subclass should also provide the279    following attributes:280        * ``hasdst``: Boolean whether or not the zone uses DST.281        * ``_dst_offset`` / ``_std_offset``: :class:`datetime.timedelta` objects282          representing the respective UTC offsets.283        * ``_dst_abbr`` / ``_std_abbr``: Strings representing the timezone short284          abbreviations in DST and STD, respectively.285        * ``_hasdst``: Whether or not the zone has DST.286 287    .. versionadded:: 2.6.0288    """289    def __init__(self):290        raise NotImplementedError('tzrangebase is an abstract base class')291 292    def utcoffset(self, dt):293        isdst = self._isdst(dt)294 295        if isdst is None:296            return None297        elif isdst:298            return self._dst_offset299        else:300            return self._std_offset301 302    def dst(self, dt):303        isdst = self._isdst(dt)304 305        if isdst is None:306            return None307        elif isdst:308            return self._dst_base_offset309        else:310            return ZERO311 312    @tzname_in_python2313    def tzname(self, dt):314        if self._isdst(dt):315            return self._dst_abbr316        else:317            return self._std_abbr318 319    def fromutc(self, dt):320        """ Given a datetime in UTC, return local time """321        if not isinstance(dt, datetime):322            raise TypeError("fromutc() requires a datetime argument")323 324        if dt.tzinfo is not self:325            raise ValueError("dt.tzinfo is not self")326 327        # Get transitions - if there are none, fixed offset328        transitions = self.transitions(dt.year)329        if transitions is None:330            return dt + self.utcoffset(dt)331 332        # Get the transition times in UTC333        dston, dstoff = transitions334 335        dston -= self._std_offset336        dstoff -= self._std_offset337 338        utc_transitions = (dston, dstoff)339        dt_utc = dt.replace(tzinfo=None)340 341        isdst = self._naive_isdst(dt_utc, utc_transitions)342 343        if isdst:344            dt_wall = dt + self._dst_offset345        else:346            dt_wall = dt + self._std_offset347 348        _fold = int(not isdst and self.is_ambiguous(dt_wall))349 350        return enfold(dt_wall, fold=_fold)351 352    def is_ambiguous(self, dt):353        """354        Whether or not the "wall time" of a given datetime is ambiguous in this355        zone.356 357        :param dt:358            A :py:class:`datetime.datetime`, naive or time zone aware.359 360 361        :return:362            Returns ``True`` if ambiguous, ``False`` otherwise.363 364        .. versionadded:: 2.6.0365        """366        if not self.hasdst:367            return False368 369        start, end = self.transitions(dt.year)370 371        dt = dt.replace(tzinfo=None)372        return (end <= dt < end + self._dst_base_offset)373 374    def _isdst(self, dt):375        if not self.hasdst:376            return False377        elif dt is None:378            return None379 380        transitions = self.transitions(dt.year)381 382        if transitions is None:383            return False384 385        dt = dt.replace(tzinfo=None)386 387        isdst = self._naive_isdst(dt, transitions)388 389        # Handle ambiguous dates390        if not isdst and self.is_ambiguous(dt):391            return not self._fold(dt)392        else:393            return isdst394 395    def _naive_isdst(self, dt, transitions):396        dston, dstoff = transitions397 398        dt = dt.replace(tzinfo=None)399 400        if dston < dstoff:401            isdst = dston <= dt < dstoff402        else:403            isdst = not dstoff <= dt < dston404 405        return isdst406 407    @property408    def _dst_base_offset(self):409        return self._dst_offset - self._std_offset410 411    __hash__ = None412 413    def __ne__(self, other):414        return not (self == other)415 416    def __repr__(self):417        return "%s(...)" % self.__class__.__name__418 419    __reduce__ = object.__reduce__420 
codekingpro/portable-devtools · Team Ai