New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
time.mktime doesn't update time.tzname #66987
Comments
time.tzname is initialized from C tzname variable or tm_zone around Jan, Jul of the current year. If time.mktime() is called with a time tuple from the past/future then after the call time.tzname might be out-of-sync with the corresponding C tzname and tm_zone values. Because C mktime may change tzname, tm_zone values on some systems and time.mktime calls C mktime. I've attached test_mktime_changes_tzname.c file that demonstrates that mktime() may change tzname. |
I've attached test-timezone-info-is-updated.diff file -- a patch for Lib/test/test_time.py that demonstrates that time functions fail to update the timezone info. The test uses Europe/Moscow timezone but most timezones around the world had/will have different tzname/utc offset (unrelated to DST changes) in the past/future. |
One of the ways to fix this issue is to synchronize time.tzname I've uploaded sync-time-timezone-attr-with-c.diff patch that The patch also fixes "wrong time.timezone" issue The patch doesn't use tm_gmtoff, tm_zone C values from jan, jul to set I've removed the cygwin branch in the code (I haven't found cygwin |
C mktime itself should not change timezone globals, but it may indirectly if it calls tzset() and TZ changed between calls. I don't understand what this issue is about. Unlike C mktime, time.mktime is not documented to call time.tzset(). If you want to change time.tzname - you need to call time.tzset() after changing TZ. |
You should have run the attached test_mktime_changes_tzname.c which demonstrates that (at least on some systems) C mktime *does* change tzname global even if TZ envvar is constant in the test. Nowhere in my bug report I had mentioned different TZ values. I did mentioned *past* *future* dates -- the same timezone may have different utc offset, timezone abbreviations at different times. These timezone globals can change even if TZ is constant. |
I did run it and tried several times to understand what it does. It did not help. If you claim that calling mktime() changes the value of the global tzname variable, you should be able to demonstrate it in five lines of code: extern char **tzname;
tzset();
printf("%s %s\n", tzname[0], tzname[1]);
mktime(&tt);
printf("%s %s\n", tzname[0], tzname[1]); (plus a few lines to initialize tt) If the code above prints two different lines - file a bug report with your C library vendor. |
The C code produces correct values according to the tz database. If TZ=Europe/Moscow then The code calls C mktime() with corresponding time tuples If C code is incomprehensible; here's its Python analog: >>> import os
>>> os.environ['TZ'] = 'Europe/Moscow'
>>> import time
>>> time.tzset()
>>> time.mktime((2010,7,1,0,0,0,-1,-1,-1))
1277928000.0
>>> time.tzname #XXX expected ('MSK', 'MSD')
('MSK', 'MSK')
>>> time.mktime((2015,7,1,0,0,0,-1,-1,-1))
1435698000.0
>>> time.tzname
('MSK', 'MSK') C tzname changes on my machine after the corresponding C mktime() calls |
Did you run the code that I posted? If not - please do and report the results. (I think there is a valid issue behind all this, but you are not helping to reveal it. I suspect we can do better by just relying on system tzset() and not have the try June and January work-around. That's why I added Antoine to the "nosy" as the developer who last touched this code.) |
mktime() does change several global C runtime variables on Linux. See the man page on http://linux.die.net/man/3/mktime However, the Python documentation does not mention anything about having time.tzname being tied to the C lib global: https://docs.python.org/3.5/library/time.html#time.tzname So I don't think this qualifies as bug. Note that those global C variable are not thread-safe, so you can't really trust their values relating to anything you've just set using mktime() anyway. |
BTW: The most portable way to access the timezone name of a given local time is to use strftime() with %Z as format character. |
Akira, Would bpo-22798.diff patch address your issue? |
Is there any platform where mktime resets global tzname but does not provide tm_zone? If not, OP's issue is largely theoretical, but I still like MAL's suggestion of using strftime("%Z") as a fall-back. |
No. The issue is that C mktime() may update C tzname on some platforms Most of the functions defined in this module call platform C library --- Unrelated: time.strftime('%Z') and |
On 29.09.2015 11:31, Akira Li wrote:
tzname is set when the module is being loaded and not updated Note: The fact that tzset() does update the module globals is
The StackOverflow discussion targets %z (lower case z), I think this is just a documentation bug. Overall, I think that relying on those module globals is |
Marc-Andre Lemburg <report@bugs.python.org> writes:
Python docs recommend reading platform docs. Why wouldn't I assume that mktime() may change tzname?
It is documented e.g., I see: "These will be propagated into Though tzset() has nothing to do with the issue (TZ envvar hasn't been
Look at the answers. The examples clearly shows both %Z and %z.
Agree. Moreover, you can't trust them even "right after module import" Though, some predictability in the behavior such as "time.tzname is |
MAL> The fact that tzset() does update the module globals is See bpo-28130. |
I ran the attached test-mktime.c program on Linux and MacOS X with the following results (TZ=America/New_York): $ cat test-mktime.c
#include <time.h>
#include <stdio.h>
static void
print_globals(struct tm *tm)
{
printf("%04d-%02d: %s/%s (%s) %d %ld (%ld)\n",
1900 + tm->tm_year, 1 + tm->tm_mon,
tzname[0], tzname[1], tm->tm_zone?tm->tm_zone:"NULL",
daylight, timezone, -tm->tm_gmtoff);
}
int main() {
struct tm tm = {0, 0, 0, 1, 0, -100};
print_globals(&tm);
tzset();
print_globals(&tm);
mktime(&tm);
print_globals(&tm);
tm.tm_year = 43;
mktime(&tm);
print_globals(&tm);
tm.tm_year = 45;
tm.tm_mon = 8;
mktime(&tm);
print_globals(&tm);
} Linux: $ gcc test-mktime.c && ./a.out
1800-01: GMT/GMT (NULL) 0 0 (0)
1800-01: EST/EDT (NULL) 1 18000 (0)
1800-01: LMT/EDT (LMT) 1 18000 (17762)
1943-01: EST/EWT (EWT) 1 18000 (14400)
1945-09: EST/EPT (EPT) 1 18000 (14400) MacOS X: $ clang test-mktime.c && ./a.out
1800-01: WILDABBR/WILDABBR (NULL) 0 0 (0)
1800-01: EST/EDT (NULL) 1 18000 (0)
1800-01: EST/EDT (NULL) 1 18000 (0)
1943-01: EST/EWT (EWT) 1 18000 (14400)
1945-09: EST/EPT (EPT) 1 18000 (14400) Indeed, mktime changes tzname as a side effect, but the result is system dependent (see LMT/EDT vs. EST/EDT in the third line). Furthermore, the values of daylight and timezone variables do not get updated, resulting in an inconsistent state. For this reason, I don't think Python should expose the new values of tzname. People who really need access to those can use ctypes. |
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: