There's code in tzcode (and, I suspect not coincidentally, glibc's localtime implementation as well) to not call the internal version of tzset on calls to localtime_r, but rather only on calls to localtime. The justification is that Posix says localtime_r doesn't need to update the global tzname variable. I don't have a copy of the Posix spec handy, so I can't check: does Posix also say that localtime_r doesn't need to check for and make use of a possibly-changed value of TZ? As implemented (both in tzcode and glibc), it doesn't. If a program calls localtime, then calls setenv to set a new value of TZ, then calls localtime again, it works as expected, but if it calls localtime_r, setenv, localtime_r, it doesn't. (The workaround, of course, is to call localtime_r, setenv, tzset, localtime_r.)
On Dec 15, 2017, at 12:37 AM, Steve Summit <scs@eskimo.com> wrote:
There's code in tzcode (and, I suspect not coincidentally, glibc's localtime implementation as well) to not call the internal version of tzset on calls to localtime_r, but rather only on calls to localtime. The justification is that Posix says localtime_r doesn't need to update the global tzname variable.
I don't have a copy of the Posix spec handy,
https://publications.opengroup.org/t101 You'll need to set up a free account to read it.
so I can't check: does Posix also say that localtime_r doesn't need to check for and make use of a possibly-changed value of TZ?
It doesn't say that, but it also doesn't say it *does* need to. The tzset() page: http://pubs.opengroup.org/onlinepubs/9699919799/functions/tzset.html says The tzset() function shall use the value of the environment variable TZ to set time conversion information used by ctime, localtime, mktime, and strftime. If TZ is absent from the environment, implementation-defined default timezone information shall be used. but says nothing about what localtime_r() uses. The informative (rather than normative) APPLICATION USAGE section of that page says Since the ctime(), localtime(), mktime(), strftime(), and strftime_l() functions are required to set timezone information as if by callingtzset(), there is no need for an explicit tzset() call before using these functions. However, portable applications should call tzset() explicitly before using ctime_r() or localtime_r() because setting timezone information is optional for those functions. which appears to imply that acting as if tzset() were called, and *not* acting as if tzset() were called, are both possible. The "Environment Variables" page: http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html says: TZ This variable shall represent timezone information. The contents of the environment variable named TZ shall be used by the ctime(), ctime_r(), localtime(), localtime_r() strftime(), mktime(), functions, and by various utilities, to override the default timezone. ... so, as I read the Single UNIX Specification, localtime_r() (not surprisingly) honors TZ, but does not necessarily honor *changes* in TZ unless you call tzset() before calling localtime_r(). (BTW, macOS Sierra wants to autocorrect "tzset" to "taser".)
On 12/15/2017 01:25 AM, Guy Harris wrote:
as I read the Single UNIX Specification, localtime_r() (not surprisingly) honors TZ, but does not necessarily honor*changes* in TZ unless you call tzset() before calling localtime_r().
Yes, that's my understanding too (and I agree POSIX is vague here). That is, as I understand it, although localtime_r and tzset are both required to lock each other out safely, localtime_r is not required to call tzset (though it is allowed to). Also, behavior is undefined if one thread calls tzset (perhaps via localtime, ugh) while another thread is modifying TZ or any other environment variable (either directly, or via putenv/setenv). Portable multithreaded programs need to call tzset before they start threads that care about time, and they should run all their threads in the same time zone. This limitation is fixed by localtime_rz etc., which is in the reference implementation and in NetBSD but not elsewhere (yet).
Guy Harris <guy@alum.mit.edu> wrote:
https://publications.opengroup.org/t101
You'll need to set up a free account to read it.
Or use the direct link: http://www.opengroup.org/onlinepubs/9699919799.2016edition/ Previous editions: http://www.opengroup.org/onlinepubs/9699919799.2013edition/nfindex.html http://www.opengroup.org/onlinepubs/9699919799.2008edition/nfindex.html http://www.opengroup.org/onlinepubs/007904975/nfindex.html http://www.opengroup.org/onlinepubs/007908799/ Tony. -- f.anthony.n.finch <dot@dotat.at> http://dotat.at/ - I xn--zr8h punycode Viking, North Utsire, South Utsire: Northwesterly, backing southerly, 5 or 6. Moderate or rough. Occasional rain. Good, occasionally poor.
On 2017-12-15 01:37, Steve Summit wrote:
There's code in tzcode (and, I suspect not coincidentally, glibc's localtime implementation as well) to not call the internal version of tzset on calls to localtime_r, but rather only on calls to localtime. The justification is that Posix says localtime_r doesn't need to update the global tzname variable.
I don't have a copy of the Posix spec handy, so I can't check: does Posix also say that localtime_r doesn't need to check for and make use of a possibly-changed value of TZ? As implemented (both in tzcode and glibc), it doesn't. If a program calls localtime, then calls setenv to set a new value of TZ, then calls localtime again, it works as expected, but if it calls localtime_r, setenv, localtime_r, it doesn't. (The workaround, of course, is to call localtime_r, setenv, tzset, localtime_r.)
Looks like localtime() SHALL but localtime_r() MAY set timezone information as if by calling tzset(); from https://publications.opengroup.org/t101: localtime|_r(): "[CX] [Option Start] Local timezone information is used as though localtime() calls tzset(). ... Unlike localtime(), the localtime_r() function is not required to set tzname. If localtime_r() sets tzname, it shall also set daylight and timezone. If localtime_r() does not set tzname, it shall not set daylight and shall not set timezone. [Option End]" tzset(): "APPLICATION USAGE Since the ctime(), localtime(), mktime(), strftime(), and strftime_l() functions are required to set timezone information as if by calling tzset(), there is no need for an explicit tzset() call before using these functions. *However, portable applications should call tzset() explicitly before using ctime_r() or localtime_r() because setting timezone information is optional for those functions.*" [My *emphasis*] I no longer have any copies of ISO/IEC 9899:1999[2007] C as referenced by POSIX 2008, but 9899:2011[2012] still does not include tzset(), so all aspects of local and daylight saving time determination are still implementation defined. Normative Annex K "Bounds-checking interfaces" section K.3.8 defines a localtime_s() function which appears identical to localtime_r(), and other ..._s suffixed functions enabled by defining __STDC_LIB_EXT1__ like POSIX ..._r suffixed functions, without any additional requirements. -- Take care. Thanks, Brian Inglis, Calgary, Alberta, Canada
participants (5)
-
Brian Inglis -
Guy Harris -
Paul Eggert -
scs@eskimo.com -
Tony Finch