def _process_eras(self, zones_map: ZonesMap) -> ZonesMap: """Convert various ZoneRule fields into values that are consumed by the ZoneInfo and ZonePolicy classes of the Arduino AceTime library. """ for zone_name, eras in zones_map.items(): for era in eras: # Determine the current delta seconds, based on the RULES field. rule_policy_name = era['rules'] if rule_policy_name == ':': delta_seconds = era['rules_delta_seconds_truncated'] else: delta_seconds = 0 # Generate the STDOFF and DST delta offset codes. encoded_offset = _to_offset_and_delta( offset_seconds=era['offset_seconds_truncated'], delta_seconds=delta_seconds, scope=self.scope, ) era['offset_code'] = encoded_offset.offset_code era['offset_minute'] = encoded_offset.offset_minute era['delta_code'] = encoded_offset.delta_code era['delta_code_encoded'] = encoded_offset.delta_code_encoded # Check if STDOFF is not on 15-minute boundary if encoded_offset.offset_minute != 0: logging.info(f"Notable zone: {zone_name}: " f"STDOFF '{era['offset_string']}' " "not on 15-minute boundary") add_comment( self.tresult.notable_zones, zone_name, f"STDOFF '{era['offset_string']}' " "not on 15-minute boundary") # Generate the UNTIL fields needed by Arduino ZoneProcessors era['until_year_tiny'] = _to_tiny_until_year(era['until_year']) encoded_until_time = _to_encoded_time( seconds=era['until_seconds_truncated'], suffix=era['until_time_suffix'], ) era['until_time_code'] = encoded_until_time.time_code era['until_time_minute'] = encoded_until_time.time_minute era['until_time_modifier'] = encoded_until_time.modifier # Check if UNTIL is not on 15-minute boundary if encoded_until_time.time_minute != 0: logging.info( f"Notable zone: {zone_name}: " f"UNTIL '{era['until_time']}' not on 15-minute boundary" ) add_comment( self.tresult.notable_zones, zone_name, f"UNTIL '{era['until_time']}' not on 15-minute boundary" ) # FORMAT field for Arduino C++ replaces %s with just a %. era['format_short'] = era['format'].replace('%s', '%') return self.zones_map
def __init__( self, invocation: str, tz_version: str, tz_files: str, scope: str, db_namespace: str, zones_map: ZonesMap, links_map: LinksMap, zone_ids: Dict[str, int], link_ids: Dict[str, int], ): self.invocation = invocation self.tz_version = tz_version self.tz_files = tz_files self.scope = scope self.db_namespace = db_namespace self.zones_map = zones_map self.links_map = links_map self.zone_ids = zone_ids self.link_ids = link_ids self.db_header_namespace = self.db_namespace.upper() self.zones_and_links = list(zones_map.keys()) + list(links_map.keys()) self.zone_and_link_ids = zone_ids.copy() self.zone_and_link_ids.update(link_ids)
def _generate_fragments(zones_map: ZonesMap, links_map: LinksMap) -> IndexMap: """Generate a list of fragments and their indexes, sorted by fragment. E.g. { "Africa": 1, "America": 2, ... } """ # Collect the frequency of fragments longer than 3 characters fragments: Dict[str, int] = Counter() for name in itertools.chain(zones_map.keys(), links_map.keys()): fragment_list = _extract_fragments(name) for fragment in fragment_list: if len(fragment) > 3: fragments[fragment] += 1 # Collect fragments which occur more than 3 times. fragments_map: IndexMap = OrderedDict() index = 1 # start at 1 because '\0' is the c-string termination char for fragment, count in sorted(fragments.items()): if count > 3: fragments_map[fragment] = index index += 1 else: logging.info( f"Ignoring fragment '{fragment}' with count {count}, too few") # Make sure that index is < 32, before ASCII-space. if index >= 32: raise Exception("Too many fragments {index}") return fragments_map
def _generate_info_map_items(self, zones_map: ZonesMap) -> str: """Generate a map of (zone_name -> zoneInfo), shorted by name. """ info_map_items = '' for zone_name, zones in sorted(zones_map.items(), key=lambda x: normalize_name(x[0])): info_map_items += self.ZONE_INFO_MAP_ITEM.format( zoneNormalizedName=normalize_name(zone_name), zoneFullName=zone_name) return info_map_items
def _generate_compressed_names( zones_map: ZonesMap, links_map: LinksMap, fragments_map: IndexMap, ) -> Dict[str, str]: compressed_names: Dict[str, str] = OrderedDict() for name in sorted(zones_map.keys()): compressed_names[name] = _compress_name(name, fragments_map) for name in sorted(links_map.keys()): compressed_names[name] = _compress_name(name, fragments_map) return compressed_names
def _collect_format_strings(zones_map: ZonesMap) -> IndexMap: """Collect the 'formats' field and return a map of indexes.""" short_formats: Set[str] = set() for zone_name, eras in zones_map.items(): for era in eras: format = era['format'] short_format = format.replace('%s', '%') short_formats.add(short_format) index = 0 short_formats_map: IndexMap = OrderedDict() for short_format in sorted(short_formats): short_formats_map[short_format] = index index += 1 return short_formats_map
def _generate_zone_ids(zones_map: ZonesMap) -> Dict[str, int]: """Generate {zoneName -> zoneId} map.""" ids: Dict[str, int] = {name: hash_name(name) for name in zones_map.keys()} return OrderedDict(sorted(ids.items()))