Пример #1
0
    def test_local_time_positive_integer(self):
        d = Pendulum(2016, 8, 7, 12, 34, 56)

        t = local_time(d.timestamp, 0)
        self.assertEqual(d.year, t[0])
        self.assertEqual(d.month, t[1])
        self.assertEqual(d.day, t[2])
        self.assertEqual(d.hour, t[3])
        self.assertEqual(d.minute, t[4])
        self.assertEqual(d.second, t[5])
Пример #2
0
    def fromutc(self, dt):  # type: (_D) -> _D
        stamp = timestamp(dt)

        transition = self._lookup_transition(stamp, is_utc=True)
        if stamp < transition.at and transition.previous is not None:
            transition = transition.previous

        stamp += transition.ttype.offset

        return dt.__class__(*local_time(stamp, 0, dt.microsecond), tzinfo=self)
Пример #3
0
    def _normalize(
        self,
        dt,
        dst_rule=None  # type: datetime  # type: Union[str, None]
    ):  # type: (...) -> datetime
        sec = timestamp(dt)
        fold = 0
        transition = self._lookup_transition(sec)

        if not _HAS_FOLD and dst_rule is None:
            dst_rule = POST_TRANSITION

        if dst_rule is None:
            dst_rule = PRE_TRANSITION
            if dt.fold == 1:
                dst_rule = POST_TRANSITION

        if sec < transition.local:
            if transition.is_ambiguous(sec):
                # Ambiguous time
                if dst_rule == TRANSITION_ERROR:
                    raise AmbiguousTime(dt)

                # We set the fold attribute for later
                if dst_rule == POST_TRANSITION:
                    fold = 1
            else:
                transition = transition.previous

        if transition.is_ambiguous(sec):
            # Ambiguous time
            if dst_rule == TRANSITION_ERROR:
                raise AmbiguousTime(dt)

            # We set the fold attribute for later
            if dst_rule == POST_TRANSITION:
                fold = 1
        elif transition.is_missing(sec):
            # Skipped time
            if dst_rule == TRANSITION_ERROR:
                raise NonExistingTime(dt)

            # We adjust accordingly
            if dst_rule == POST_TRANSITION:
                sec += transition.fix
                fold = 1
            else:
                sec -= transition.fix

        kwargs = {"tzinfo": self}
        if _HAS_FOLD or isinstance(dt, pendulum.DateTime):
            kwargs["fold"] = fold

        return dt.__class__(*local_time(sec, 0, dt.microsecond), **kwargs)
Пример #4
0
    def test_local_time_negative_integer(self):
        d = Pendulum(1951, 8, 7, 12, 34, 56, 123456)

        t = local_time(d.int_timestamp, 0, d.microsecond)
        self.assertEqual(d.year, t[0])
        self.assertEqual(d.month, t[1])
        self.assertEqual(d.day, t[2])
        self.assertEqual(d.hour, t[3])
        self.assertEqual(d.minute, t[4])
        self.assertEqual(d.second, t[5])
        self.assertEqual(d.microsecond, t[6])
Пример #5
0
def test_local_time_positive_integer():
    d = pendulum.datetime(2016, 8, 7, 12, 34, 56, 123456)

    t = local_time(d.int_timestamp, 0, d.microsecond)
    assert d.year == t[0]
    assert d.month == t[1]
    assert d.day == t[2]
    assert d.hour == t[3]
    assert d.minute == t[4]
    assert d.second == t[5]
    assert d.microsecond == t[6]
Пример #6
0
    def _convert(self, dt):  # type: (datetime) -> datetime
        if dt.tzinfo is self:
            return self._normalize(dt, dst_rule=POST_TRANSITION)

        if not isinstance(dt.tzinfo, Timezone):
            return dt.astimezone(self)

        stamp = timestamp(dt)

        if isinstance(dt.tzinfo, FixedTimezone):
            offset = dt.tzinfo.offset
        else:
            transition = dt.tzinfo._lookup_transition(stamp)
            offset = transition.ttype.offset

            if stamp < transition.local:
                if (transition.previous.is_ambiguous(stamp)
                        and getattr(dt, 'fold', 1) == 0):
                    pass
                else:
                    offset = transition.previous.ttype.offset

        stamp -= offset

        transition = self._lookup_transition(stamp, is_utc=True)
        if stamp < transition.at:
            transition = transition.previous

        offset = transition.ttype.offset
        stamp += offset
        fold = int(not transition.ttype.is_dst())

        kwargs = {
            'tzinfo': self,
        }

        if _HAS_FOLD or isinstance(dt, pendulum.DateTime):
            kwargs['fold'] = fold

        return dt.__class__(*local_time(stamp, 0, dt.microsecond), **kwargs)
Пример #7
0
    def _check_parsed(self, parsed,
                      now):  # type: (dict, pendulum.DateTime) -> dict
        """
        Checks validity of parsed elements.

        :param parsed: The elements to parse.

        :return: The validated elements.
        """
        validated = {
            "year": parsed["year"],
            "month": parsed["month"],
            "day": parsed["day"],
            "hour": parsed["hour"],
            "minute": parsed["minute"],
            "second": parsed["second"],
            "microsecond": parsed["microsecond"],
            "tz": None,
        }

        # If timestamp has been specified
        # we use it and don't go any further
        if parsed["timestamp"] is not None:
            str_us = str(parsed["timestamp"])
            if "." in str_us:
                microseconds = int("{}".format(
                    str_us.split(".")[1].ljust(6, "0")))
            else:
                microseconds = 0

            time = local_time(parsed["timestamp"], 0, microseconds)
            validated["year"] = time[0]
            validated["month"] = time[1]
            validated["day"] = time[2]
            validated["hour"] = time[3]
            validated["minute"] = time[4]
            validated["second"] = time[5]
            validated["microsecond"] = time[6]

            return validated

        if parsed["quarter"] is not None:
            if validated["year"] is not None:
                dt = pendulum.datetime(validated["year"], 1, 1)
            else:
                dt = now

            dt = dt.start_of("year")

            while dt.quarter != parsed["quarter"]:
                dt = dt.add(months=3)

            validated["year"] = dt.year
            validated["month"] = dt.month
            validated["day"] = dt.day

        if validated["year"] is None:
            validated["year"] = now.year

        if parsed["day_of_year"] is not None:
            dt = pendulum.parse("{}-{}".format(validated["year"],
                                               parsed["day_of_year"]))

            validated["month"] = dt.month
            validated["day"] = dt.day

        if parsed["day_of_week"] is not None:
            dt = pendulum.datetime(
                validated["year"],
                validated["month"] or now.month,
                validated["day"] or now.day,
            )
            dt = dt.start_of("week").subtract(days=1)
            dt = dt.next(parsed["day_of_week"])
            validated["year"] = dt.year
            validated["month"] = dt.month
            validated["day"] = dt.day

        # Meridiem
        if parsed["meridiem"] is not None:
            # If the time is greater than 13:00:00
            # This is not valid
            if validated["hour"] is None:
                raise ValueError("Invalid Date")

            t = (
                validated["hour"],
                validated["minute"],
                validated["second"],
                validated["microsecond"],
            )
            if t >= (13, 0, 0, 0):
                raise ValueError("Invalid date")

            pm = parsed["meridiem"] == "pm"
            validated["hour"] %= 12
            if pm:
                validated["hour"] += 12

        if validated["month"] is None:
            if parsed["year"] is not None:
                validated["month"] = parsed["month"] or 1
            else:
                validated["month"] = parsed["month"] or now.month

        if validated["day"] is None:
            if parsed["year"] is not None or parsed["month"] is not None:
                validated["day"] = parsed["day"] or 1
            else:
                validated["day"] = parsed["day"] or now.day

        for part in ["hour", "minute", "second", "microsecond"]:
            if validated[part] is None:
                validated[part] = 0

        validated["tz"] = parsed["tz"]

        return validated
Пример #8
0
    def _extends(self):
        if not self._posix_rule:
            return

        posix = self._posix_rule

        if not posix.dst_abbr:
            # std only
            # The future specification should match the last/default transition
            ttype = self._transitions[-1].ttype
            if not self._check_ttype(ttype, posix.std_offset, False,
                                     posix.std_abbr):
                raise ValueError("Posix spec does not match last transition")

            return

        if len(self._transitions) < 2:
            raise ValueError("Too few transitions for POSIX spec")

        # Extend the transitions for an additional 400 years
        # using the future specification

        # The future specification should match the last two transitions,
        # and those transitions should have different is_dst flags.
        tr0 = self._transitions[-1]
        tr1 = self._transitions[-2]
        tt0 = tr0.ttype
        tt1 = tr1.ttype
        if tt0.is_dst():
            dst = tt0
            std = tt1
        else:
            dst = tt1
            std = tt0

        self._check_ttype(dst, posix.dst_offset, True, posix.dst_abbr)
        self._check_ttype(std, posix.std_offset, False, posix.std_abbr)

        # Add the transitions to tr1 and back to tr0 for each extra year.
        last_year = local_time(tr0.local, 0, 0)[0]
        leap_year = is_leap(last_year)
        jan1 = datetime(last_year, 1, 1)
        jan1_time = timestamp(jan1)
        jan1_weekday = week_day(jan1.year, jan1.month, jan1.day) % 7

        if local_time(tr1.local, 0, 0)[0] != last_year:
            # Add a single extra transition to align to a calendar year.
            if tt0.is_dst():
                pt1 = posix.dst_end
            else:
                pt1 = posix.dst_start

            tr1_offset = pt1.trans_offset(leap_year, jan1_weekday)
            tr = Transition(jan1_time + tr1_offset - tt0.offset, tr1.ttype,
                            tr0)
            tr0 = tr
            tr1 = tr0
            tt0 = tr0.ttype
            tt1 = tr1.ttype

        if tt0.is_dst():
            pt1 = posix.dst_end
            pt0 = posix.dst_start
        else:
            pt1 = posix.dst_start
            pt0 = posix.dst_end

        tr = tr0
        for year in range(last_year + 1, last_year + 401):
            jan1_time += SECS_PER_YEAR[leap_year]
            jan1_weekday = (jan1_weekday + DAYS_PER_YEAR[leap_year]) % 7
            leap_year = not leap_year and is_leap(year)

            tr1_offset = pt1.trans_offset(leap_year, jan1_weekday)
            tr = Transition(jan1_time + tr1_offset - tt0.offset, tt1, tr)
            self._transitions.append(tr)

            tr0_offset = pt0.trans_offset(leap_year, jan1_weekday)
            tr = Transition(jan1_time + tr0_offset - tt1.offset, tt0, tr)
            self._transitions.append(tr)
Пример #9
0
    def _check_parsed(self, parsed,
                      now):  # type: (dict, pendulum.DateTime) -> dict
        """
        Checks validity of parsed elements.

        :param parsed: The elements to parse.

        :return: The validated elements.
        """
        validated = {
            'year': parsed['year'],
            'month': parsed['month'],
            'day': parsed['day'],
            'hour': parsed['hour'],
            'minute': parsed['minute'],
            'second': parsed['second'],
            'microsecond': parsed['microsecond'],
            'tz': None
        }

        # If timestamp has been specified
        # we use it and don't go any further
        if parsed['timestamp'] is not None:
            str_us = str(parsed['timestamp'])
            if '.' in str_us:
                microseconds = int("{}".format(
                    str_us.split('.')[1].ljust(6, '0')))
            else:
                microseconds = 0

            time = local_time(parsed['timestamp'], 0, microseconds)
            validated['year'] = time[0]
            validated['month'] = time[1]
            validated['day'] = time[2]
            validated['hour'] = time[3]
            validated['minute'] = time[4]
            validated['second'] = time[5]
            validated['microsecond'] = time[6]

            return validated

        if parsed['quarter'] is not None:
            if validated['year'] is not None:
                dt = pendulum.datetime(validated['year'], 1, 1)
            else:
                dt = now

            dt = dt.start_of('year')

            while dt.quarter != parsed['quarter']:
                dt = dt.add(months=3)

            validated['year'] = dt.year
            validated['month'] = dt.month
            validated['day'] = dt.day

        if validated['year'] is None:
            validated['year'] = now.year

        if parsed['day_of_year'] is not None:
            dt = pendulum.parse("{}-{}".format(validated['year'],
                                               parsed['day_of_year']))

            validated['month'] = dt.month
            validated['day'] = dt.day

        if parsed['day_of_week'] is not None:
            dt = pendulum.datetime(validated['year'], validated['month']
                                   or now.month, validated['day'] or now.day)
            dt = dt.start_of('week').subtract(days=1)
            dt = dt.next(parsed['day_of_week'])
            validated['year'] = dt.year
            validated['month'] = dt.month
            validated['day'] = dt.day

        # Meridiem
        if parsed['meridiem'] is not None:
            # If the time is greater than 13:00:00
            # This is not valid
            if validated['hour'] is None:
                raise ValueError('Invalid Date')

            t = (validated['hour'], validated['minute'], validated['second'],
                 validated['microsecond'])
            if t >= (13, 0, 0, 0):
                raise ValueError('Invalid date')

            pm = parsed['meridiem'] == 'pm'
            validated['hour'] %= 12
            if pm:
                validated['hour'] += 12

        if validated['month'] is None:
            if parsed['year'] is not None:
                validated['month'] = parsed['month'] or 1
            else:
                validated['month'] = parsed['month'] or now.month

        if validated['day'] is None:
            if parsed['year'] is not None or parsed['month'] is not None:
                validated['day'] = parsed['day'] or 1
            else:
                validated['day'] = parsed['day'] or now.day

        for part in ['hour', 'minute', 'second', 'microsecond']:
            if validated[part] is None:
                validated[part] = 0

        validated['tz'] = parsed['tz']

        return validated
Пример #10
0
    def _extends(self):
        if not self._posix_rule:
            return

        posix = self._posix_rule

        if not posix.dst_abbr:
            # std only
            # The future specification should match the last/default transition
            ttype = self._transitions[-1].ttype
            if not self._check_ttype(ttype, posix.std_offset, False, posix.std_abbr):
                raise ValueError("Posix spec does not match last transition")

            return

        if len(self._transitions) < 2:
            raise ValueError("Too few transitions for POSIX spec")

        # Extend the transitions for an additional 400 years
        # using the future specification

        # The future specification should match the last two transitions,
        # and those transitions should have different is_dst flags.
        tr0 = self._transitions[-1]
        tr1 = self._transitions[-2]
        tt0 = tr0.ttype
        tt1 = tr1.ttype
        if tt0.is_dst():
            dst = tt0
            std = tt1
        else:
            dst = tt1
            std = tt0

        self._check_ttype(dst, posix.dst_offset, True, posix.dst_abbr)
        self._check_ttype(std, posix.std_offset, False, posix.std_abbr)

        # Add the transitions to tr1 and back to tr0 for each extra year.
        last_year = local_time(tr0.local, 0, 0)[0]
        leap_year = is_leap(last_year)
        jan1 = datetime(last_year, 1, 1)
        jan1_time = timestamp(jan1)
        jan1_weekday = week_day(jan1.year, jan1.month, jan1.day) % 7

        if local_time(tr1.local, 0, 0)[0] != last_year:
            # Add a single extra transition to align to a calendar year.
            if tt0.is_dst():
                pt1 = posix.dst_end
            else:
                pt1 = posix.dst_start

            tr1_offset = pt1.trans_offset(leap_year, jan1_weekday)
            tr = Transition(jan1_time + tr1_offset - tt0.offset, tr1.ttype, tr0)
            tr0 = tr
            tr1 = tr0
            tt0 = tr0.ttype
            tt1 = tr1.ttype

        if tt0.is_dst():
            pt1 = posix.dst_end
            pt0 = posix.dst_start
        else:
            pt1 = posix.dst_start
            pt0 = posix.dst_end

        tr = tr0
        for year in range(last_year + 1, last_year + 401):
            jan1_time += SECS_PER_YEAR[leap_year]
            jan1_weekday = (jan1_weekday + DAYS_PER_YEAR[leap_year]) % 7
            leap_year = not leap_year and is_leap(year)

            tr1_offset = pt1.trans_offset(leap_year, jan1_weekday)
            tr = Transition(jan1_time + tr1_offset - tt0.offset, tt1, tr)
            self._transitions.append(tr)

            tr0_offset = pt0.trans_offset(leap_year, jan1_weekday)
            tr = Transition(jan1_time + tr0_offset - tt1.offset, tt0, tr)
            self._transitions.append(tr)
Пример #11
0
    def _check_parsed(self, parsed, now):  # type: (dict, pendulum.DateTime) -> dict
        """
        Checks validity of parsed elements.

        :param parsed: The elements to parse.

        :return: The validated elements.
        """
        validated = {
            "year": parsed["year"],
            "month": parsed["month"],
            "day": parsed["day"],
            "hour": parsed["hour"],
            "minute": parsed["minute"],
            "second": parsed["second"],
            "microsecond": parsed["microsecond"],
            "tz": None,
        }

        # If timestamp has been specified
        # we use it and don't go any further
        if parsed["timestamp"] is not None:
            str_us = str(parsed["timestamp"])
            if "." in str_us:
                microseconds = int("{}".format(str_us.split(".")[1].ljust(6, "0")))
            else:
                microseconds = 0

            time = local_time(parsed["timestamp"], 0, microseconds)
            validated["year"] = time[0]
            validated["month"] = time[1]
            validated["day"] = time[2]
            validated["hour"] = time[3]
            validated["minute"] = time[4]
            validated["second"] = time[5]
            validated["microsecond"] = time[6]

            return validated

        if parsed["quarter"] is not None:
            if validated["year"] is not None:
                dt = pendulum.datetime(validated["year"], 1, 1)
            else:
                dt = now

            dt = dt.start_of("year")

            while dt.quarter != parsed["quarter"]:
                dt = dt.add(months=3)

            validated["year"] = dt.year
            validated["month"] = dt.month
            validated["day"] = dt.day

        if validated["year"] is None:
            validated["year"] = now.year

        if parsed["day_of_year"] is not None:
            dt = pendulum.parse(
                "{}-{}".format(validated["year"], parsed["day_of_year"])
            )

            validated["month"] = dt.month
            validated["day"] = dt.day

        if parsed["day_of_week"] is not None:
            dt = pendulum.datetime(
                validated["year"],
                validated["month"] or now.month,
                validated["day"] or now.day,
            )
            dt = dt.start_of("week").subtract(days=1)
            dt = dt.next(parsed["day_of_week"])
            validated["year"] = dt.year
            validated["month"] = dt.month
            validated["day"] = dt.day

        # Meridiem
        if parsed["meridiem"] is not None:
            # If the time is greater than 13:00:00
            # This is not valid
            if validated["hour"] is None:
                raise ValueError("Invalid Date")

            t = (
                validated["hour"],
                validated["minute"],
                validated["second"],
                validated["microsecond"],
            )
            if t >= (13, 0, 0, 0):
                raise ValueError("Invalid date")

            pm = parsed["meridiem"] == "pm"
            validated["hour"] %= 12
            if pm:
                validated["hour"] += 12

        if validated["month"] is None:
            if parsed["year"] is not None:
                validated["month"] = parsed["month"] or 1
            else:
                validated["month"] = parsed["month"] or now.month

        if validated["day"] is None:
            if parsed["year"] is not None or parsed["month"] is not None:
                validated["day"] = parsed["day"] or 1
            else:
                validated["day"] = parsed["day"] or now.day

        for part in ["hour", "minute", "second", "microsecond"]:
            if validated[part] is None:
                validated[part] = 0

        validated["tz"] = parsed["tz"]

        return validated