{"record":{"id":"df5a1244abf3d1f8","repo":"666ghj/MiroFish","slug":"history-timezone-must-be-utc","errorCode":null,"errorMessage":"history timezone must be UTC","messagePattern":"history timezone must be UTC","errorType":"exception","errorClass":"StarHistoryError","httpStatus":null,"severity":"error","filePath":"scripts/star_history.py","lineNumber":364,"sourceCode":"        raise StarHistoryError(\"history state must be a JSON object\")\n    _expect_keys(\n        state,\n        {\n            \"schema_version\",\n            \"repository\",\n            \"timezone\",\n            \"ongoing_interval_days\",\n            \"reconstruction\",\n            \"snapshots\",\n        },\n        \"history state\",\n    )\n    if state[\"schema_version\"] != 1 or type(state[\"schema_version\"]) is not int:\n        raise StarHistoryError(\"unsupported history schema_version\")\n    if state[\"repository\"] != REPOSITORY:\n        raise StarHistoryError(\"history repository does not match configured repository\")\n    if state[\"timezone\"] != \"UTC\":\n        raise StarHistoryError(\"history timezone must be UTC\")\n    if (\n        state[\"ongoing_interval_days\"] != INTERVAL_DAYS\n        or type(state[\"ongoing_interval_days\"]) is not int\n    ):\n        raise StarHistoryError(\n            f\"history interval must be exactly {INTERVAL_DAYS} days\"\n        )\n\n    reconstruction = state[\"reconstruction\"]\n    if not isinstance(reconstruction, dict):\n        raise StarHistoryError(\"reconstruction must be an object\")\n    _expect_keys(\n        reconstruction,\n        {\"method\", \"generated_at\", \"daily\"},\n        \"reconstruction\",\n    )\n    reconstruction_method = reconstruction[\"method\"]\n    if reconstruction_method not in {","sourceCodeStart":346,"sourceCodeEnd":382,"githubUrl":"https://github.com/666ghj/MiroFish/blob/b5b53acc57189a4a42e44a23e149dc655c98fe82/scripts/star_history.py#L346-L382","documentation":"validate_state requires state['timezone'] to be exactly the string 'UTC'; anything else raises StarHistoryError('history timezone must be UTC'). All stored timestamps use the strict Z-suffix UTC format and all bucket math assumes UTC, so a state declaring another zone would be internally inconsistent.","triggerScenarios":"State file with timezone: 'utc', 'GMT', 'Etc/UTC', '+00:00', or an actual local zone like 'America/New_York' — usually from hand-editing or a third-party writer.","commonSituations":"Someone 'localized' the state file by editing the timezone field; a fork added timezone support but ran against the strict validator.","solutions":["Set the field back to exactly 'UTC' and ensure all timestamps in the file are Z-suffixed UTC","Or regenerate the state file from scratch","Keep all timezone conversion at the presentation layer, not in the state file"],"exampleFix":"// before (state file)\n\"timezone\": \"America/New_York\"\n\n// after (state file)\n\"timezone\": \"UTC\"","handlingStrategy":"validation","validationCode":"if state.get(\"timezone\") != \"UTC\":\n    raise StarHistoryError(f\"state timezone {state.get('timezone')!r} unsupported; must be 'UTC'\")","typeGuard":"def state_timezone_is_utc(state: object) -> TypeGuard[dict]:\n    return isinstance(state, dict) and state.get(\"timezone\") == \"UTC\"","tryCatchPattern":"try:\n    validate_state(state)\nexcept StarHistoryError as exc:\n    if \"timezone must be UTC\" in str(exc):\n        regenerate_state_file()\n    else:\n        raise","preventionTips":["Do localization at display time only; persisted state is always UTC","Use the literal string 'UTC' — 'utc', 'GMT', or offsets are rejected"],"tags":["state-file","timezone","utc","validation","star-history"],"backgroundTag":null,"analyzedSha":"b5b53acc57189a4a42e44a23e149dc655c98fe82","analyzedAt":"2026-08-14T22:29:33.146Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}