python/cpython · error · ValueError

timezone changed during initialization

Error message

timezone changed during initialization

What it means

Raised by _strptime.LocaleTime.__init__ after it finishes computing cached locale tables (weekday/month names, AM/PM, timezone data). At the end it re-reads time.tzname and time.daylight and compares them to the values captured at the start; if they differ, the cached tables would be inconsistent, so a ValueError is raised. It is a concurrency/environment-consistency guard, not a user-input error.

Source

Thrown at Lib/_strptime.py:117

        running.  The check here is done in case someone does not think about
        doing this.

        Only other possible issue is if someone changed the timezone and did
        not call tz.tzset .  That is an issue for the programmer, though,
        since changing the timezone is worthless without that call.

        """
        self.lang = _getlang()
        self.__calc_weekday()
        self.__calc_month()
        self.__calc_am_pm()
        self.__calc_alt_digits()
        self.__calc_timezone()
        self.__calc_date_time()
        if _getlang() != self.lang:
            raise ValueError("locale changed during initialization")
        if time.tzname != self.tzname or time.daylight != self.daylight:
            raise ValueError("timezone changed during initialization")

    def __calc_weekday(self):
        # Set self.a_weekday and self.f_weekday using the calendar
        # module.
        a_weekday = [calendar.day_abbr[i].lower() for i in range(7)]
        f_weekday = [calendar.day_name[i].lower() for i in range(7)]
        self.a_weekday = a_weekday
        self.f_weekday = f_weekday

    def __calc_month(self):
        # Set self.f_month and self.a_month using the calendar module.
        a_month = [calendar.month_abbr[i].lower() for i in range(13)]
        f_month = [calendar.month_name[i].lower() for i in range(13)]
        self.a_month = a_month
        self.f_month = f_month

    def __calc_am_pm(self):
        # Set self.am_pm by using time.strftime().

View on GitHub (pinned to bc6749cc3b)

Solutions

  1. Set the TZ environment variable and call time.tzset() once at process startup, before spawning worker threads that call strptime
  2. Retry the strptime() call once in an except ValueError block to let the cache rebuild consistently
  3. Serialize TZ/locale changes and strptime use behind a lock
  4. Pass explicit tz-aware parsing (e.g. fromisoformat or dateutil) instead of relying on the process timezone

Example fix

# before
os.environ['TZ'] = 'UTC'  # racing with strptime in other threads
time.tzset()

# after
os.environ['TZ'] = 'UTC'
time.tzset()  # do once at startup, before threads start parsing
Defensive patterns

Strategy: retry

Validate before calling

def safe_strptime(s, fmt, retries=2):
    for i in range(retries + 1):
        try:
            return _strptime_time(s, fmt)
        except ValueError as e:
            if 'changed during initialization' in str(e) and i < retries:
                continue
            raise

Try / catch

try:
    t = time.strptime(data, fmt)
except ValueError as e:
    if 'changed during initialization' in str(e):
        t = time.strptime(data, fmt)  # cache rebuilt consistently on 2nd call
    else:
        raise

Prevention

When it happens

Trigger: Another thread calls time.tzset() (or sets os.environ['TZ'] followed by tzset) while a strptime() call is rebuilding the _TimeRE_cache (e.g. first strptime call after locale/TZ change, or after cache invalidation because _getlang()/time.tzname/time.daylight no longer match the cache).

Common situations: Multithreaded servers or test suites where one thread changes the TZ environment variable or locale while other threads parse datetime strings with time.strptime or datetime.datetime.strptime; rare race on first use after a TZ change.

Related errors


AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14). Data as JSON: /api/errors/9b2ea74e4461e5db. Report an issue: GitHub.