PEP 615: Support for the IANA Time Zone Database in the Standard Library

Thanks for the quick feedback! Here are my responses (roughly ordered in terms of how complicated the response is):

Yes, agreed (that’s how it’s implemented in the reference implementation, too). I have added it as an example.

Done.

Fixed, thanks.

This I will have to think about more deeply, but I think it’s a very valid way to go, and I’m leaning towards changing it to this behavior.

To be honest, it’s been a while since we came up with this scheme and I am not entirely sure what my justification for selecting “APPEND” only was, I think that it was by analogy to PYTHONPATH, which always appends to the search path. The initial discussion happened in this dateutil PR, and I did a twitter poll where 4 people said they would want to “augment” the path and 8 people said they would want to replace it.

As you can see from the “Open issues” section, I am quite ambivalent about the whole thing, but if I were to take a stab at possible reasons why people would want to mess with their time zone path, I’d say:

  1. Replace: You would want to do this for testing purposes (I would use it to test against master of the time zone database, for example). You would also want to use this if you deploy your time zones somewhere non-standard but you are not compiling your own Python (and thus can’t use the compile-time argument).
  2. Prepend: You have deployed some time zones somewhere and would like to preferentially use them, but if a zone is missing or something you’d like to fall back to the standard search path.
  3. Append: You have deployed your own custom time zones not in the IANA database, for your own purposes (again possibly testing purposes), and you’d like this to be the fallback location to look.

With more consideration, I am thinking that option 2 is more likely to be a reasonable use case than option 3, though neither of those seems terribly likely to be useful.

I am hesitant to say that these would be completely usless, but maybe these things are so unlikely to be useful that if you want them, it will be sufficient to do: PYTHONPATH=/my/path:$(python -c "import os; import zoneinfo; print(os.pathsep.join(zoneinfo.TZPATH))"?

Yeah, that is a good point. I think I originally conceived of this as an analogy to time.tzset, which actually resets the path.

Alternatively, I could rename set_tzpath(tzpaths=None) to something like reset_tzpath(to=DEFAULT).

Yes, this was actually my original design and I think it’s still on the table, but I went with the global state because it complicated the implementation and semantics of the cache.

Assuming we went with a design like ZoneInfo.from_key(key, *, tzpath=None), the issue would be that there are three options for how the cache would work, all unpalatable:

  1. keep track of a per-tzpath cache - this would be complicated to implement and there are a lot of issues with getting the semantics of that right.
  2. passing tzpath would necessarily mean that you are bypassing the cache
  3. passing tzpath uses the global cache, which means that sometimes ZoneInfo.from_key would use the specified tzpath and sometimes it wouldn’t (and also that using ZoneInfo.from_key could “pollute” your normal cache).

The most common use case I imagine for this sort of feature would be if you want to force your ZoneInfo calls to use tzdata globally for some reason - either for testing purposes or because for some reason you are prevented from using the environment variables. In that case, you would need to modify the constructor calls for anything that uses ZoneInfo, and in some of these options (notably #2), it would have an affect on the semantics of the operations!