Bug 601 - mktime(3) unreliable after calling tzset(3) when TZ is NULL or bogus
Summary: mktime(3) unreliable after calling tzset(3) when TZ is NULL or bogus
Status: RESOLVED FIXED
Alias: None
Product: glibc
Classification: Unclassified
Component: libc (show other bugs)
Version: unspecified
: P2 normal
Target Milestone: ---
Assignee: GOTO Masanori
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2004-12-06 21:19 UTC by Richard B. Kreckel
Modified: 2015-11-20 18:31 UTC (History)
2 users (show)

See Also:
Host:
Target:
Build:
Last reconfirmed:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Richard B. Kreckel 2004-12-06 21:19:46 UTC
#include <time.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>

int main()
{
    unsigned sec;
    struct tm t;
    time_t t1, t2;
    const char* currTz;
    t.tm_year = 100; // 2000
    t.tm_mon = 0; // Jan
    t.tm_mday = 1;
    t.tm_hour = 0;
    t.tm_min = 0;
    t.tm_sec = 0;
    t.tm_isdst = 0;
    t1 = mktime( &t );
    for ( sec = 0; sec < 24*60*60; ++sec ) {
        t.tm_hour = sec / ( 60 * 60 );
        t.tm_min = ( sec / ( 60 ) ) % 60;
        t.tm_sec = sec % ( 60 );
        printf("%02d:%02d:%02d\t",t.tm_hour,t.tm_min,t.tm_sec);

        currTz = getenv( "TZ" );
        //setenv( "TZ", "#!@+*%&
NULLCannotBeUsedBecauseOfABugWhenLinkingWithLibPthreadStringCannotBeUsedEither",
1 );  // leads to errors
        setenv( "TZ", NULL, 1 );  // leads to errors
        //setenv( "TZ", "UTC", 1 );  // seems to work
        tzset();

        t2 = mktime( &t );

        if ( currTz )
            setenv( "TZ", currTz, 1 );
        else
            unsetenv( "TZ" );

        printf("%d\n",t2-t1);
        t1 = t2;
    }
}

The above program should print:
00:00:00        3600
00:00:01        1
00:00:02        1
00:00:03        1

The left column should always be the time difference to the previous line or, in
the case of the first line, the difference of the local time to UTC.  However,
it sometimes prints:
00:00:00        3601
00:00:01        0
00:00:02        1
00:00:03        1
[...]

or, even stranger things like
[...]
00:01:15        1
00:01:16        7201
00:01:17        -7199
00:01:18        1
[...]

This happens only if I set TZ to NULL or to some bogus (see code) value *inside*
the loop. However, according to tzset(3): "If the TZ variable does appear in the
environment but its value is NULL or its value cannot be interpreted using any 
of the formats specified below, Coordinated Universal Time (UTC) is used."

I have reproduced this bug with the glibc shipped with SuSE Professional Linux
8.1, SuSE Linux Linux 9.2 and Debian 3.0, so I assume it must be from upstream.
Comment 1 Jakub Jelinek 2004-12-06 22:41:15 UTC
Calling setenv with second argument NULL is a bug, just don't do that.
The man page you are citing is badly worded and inaccurate, e.g. it is not
possible to have a variable set in the environment, but its value NULL,
probably it means its value being an empty string.
For the "#!@+*%&" value you shouldn't rely on anything, certainly no standard
requires any specific behaviour and the man page is not what determines glibc
behaviour.
But I'll post a patch that will in that case make the offset 0 rather than -1L.
Comment 2 cvs-commit@gcc.gnu.org 2004-12-06 22:50:46 UTC
Subject: Bug 601

CVSROOT:	/cvs/glibc
Module name:	libc
Changes by:	drepper@sources.redhat.com	2004-12-06 22:50:41

Modified files:
	time           : tzset.c 

Log message:
	(tzset_internal): If + or - is seen, but no offset after it, reset
	offset to 0.  [BZ #601]

Patches:
http://sources.redhat.com/cgi-bin/cvsweb.cgi/libc/time/tzset.c.diff?cvsroot=glibc&r1=1.78&r2=1.79

Comment 3 Andreas Jaeger 2004-12-18 15:48:14 UTC
The issue has been resolved.
Comment 4 Elion 2015-11-20 18:31:07 UTC Comment hidden (spam)