this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Initial "closest noon" impl.

alice 81578b04 50587dc7

+1089 -12
+223
generate_tz_list.py
··· 1 + # Requires Python 3.9+ for zoneinfo 2 + import zoneinfo 3 + from datetime import datetime, timedelta, timezone 4 + # No unused imports found (time is used for .timestamp()) 5 + 6 + # Helper to get offset and DST component safely 7 + def get_tz_details(tz_name: str, dt_utc: datetime) -> tuple[int, timedelta] | None: 8 + """Gets total offset in seconds and DST component as timedelta.""" 9 + try: 10 + tz = zoneinfo.ZoneInfo(tz_name) 11 + offset_td = tz.utcoffset(dt_utc) 12 + dst_td = tz.dst(dt_utc) 13 + 14 + # Ensure DST is not None, default to zero if it is (e.g., for UTC) 15 + if dst_td is None: 16 + dst_td = timedelta(0) 17 + 18 + if offset_td is not None: 19 + return int(offset_td.total_seconds()), dst_td 20 + # If offset_td is None, implicitly returns None below 21 + except Exception: 22 + # print(f"Warning: Could not get details for {tz_name} at {dt_utc}: {e}") 23 + pass # Silently ignore errors for individual lookups 24 + return None 25 + 26 + # Function to find DST transitions within a year 27 + def find_dst_transitions_accurate(tz_name: str, year: int) -> tuple[int, int, int, int]: 28 + """ Finds precise DST transition UTC timestamps for a given year. 29 + Returns (std_offset_sec, dst_offset_sec, last_start_utc_ts, last_end_utc_ts) 30 + Timestamps are UTC seconds (epoch). 0 if no transition/no DST found in the year. 31 + """ 32 + start_ts = 0 33 + end_ts = 0 34 + std_offset_sec = None 35 + dst_offset_sec = None # Offset *during* DST 36 + initial_offset_sec = None # Store the very first valid offset 37 + 38 + try: 39 + # Start iterating from one hour before the target year begins 40 + # Ensures transitions exactly at year start are caught 41 + current_dt = datetime(year , 1, 1, 0, 0, 0, tzinfo=timezone.utc) - timedelta(hours=1) 42 + initial_details = get_tz_details(tz_name, current_dt) 43 + 44 + if not initial_details: 45 + # Fallback if start fails: try noon on Jan 1st 46 + current_dt = datetime(year , 1, 1, 12, 0, 0, tzinfo=timezone.utc) 47 + initial_details = get_tz_details(tz_name, current_dt) 48 + if not initial_details: 49 + print(f"Warning: Cannot get initial offset for {tz_name} in {year}") 50 + return 0, 0, 0, 0 51 + 52 + prev_offset_sec, prev_dst_td = initial_details 53 + initial_offset_sec = prev_offset_sec # Store the first offset we found 54 + 55 + # Iterate hour by hour through the target year plus a few hours into the next 56 + total_hours_to_check = (366 * 24) + 3 # Cover leap year + buffer 57 + 58 + for _ in range(total_hours_to_check): 59 + current_dt += timedelta(hours=1) 60 + details = get_tz_details(tz_name, current_dt) 61 + 62 + if not details: continue # Skip if data unavailable for this hour 63 + 64 + current_offset_sec, current_dst_td = details 65 + 66 + # --- Determine Standard vs DST offset --- 67 + # Continuously update std/dst based on whether DST is active 68 + if current_dst_td == timedelta(0): 69 + std_offset_sec = current_offset_sec 70 + if current_dst_td > timedelta(0): 71 + dst_offset_sec = current_offset_sec 72 + 73 + # --- Detect transition based on change in DST component --- 74 + if prev_dst_td != current_dst_td: 75 + transition_ts = int(current_dt.timestamp()) 76 + 77 + # Record transition timestamp if it happens *within* the target year 78 + if current_dt.year == year: 79 + if prev_dst_td == timedelta(0) and current_dst_td > timedelta(0): 80 + # Entered DST (Std -> Dst) 81 + start_ts = transition_ts 82 + # Ensure offsets are recorded based on this transition 83 + if std_offset_sec is None: std_offset_sec = prev_offset_sec 84 + if dst_offset_sec is None: dst_offset_sec = current_offset_sec 85 + 86 + elif prev_dst_td > timedelta(0) and current_dst_td == timedelta(0): 87 + # Exited DST (Dst -> Std) 88 + end_ts = transition_ts 89 + # Ensure offsets are recorded based on this transition 90 + if std_offset_sec is None: std_offset_sec = current_offset_sec 91 + if dst_offset_sec is None: dst_offset_sec = prev_offset_sec 92 + 93 + prev_offset_sec, prev_dst_td = current_offset_sec, current_dst_td 94 + 95 + # --- Post-processing --- 96 + # Use the very first offset seen if std/dst couldn't be determined otherwise 97 + if std_offset_sec is None: std_offset_sec = initial_offset_sec 98 + if dst_offset_sec is None: dst_offset_sec = std_offset_sec # Default DST offset to STD if not seen 99 + 100 + # If offsets are effectively the same, clear transition timestamps 101 + OFFSET_DIFF_THRESHOLD_SECONDS = 60 # Use a named constant 102 + if abs(std_offset_sec - dst_offset_sec) < OFFSET_DIFF_THRESHOLD_SECONDS: 103 + start_ts = 0 104 + end_ts = 0 105 + dst_offset_sec = std_offset_sec # Ensure they are identical if no DST 106 + 107 + except zoneinfo.ZoneInfoNotFoundError: 108 + print(f"Warning: Timezone '{tz_name}' not found during transition check.") 109 + return 0, 0, 0, 0 110 + except Exception as e: 111 + print(f"Error finding transitions for {tz_name}: {e}") 112 + return 0, 0, 0, 0 113 + 114 + # Ensure we return non-None values 115 + std_offset_sec = std_offset_sec if std_offset_sec is not None else 0 116 + dst_offset_sec = dst_offset_sec if dst_offset_sec is not None else 0 117 + 118 + return std_offset_sec, dst_offset_sec, start_ts, end_ts 119 + 120 + def generate_tz_list_c_code(): 121 + """Generates C code for a static timezone list with DST transition timestamps.""" 122 + 123 + target_year = datetime.now().year # Use current year for transitions 124 + print(f"Finding DST transitions for year {target_year}...") 125 + 126 + available_zones = zoneinfo.available_timezones() 127 + print(f"Found {len(available_zones)} available timezones.") 128 + 129 + processed_zones = {} # Key: std_offset_hours, Value: Dict of zone data 130 + 131 + for tz_name in available_zones: 132 + # Basic filtering (no dead code here) 133 + if tz_name.startswith("Etc/") or "/" not in tz_name: continue 134 + if tz_name in ["Factory", "factory"] or tz_name.lower().startswith("right/") or tz_name.lower().startswith("posix/"): continue 135 + 136 + std_offset_s, dst_offset_s, start_utc, end_utc = find_dst_transitions_accurate(tz_name, target_year) 137 + city_name = tz_name.split('/')[-1].replace('_', ' ') 138 + 139 + # Convert offsets back to hours 140 + std_offset_h = std_offset_s / 3600.0 141 + dst_offset_h = dst_offset_s / 3600.0 142 + 143 + # Group by standard offset 144 + key_offset = std_offset_h 145 + if city_name and city_name[0].isupper(): 146 + if key_offset not in processed_zones: 147 + processed_zones[key_offset] = { 148 + "std_offset": std_offset_h, 149 + "dst_offset": dst_offset_h, 150 + "start_utc": start_utc, 151 + "end_utc": end_utc, 152 + "names": [] 153 + } 154 + # Add city name if not already present 155 + if city_name not in processed_zones[key_offset]["names"]: 156 + processed_zones[key_offset]["names"].append(city_name) 157 + 158 + # Convert dict values to a list and sort by standard offset 159 + tz_data_list = sorted(processed_zones.values(), key=lambda x: x["std_offset"]) 160 + print(f"Generated data for {len(tz_data_list)} unique standard offsets.") 161 + 162 + # --- C Code Generation --- 163 + # Build C code string (no obvious dead code here) 164 + c_code = "// Generated by Python script using zoneinfo\n" 165 + c_code += f"// Includes Standard & DST offsets and UTC transition timestamps for {target_year}.\n" 166 + c_code += "// WARNING: DST rules accurate only for the generated year.\n\n" 167 + # Include stdint.h for int64_t type used below 168 + c_code += "#include <stdint.h>\n\n" 169 + c_code += "// Holds a single city name string\n" 170 + c_code += "typedef struct {\n" 171 + c_code += " const char* name;\n" 172 + c_code += "} TzCityName;\n\n" 173 + c_code += "// Holds offset info and points to an array of names\n" 174 + c_code += "typedef struct {\n" 175 + c_code += " float std_offset_hours; // Offset during standard time\n" 176 + c_code += " float dst_offset_hours; // Offset during daylight time (if applicable)\n" 177 + # Using int64_t for Pebble time_t safety, check SDK if 32-bit preferred 178 + c_code += " int64_t dst_start_utc; // UTC timestamp (time_t) for DST start (0 if N/A)\n" 179 + c_code += " int64_t dst_end_utc; // UTC timestamp (time_t) for DST end (0 if N/A)\n" 180 + c_code += " const TzCityName* names; // Pointer to the array of names\n" 181 + c_code += " int name_count; // How many names are in the array\n" 182 + c_code += "} TzInfo;\n\n" 183 + 184 + # Generate the static arrays of names first 185 + name_array_definitions = "" 186 + unique_names_id = 0 187 + for zone_data in tz_data_list: 188 + c_array_name = f"tz_names_{unique_names_id}" 189 + zone_data["c_array_name"] = c_array_name # Store for later use 190 + unique_names_id += 1 191 + name_array_definitions += f"static const TzCityName {c_array_name}[] = {{\n" 192 + # Sort names alphabetically for consistency 193 + for name in sorted(zone_data["names"]): 194 + name_array_definitions += f" {{ \"{name}\" }},\n" 195 + name_array_definitions += "};\n\n" 196 + c_code += name_array_definitions 197 + 198 + # Generate the main tz_list array 199 + c_code += "// Main list mapping offsets/DST info to their respective name arrays\n" 200 + c_code += "static const TzInfo tz_list[] = {\n" 201 + for zone_data in tz_data_list: 202 + c_array_name = zone_data["c_array_name"] 203 + name_count = len(zone_data["names"]) 204 + # Use 'LL' suffix for int64_t timestamp constants in C 205 + c_code += (f" {{ {zone_data['std_offset']:.2f}f, {zone_data['dst_offset']:.2f}f, " 206 + f"{zone_data['start_utc']}LL, {zone_data['end_utc']}LL, " 207 + f"{c_array_name}, {name_count} }},\n") 208 + 209 + c_code += "};\n\n" 210 + c_code += f"#define TZ_LIST_COUNT {len(tz_data_list)}\n" 211 + 212 + return c_code 213 + 214 + # --- Main execution --- 215 + if __name__ == "__main__": 216 + c_code_output = generate_tz_list_c_code() 217 + output_filename = "src/c/tz_list.c" # Output path 218 + try: 219 + with open(output_filename, "w") as f: 220 + f.write(c_code_output) 221 + print(f"\nSuccessfully written timezone data (with accurate DST timestamps) to {output_filename}") 222 + except IOError as e: 223 + print(f"\nError: Could not write to file {output_filename}: {e}")
+677
src/c/tz_list.c
··· 1 + // Generated by Python script using zoneinfo 2 + // Includes Standard & DST offsets and UTC transition timestamps for 2025. 3 + // WARNING: DST rules accurate only for the generated year. 4 + 5 + #include <stdint.h> 6 + 7 + // Holds a single city name string 8 + typedef struct { 9 + const char* name; 10 + } TzCityName; 11 + 12 + // Holds offset info and points to an array of names 13 + typedef struct { 14 + float std_offset_hours; // Offset during standard time 15 + float dst_offset_hours; // Offset during daylight time (if applicable) 16 + int64_t dst_start_utc; // UTC timestamp (time_t) for DST start (0 if N/A) 17 + int64_t dst_end_utc; // UTC timestamp (time_t) for DST end (0 if N/A) 18 + const TzCityName* names; // Pointer to the array of names 19 + int name_count; // How many names are in the array 20 + } TzInfo; 21 + 22 + static const TzCityName tz_names_0[] = { 23 + { "Midway" }, 24 + { "Niue" }, 25 + { "Pago Pago" }, 26 + { "Samoa" }, 27 + }; 28 + 29 + static const TzCityName tz_names_1[] = { 30 + { "Adak" }, 31 + { "Aleutian" }, 32 + { "Atka" }, 33 + { "Hawaii" }, 34 + { "Honolulu" }, 35 + { "Johnston" }, 36 + { "Rarotonga" }, 37 + { "Tahiti" }, 38 + }; 39 + 40 + static const TzCityName tz_names_2[] = { 41 + { "Marquesas" }, 42 + }; 43 + 44 + static const TzCityName tz_names_3[] = { 45 + { "Alaska" }, 46 + { "Anchorage" }, 47 + { "Gambier" }, 48 + { "Juneau" }, 49 + { "Metlakatla" }, 50 + { "Nome" }, 51 + { "Sitka" }, 52 + { "Yakutat" }, 53 + }; 54 + 55 + static const TzCityName tz_names_4[] = { 56 + { "BajaNorte" }, 57 + { "Ensenada" }, 58 + { "Los Angeles" }, 59 + { "Pacific" }, 60 + { "Pitcairn" }, 61 + { "Santa Isabel" }, 62 + { "Tijuana" }, 63 + { "Vancouver" }, 64 + }; 65 + 66 + static const TzCityName tz_names_5[] = { 67 + { "Arizona" }, 68 + { "BajaSur" }, 69 + { "Boise" }, 70 + { "Cambridge Bay" }, 71 + { "Ciudad Juarez" }, 72 + { "Creston" }, 73 + { "Dawson" }, 74 + { "Dawson Creek" }, 75 + { "Denver" }, 76 + { "Edmonton" }, 77 + { "Fort Nelson" }, 78 + { "Hermosillo" }, 79 + { "Inuvik" }, 80 + { "Mazatlan" }, 81 + { "Mountain" }, 82 + { "Phoenix" }, 83 + { "Shiprock" }, 84 + { "Whitehorse" }, 85 + { "Yellowknife" }, 86 + { "Yukon" }, 87 + }; 88 + 89 + static const TzCityName tz_names_6[] = { 90 + { "Bahia Banderas" }, 91 + { "Belize" }, 92 + { "Beulah" }, 93 + { "Center" }, 94 + { "Central" }, 95 + { "Chicago" }, 96 + { "Chihuahua" }, 97 + { "Costa Rica" }, 98 + { "Easter" }, 99 + { "EasterIsland" }, 100 + { "El Salvador" }, 101 + { "Galapagos" }, 102 + { "General" }, 103 + { "Guatemala" }, 104 + { "Indiana-Starke" }, 105 + { "Knox" }, 106 + { "Knox IN" }, 107 + { "Managua" }, 108 + { "Matamoros" }, 109 + { "Menominee" }, 110 + { "Merida" }, 111 + { "Mexico City" }, 112 + { "Monterrey" }, 113 + { "New Salem" }, 114 + { "Ojinaga" }, 115 + { "Rainy River" }, 116 + { "Rankin Inlet" }, 117 + { "Regina" }, 118 + { "Resolute" }, 119 + { "Saskatchewan" }, 120 + { "Swift Current" }, 121 + { "Tegucigalpa" }, 122 + { "Tell City" }, 123 + { "Winnipeg" }, 124 + }; 125 + 126 + static const TzCityName tz_names_7[] = { 127 + { "Acre" }, 128 + { "Atikokan" }, 129 + { "Bogota" }, 130 + { "Cancun" }, 131 + { "Cayman" }, 132 + { "Coral Harbour" }, 133 + { "Detroit" }, 134 + { "East-Indiana" }, 135 + { "Eastern" }, 136 + { "Eirunepe" }, 137 + { "Fort Wayne" }, 138 + { "Grand Turk" }, 139 + { "Guayaquil" }, 140 + { "Havana" }, 141 + { "Indianapolis" }, 142 + { "Iqaluit" }, 143 + { "Jamaica" }, 144 + { "Lima" }, 145 + { "Louisville" }, 146 + { "Marengo" }, 147 + { "Michigan" }, 148 + { "Monticello" }, 149 + { "Montreal" }, 150 + { "Nassau" }, 151 + { "New York" }, 152 + { "Nipigon" }, 153 + { "Panama" }, 154 + { "Pangnirtung" }, 155 + { "Petersburg" }, 156 + { "Port-au-Prince" }, 157 + { "Porto Acre" }, 158 + { "Rio Branco" }, 159 + { "Thunder Bay" }, 160 + { "Toronto" }, 161 + { "Vevay" }, 162 + { "Vincennes" }, 163 + { "Winamac" }, 164 + }; 165 + 166 + static const TzCityName tz_names_8[] = { 167 + { "Anguilla" }, 168 + { "Antigua" }, 169 + { "Aruba" }, 170 + { "Atlantic" }, 171 + { "Barbados" }, 172 + { "Bermuda" }, 173 + { "Blanc-Sablon" }, 174 + { "Boa Vista" }, 175 + { "Campo Grande" }, 176 + { "Caracas" }, 177 + { "Continental" }, 178 + { "Cuiaba" }, 179 + { "Curacao" }, 180 + { "Dominica" }, 181 + { "Glace Bay" }, 182 + { "Goose Bay" }, 183 + { "Grenada" }, 184 + { "Guadeloupe" }, 185 + { "Guyana" }, 186 + { "Halifax" }, 187 + { "Kralendijk" }, 188 + { "La Paz" }, 189 + { "Lower Princes" }, 190 + { "Manaus" }, 191 + { "Marigot" }, 192 + { "Martinique" }, 193 + { "Moncton" }, 194 + { "Montserrat" }, 195 + { "Port of Spain" }, 196 + { "Porto Velho" }, 197 + { "Puerto Rico" }, 198 + { "Santiago" }, 199 + { "Santo Domingo" }, 200 + { "St Barthelemy" }, 201 + { "St Kitts" }, 202 + { "St Lucia" }, 203 + { "St Thomas" }, 204 + { "St Vincent" }, 205 + { "Thule" }, 206 + { "Tortola" }, 207 + { "Virgin" }, 208 + { "West" }, 209 + }; 210 + 211 + static const TzCityName tz_names_9[] = { 212 + { "Newfoundland" }, 213 + { "St Johns" }, 214 + }; 215 + 216 + static const TzCityName tz_names_10[] = { 217 + { "Araguaina" }, 218 + { "Asuncion" }, 219 + { "Bahia" }, 220 + { "Belem" }, 221 + { "Buenos Aires" }, 222 + { "Catamarca" }, 223 + { "Cayenne" }, 224 + { "ComodRivadavia" }, 225 + { "Cordoba" }, 226 + { "East" }, 227 + { "Fortaleza" }, 228 + { "Jujuy" }, 229 + { "La Rioja" }, 230 + { "Maceio" }, 231 + { "Mendoza" }, 232 + { "Miquelon" }, 233 + { "Montevideo" }, 234 + { "Palmer" }, 235 + { "Paramaribo" }, 236 + { "Punta Arenas" }, 237 + { "Recife" }, 238 + { "Rio Gallegos" }, 239 + { "Rosario" }, 240 + { "Rothera" }, 241 + { "Salta" }, 242 + { "San Juan" }, 243 + { "San Luis" }, 244 + { "Santarem" }, 245 + { "Sao Paulo" }, 246 + { "Stanley" }, 247 + { "Tucuman" }, 248 + { "Ushuaia" }, 249 + }; 250 + 251 + static const TzCityName tz_names_11[] = { 252 + { "DeNoronha" }, 253 + { "Godthab" }, 254 + { "Noronha" }, 255 + { "Nuuk" }, 256 + { "Scoresbysund" }, 257 + { "South Georgia" }, 258 + }; 259 + 260 + static const TzCityName tz_names_12[] = { 261 + { "Azores" }, 262 + { "Cape Verde" }, 263 + }; 264 + 265 + static const TzCityName tz_names_13[] = { 266 + { "Abidjan" }, 267 + { "Accra" }, 268 + { "Bamako" }, 269 + { "Banjul" }, 270 + { "Belfast" }, 271 + { "Bissau" }, 272 + { "Canary" }, 273 + { "Casablanca" }, 274 + { "Conakry" }, 275 + { "Dakar" }, 276 + { "Danmarkshavn" }, 277 + { "Dublin" }, 278 + { "El Aaiun" }, 279 + { "Faeroe" }, 280 + { "Faroe" }, 281 + { "Freetown" }, 282 + { "Guernsey" }, 283 + { "Isle of Man" }, 284 + { "Jersey" }, 285 + { "Lisbon" }, 286 + { "Lome" }, 287 + { "London" }, 288 + { "Madeira" }, 289 + { "Monrovia" }, 290 + { "Nouakchott" }, 291 + { "Ouagadougou" }, 292 + { "Reykjavik" }, 293 + { "Sao Tome" }, 294 + { "St Helena" }, 295 + { "Timbuktu" }, 296 + { "Troll" }, 297 + }; 298 + 299 + static const TzCityName tz_names_14[] = { 300 + { "Algiers" }, 301 + { "Amsterdam" }, 302 + { "Andorra" }, 303 + { "Bangui" }, 304 + { "Belgrade" }, 305 + { "Berlin" }, 306 + { "Bratislava" }, 307 + { "Brazzaville" }, 308 + { "Brussels" }, 309 + { "Budapest" }, 310 + { "Busingen" }, 311 + { "Ceuta" }, 312 + { "Copenhagen" }, 313 + { "Douala" }, 314 + { "Gibraltar" }, 315 + { "Jan Mayen" }, 316 + { "Kinshasa" }, 317 + { "Lagos" }, 318 + { "Libreville" }, 319 + { "Ljubljana" }, 320 + { "Longyearbyen" }, 321 + { "Luanda" }, 322 + { "Luxembourg" }, 323 + { "Madrid" }, 324 + { "Malabo" }, 325 + { "Malta" }, 326 + { "Monaco" }, 327 + { "Ndjamena" }, 328 + { "Niamey" }, 329 + { "Oslo" }, 330 + { "Paris" }, 331 + { "Podgorica" }, 332 + { "Porto-Novo" }, 333 + { "Prague" }, 334 + { "Rome" }, 335 + { "San Marino" }, 336 + { "Sarajevo" }, 337 + { "Skopje" }, 338 + { "Stockholm" }, 339 + { "Tirane" }, 340 + { "Tunis" }, 341 + { "Vaduz" }, 342 + { "Vatican" }, 343 + { "Vienna" }, 344 + { "Warsaw" }, 345 + { "Zagreb" }, 346 + { "Zurich" }, 347 + }; 348 + 349 + static const TzCityName tz_names_15[] = { 350 + { "Athens" }, 351 + { "Beirut" }, 352 + { "Blantyre" }, 353 + { "Bucharest" }, 354 + { "Bujumbura" }, 355 + { "Cairo" }, 356 + { "Chisinau" }, 357 + { "Famagusta" }, 358 + { "Gaborone" }, 359 + { "Gaza" }, 360 + { "Harare" }, 361 + { "Hebron" }, 362 + { "Helsinki" }, 363 + { "Jerusalem" }, 364 + { "Johannesburg" }, 365 + { "Juba" }, 366 + { "Kaliningrad" }, 367 + { "Khartoum" }, 368 + { "Kiev" }, 369 + { "Kigali" }, 370 + { "Kyiv" }, 371 + { "Lubumbashi" }, 372 + { "Lusaka" }, 373 + { "Maputo" }, 374 + { "Mariehamn" }, 375 + { "Maseru" }, 376 + { "Mbabane" }, 377 + { "Nicosia" }, 378 + { "Riga" }, 379 + { "Sofia" }, 380 + { "Tallinn" }, 381 + { "Tel Aviv" }, 382 + { "Tiraspol" }, 383 + { "Tripoli" }, 384 + { "Uzhgorod" }, 385 + { "Vilnius" }, 386 + { "Windhoek" }, 387 + { "Zaporozhye" }, 388 + }; 389 + 390 + static const TzCityName tz_names_16[] = { 391 + { "Addis Ababa" }, 392 + { "Aden" }, 393 + { "Amman" }, 394 + { "Antananarivo" }, 395 + { "Asmara" }, 396 + { "Asmera" }, 397 + { "Baghdad" }, 398 + { "Bahrain" }, 399 + { "Comoro" }, 400 + { "Damascus" }, 401 + { "Dar es Salaam" }, 402 + { "Djibouti" }, 403 + { "Istanbul" }, 404 + { "Kampala" }, 405 + { "Kirov" }, 406 + { "Kuwait" }, 407 + { "Mayotte" }, 408 + { "Minsk" }, 409 + { "Mogadishu" }, 410 + { "Moscow" }, 411 + { "Nairobi" }, 412 + { "Qatar" }, 413 + { "Riyadh" }, 414 + { "Simferopol" }, 415 + { "Syowa" }, 416 + { "Volgograd" }, 417 + }; 418 + 419 + static const TzCityName tz_names_17[] = { 420 + { "Tehran" }, 421 + }; 422 + 423 + static const TzCityName tz_names_18[] = { 424 + { "Astrakhan" }, 425 + { "Baku" }, 426 + { "Dubai" }, 427 + { "Mahe" }, 428 + { "Mauritius" }, 429 + { "Muscat" }, 430 + { "Reunion" }, 431 + { "Samara" }, 432 + { "Saratov" }, 433 + { "Tbilisi" }, 434 + { "Ulyanovsk" }, 435 + { "Yerevan" }, 436 + }; 437 + 438 + static const TzCityName tz_names_19[] = { 439 + { "Kabul" }, 440 + }; 441 + 442 + static const TzCityName tz_names_20[] = { 443 + { "Almaty" }, 444 + { "Aqtau" }, 445 + { "Aqtobe" }, 446 + { "Ashgabat" }, 447 + { "Ashkhabad" }, 448 + { "Atyrau" }, 449 + { "Dushanbe" }, 450 + { "Karachi" }, 451 + { "Kerguelen" }, 452 + { "Maldives" }, 453 + { "Mawson" }, 454 + { "Oral" }, 455 + { "Qostanay" }, 456 + { "Qyzylorda" }, 457 + { "Samarkand" }, 458 + { "Tashkent" }, 459 + { "Vostok" }, 460 + { "Yekaterinburg" }, 461 + }; 462 + 463 + static const TzCityName tz_names_21[] = { 464 + { "Calcutta" }, 465 + { "Colombo" }, 466 + { "Kolkata" }, 467 + }; 468 + 469 + static const TzCityName tz_names_22[] = { 470 + { "Kathmandu" }, 471 + { "Katmandu" }, 472 + }; 473 + 474 + static const TzCityName tz_names_23[] = { 475 + { "Bishkek" }, 476 + { "Chagos" }, 477 + { "Dacca" }, 478 + { "Dhaka" }, 479 + { "Kashgar" }, 480 + { "Omsk" }, 481 + { "Thimbu" }, 482 + { "Thimphu" }, 483 + { "Urumqi" }, 484 + }; 485 + 486 + static const TzCityName tz_names_24[] = { 487 + { "Cocos" }, 488 + { "Rangoon" }, 489 + { "Yangon" }, 490 + }; 491 + 492 + static const TzCityName tz_names_25[] = { 493 + { "Bangkok" }, 494 + { "Barnaul" }, 495 + { "Christmas" }, 496 + { "Davis" }, 497 + { "Ho Chi Minh" }, 498 + { "Hovd" }, 499 + { "Jakarta" }, 500 + { "Krasnoyarsk" }, 501 + { "Novokuznetsk" }, 502 + { "Novosibirsk" }, 503 + { "Phnom Penh" }, 504 + { "Pontianak" }, 505 + { "Saigon" }, 506 + { "Tomsk" }, 507 + { "Vientiane" }, 508 + }; 509 + 510 + static const TzCityName tz_names_26[] = { 511 + { "Brunei" }, 512 + { "Casey" }, 513 + { "Choibalsan" }, 514 + { "Chongqing" }, 515 + { "Chungking" }, 516 + { "Harbin" }, 517 + { "Hong Kong" }, 518 + { "Irkutsk" }, 519 + { "Kuala Lumpur" }, 520 + { "Kuching" }, 521 + { "Macao" }, 522 + { "Macau" }, 523 + { "Makassar" }, 524 + { "Manila" }, 525 + { "Perth" }, 526 + { "Shanghai" }, 527 + { "Singapore" }, 528 + { "Taipei" }, 529 + { "Ujung Pandang" }, 530 + { "Ulaanbaatar" }, 531 + { "Ulan Bator" }, 532 + { "West" }, 533 + }; 534 + 535 + static const TzCityName tz_names_27[] = { 536 + { "Eucla" }, 537 + }; 538 + 539 + static const TzCityName tz_names_28[] = { 540 + { "Chita" }, 541 + { "Dili" }, 542 + { "Jayapura" }, 543 + { "Khandyga" }, 544 + { "Palau" }, 545 + { "Pyongyang" }, 546 + { "Seoul" }, 547 + { "Tokyo" }, 548 + { "Yakutsk" }, 549 + }; 550 + 551 + static const TzCityName tz_names_29[] = { 552 + { "Adelaide" }, 553 + { "Broken Hill" }, 554 + { "Darwin" }, 555 + { "North" }, 556 + { "South" }, 557 + { "Yancowinna" }, 558 + }; 559 + 560 + static const TzCityName tz_names_30[] = { 561 + { "ACT" }, 562 + { "Brisbane" }, 563 + { "Canberra" }, 564 + { "Chuuk" }, 565 + { "Currie" }, 566 + { "DumontDUrville" }, 567 + { "Guam" }, 568 + { "Hobart" }, 569 + { "Lindeman" }, 570 + { "Macquarie" }, 571 + { "Melbourne" }, 572 + { "NSW" }, 573 + { "Port Moresby" }, 574 + { "Queensland" }, 575 + { "Saipan" }, 576 + { "Sydney" }, 577 + { "Tasmania" }, 578 + { "Truk" }, 579 + { "Ust-Nera" }, 580 + { "Victoria" }, 581 + { "Vladivostok" }, 582 + { "Yap" }, 583 + }; 584 + 585 + static const TzCityName tz_names_31[] = { 586 + { "LHI" }, 587 + { "Lord Howe" }, 588 + }; 589 + 590 + static const TzCityName tz_names_32[] = { 591 + { "Bougainville" }, 592 + { "Efate" }, 593 + { "Guadalcanal" }, 594 + { "Kosrae" }, 595 + { "Magadan" }, 596 + { "Norfolk" }, 597 + { "Noumea" }, 598 + { "Pohnpei" }, 599 + { "Ponape" }, 600 + { "Sakhalin" }, 601 + { "Srednekolymsk" }, 602 + }; 603 + 604 + static const TzCityName tz_names_33[] = { 605 + { "Anadyr" }, 606 + { "Auckland" }, 607 + { "Fiji" }, 608 + { "Funafuti" }, 609 + { "Kamchatka" }, 610 + { "Kwajalein" }, 611 + { "Majuro" }, 612 + { "McMurdo" }, 613 + { "Nauru" }, 614 + { "South Pole" }, 615 + { "Tarawa" }, 616 + { "Wake" }, 617 + { "Wallis" }, 618 + }; 619 + 620 + static const TzCityName tz_names_34[] = { 621 + { "Chatham" }, 622 + }; 623 + 624 + static const TzCityName tz_names_35[] = { 625 + { "Apia" }, 626 + { "Enderbury" }, 627 + { "Fakaofo" }, 628 + { "Kanton" }, 629 + { "Tongatapu" }, 630 + }; 631 + 632 + static const TzCityName tz_names_36[] = { 633 + { "Kiritimati" }, 634 + }; 635 + 636 + // Main list mapping offsets/DST info to their respective name arrays 637 + static const TzInfo tz_list[] = { 638 + { -11.00f, -11.00f, 0LL, 0LL, tz_names_0, 4 }, 639 + { -10.00f, -10.00f, 0LL, 0LL, tz_names_1, 8 }, 640 + { -9.50f, -9.50f, 0LL, 0LL, tz_names_2, 1 }, 641 + { -9.00f, -8.00f, 1741489200LL, 1762048800LL, tz_names_3, 8 }, 642 + { -8.00f, -7.00f, 1741489200LL, 1762048800LL, tz_names_4, 8 }, 643 + { -7.00f, -6.00f, 1741489200LL, 1762048800LL, tz_names_5, 20 }, 644 + { -6.00f, -6.00f, 0LL, 0LL, tz_names_6, 34 }, 645 + { -5.00f, -4.00f, 1741489200LL, 1762048800LL, tz_names_7, 37 }, 646 + { -4.00f, -3.00f, 1741489200LL, 1762048800LL, tz_names_8, 42 }, 647 + { -3.50f, -2.50f, 1741489200LL, 1762048800LL, tz_names_9, 2 }, 648 + { -3.00f, -3.00f, 0LL, 0LL, tz_names_10, 32 }, 649 + { -2.00f, -1.00f, 1743292800LL, 1761436800LL, tz_names_11, 6 }, 650 + { -1.00f, 0.00f, 1743296400LL, 1761440400LL, tz_names_12, 2 }, 651 + { 0.00f, 1.00f, 1743300000LL, 1761444000LL, tz_names_13, 31 }, 652 + { 1.00f, 2.00f, 1743303600LL, 1761447600LL, tz_names_14, 47 }, 653 + { 2.00f, 3.00f, 1745542800LL, 1761868800LL, tz_names_15, 38 }, 654 + { 3.00f, 3.00f, 0LL, 0LL, tz_names_16, 26 }, 655 + { 3.50f, 3.50f, 0LL, 0LL, tz_names_17, 1 }, 656 + { 4.00f, 4.00f, 0LL, 0LL, tz_names_18, 12 }, 657 + { 4.50f, 4.50f, 0LL, 0LL, tz_names_19, 1 }, 658 + { 5.00f, 5.00f, 0LL, 0LL, tz_names_20, 18 }, 659 + { 5.50f, 5.50f, 0LL, 0LL, tz_names_21, 3 }, 660 + { 5.75f, 5.75f, 0LL, 0LL, tz_names_22, 2 }, 661 + { 6.00f, 6.00f, 0LL, 0LL, tz_names_23, 9 }, 662 + { 6.50f, 6.50f, 0LL, 0LL, tz_names_24, 3 }, 663 + { 7.00f, 7.00f, 0LL, 0LL, tz_names_25, 15 }, 664 + { 8.00f, 8.00f, 0LL, 0LL, tz_names_26, 22 }, 665 + { 8.75f, 8.75f, 0LL, 0LL, tz_names_27, 1 }, 666 + { 9.00f, 9.00f, 0LL, 0LL, tz_names_28, 9 }, 667 + { 9.50f, 10.50f, 1759633200LL, 1743908400LL, tz_names_29, 6 }, 668 + { 10.00f, 10.00f, 0LL, 0LL, tz_names_30, 22 }, 669 + { 10.50f, 11.00f, 1759633200LL, 1743904800LL, tz_names_31, 2 }, 670 + { 11.00f, 11.00f, 0LL, 0LL, tz_names_32, 11 }, 671 + { 12.00f, 12.00f, 0LL, 0LL, tz_names_33, 13 }, 672 + { 12.75f, 13.75f, 1759032000LL, 1743912000LL, tz_names_34, 1 }, 673 + { 13.00f, 13.00f, 0LL, 0LL, tz_names_35, 5 }, 674 + { 14.00f, 14.00f, 0LL, 0LL, tz_names_36, 1 }, 675 + }; 676 + 677 + #define TZ_LIST_COUNT 37
+189 -12
src/c/watchface.c
··· 3 3 #include <string.h> 4 4 #include <stdint.h> 5 5 #include <time.h> // Required for time_t 6 + #include <math.h> // Required for fabsf 7 + #include <stdbool.h> // Required for bool type 8 + 9 + // --- Include Generated Timezone Data --- 10 + // This assumes tz_list.c defines TzInfo, tz_list[], and tz_list_count 11 + #include "tz_list.c" 6 12 7 13 // --- Constants --- 8 14 #define HOUR_LENGTH 3600 // Seconds in an hour ··· 13 19 static TextLayer *s_beat_layer; // Layer for Swatch Beat Time 14 20 static TextLayer *s_tid_layer; // Layer for TID Time (renamed from s_time_layer) 15 21 static TextLayer *s_noonzone_layer; // Layer for Noon Zone Time 22 + static TextLayer *s_closest_noon_layer; // Layer for Closest-to-Noon TZ 16 23 17 24 static const char S32_CHAR[] = "234567abcdefghijklmnopqrstuvwxyz"; 18 25 #define S32_CHAR_LEN (sizeof(S32_CHAR) - 1) // 32 ··· 229 236 last_noonzone_update_secs = current_seconds_utc; 230 237 } 231 238 239 + // --- Closest to Noon Timezone Code --- 240 + #define NOON_SECONDS (12 * 3600L) // Noon in seconds past midnight 241 + static char s_closest_noon_buffer[32]; // Buffer for "City Name:MM:SS\0" 242 + static int last_closest_noon_update_secs = -1; // Cache based on seconds 243 + static int last_closest_candidate_indices[TZ_LIST_COUNT]; // Cache indices of previous winners 244 + static int last_closest_candidate_count = 0; // Cache count of previous winners 245 + static const char* last_chosen_closest_name = NULL; // Cache the specific name pointer displayed 246 + 247 + /** 248 + * Comparison function for qsort (integers). 249 + */ 250 + static int compare_ints(const void *a, const void *b) { 251 + return (*(int*)a - *(int*)b); 252 + } 253 + 254 + /** 255 + * Checks if two arrays of integers (candidate indices) are identical. 256 + * Assumes arrays are sorted beforehand. 257 + */ 258 + static bool are_candidate_sets_equal(int* set1, int count1, int* set2, int count2) { 259 + if (count1 != count2) { 260 + return false; 261 + } 262 + // Assumes counts are equal now 263 + for (int i = 0; i < count1; ++i) { 264 + if (set1[i] != set2[i]) { 265 + return false; 266 + } 267 + } 268 + return true; 269 + } 270 + 271 + /** 272 + * Updates the Closest-to-Noon timezone TextLayer if the second has changed. 273 + */ 274 + static void update_closest_noon_time(time_t current_seconds_utc) { 275 + // --- Caching --- 276 + if (current_seconds_utc == last_closest_noon_update_secs) { 277 + return; 278 + } 279 + 280 + // --- Get Current UTC MM:SS --- 281 + struct tm *utc_tm = gmtime(&current_seconds_utc); 282 + if (!utc_tm) return; 283 + int utc_min = utc_tm->tm_min; 284 + int utc_sec = utc_tm->tm_sec; 285 + 286 + // --- Calculate UTC seconds past midnight --- 287 + // Note: tm_yday is 0-365, time_t is secs since epoch. Simpler to use modulo. 288 + uint32_t utc_seconds_today = current_seconds_utc % DAY_LENGTH; 289 + 290 + // --- Find Timezone Closest to Noon --- 291 + long min_diff_secs = DAY_LENGTH; // Init with impossibly large difference 292 + int current_candidate_indices[TZ_LIST_COUNT]; // Store indices of current winners 293 + int current_candidate_count = 0; 294 + 295 + for (int i = 0; i < TZ_LIST_COUNT; ++i) { 296 + // Calculate offset in seconds (handle float) 297 + long offset_seconds = (long)(tz_list[i].offset_hours * 3600.0f); 298 + 299 + // Calculate local time in seconds past midnight (handle wrap around DAY_LENGTH) 300 + long local_seconds_today = (long)utc_seconds_today + offset_seconds; 301 + // Modulo that handles negative results correctly 302 + local_seconds_today = (local_seconds_today % DAY_LENGTH + DAY_LENGTH) % DAY_LENGTH; 303 + 304 + // Calculate the shortest difference to noon (around the 24h clock) 305 + long diff1 = labs(local_seconds_today - NOON_SECONDS); // labs for long 306 + long current_min_diff = (diff1 <= DAY_LENGTH / 2) ? diff1 : DAY_LENGTH - diff1; 307 + 308 + // --- Update candidate list --- 309 + if (current_min_diff < min_diff_secs) { 310 + // New minimum found 311 + min_diff_secs = current_min_diff; 312 + current_candidate_indices[0] = i; 313 + current_candidate_count = 1; 314 + } else if (current_min_diff == min_diff_secs) { 315 + // Tie found, add to candidates (if space permits - should be fine) 316 + if (current_candidate_count < TZ_LIST_COUNT) { // Safety check 317 + current_candidate_indices[current_candidate_count++] = i; 318 + } 319 + } 320 + } 321 + 322 + // --- Select Final Timezone and Name --- 323 + const char *final_name = "???"; 324 + 325 + // Sort current candidates for comparison 326 + if (current_candidate_count > 1) { 327 + qsort(current_candidate_indices, current_candidate_count, sizeof(int), compare_ints); 328 + } 329 + 330 + // Check if the winning set is the same as last time 331 + bool reuse_last_name = false; 332 + if (last_chosen_closest_name != NULL) { // Only reuse if we have a previous name 333 + // Assume last_closest_candidate_indices is already sorted from previous run 334 + if (are_candidate_sets_equal(current_candidate_indices, current_candidate_count, 335 + last_closest_candidate_indices, last_closest_candidate_count)) { 336 + reuse_last_name = true; 337 + final_name = last_chosen_closest_name; // Tentatively reuse 338 + } 339 + } 340 + 341 + // If not reusing, or first time, perform selection 342 + if (!reuse_last_name) { 343 + if (current_candidate_count > 0) { 344 + int chosen_list_index; 345 + if (current_candidate_count == 1) { 346 + chosen_list_index = current_candidate_indices[0]; 347 + } else { 348 + // Randomly select among tied candidates 349 + chosen_list_index = current_candidate_indices[rand() % current_candidate_count]; 350 + } 351 + 352 + // Select random name if multiple exist for the chosen offset 353 + const TzInfo *chosen_tz = &tz_list[chosen_list_index]; 354 + if (chosen_tz->name_count > 0) { 355 + int name_index = (chosen_tz->name_count == 1) ? 0 : (rand() % chosen_tz->name_count); 356 + if (name_index < chosen_tz->name_count) { // Bounds check 357 + final_name = chosen_tz->names[name_index].name; 358 + } 359 + } 360 + 361 + // --- Update Cache for next time --- 362 + last_chosen_closest_name = final_name; // Cache the chosen name pointer 363 + // Copy current candidates to last candidates cache (already sorted) 364 + memcpy(last_closest_candidate_indices, current_candidate_indices, current_candidate_count * sizeof(int)); 365 + last_closest_candidate_count = current_candidate_count; 366 + 367 + } else { 368 + // Should not happen if tz_list is not empty, but handle defensively 369 + last_chosen_closest_name = NULL; // Reset cache if no candidates found 370 + last_closest_candidate_count = 0; 371 + } 372 + } // end selection block 373 + 374 + // --- Format Output and Update Layer --- 375 + // Use the determined final_name (either reused or newly selected) 376 + snprintf(s_closest_noon_buffer, sizeof(s_closest_noon_buffer), 377 + "%s:%02d:%02d", final_name, utc_min, utc_sec); 378 + 379 + text_layer_set_text(s_closest_noon_layer, s_closest_noon_buffer); 380 + 381 + // Cache the update time 382 + last_closest_noon_update_secs = current_seconds_utc; 383 + } 384 + 232 385 // --- TID Time Update --- 233 386 static char s_tid_buffer[14]; // Buffer for TID string (13 chars + null) 234 387 ··· 260 413 update_beat_time(seconds); 261 414 update_tid_time(seconds, milliseconds); 262 415 update_noonzone_time(seconds); 416 + update_closest_noon_time(seconds); 263 417 } 264 418 265 419 static void main_window_load(Window *window) { ··· 267 421 GRect bounds = layer_get_bounds(window_layer); 268 422 269 423 // Define layout constants (adjust heights based on font sizes) 270 - const int16_t v_padding = 2; // Vertical padding between layers 424 + const int16_t v_padding = 1; // Tighten padding for 4 layers 271 425 // Allocate heights roughly - adjust based on visual results 272 - const int16_t beat_h = 30; 273 - const int16_t noonzone_h = 30; 274 - const int16_t tid_h = 26; 426 + const int16_t beat_h = 28; 427 + const int16_t noonzone_h = 26; 428 + const int16_t closest_h = 26; 429 + const int16_t tid_h = 26; 275 430 // Calculate total height needed (excluding top/bottom margins provided by layer positioning) 276 - const int16_t total_inner_h = beat_h + noonzone_h + tid_h + 2 * v_padding; 431 + const int16_t total_inner_h = beat_h + noonzone_h + closest_h + tid_h + 3 * v_padding; 277 432 // Distribute remaining vertical space as top/bottom margin 278 433 const int16_t top_margin = (bounds.size.h - total_inner_h) / 2; 279 - const int16_t bottom_margin = bounds.size.h - total_inner_h - top_margin; 434 + 435 + // Font sizes (adjust as needed) 436 + #define BEAT_FONT FONT_KEY_GOTHIC_24_BOLD 437 + #define NOONZONE_FONT FONT_KEY_GOTHIC_18_BOLD // Smaller for longer name 438 + #define CLOSEST_FONT FONT_KEY_GOTHIC_18_BOLD // Smaller for city name 439 + #define TID_FONT FONT_KEY_GOTHIC_18_BOLD // Smaller TID 280 440 281 441 // Calculate Y positions 282 - int16_t beat_y = top_margin; 283 - int16_t noonzone_y = beat_y + beat_h + v_padding; 284 - int16_t tid_y = noonzone_y + noonzone_h + v_padding; 442 + int16_t current_y = top_margin; 443 + int16_t beat_y = current_y; 444 + current_y += beat_h + v_padding; 445 + int16_t noonzone_y = current_y; 446 + current_y += noonzone_h + v_padding; 447 + int16_t closest_y = current_y; 448 + current_y += closest_h + v_padding; 449 + int16_t tid_y = current_y; 285 450 286 451 // Create Beat Time TextLayer (Top) 287 452 s_beat_layer = text_layer_create( ··· 289 454 text_layer_set_background_color(s_beat_layer, GColorClear); 290 455 text_layer_set_text_color(s_beat_layer, GColorBlack); 291 456 text_layer_set_text(s_beat_layer, "@--.-"); // Initial placeholder 292 - text_layer_set_font(s_beat_layer, fonts_get_system_font(FONT_KEY_GOTHIC_28_BOLD)); // Adjust font as needed 457 + text_layer_set_font(s_beat_layer, fonts_get_system_font(BEAT_FONT)); 293 458 text_layer_set_text_alignment(s_beat_layer, GTextAlignmentCenter); 294 459 layer_add_child(window_layer, text_layer_get_layer(s_beat_layer)); 295 460 ··· 299 464 text_layer_set_background_color(s_noonzone_layer, GColorClear); 300 465 text_layer_set_text_color(s_noonzone_layer, GColorBlack); 301 466 text_layer_set_text(s_noonzone_layer, "ZONE:--:--"); // Initial placeholder 302 - text_layer_set_font(s_noonzone_layer, fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD)); // Adjust font 467 + text_layer_set_font(s_noonzone_layer, fonts_get_system_font(NOONZONE_FONT)); 303 468 text_layer_set_text_alignment(s_noonzone_layer, GTextAlignmentCenter); 304 469 layer_add_child(window_layer, text_layer_get_layer(s_noonzone_layer)); 305 470 471 + // Create Closest Noon Time TextLayer (Middle-Bottom) 472 + s_closest_noon_layer = text_layer_create( 473 + GRect(0, closest_y, bounds.size.w, closest_h)); 474 + text_layer_set_background_color(s_closest_noon_layer, GColorClear); 475 + text_layer_set_text_color(s_closest_noon_layer, GColorBlack); 476 + text_layer_set_text(s_closest_noon_layer, "City:--:--"); // Initial placeholder 477 + text_layer_set_font(s_closest_noon_layer, fonts_get_system_font(CLOSEST_FONT)); 478 + text_layer_set_text_alignment(s_closest_noon_layer, GTextAlignmentCenter); 479 + layer_add_child(window_layer, text_layer_get_layer(s_closest_noon_layer)); 480 + 306 481 // Create TID TextLayer (Bottom) 307 482 s_tid_layer = text_layer_create( 308 483 GRect(0, tid_y, bounds.size.w, tid_h)); 309 484 text_layer_set_background_color(s_tid_layer, GColorClear); 310 485 text_layer_set_text_color(s_tid_layer, GColorBlack); 311 486 text_layer_set_text(s_tid_layer, "loading tid..."); // Initial placeholder 312 - text_layer_set_font(s_tid_layer, fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD)); // Adjust font as needed 487 + text_layer_set_font(s_tid_layer, fonts_get_system_font(TID_FONT)); 313 488 text_layer_set_text_alignment(s_tid_layer, GTextAlignmentCenter); 314 489 layer_add_child(window_layer, text_layer_get_layer(s_tid_layer)); 315 490 } ··· 319 494 text_layer_destroy(s_beat_layer); 320 495 text_layer_destroy(s_tid_layer); // Renamed from s_time_layer 321 496 text_layer_destroy(s_noonzone_layer); 497 + text_layer_destroy(s_closest_noon_layer); 322 498 } 323 499 324 500 static void init() { ··· 340 516 update_beat_time(seconds); // Initial beat time 341 517 update_tid_time(seconds, milliseconds); // Initial TID time 342 518 update_noonzone_time(seconds); // Initial noon zone time 519 + update_closest_noon_time(seconds); // Initial closest noon time 343 520 344 521 345 522 // Register with TickTimerService to update every second