regebro / tzlocal

A Python module that tries to figure out what your local timezone is

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

…offset does not match… exception when files exist that are meaningless to current platform

stevecj opened this issue · comments

Currently, the code in unix.py for get_localzone() assumes that if any of several platform-specific files exists, then it is actually relevant and appropriate to use. That turns out to be a pretty unsafe assumption though.

It is very common, for instance, for a /etc/sysconfig/clock file to exist on a CentOS 7 system because someone assumed that what used to work on CentOS 6 (based on Sys-V) still applies to CentOS 7 (which is based on systemd) as well. If the system's timezone is actually still UTC because that didn't work, or if the system's timezone is later changed to one that no longer matches what the junk /etc/sysconfig/clock file says, then calling get_localzone() causes ValueError: Timezone offset does not match system offset: <value 1> != <value 2>. Please, check your config files.

I would be willing to work on a pull request to improve that.

My suggestion is to check whether the system is based on systemd and to then only check either the files that are relevant for systemd or those relevant to other init systems (Sys-V, etc.) If it is not possible to determine whether the system is based on systemd, then check all files as we do now.

is_systemd = None
try:
    init_ps = next(
        (ps for ps in psutil.process_iter(['pid', 'name']) if ps.pid >= 1),
        None)
    if init_ps:
        is_systemd = init_ps.name == 'systemd'
except Exception:
    pass

This kind of implementation would require adding a dependency on the psutil package. Another option would be to run the ps command in a subprocess.

So, firstly: If your system is misconfigured, then tzlocal would return the incorrect result. The check that the predicted offset matches the actual offset is the sanity check of this. tzlocal will not and can not support misconfigured systems, because that amounts to guessing which of several configurations is correct, and down that path lies dragons.

However, I just thought about another option: Maybe tzlocal shouldn't just find a timezone configuration. Maybe it should look at all configurations it can find, and complain if they don't agree? Because those checks does not take a long time, it's just looking at the contents of a few files, and one symlink.

Having an error message to the effect of "Your /etc/timezone says Europe/Paris, but your /etc/localtime points to America/New_York, please fix your configuration" seems doable and reasonable.

I guess it might be a matter of semantics, but if a file exists that the platform will pay no attention to, then is the system actually misconfigured?

In any case, I think it is fine to check all and report warnings for files that are not applicable. I don't think an exception should be raised in that case though, so long as everything is good with the files that are applicable to the platform — especially since, as it stands today, it is not at all clear what is actually wrong based on the exception.

It took us several days of back and forth with a customer in a different timezone along with careful analysis of the tzlocal source code to figure out what was wrong on our customer's system. One of my teammates missed out on part of a family vacation for that.

There will also be cases where the person experiencing the problem has insufficient permissions on the system to remove an offending file.

From the view of tzlocal it is misconfigured, as there are multiple configuration files that contradict each other. Trying to map exactly which distribution of Linux uses which configuration files is a maintenance nightmare I'm not going to get into.

You can always override the configuration by setting a TZ environment variable. Then tzlocal will use that.

It took us several days of back and forth with a customer in a different timezone along with careful analysis of the tzlocal source code to figure out what was wrong on our customer's system. One of my teammates missed out on part of a family vacation for that.

Yeah, I definitely need to have that sort of explicit check of all configurations, then.

Regarding

Having an error message to the effect of "Your /etc/timezone says Europe/Paris, but your /etc/localtime points to America/New_York, please fix your configuration" seems doable and reasonable.

If I understand that correctly, you're saying to run all the checks and then report when any of them disagree with one another. Is that right? I like that idea.

The implementation might get a little hairy around the calls to assert_tz_offset() since we would need to defer those checks until after the checks for multiple sources of the zone value are done.

Maybe build a list of objects with source_description, zone, and requires_offset_assertion properties. If the list has contradictory entries, then report that. If it has only 1 entry, then do the assertion on it if applicable.

What if multiple sources are found and they are in agreement? Maybe raise a warning about that but not an exception?

I released a 4.0a1 that implements this. And it only complains if the sources are in agreement. I currently don't test if there are offset differences, only if the name is different (except for GMT, UTC and Etc/UTC which are changed just to UTC). This is partly because you can have time zones that have the same offset during winter and different during summer, and then it would surprise fail on you when DST hits.

If only one config agrees with the actual offset, I could theoretically pick that one and then only warn about the rest, but that could again surprise you at a DST change... So I think it's better to just fail.

That's great! The only other thing I might add is to pass the config keys and value to assert_tz_offset and include that info in the exception message if the assertion fails.

Thanks very much for making these changes. I'm sure that maintaining this is not the only thing you are doing. :)

Do you agree or disagree with the above suggestion. If you agree that it is good, I would be willing to work on a PR.

The reason for my suggestion is that then if someone can still find a way to encounter that exception, they'll have some info to use to start tracking down the cause.

I'm not sure the offset is useful, it's not used, only the timezone name is used, so I'm thinking it might be confusing. You could for example have a configuration with Europe/Stockholm in one place and Europe/Paris in another, and that would be wrong, but the offset would be the same.

Fixed offsets like GMT+2 and UTC+2 should probably be seen as synonyms though.

Turns out ZoneInfo doesn't even have that information readily available, so it's a lot of code to get information that probably isn't useful. I updated the synonym handling a bit, and will release this as a beta.

Beta's with this refactoring is out now.

And 4.0 is released, so I'm closing this.