pythonpython-3.xtimezonezoneinfotzdata

Get the canonical timezone name of a backward linked timezone in python?


As title said, I'm looking for a simple way to get the canonical name of a timezone when providing a timezone name.

For example Asia/Calcutta is the backward liked name of Asia/Kolkata.

I'd expect ZoneInfo("Asia/Calcutta").key to be Asia/Kolkata, but it's not the case and ZoneInfo doesn't have any apparent way to follow link of get the canonical name.

Is there any way to get this information? Either in ZoneInfo or directly in tzdata?


Solution

  • Finally found an answer, turns out there's a file tzdata/zoneinfo/tzdata.zi that contains amongst other things the data I needed.

    Links are formated this way L CANONICAL_NAME BACKWARD_NAME so I just parse the file to get the associations I need. I made a child class of ZoneInfo for it as it was the most convenient for my usage:

    import importlib.resources
    from zoneinfo import ZoneInfo
    
    
    class ZoneInfoCanonical(ZoneInfo):
        __backward  = {}
    
        @property
        def canonical(self) -> str:
            """
            :return: Canonical name of the timezone it one exist, or timezone name.
            """
            return self.backward_dict().get(self.key, self.key)
    
        @classmethod
        def backward_dict(cls) -> dict:
            """
            Build a list of backward timezones names with link to the canonical name.
            Store the list in class to avoid rebuilding it every time.
            :return: Dict with key(backward name) and value(canonical name).
            """
            # Load backward dict from tzdata files
            if not cls.__backward:
                # Path to the tzdata's backward file
                backward_file = importlib.resources.files("tzdata") / "zoneinfo" / "tzdata.zi"
    
                with backward_file.open("r") as file:
                    for line in file:
                        if line.startswith("L "):
                            canonical, backward = line.split()[1:3]
                            cls.__backward[backward] = canonical
    
            return cls.__backward