コード例 #1
0
ファイル: validator.py プロジェクト: smcicoss/AceTime
    def _validate_test_data_for_zone(
        self,
        zone_name: str,
        items: List[TestItem],
    ) -> int:
        """Compare the given test 'items' generatd by TestDataGenerator (using
        pytz) with the expected datetime components from ZoneSpecifier. Returns
        the number of errors.
        """
        zone_info = self.zone_infos[zone_name]
        zone_specifier = ZoneSpecifier(
            zone_info_data=zone_info,
            viewing_months=self.viewing_months,
            debug=self.debug_specifier,
            in_place_transitions=self.in_place_transitions,
            optimize_candidates=self.optimize_candidates)

        num_errors = 0
        for item in items:
            if self.year is not None and self.year != item.y:
                continue

            # Print out diagnostics if mismatch detected or if debug flag given
            unix_seconds = item.epoch + SECONDS_SINCE_UNIX_EPOCH
            ldt = datetime.utcfromtimestamp(unix_seconds)
            header = (f'======== Testing {zone_name}; '
                      f'at {_test_item_to_string(item)}w; '
                      f'utc {ldt}; '
                      f'epoch {item.epoch}; '
                      f'unix {unix_seconds}')

            if self.debug_specifier:
                logging.info(header)

            info = zone_specifier.get_timezone_info_for_seconds(item.epoch)
            if not info:
                logging.info("timezone info not found")
                continue

            is_matched = info.total_offset == item.total_offset
            status = '**Matched**' if is_matched else '**Mismatched**'
            ace_time_string = to_utc_string(info.utc_offset, info.dst_offset)
            utc_string = to_utc_string(item.total_offset - item.dst_offset,
                                       item.dst_offset)
            body = (f'{status}: '
                    f'AceTime({ace_time_string}); '
                    f'Expected({utc_string})')
            if is_matched:
                if self.debug_specifier:
                    logging.info(body)
                    zone_specifier.print_matches_and_transitions()
            else:
                num_errors += 1
                if not self.debug_specifier:
                    logging.error(header)
                logging.error(body)
                zone_specifier.print_matches_and_transitions()

        return num_errors
コード例 #2
0
ファイル: acetz.py プロジェクト: smcicoss/AceTime
class acetz(tzinfo):
    """An implementation of datetime.tzinfo using the ZoneSpecifier class
    from AceTime/tools.
    """
    def __init__(self, zone_info: ZoneInfo):
        self.zone_info = zone_info
        self.zs = ZoneSpecifier(zone_info, use_python_transition=True)

    def utcoffset(self, dt: Optional[datetime]) -> timedelta:
        assert dt
        info = self.zs.get_timezone_info_for_datetime(dt)
        if not info:
            raise Exception(f'Unknown timezone info for '
                            f'{dt.year:04}-{dt.month:02}-{dt.day:02} '
                            f'{dt.hour:02}:{dt.minute:02}:{dt.second:02}')

        return timedelta(seconds=info.total_offset)

    def dst(self, dt: Optional[datetime]) -> timedelta:
        assert dt
        offset_info = self.zs.get_timezone_info_for_datetime(dt)
        if not offset_info:
            raise Exception(f'Unknown timezone info for '
                            f'{dt.year:04}-{dt.month:02}-{dt.day:02} '
                            f'{dt.hour:02}:{dt.minute:02}:{dt.second:02}')
        return timedelta(seconds=offset_info.dst_offset)

    def tzname(self, dt: Optional[datetime]) -> str:
        assert dt
        offset_info = self.zs.get_timezone_info_for_datetime(dt)
        if not offset_info:
            raise Exception(f'Unknown timezone info for '
                            f'{dt.year:04}-{dt.month:02}-{dt.day:02} '
                            f'{dt.hour:02}:{dt.minute:02}:{dt.second:02}')
        return offset_info.abbrev

    def fromutc(self, dt: Optional[datetime]) -> datetime:
        """Override the default implementation in tzinfo which does not make
        sense for acetz.

        The 'dt' passed into this function from datetime.astimezone() is a weird
        one: the components are in UTC time, but the timezone is the target
        tzinfo, in other words, the same acetz as self.

        Warning: Do NOT call dt.isoformat() from this method, because it causes
        an infinite recursion as it tries to figure out the UTC offset. Use
        {dt.date()} and {dt.time()} instead.
        """
        if not isinstance(dt, datetime):
            raise TypeError("fromutc() requires a datetime argument")
        if dt.tzinfo is not self:
            raise ValueError("dt.tzinfo is not self")

        # Extract the epoch_seconds of the source 'dt'
        assert dt is not None
        utcdt = dt.replace(tzinfo=timezone.utc)
        unix_seconds = int(utcdt.timestamp())
        epoch_seconds = unix_seconds - SECONDS_SINCE_UNIX_EPOCH

        # Search the transitions for the matching Transition
        offset_info = self.zs.get_timezone_info_for_seconds(epoch_seconds)
        if not offset_info:
            raise ValueError(f"transition not found for {epoch_seconds}")

        # Convert the date/time fields into local date/time and attach
        # the current acetz object.
        newutcdt = utcdt + timedelta(seconds=offset_info.total_offset)
        newdt = newutcdt.replace(tzinfo=self, fold=offset_info.fold)

        return newdt

    def zone_specifier(self) -> ZoneSpecifier:
        return self.zs