def _generate_policy_map_items(self, rules_map: RulesMap) -> str: policy_map_items = '' for name, rules in sorted(rules_map.items(), key=lambda x: normalize_name(x[0])): policy_map_items += self.ZONE_POLICY_MAP_ITEM.format( policyName=normalize_name(name)) return policy_map_items
def _generate_link_item(self, link_name: str, zone_name: str) -> str: return self.ZONE_INFOS_CPP_LINK_ITEM.format( scope=self.scope, linkFullName=link_name, linkNormalizedName=normalize_name(link_name), zoneFullName=zone_name, zoneNormalizedName=normalize_name(zone_name))
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_infos_h(self) -> str: info_items = '' for zone_name, eras in sorted(self.zones_map.items()): info_items += self.ZONE_INFOS_H_INFO_ITEM.format( scope=self.scope, zoneNormalizedName=normalize_name(zone_name), zoneFullName=zone_name) removed_info_items = '' for zone_name, reasons in sorted(self.removed_zones.items()): removed_info_items += self.ZONE_INFOS_H_REMOVED_INFO_ITEM.format( zoneFullName=zone_name, reason=', '.join(reasons)) notable_info_items = '' for zone_name, reasons in sorted(self.notable_zones.items()): notable_info_items += self.ZONE_INFOS_H_NOTABLE_INFO_ITEM.format( zoneFullName=zone_name, reason=', '.join(reasons)) link_items = '' for link_name, zone_name in sorted(self.links_map.items()): link_items += self.ZONE_INFOS_H_LINK_ITEM.format( scope=self.scope, linkNormalizedName=normalize_name(link_name), linkFullName=link_name, zoneFullName=zone_name) removed_link_items = '' for link_name, reasons in sorted(self.removed_links.items()): removed_link_items += self.ZONE_INFOS_H_REMOVED_LINK_ITEM.format( linkFullName=link_name, reason=', '.join(reasons)) notable_link_items = '' for link_name, reasons in sorted(self.notable_links.items()): notable_link_items += self.ZONE_INFOS_H_NOTABLE_LINK_ITEM.format( linkFullName=link_name, reason=', '.join(reasons)) return self.ZONE_INFOS_H_FILE.format( invocation=self.invocation, tz_version=self.tz_version, scope=self.scope, dbNamespace=self.db_namespace, dbHeaderNamespace=self.db_header_namespace, tz_files=', '.join(self.tz_files), numInfos=len(self.zones_map), infoItems=info_items, numLinks=len(self.links_map), linkItems=link_items, numRemovedInfos=len(self.removed_zones), removedInfoItems=removed_info_items, numNotableInfos=len(self.notable_zones), notableInfoItems=notable_info_items, numRemovedLinks=len(self.removed_links), removedLinkItems=removed_link_items, numNotableLinks=len(self.notable_links), notableLinkItems=notable_link_items)
def _generate_test_cases(self, test_data: TestData) -> str: dst_blacklist: Set[str] = (set( self.validation_data.get('dst_blacklist') or [])) has_valid_abbrev = self.validation_data['has_valid_abbrev'] has_valid_dst = self.validation_data['has_valid_dst'] test_cases = '' for zone_name, _ in sorted(test_data.items()): normalized_name = normalize_name(zone_name) test_dst = ('true' if has_valid_dst and (zone_name not in dst_blacklist) else 'false') test_dst_comment = (' BLACKLISTED' if has_valid_dst and (zone_name in dst_blacklist) else '') test_abbrev = 'true' if has_valid_abbrev else 'false' test_case = f"""\ testF({self.test_class}, {normalized_name}) {{ assertValid( &kZone{normalized_name}, &kValidationData{normalized_name}, {test_dst} /*validateDst{test_dst_comment}*/, {test_abbrev} /*validateAbbrev*/); }} """ test_cases += test_case return test_cases
def generate_policies_h(self) -> str: policy_items = '' for name, rules in sorted(self.rules_map.items()): policy_items += self.ZONE_POLICIES_H_POLICY_ITEM.format( policyName=normalize_name(name), scope=self.scope) removed_policy_items = '' for name, reasons in sorted(self.removed_policies.items()): removed_policy_items += \ self.ZONE_POLICIES_H_REMOVED_POLICY_ITEM.format( policyName=name, policyReason=', '.join(reasons)) notable_policy_items = '' for name, reasons in sorted(self.notable_policies.items()): notable_policy_items += \ self.ZONE_POLICIES_H_NOTABLE_POLICY_ITEM.format( policyName=name, policyReason=', '.join(reasons)) return self.ZONE_POLICIES_H_FILE.format( invocation=self.invocation, tz_version=self.tz_version, dbNamespace=self.db_namespace, dbHeaderNamespace=self.db_header_namespace, tz_files=', '.join(self.tz_files), numPolicies=len(self.rules_map), policyItems=policy_items, numRemovedPolicies=len(self.removed_policies), removedPolicyItems=removed_policy_items, numNotablePolicies=len(self.notable_policies), notablePolicyItems=notable_policy_items)
def _generate_test_cases(self, test_data: TestData) -> str: has_valid_abbrev = self.validation_data['has_valid_abbrev'] has_valid_dst = self.validation_data['has_valid_dst'] test_cases = '' for zone_name, _ in sorted(test_data.items()): normalized_name = normalize_name(zone_name) ( dst_validation_scope, dst_validation_comment, ) = _get_validation_scope( has_valid_dst, self.blacklist.get(zone_name), ) ( abbrev_validation_scope, abbrev_validation_comment, ) = _get_validation_scope( has_valid_abbrev, self.blacklist.get(zone_name), ) test_case = f"""\ testF({self.test_class}, {normalized_name}) {{ assertValid( &kZone{normalized_name}, &kValidationData{normalized_name}, {dst_validation_scope} /*dstValidationScope{dst_validation_comment}*/, {abbrev_validation_scope} \ /*abbrevValidationScope{abbrev_validation_comment}*/); }} """ test_cases += test_case return test_cases
def _generate_validation_data_h_items(self, test_data: TestData) -> str: validation_items = '' for zone_name, test_items in sorted(test_data.items()): validation_items += self.VALIDATION_DATA_H_ITEM.format( validationDataClass=self.validation_data_class, zoneNormalizedName=normalize_name(zone_name)) return validation_items
def _generate_validation_data_cpp_items(self, test_data: TestData) -> str: validation_items = '' for zone_name, test_items in sorted(test_data.items()): test_items_string = self._generate_validation_data_cpp_test_items( zone_name, test_items) normalized_name = normalize_name(zone_name) validation_item = f"""\ //--------------------------------------------------------------------------- // Zone name: {zone_name} //--------------------------------------------------------------------------- static const testing::ValidationItem kValidationItems{normalized_name}[] = {{ // epoch, utc, dst, y, m, d, h, m, s, abbrev {test_items_string} }}; const testing::ValidationData kValidationData{normalized_name} = {{ {len(test_items)} /*numItems*/, kValidationItems{normalized_name} /*items*/, }}; """ validation_items += validation_item return validation_items
def _generate_infos(self) -> None: for zone_name, eras in self.zones_map.items(): zone_eras: List[ZoneEra] = [] for era in eras: policy_name = era['rules'] zone_policy: Union[ZonePolicy, str] if policy_name in ['-', ':']: zone_policy = policy_name else: policy_name = normalize_name(policy_name) zone_policy = self.zone_policies[policy_name] # yapf: disable zone_eras.append({ 'offsetSeconds': era['offsetSecondsTruncated'], 'zonePolicy': zone_policy, 'rulesDeltaSeconds': era['rulesDeltaSecondsTruncated'], 'format': era['format'], 'untilYear': era['untilYear'], 'untilMonth': era['untilMonth'], 'untilDay': era['untilDay'], 'untilSeconds': era['untilSecondsTruncated'], 'untilTimeSuffix': era['untilTimeSuffix'], }) # yapf: enable self.zone_infos[zone_name] = {'name': zone_name, 'eras': zone_eras}
def _generate_test_cases(self, test_data: TestData) -> str: test_cases = '' for zone_name, _ in sorted(test_data.items()): test_case = self.TEST_CASE.format( dbNamespace=self.db_namespace, testClass=self.test_class, zoneNormalizedName=normalize_name(zone_name)) test_cases += test_case return test_cases
def _generate_removed_policy_items( self, removed_policies: CommentsMap, ) -> str: removed_policy_items = '' for name, reason in sorted(removed_policies.items()): removed_policy_items += (self.ZONE_REMOVED_POLICY_ITEM.format( policyName=normalize_name(name), policyReason=reason)) return removed_policy_items
def _generate_validation_data_h_items(self, test_data: TestData) -> str: validation_items = '' for zone_name, test_items in sorted(test_data.items()): normalized_name = normalize_name(zone_name) validation_items += f"""\ extern const testing::ValidationData kValidationData{normalized_name}; """ return validation_items
def _generate_notable_policy_items( self, notable_policies: CommentsMap, ) -> str: notable_policy_items = '' for name, reason in sorted(notable_policies.items()): notable_policy_items += (self.ZONE_NOTABLE_POLICY_ITEM.format( policyName=normalize_name(name), policyReason=reason)) return notable_policy_items
def _generate_policy_item(self, name: str, rules: List[ZoneRuleRaw]) -> str: rule_items = '' for rule in rules: rule_items += self.ZONE_RULE_ITEM.format( policyName=normalize_name(name), rawLine=normalize_raw(rule['rawLine']), fromYear=rule['fromYear'], toYear=rule['toYear'], inMonth=rule['inMonth'], onDayOfWeek=rule['onDayOfWeek'], onDayOfMonth=rule['onDayOfMonth'], atSeconds=rule['atSecondsTruncated'], atTimeSuffix=rule['atTimeSuffix'], deltaSeconds=rule['deltaSecondsTruncated'], letter=rule['letter']) return self.ZONE_POLICY_ITEM.format(policyName=normalize_name(name), numRules=len(rules), ruleItems=rule_items)
def _generate_validation_data_cpp_items(self, test_data: TestData) -> str: validation_items = '' for zone_name, test_items in sorted(test_data.items()): test_items_string = self._generate_validation_data_cpp_test_items( zone_name, test_items) validation_item = self.VALIDATION_DATA_CPP_ITEM.format( validationDataClass=self.validation_data_class, zoneFullName=zone_name, zoneNormalizedName=normalize_name(zone_name), testItems=test_items_string) validation_items += validation_item return validation_items
def _generate_era_item( self, zone_name: str, era: ZoneEraRaw ) -> Tuple[str, int]: policy_name = era['rules'] if policy_name == '-' or policy_name == ':': zone_policy = 'nullptr' delta_seconds = era['rulesDeltaSecondsTruncated'] else: zone_policy = '&kPolicy%s' % normalize_name(policy_name) delta_seconds = 0 if self.scope == 'extended': offset_code, delta_code = _to_extended_offset_and_delta( era['offsetSecondsTruncated'], delta_seconds) else: offset_code = div_to_zero(era['offsetSecondsTruncated'], 900) delta_code = str(div_to_zero(delta_seconds, 900)) until_year = era['untilYear'] if until_year == MAX_UNTIL_YEAR: until_year_tiny = MAX_UNTIL_YEAR_TINY else: until_year_tiny = until_year - EPOCH_YEAR until_month = era['untilMonth'] if not until_month: until_month = 1 until_day = era['untilDay'] if not until_day: until_day = 1 until_time_code, until_time_modifier = _to_code_and_modifier( era['untilSecondsTruncated'], era['untilTimeSuffix'], self.scope) # Replace %s with just a % for C++ format = era['format'].replace('%s', '%') string_length = len(format) + 1 era_item = self.ZONE_INFOS_CPP_ERA_ITEM.format( rawLine=normalize_raw(era['rawLine']), offsetCode=offset_code, deltaCode=delta_code, zonePolicy=zone_policy, format=format, untilYearTiny=until_year_tiny, untilMonth=until_month, untilDay=until_day, untilTimeCode=until_time_code, untilTimeModifier=until_time_modifier) return (era_item, string_length)
def _generate_info_item( self, zone_name: str, eras: List[ZoneEraRaw], ) -> str: era_items = '' for era in eras: era_items += self._generate_era_item(era) return self.ZONE_INFO_ITEM.format( zoneFullName=zone_name, zoneNormalizedName=normalize_name(zone_name), numEras=len(eras), eraItems=era_items)
def generate_registry_cpp(self) -> str: zone_registry_items = '' for zone_name, eras in sorted(self.zones_map.items()): name = normalize_name(zone_name) zone_registry_items += f' &kZone{name}, // {zone_name}\n' return self.ZONE_REGISTRY_CPP_FILE.format( invocation=self.invocation, tz_version=self.tz_version, scope=self.scope, dbNamespace=self.db_namespace, dbHeaderNamespace=self.db_header_namespace, numZones=len(self.zones_map), zoneRegistryItems=zone_registry_items, progmem='ACE_TIME_PROGMEM')
def _generate_era_item(self, era: ZoneEraRaw) -> str: policy_name = era['rules'] if policy_name in ['-', ':']: zone_policy = "'%s'" % policy_name else: zone_policy = 'ZONE_POLICY_%s' % normalize_name(policy_name) return self.ZONE_ERA_ITEM.format( rawLine=normalize_raw(era['rawLine']), offsetSeconds=era['offsetSecondsTruncated'], zonePolicy=zone_policy, rulesDeltaSeconds=era['rulesDeltaSecondsTruncated'], format=era['format'], # preserve the %s untilYear=era['untilYear'], untilMonth=era['untilMonth'], untilDay=era['untilDay'], untilSeconds=era['untilSecondsTruncated'], untilTimeSuffix=era['untilTimeSuffix'])
def _generate_info_item( self, zone_name: str, eras: List[ZoneEraRaw], ) -> Tuple[str, int]: era_items = '' string_length = 0 for era in eras: (era_item, length) = self._generate_era_item(zone_name, era) era_items += era_item string_length += length string_length += len(zone_name) + 1 num_eras = len(eras) memory8 = ( string_length + num_eras * self.SIZEOF_ZONE_ERA_8 + 1 * self.SIZEOF_ZONE_INFO_8) memory32 = ( string_length + num_eras * self.SIZEOF_ZONE_ERA_32 + 1 * self.SIZEOF_ZONE_INFO_32) transition_buf_size = self.buf_sizes[zone_name] info_item = self.ZONE_INFOS_CPP_INFO_ITEM.format( scope=self.scope, zoneFullName=zone_name, zoneNormalizedName=normalize_name(zone_name), zoneNameHash=hash_name(zone_name), transitionBufSize=transition_buf_size, numEras=num_eras, stringLength=string_length, memory8=memory8, memory32=memory32, eraItems=era_items, progmem='ACE_TIME_PROGMEM') return (info_item, string_length)
def _generate_policies(self) -> None: for name, rules in self.rules_map.items(): policy_rules: List[ZoneRule] = [] for rule in rules: # yapf: disable policy_rules.append({ 'fromYear': rule['fromYear'], 'toYear': rule['toYear'], 'inMonth': rule['inMonth'], 'onDayOfWeek': rule['onDayOfWeek'], 'onDayOfMonth': rule['onDayOfMonth'], 'atSeconds': rule['atSecondsTruncated'], 'atTimeSuffix': rule['atTimeSuffix'], 'deltaSeconds': rule['deltaSecondsTruncated'], 'letter': rule['letter'] }) # yapf: enable normalized_name = normalize_name(name) self.zone_policies[normalized_name] = { 'name': name, # policy name 'rules': policy_rules }
def _generate_policy_item( self, name: str, rules: List[ZoneRuleRaw], indexed_letters: Optional[IndexedLetters], ) -> Tuple[str, int, int]: # Generate kZoneRules*[] rule_items = '' for rule in rules: at_time_code, at_time_modifier = _to_code_and_modifier( rule['atSecondsTruncated'], rule['atTimeSuffix'], self.scope) if self.scope == 'extended': delta_code = _to_extended_delta_code( rule['deltaSecondsTruncated']) else: delta_code = str(div_to_zero( rule['deltaSecondsTruncated'], 900 )) from_year = rule['fromYear'] from_year_tiny = to_tiny_year(from_year) to_year = rule['toYear'] to_year_tiny = to_tiny_year(to_year) # Single-character 'letter' values are represented as themselves # using the C++ 'char' type ('A'-'Z'). But some 'letter' fields hold # a multi-character string. We can encode these multi-character # strings as an index into an array of NUL-terminated strings. # ASCII codes less than 32 (space) are non-printable control # characters so they will not collide with the printable characters # 'A' - 'Z'. Therefore we can hold to up to 31 multi-character # strings per-zone. In practice, for a single zone, the maximum # number of multi-character strings that I've seen is 2. if len(rule['letter']) == 1: letter = "'%s'" % rule['letter'] letterComment = '' elif len(rule['letter']) > 1: letters = cast(IndexedLetters, indexed_letters) index = letters[rule['letter']] if index >= 32: raise Exception('Number of indexed letters >= 32') letter = str(index) letterComment = ('; "%s"' % rule['letter']) else: raise Exception( 'len(%s) == 0; should not happen' % rule['letter']) rule_items += self.ZONE_POLICIES_CPP_RULE_ITEM.format( rawLine=normalize_raw(rule['rawLine']), fromYearTiny=from_year_tiny, toYearTiny=to_year_tiny, inMonth=rule['inMonth'], onDayOfWeek=rule['onDayOfWeek'], onDayOfMonth=rule['onDayOfMonth'], atTimeCode=at_time_code, atTimeModifier=at_time_modifier, deltaCode=delta_code, letter=letter, letterComment=letterComment) # Generate kLetters*[] policyName = normalize_name(name) numLetters = len(indexed_letters) if indexed_letters else 0 memoryLetters8 = 0 memoryLetters32 = 0 if numLetters: letters = cast(IndexedLetters, indexed_letters) letterArrayRef = 'kLetters%s' % policyName letterItems = '' for name, index in letters.items(): letterItems += (' /*%d*/ "%s",\n' % (index, name)) memoryLetters8 += len(name) + 1 + 2 # NUL terminated memoryLetters32 += len(name) + 1 + 4 # NUL terminated letterArray = self.ZONE_POLICIES_LETTER_ARRAY.format( policyName=policyName, letterItems=letterItems, progmem='ACE_TIME_PROGMEM') else: letterArrayRef = 'nullptr' letterArray = '' # Calculate the memory consumed by structs and arrays num_rules = len(rules) memory8 = ( 1 * self.SIZEOF_ZONE_POLICY_8 + num_rules * self.SIZEOF_ZONE_RULE_8 + memoryLetters8) memory32 = ( 1 * self.SIZEOF_ZONE_POLICY_32 + num_rules * self.SIZEOF_ZONE_RULE_32 + memoryLetters32) policy_item = self.ZONE_POLICIES_CPP_POLICY_ITEM.format( scope=self.scope, policyName=policyName, numRules=num_rules, memory8=memory8, memory32=memory32, ruleItems=rule_items, numLetters=numLetters, letterArrayRef=letterArrayRef, letterArray=letterArray, progmem='ACE_TIME_PROGMEM') return (policy_item, memory8, memory32)